diff --git a/README.md b/README.md
index e2b0d68..8e54d87 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,21 @@
-# Wii U GTX Extractor v2.2
-Extracts textures from the GFD ('Gfx2' / .gtx file extension) format used in Wii U games.
+# Wii U GTX Extractor v3.0
+Extracts textures from the GFD ('Gfx2' / .gtx file extension) format used in Wii U games, and save them as DDS.
-Can Also convert .png files into .gtx files!
+Can Also convert DDS files into .gtx files!
A bit of work could get it to extract .bflim files, too.
# Requirements:
Python 3.4 or higher.
-PyQt5.
-
-Compressonator.
-
cx_Freeze. (Optional)
-Pillow. (If using gtx_extract_nomipmap)
-
# Credits:
-Treeki - Original GTX Extractor
-
-AboodXD - porting GTX Extractor to python, adding new stuff.
+AboodXD - Writing this thingy.
# Special thanks to:
-Exzap - Helping with swizzling.
+Treeki - This tool was based on his tool.
+
+Exzap, AddrLib - Helping with swizzling.
-RoadrunnerWMC - Helping with implementing the "creating .gtx files" feature.
\ No newline at end of file
+RoadrunnerWMC - The writeGFD() function was based on a function in his program, PuzzleHD.
\ No newline at end of file
diff --git a/build.py b/build.py
index f68abbf..04cfda5 100644
--- a/build.py
+++ b/build.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# GTX Extractor
-# Version v2.2.1
+# Version v3.0
# Copyright © 2014 Treeki, 2015-2016 AboodXD
# This file is part of GTX Extractor.
@@ -24,7 +24,7 @@
import os, shutil, sys
from cx_Freeze import setup, Executable
-version = 'v2.2.1'
+version = 'v3.0'
# Pick a build directory
dir_ = 'gtx_extract ' + version
@@ -39,19 +39,12 @@
os.makedirs(dir_)
print('>> Directory ready!')
-# exclude QtWebKit to save space, plus Python stuff we don't use
-excludes = ['doctest', 'pdb', 'unittest', 'difflib', 'inspect',
- 'os2emxpath', 'posixpath', 'optpath', 'locale', 'calendar',
- 'select', 'multiprocessing', 'ssl',
- 'PyQt5.QtWebKit', 'PyQt5.QtNetwork']
-
setup(
name = 'gtx_extract',
version = version,
description = 'Wii U GFD (GTX) extractor',
options={
'build_exe': {
- 'excludes': excludes,
'build_exe': dir_,
},
},
@@ -67,4 +60,4 @@
shutil.copy('README.md', dir_)
print('>> Files copied!')
-print('>> GTX Extractor has been frozen to %s !' % dir_)
+print('>> GTX Extractor has been frozen to %s!' % dir_)
diff --git a/build_nomipmap.py b/build_nomipmap.py
deleted file mode 100644
index 0ce3a1a..0000000
--- a/build_nomipmap.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python
-
-# GTX Extractor
-# Version v2.2.1
-# Copyright © 2014 Treeki, 2015-2016 AboodXD
-
-# This file is part of GTX Extractor.
-
-# GTX Extractor is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# GTX Extractor is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""build.py: Build an executable for GTX Extractor."""
-
-import os, shutil, sys
-from cx_Freeze import setup, Executable
-
-version = 'v2.2.1'
-
-# Pick a build directory
-dir_ = 'gtx_extract_nomipmap ' + version
-
-# Add the "build" parameter to the system argument list
-if 'build' not in sys.argv:
- sys.argv.append('build')
-
-# Clear the directory
-print('>> Clearing/creating directory...')
-if os.path.isdir(dir_): shutil.rmtree(dir_)
-os.makedirs(dir_)
-print('>> Directory ready!')
-
-# exclude QtWebKit to save space, plus Python stuff we don't use
-excludes = ['doctest', 'pdb', 'unittest', 'difflib', 'inspect',
- 'os2emxpath', 'posixpath', 'optpath', 'locale', 'calendar',
- 'select', 'multiprocessing', 'ssl',
- 'PyQt5.QtWebKit', 'PyQt5.QtNetwork']
-
-setup(
- name = 'gtx_extract_nomipmap',
- version = version,
- description = 'Wii U GFD (GTX) extractor',
- options={
- 'build_exe': {
- 'excludes': excludes,
- 'build_exe': dir_,
- },
- },
- executables = [
- Executable(
- 'gtx_extract_nomipmap.py',
- ),
- ],
- )
-
-print('>> Attempting to copy required files...')
-shutil.copy('COPYING', dir_)
-shutil.copy('README.md', dir_)
-print('>> Files copied!')
-
-print('>> GTX Extractor has been frozen to %s !' % dir_)
diff --git a/gtx_extract.py b/gtx_extract.py
index c41fc91..5bb6543 100644
--- a/gtx_extract.py
+++ b/gtx_extract.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# GTX Extractor
-# Version v2.2.1
+# Version v3.0
# Copyright © 2014 Treeki, 2015-2016 AboodXD
# This file is part of GTX Extractor.
@@ -19,16 +19,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""gtx_extract.py: Decode GFD (GTX/GSH) images."""
+"""gtx_extract.py: Decode GFD (GTX) images."""
import os, struct, sys, time
-from PyQt5 import QtCore, QtGui
-Qt = QtCore.Qt
-
__author__ = "AboodXD"
__copyright__ = "Copyright 2014 Treeki, 2015-2016 AboodXD"
-__credits__ = ["AboodXD", "libtxc_dxtn", "Treeki",
+__credits__ = ["AboodXD", "Treeki", "AddrLib",
"Exzap", "RoadrunnerWMC"]
formats = {0x00000000: 'GX2_SURFACE_FORMAT_INVALID',
@@ -42,227 +39,17 @@
0x0000000a: 'GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM',
0x0000000b: 'GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM',
0x00000001: 'GX2_SURFACE_FORMAT_TC_R8_UNORM',
+ 0x00000007: 'GX2_SURFACE_FORMAT_TC_R8_G8_UNORM',
0x00000031: 'GX2_SURFACE_FORMAT_T_BC1_UNORM',
0x00000431: 'GX2_SURFACE_FORMAT_T_BC1_SRGB',
0x00000032: 'GX2_SURFACE_FORMAT_T_BC2_UNORM',
0x00000432: 'GX2_SURFACE_FORMAT_T_BC2_SRGB',
0x00000033: 'GX2_SURFACE_FORMAT_T_BC3_UNORM',
- 0x00000433: 'GX2_SURFACE_FORMAT_T_BC3_SRGB'
+ 0x00000433: 'GX2_SURFACE_FORMAT_T_BC3_SRGB',
+ 0x00000034: 'GX2_SURFACE_FORMAT_T_BC4_UNORM',
+ 0x00000035: 'GX2_SURFACE_FORMAT_T_BC5_UNORM'
}
-m_banks = 4
-m_banksBitcount = 2
-m_pipes = 2
-m_pipesBitcount = 1
-m_pipeInterleaveBytes = 256
-m_pipeInterleaveBytesBitcount = 8
-m_rowSize = 2048
-m_swapSize = 256
-m_splitSize = 2048
-
-m_chipFamily = 2
-
-# ----------\/-Start of libtxc_dxtn section-\/---------- #
-def EXP5TO8R(packedcol):
- return int((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x07))
-
-def EXP6TO8G(packedcol):
- return int((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x03))
-
-def EXP5TO8B(packedcol):
- return int((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x07))
-
-def EXP4TO8(col):
- return int((col) | ((col) << 4))
-
-# inefficient. To be efficient, it would be necessary to decode 16 pixels at once
-
-def dxt135_decode_imageblock(pixdata, img_block_src, i, j, dxt_type):
- color0 = pixdata[img_block_src] | (pixdata[img_block_src + 1] << 8)
- color1 = pixdata[img_block_src + 2] | (pixdata[img_block_src + 3] << 8)
- bits = pixdata[img_block_src + 4] | (pixdata[img_block_src + 5] << 8) | (pixdata[img_block_src + 6] << 16) | (pixdata[img_block_src + 7] << 24)
- # What about big/little endian?
- bit_pos = 2 * (j * 4 + i)
- code = (bits >> bit_pos) & 3
-
- ACOMP = 255
- if code == 0:
- RCOMP = EXP5TO8R(color0)
- GCOMP = EXP6TO8G(color0)
- BCOMP = EXP5TO8B(color0)
- elif code == 1:
- RCOMP = EXP5TO8R(color1)
- GCOMP = EXP6TO8G(color1)
- BCOMP = EXP5TO8B(color1)
- elif code == 2:
- if (dxt_type > 1) or (color0 > color1):
- RCOMP = ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) // 3)
- GCOMP = ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) // 3)
- BCOMP = ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) // 3)
- else:
- RCOMP = ((EXP5TO8R(color0) + EXP5TO8R(color1)) // 2)
- GCOMP = ((EXP6TO8G(color0) + EXP6TO8G(color1)) // 2)
- BCOMP = ((EXP5TO8B(color0) + EXP5TO8B(color1)) // 2)
- elif code == 3:
- if (dxt_type > 1) or (color0 > color1):
- RCOMP = ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) // 3)
- GCOMP = ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) // 3)
- BCOMP = ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) // 3)
- else:
- RCOMP = 0
- GCOMP = 0
- BCOMP = 0
- if dxt_type == 1: ACOMP = 0
- else:
- # CANNOT happen (I hope)
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- return ACOMP, RCOMP, GCOMP, BCOMP
-
-def fetch_2d_texel_rgb_dxt1(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 8
- test = pixdata[blksrc]
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc, i & 3, j & 3, 0)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt1(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 8
- test = pixdata[blksrc]
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc, i & 3, j & 3, 1)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt3(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 16
- anibble = (pixdata[blksrc + ((j & 3) * 4 + (i & 3)) // 2] >> (4 * (i & 1))) & 0x0f
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc + 8, i & 3, j & 3, 2)
- ACOMP = EXP4TO8(anibble)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt5(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 16
- alpha0 = pixdata[blksrc]
- alpha1 = pixdata[blksrc + 1]
- # TODO test this!
- bit_pos = ((j & 3) * 4 + (i & 3)) * 3
- acodelow = pixdata[blksrc + 2 + bit_pos // 8]
- acodehigh = pixdata[blksrc + 3 + bit_pos // 8]
- code = (acodelow >> (bit_pos & 0x07) |
- (acodehigh << (8 - (bit_pos & 0x07)))) & 0x07
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc + 8, i & 3, j & 3, 2)
-
- if code == 0:
- ACOMP = alpha0
- elif code == 1:
- ACOMP = alpha1
- elif alpha0 > alpha1:
- ACOMP = (alpha0 * (8 - code) + (alpha1 * (code - 1))) // 7
- elif code < 6:
- ACOMP = (alpha0 * (6 - code) + (alpha1 * (code - 1))) // 5
- elif code == 6:
- ACOMP = 0
- else:
- ACOMP = 255
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt(data, width, height, format_):
-
- """
- Does the decompression for DXT compressed images.
- """
-
- output = bytearray(width * height * 4)
-
- for y in range(height):
- for x in range(width):
- if (format_ == 0x31 or format_ == 0x431):
- try:
- outValue = fetch_2d_texel_rgba_dxt1(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- except:
- outValue = fetch_2d_texel_rgb_dxt1(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- elif (format_ == 0x32 or format_ == 0x432):
- outValue = fetch_2d_texel_rgba_dxt3(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- elif (format_ == 0x33 or format_ == 0x433):
- outValue = fetch_2d_texel_rgba_dxt5(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
-
- return output
-
# ----------\/-Start of GFD Extracting section-\/------------- #
class GFDData():
width, height = 0, 0
@@ -379,7 +166,7 @@ def readGFD(f):
return gfd
-def writePNG(gfd):
+def get_deswizzled_data(gfd):
if gfd.format in formats:
if gfd.depth != 1:
raise NotImplementedError("Unsupported depth!")
@@ -387,17 +174,46 @@ def writePNG(gfd):
raise ValueError("Invalid texture format!")
else:
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- result = swizzle(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data)
-
- img = QtGui.QImage(result, gfd.width, gfd.height, QtGui.QImage.Format_RGBA8888)
+ if gfd.format == 0x823:
+ format_ = 116
+ elif gfd.format == 0x1f:
+ format_ = 36
+ elif gfd.format == 0x820:
+ format_ = 113
+ elif (gfd.format == 0x1a or gfd.format == 0x41a):
+ format_ = 32
+ elif gfd.format == 0x19:
+ format_ = 31
+ elif gfd.format == 0x8:
+ format_ = 23
+ elif gfd.format == 0xa:
+ format_ = 25
+ elif gfd.format == 0xb:
+ format_ = 26
+ elif gfd.format == 0x1:
+ format_ = 50
+ elif gfd.format == 0x7:
+ format_ = 51
+ elif (gfd.format == 0x31 or gfd.format == 0x431):
+ format_ = "BC1"
+ elif (gfd.format == 0x32 or gfd.format == 0x432):
+ format_ = "BC2"
+ elif (gfd.format == 0x33 or gfd.format == 0x433):
+ format_ = "BC3"
+ elif gfd.format == 0x34:
+ format_ = "BC4"
+ elif gfd.format == 0x35:
+ format_ = "BC5"
+
+ if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433 and gfd.format != 0x34 and gfd.format != 0x35):
+ result = swizzle(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data, gfd.dataSize)
+
+ hdr = writeHeader(1, gfd.width, gfd.height, format_, compressed=False)
else:
- result = swizzle_BC(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data)
-
- output = fetch_2d_texel_rgba_dxt(result, gfd.width, gfd.height, gfd.format)
+ result = swizzle_BC(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data, gfd.dataSize)
- img = QtGui.QImage(output, gfd.width, gfd.height, QtGui.QImage.Format_RGBA8888)
+ hdr = writeHeader(1, gfd.width, gfd.height, format_, compressed=True)
else:
print("")
@@ -406,59 +222,13 @@ def writePNG(gfd):
time.sleep(5)
sys.exit(1)
- yield img.copy(0, 0, gfd.width, gfd.height)
-
-def writeGFD(gfd, f):
- # Thanks RoadrunnerWMC
- mipmaps = []
- for i in range(gfd.numMips):
- mipmaps.append(QtGui.QImage(sys.argv[1]).scaledToWidth(gfd.width >> i, Qt.SmoothTransformation))
+ return hdr, result
+def writeGFD(gfd, f, f1):
+ # Well, let's credit RoadrunnerWMC anyway :P
if gfd.format in formats:
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- data = []
- for mip in mipmaps:
- ptr = mip.bits()
- ptr.setsize(mip.byteCount())
- data.append(ptr.asstring())
- else:
- if not os.path.isdir('DDSConv'):
- os.makedirs('DDSConv')
-
- for i, tex in enumerate(mipmaps):
- tex.save('DDSConv/mipmap_%d.png' % i)
-
- import struct
-
- ddsmipmaps = []
- for i in range(gfd.numMips):
- if (gfd.format == 0x31 or gfd.format == 0x431):
- if (struct.calcsize("P") * 8) == 32:
- os.system(('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC1 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
- elif (struct.calcsize("P") * 8) == 64:
- os.system(('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC1 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
- elif (gfd.format == 0x32 or gfd.format == 0x432):
- if (struct.calcsize("P") * 8) == 32:
- os.system(('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC2 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
- elif (struct.calcsize("P") * 8) == 64:
- os.system(('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC2 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
- elif (gfd.format == 0x33 or gfd.format == 0x433):
- if (struct.calcsize("P") * 8) == 32:
- os.system(('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC3 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
- elif (struct.calcsize("P") * 8) == 64:
- os.system(('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC3 -nomipmap DDSConv/mipmap_%d.png' % i) + (' DDSConv/mipmap_%d.dds' % i))
-
- with open('DDSConv/mipmap_%d.dds' % i, 'rb') as f1:
- ddsmipmaps.append(f1.read())
- f1.close()
-
- data = []
- for mip in ddsmipmaps:
- data.append(mip[0x80:])
-
- for filename in os.listdir('DDSConv'):
- os.remove(os.path.join('DDSConv', filename))
- import shutil; shutil.rmtree('DDSConv')
+ data = f1[0x80:0x80 + gfd.dataSize]
+
else:
print("")
print("Unsupported texture format: " + hex(gfd.format))
@@ -467,23 +237,14 @@ def writeGFD(gfd, f):
sys.exit(1)
swizzled_data = []
- for i, data in enumerate(data):
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- result = swizzle(gfd.width >> i, gfd.height >> i, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, True)
- swizzled_data.append(result[:(gfd.width >> i) * (gfd.height >> i) * 4])
- else:
- result = swizzle_BC(gfd.width >> i, gfd.height >> i, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, True)
- swizzled_data.append(result[:(gfd.width >> i) * (gfd.height >> i)])
-
- # Put the smaller swizzled mips together.
- swizzled_mips = b''
- for mip in swizzled_data[1:]:
- swizzled_mips += mip
- correctLen = gfd.mipSize
- swizzled_mips += b'\0' * (correctLen - len(swizzled_mips))
- assert len(swizzled_mips) == correctLen
-
- # Put it together into a proper GTX.
+ if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433 and gfd.format != 0x34 and gfd.format != 0x35):
+ result = swizzle(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, gfd.dataSize, True)
+ else:
+ result = swizzle_BC(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, gfd.dataSize, True)
+
+ swizzled_data.append(result[:gfd.dataSize])
+
+ # Put it together into a proper .gtx file.
pos = 0
header = GFDHeader()
@@ -493,18 +254,16 @@ def writeGFD(gfd, f):
real = False
- while pos < len(f): # Loop through the entire file, stop if reached the end of the file.
+ while pos < len(f): # Loop through the entire file.
block = GFDBlockHeader()
block.data(f, pos)
pos += block.size
if block.type_ == 0x0B:
- surface = GFDSurface()
- surface.data(f, pos)
+ offset = pos
- pos += surface.size
- pos += (23 * 4)
+ pos += block.dataSize
elif block.type_ == 0x0C:
head1 = f[:pos] # it works :P
@@ -524,26 +283,15 @@ def writeGFD(gfd, f):
time.sleep(5)
sys.exit(1)
- if struct.unpack(">I", f[(len(head1) + gfd.dataSize + 0x10):(len(head1) + gfd.dataSize + 0x14)])[0] == 0x02:
- pad = struct.unpack(">I", f[(len(head1) + gfd.dataSize + 0x14):(len(head1) + gfd.dataSize + 0x18)])[0]
- head2 = f[(len(head1) + gfd.dataSize):(len(head1) + gfd.dataSize + 0x20 + pad + 0x20)]
- head3 = f[(len(head1) + gfd.dataSize + 0x20 + pad + 0x20 + gfd.mipSize):(len(head1) + gfd.dataSize + 0x20 + pad + 0x20 + gfd.mipSize + 0x20)]
- return head1 + swizzled_data[0] + head2 + swizzled_mips + head3
-
- elif struct.unpack(">I", f[(len(head1) + gfd.dataSize + 0x10):(len(head1) + gfd.dataSize + 0x14)])[0] == 0x01:
- head2 = f[(len(head1) + gfd.dataSize):(len(head1) + gfd.dataSize + 0x20)]
- return head1 + swizzled_data[0] + head2
-
- else:
- print("")
- print("Bad .gtx file!")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
+ head1 = bytearray(head1)
+ head1[offset + 0x10:offset + 0x14] = bytes(bytearray.fromhex("00000001"))
+ head1[offset + 0x28:offset + 0x2C] = bytes(bytearray.fromhex("00000000"))
+ head2 = bytes(bytearray.fromhex("424C4B7B00000020000000010000000000000001000000000000000000000000"))
+ return bytes(head1) + swizzled_data[0] + head2
# ----------\/-Start of the swizzling section-\/---------- #
-def swizzle(width, height, depth, format_, tileMode, swizzle, pitch, data, toGFD=False):
- result = bytearray(width * height * 4)
+def swizzle(width, height, depth, format_, tileMode, swizzle, pitch, data, dataSize, toGFD=False):
+ result = bytearray(dataSize)
for y in range(height):
for x in range(width):
@@ -562,14 +310,14 @@ def swizzle(width, height, depth, format_, tileMode, swizzle, pitch, data, toGFD
pos_ = (y * width + x) * 4
if toGFD:
- result[pos:pos + 4] = swapRB(data[pos_:pos_ + 4])
+ result[pos:pos + 4] = data[pos_:pos_ + 4]
else:
result[pos_:pos_ + 4] = data[pos:pos + 4]
return result
-def swizzle_BC(width, height, depth, format_, tileMode, swizzle, pitch, data, toGFD=False):
- result = bytearray(width * height)
+def swizzle_BC(width, height, depth, format_, tileMode, swizzle, pitch, data, dataSize, toGFD=False):
+ result = bytearray(dataSize)
width = width // 4
height = height // 4
@@ -608,6 +356,18 @@ def swizzle_BC(width, height, depth, format_, tileMode, swizzle, pitch, data, to
# I'd like to give a huge thanks to Exzap for this,
# Thanks Exzap!
+m_banks = 4
+m_banksBitcount = 2
+m_pipes = 2
+m_pipesBitcount = 1
+m_pipeInterleaveBytes = 256
+m_pipeInterleaveBytesBitcount = 8
+m_rowSize = 2048
+m_swapSize = 256
+m_splitSize = 2048
+
+m_chipFamily = 2
+
formatHwInfo = b"\x00\x00\x00\x01\x08\x03\x00\x01\x08\x01\x00\x01\x00\x00\x00\x01" \
b"\x00\x00\x00\x01\x10\x07\x00\x00\x10\x03\x00\x01\x10\x03\x00\x01" \
b"\x10\x0B\x00\x01\x10\x01\x00\x01\x10\x03\x00\x01\x10\x03\x00\x01" \
@@ -817,7 +577,7 @@ def computeSurfaceBankSwappedWidth(tileMode, bpp, numSamples, pitch, pSlicesPerT
else:
v8 = swapMax
bankSwapWidth = v8
- while bankSwapWidth >= (2 * pitch): # Let's wish this works :P
+ while (bankSwapWidth > (2 * pitch) or bankSwapWidth == (2 * pitch)): # Let's wish this works :P
bankSwapWidth >>= 1
return bankSwapWidth
@@ -1064,11 +824,146 @@ def AddrLib_computeCmaskInfo(pitchIn, heightIn, numSlices, isLinear, pTileInfo,
pBlockMax = blockMax
return returnCode
+# ----------\/-Start of DDS writer section-\/---------- #
+def writeHeader(num_mipmaps, w, h, format_, compressed=False):
+ hdr = bytearray(128)
+
+ if format_ == 116:
+ fourcc = 116 .to_bytes(4, 'little')
+ elif format_ == 36:
+ fourcc = 36 .to_bytes(4, 'little')
+ elif format_ == 113:
+ fourcc = 113 .to_bytes(4, 'little')
+ elif format_ == 32:
+ fmtbpp = 4
+ has_alpha = 1
+ rmask = 0x000000ff
+ gmask = 0x0000ff00
+ bmask = 0x00ff0000
+ amask = 0xff000000
+ elif format_ == 31:
+ fmtbpp = 4
+ has_alpha = 1
+ rmask = 0x000003ff
+ gmask = 0x000ffc00
+ bmask = 0x3ff00000
+ amask = 0xc0000000
+ elif format_ == 23:
+ fmtbpp = 2
+ has_alpha = 0
+ rmask = 0x0000f800
+ gmask = 0x000007e0
+ bmask = 0x0000001f
+ amask = 0x00000000
+ elif format_ == 25:
+ fmtbpp = 2
+ has_alpha = 1
+ rmask = 0x00007c00
+ gmask = 0x000003e0
+ bmask = 0x0000001f
+ amask = 0x00008000
+ elif format_ == 26:
+ fmtbpp = 2
+ has_alpha = 1
+ rmask = 0x00000f00
+ gmask = 0x000000f0
+ bmask = 0x0000000f
+ amask = 0x0000f000
+ elif format_ == 50:
+ fmtbpp = 1
+ has_alpha = 0
+ rmask = 0x000000ff
+ gmask = 0x000000ff
+ bmask = 0x000000ff
+ amask = 0x00000000
+ elif format_ == 51:
+ fmtbpp = 2
+ has_alpha = 1
+ rmask = 0x000000ff
+ gmask = 0x000000ff
+ bmask = 0x000000ff
+ amask = 0x00000000
+
+ hdr[:4] = b'DDS '
+ hdr[4:4+4] = 124 .to_bytes(4, 'little')
+ hdr[12:12+4] = h.to_bytes(4, 'little')
+ hdr[16:16+4] = w.to_bytes(4, 'little')
+ hdr[76:76+4] = 32 .to_bytes(4, 'little')
+
+ if not compressed:
+ hdr[88:88+4] = (fmtbpp << 3).to_bytes(4, 'little')
+ hdr[92:92+4] = rmask.to_bytes(4, 'little')
+ hdr[96:96+4] = gmask.to_bytes(4, 'little')
+ hdr[100:100+4] = bmask.to_bytes(4, 'little')
+ hdr[104:104+4] = amask.to_bytes(4, 'little')
+
+ flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002)
+
+ caps = (0x00001000)
+
+ if num_mipmaps != 1:
+ flags |= (0x00020000)
+ caps |= ((0x00000008) | (0x00400000))
+ elif num_mipmaps == 0: # This shouldn't be happening... :/
+ num_mipmaps = 1
+
+ hdr[28:28+4] = num_mipmaps.to_bytes(4, 'little')
+ hdr[108:108+4] = caps.to_bytes(4, 'little')
+
+ if not compressed:
+ flags |= (0x00000008)
+
+ if format_ == 28:
+ pflags = (0x00000002)
+ elif (format_ == 36 or format_ == 113 or format_ == 116):
+ pflags = (0x00000004)
+ else:
+ if (((fmtbpp == 1) or (format_ == 51)) and (format_ != 27)):
+ pflags = (0x00020000)
+ else:
+ pflags = (0x00000040)
+
+ if has_alpha != 0:
+ pflags |= (0x00000001)
+
+ hdr[8:8+4] = flags.to_bytes(4, 'little')
+ hdr[20:20+4] = (w * fmtbpp).to_bytes(4, 'little') # pitch
+ hdr[80:80+4] = pflags.to_bytes(4, 'little')
+
+ else:
+ flags |= (0x00080000)
+ pflags = (0x00000004)
+
+ if format_ == "BC1":
+ fourcc = b'DXT1'
+ elif format_ == "BC2":
+ fourcc = b'DXT3'
+ elif format_ == "BC3":
+ fourcc = b'DXT5'
+ elif format_ == "BC4":
+ fourcc = b'ATI1'
+ elif format_ == "BC5":
+ fourcc = b'ATI2'
+
+ hdr[8:8+4] = flags.to_bytes(4, 'little')
+ hdr[80:80+4] = pflags.to_bytes(4, 'little')
+ hdr[84:84+4] = fourcc
+
+ size = ((w + 3) >> 2) * ((h + 3) >> 2)
+ if (format_ == "BC1" or format_ == "BC4"):
+ size *= 8
+ else:
+ size *= 16
+
+ hdr[20:20+4] = size.to_bytes(4, 'little') # linear size
+
+ return hdr
+
def main():
"""
This place is a mess...
"""
- print("GTX Extractor v2.2.1")
+ print("GTX Extractor v3.0")
print("(C) 2014 Treeki, 2015-2016 AboodXD")
if len(sys.argv) != 2:
@@ -1088,11 +983,14 @@ def main():
print('Converting: ' + sys.argv[1])
inb = inf.read()
inf.close()
- elif sys.argv[1].endswith('.png'):
+ elif sys.argv[1].endswith('.dds'):
with open(sys.argv[2], "rb") as inf:
- print('Converting: ' + sys.argv[1])
- inb = inf.read()
- inf.close()
+ with open(sys.argv[1], "rb") as img:
+ print('Converting: ' + sys.argv[1])
+ inb = inf.read()
+ img1 = img.read()
+ inf.close()
+ img.close()
data = readGFD(inb)
@@ -1119,17 +1017,21 @@ def main():
name = os.path.splitext(sys.argv[1])[0]
if sys.argv[1].endswith('.gtx'):
- for img in writePNG(data):
- img.save(name + ".png")
- print('')
- print('Finished converting: ' + sys.argv[1])
+ hdr, data = get_deswizzled_data(data)
+
+ output = open(name + '.dds', 'wb+')
+ output.write(hdr)
+ output.write(data)
+ output.close()
+ print('')
+ print('Finished converting: ' + sys.argv[1])
- elif sys.argv[1].endswith('.png'):
+ elif sys.argv[1].endswith('.dds'):
if os.path.isfile(name + ".gtx"):
output = open(name + "2.gtx", 'wb+')
else:
output = open(name + ".gtx", 'wb+')
- output.write(writeGFD(data, inb))
+ output.write(writeGFD(data, inb, img1))
output.close()
print('')
print('Finished converting: ' + sys.argv[1])
diff --git a/gtx_extract_nomipmap.py b/gtx_extract_nomipmap.py
deleted file mode 100644
index bf6a535..0000000
--- a/gtx_extract_nomipmap.py
+++ /dev/null
@@ -1,1107 +0,0 @@
-#!/usr/bin/env python
-
-# GTX Extractor
-# Version v2.2.1
-# Copyright © 2014 Treeki, 2015-2016 AboodXD
-
-# This file is part of GTX Extractor.
-
-# GTX Extractor is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# GTX Extractor is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-"""gtx_extract.py: Decode GFD (GTX/GSH) images."""
-
-import os, struct, sys, time
-
-from PyQt5 import QtCore, QtGui
-Qt = QtCore.Qt
-
-from PIL import Image
-
-__author__ = "AboodXD"
-__copyright__ = "Copyright 2014 Treeki, 2015-2016 AboodXD"
-__credits__ = ["AboodXD", "libtxc_dxtn", "Treeki",
- "Exzap", "RoadrunnerWMC"]
-
-formats = {0x00000000: 'GX2_SURFACE_FORMAT_INVALID',
- 0x00000823: 'GX2_SURFACE_FORMAT_TC_R32_G32_B32_A32_FLOAT',
- 0x0000001f: 'GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_UNORM',
- 0x00000820: 'GX2_SURFACE_FORMAT_TC_R16_G16_B16_A16_FLOAT',
- 0x0000001a: 'GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM',
- 0x0000041a: 'GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB',
- 0x00000019: 'GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM',
- 0x00000008: 'GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM',
- 0x0000000a: 'GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM',
- 0x0000000b: 'GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM',
- 0x00000001: 'GX2_SURFACE_FORMAT_TC_R8_UNORM',
- 0x00000031: 'GX2_SURFACE_FORMAT_T_BC1_UNORM',
- 0x00000431: 'GX2_SURFACE_FORMAT_T_BC1_SRGB',
- 0x00000032: 'GX2_SURFACE_FORMAT_T_BC2_UNORM',
- 0x00000432: 'GX2_SURFACE_FORMAT_T_BC2_SRGB',
- 0x00000033: 'GX2_SURFACE_FORMAT_T_BC3_UNORM',
- 0x00000433: 'GX2_SURFACE_FORMAT_T_BC3_SRGB'
- }
-
-m_banks = 4
-m_banksBitcount = 2
-m_pipes = 2
-m_pipesBitcount = 1
-m_pipeInterleaveBytes = 256
-m_pipeInterleaveBytesBitcount = 8
-m_rowSize = 2048
-m_swapSize = 256
-m_splitSize = 2048
-
-m_chipFamily = 2
-
-# ----------\/-Start of libtxc_dxtn section-\/---------- #
-def EXP5TO8R(packedcol):
- return int((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x07))
-
-def EXP6TO8G(packedcol):
- return int((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x03))
-
-def EXP5TO8B(packedcol):
- return int((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x07))
-
-def EXP4TO8(col):
- return int((col) | ((col) << 4))
-
-# inefficient. To be efficient, it would be necessary to decode 16 pixels at once
-
-def dxt135_decode_imageblock(pixdata, img_block_src, i, j, dxt_type):
- color0 = pixdata[img_block_src] | (pixdata[img_block_src + 1] << 8)
- color1 = pixdata[img_block_src + 2] | (pixdata[img_block_src + 3] << 8)
- bits = pixdata[img_block_src + 4] | (pixdata[img_block_src + 5] << 8) | (pixdata[img_block_src + 6] << 16) | (pixdata[img_block_src + 7] << 24)
- # What about big/little endian?
- bit_pos = 2 * (j * 4 + i)
- code = (bits >> bit_pos) & 3
-
- ACOMP = 255
- if code == 0:
- RCOMP = EXP5TO8R(color0)
- GCOMP = EXP6TO8G(color0)
- BCOMP = EXP5TO8B(color0)
- elif code == 1:
- RCOMP = EXP5TO8R(color1)
- GCOMP = EXP6TO8G(color1)
- BCOMP = EXP5TO8B(color1)
- elif code == 2:
- if (dxt_type > 1) or (color0 > color1):
- RCOMP = ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) // 3)
- GCOMP = ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) // 3)
- BCOMP = ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) // 3)
- else:
- RCOMP = ((EXP5TO8R(color0) + EXP5TO8R(color1)) // 2)
- GCOMP = ((EXP6TO8G(color0) + EXP6TO8G(color1)) // 2)
- BCOMP = ((EXP5TO8B(color0) + EXP5TO8B(color1)) // 2)
- elif code == 3:
- if (dxt_type > 1) or (color0 > color1):
- RCOMP = ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) // 3)
- GCOMP = ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) // 3)
- BCOMP = ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) // 3)
- else:
- RCOMP = 0
- GCOMP = 0
- BCOMP = 0
- if dxt_type == 1: ACOMP = 0
- else:
- # CANNOT happen (I hope)
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- return ACOMP, RCOMP, GCOMP, BCOMP
-
-def fetch_2d_texel_rgb_dxt1(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 8
- test = pixdata[blksrc]
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc, i & 3, j & 3, 0)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt1(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 8
- test = pixdata[blksrc]
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc, i & 3, j & 3, 1)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt3(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 16
- anibble = (pixdata[blksrc + ((j & 3) * 4 + (i & 3)) // 2] >> (4 * (i & 1))) & 0x0f
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc + 8, i & 3, j & 3, 2)
- ACOMP = EXP4TO8(anibble)
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt5(srcRowStride, pixdata, i, j):
-
- """
- Extract the (i,j) pixel from pixdata and return it
- in RCOMP, GCOMP, BCOMP, ACOMP.
- """
-
- try:
- blksrc = ((srcRowStride + 3) // 4 * (j // 4) + (i // 4)) * 16
- alpha0 = pixdata[blksrc]
- alpha1 = pixdata[blksrc + 1]
- # TODO test this!
- bit_pos = ((j & 3) * 4 + (i & 3)) * 3
- acodelow = pixdata[blksrc + 2 + bit_pos // 8]
- acodehigh = pixdata[blksrc + 3 + bit_pos // 8]
- code = (acodelow >> (bit_pos & 0x07) |
- (acodehigh << (8 - (bit_pos & 0x07)))) & 0x07
- ACOMP, RCOMP, GCOMP, BCOMP = dxt135_decode_imageblock(pixdata, blksrc + 8, i & 3, j & 3, 2)
-
- if code == 0:
- ACOMP = alpha0
- elif code == 1:
- ACOMP = alpha1
- elif alpha0 > alpha1:
- ACOMP = (alpha0 * (8 - code) + (alpha1 * (code - 1))) // 7
- elif code < 6:
- ACOMP = (alpha0 * (6 - code) + (alpha1 * (code - 1))) // 5
- elif code == 6:
- ACOMP = 0
- else:
- ACOMP = 255
-
- return bytes([RCOMP, GCOMP, BCOMP, ACOMP])
-
- except IndexError:
- print("")
- print("Whoops, something went wrong while decompressing...")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
-def fetch_2d_texel_rgba_dxt(data, width, height, format_, dataSize):
-
- """
- Does the decompression for DXT compressed images.
- """
-
- output = bytearray(dataSize * 4)
-
- for y in range(height):
- for x in range(width):
- if (format_ == 0x31 or format_ == 0x431):
- try:
- outValue = fetch_2d_texel_rgba_dxt1(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- except:
- outValue = fetch_2d_texel_rgb_dxt1(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- elif (format_ == 0x32 or format_ == 0x432):
- outValue = fetch_2d_texel_rgba_dxt3(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
- elif (format_ == 0x33 or format_ == 0x433):
- outValue = fetch_2d_texel_rgba_dxt5(width, data, x, y)
- pos__ = (y * width + x) * 4
- output[pos__:pos__ + 4] = outValue
-
- return output
-
-# ----------\/-Start of GFD Extracting section-\/------------- #
-class GFDData():
- width, height = 0, 0
- format = 0
- dataSize = 0
- data = b''
-
-class GFDHeader(struct.Struct):
- def __init__(self):
- super().__init__('>4s7I')
-
- def data(self, data, pos):
- (self.magic,
- self.size_,
- self.majorVersion,
- self.minorVersion,
- self.gpuVersion,
- self.alignMode,
- self.reserved1,
- self.reserved2) = self.unpack_from(data, pos)
-
-class GFDBlockHeader(struct.Struct):
- def __init__(self):
- super().__init__('>4s7I')
-
- def data(self, data, pos):
- (self.magic,
- self.size_,
- self.majorVersion,
- self.minorVersion,
- self.type_,
- self.dataSize,
- self.id,
- self.typeIdx) = self.unpack_from(data, pos)
-
-class GFDSurface(struct.Struct):
- def __init__(self):
- super().__init__('>16I')
-
- def data(self, data, pos):
- (self.dim,
- self.width,
- self.height,
- self.depth,
- self.numMips,
- self.format_,
- self.aa,
- self.use,
- self.imageSize,
- self.imagePtr,
- self.mipSize,
- self.mipPtr,
- self.tileMode,
- self.swizzle,
- self.alignment,
- self.pitch) = self.unpack_from(data, pos)
-
-def swapRB(bgra):
- return bytes((bgra[2], bgra[1], bgra[0], bgra[3]))
-
-def readGFD(f):
- gfd = GFDData()
-
- pos = 0
-
- header = GFDHeader()
- header.data(f, pos)
-
- if header.magic != b'Gfx2':
- raise ValueError("Invalid file header!")
-
- pos += header.size
-
- while pos < len(f): # Loop through the entire file, stop if reached the end of the file.
- block = GFDBlockHeader()
- block.data(f, pos)
-
- if block.magic != b'BLK{':
- raise ValueError("Invalid block header!")
-
- pos += block.size
-
- if block.type_ == 0x0B:
- surface = GFDSurface()
- surface.data(f, pos)
-
- pos += surface.size
- pos += (23 * 4)
-
- gfd.dim = surface.dim
- gfd.width = surface.width
- gfd.height = surface.height
- gfd.depth = surface.depth
- gfd.numMips = surface.numMips
- gfd.format = surface.format_
- gfd.aa = surface.aa
- gfd.use = surface.use
- gfd.imageSize = surface.imageSize
- gfd.imagePtr = surface.imagePtr
- gfd.mipSize = surface.mipSize
- gfd.mipPtr = surface.mipPtr
- gfd.tileMode = surface.tileMode
- gfd.swizzle = surface.swizzle
- gfd.alignment = surface.alignment
- gfd.pitch = surface.pitch
-
- elif block.type_ == 0x0C:
- gfd.dataSize = block.dataSize
- gfd.data = f[pos:pos + block.dataSize]
- pos += block.dataSize
-
- else:
- pos += block.dataSize
-
- return gfd
-
-def writePNG(gfd):
- if gfd.format in formats:
- if gfd.depth != 1:
- raise NotImplementedError("Unsupported depth!")
- if gfd.format == 0x00:
- raise ValueError("Invalid texture format!")
-
- else:
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- result = swizzle(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data, gfd.dataSize)
-
- img = QtGui.QImage(result, gfd.width, gfd.height, QtGui.QImage.Format_RGBA8888)
-
- else:
- result = swizzle_BC(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, gfd.data, gfd.dataSize)
-
- output = fetch_2d_texel_rgba_dxt(result, gfd.width, gfd.height, gfd.format, gfd.dataSize)
-
- img = QtGui.QImage(output, gfd.width, gfd.height, QtGui.QImage.Format_RGBA8888)
-
- else:
- print("")
- print("Unsupported texture format: " + hex(gfd.format))
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- yield img.copy(0, 0, gfd.width, gfd.height)
-
-def writeGFD(gfd, f):
- # Thanks RoadrunnerWMC
- if gfd.format in formats:
- im = Image.open(sys.argv[1])
-
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- data = im.tobytes()
- else:
- if not os.path.isdir('DDSConv'):
- os.makedirs('DDSConv')
-
- im.save("DDSConv/mipmap.png")
-
- import struct
-
- if (gfd.format == 0x31 or gfd.format == 0x431):
- if (struct.calcsize("P") * 8) == 32:
- os.system('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC1 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
- elif (struct.calcsize("P") * 8) == 64:
- os.system('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC1 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
- elif (gfd.format == 0x32 or gfd.format == 0x432):
- if (struct.calcsize("P") * 8) == 32:
- os.system('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC2 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
- elif (struct.calcsize("P") * 8) == 64:
- os.system('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC2 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
- elif (gfd.format == 0x33 or gfd.format == 0x433):
- if (struct.calcsize("P") * 8) == 32:
- os.system('C:\\"Program Files"\Compressonator\CompressonatorCLI.exe -fd BC3 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
- elif (struct.calcsize("P") * 8) == 64:
- os.system('C:\\"Program Files (x86)"\Compressonator\CompressonatorCLI.exe -fd BC3 -nomipmap DDSConv/mipmap.png DDSConv/mipmap.dds')
-
- with open('DDSConv/mipmap.dds', 'rb') as f1:
- f2 = f1.read()
-
- data = f2[0x80:]
-
- for filename in os.listdir('DDSConv'):
- os.remove(os.path.join('DDSConv', filename))
- import shutil; shutil.rmtree('DDSConv')
- else:
- print("")
- print("Unsupported texture format: " + hex(gfd.format))
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- swizzled_data = []
- if (gfd.format != 0x31 and gfd.format != 0x431 and gfd.format != 0x32 and gfd.format != 0x432 and gfd.format != 0x33 and gfd.format != 0x433):
- result = swizzle(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, gfd.dataSize, True)
- else:
- result = swizzle_BC(gfd.width, gfd.height, gfd.depth, gfd.format, gfd.tileMode, gfd.swizzle, gfd.pitch, data, gfd.dataSize, True)
-
- swizzled_data.append(result[:gfd.dataSize])
-
- # Put it together into a proper .gtx file.
- pos = 0
-
- header = GFDHeader()
- header.data(f, pos)
-
- pos += header.size
-
- real = False
-
- while pos < len(f): # Loop through the entire file.
- block = GFDBlockHeader()
- block.data(f, pos)
-
- pos += block.size
-
- if block.type_ == 0x0B:
- surface = GFDSurface()
- surface.data(f, pos)
-
- pos += surface.size
- pos += (23 * 4)
-
- elif block.type_ == 0x0C:
- head1 = f[:pos] # it works :P
- pos += block.dataSize
-
- elif block.type_ == 0x02:
- real = True
- pos += block.dataSize
-
- else:
- pos += block.dataSize
-
- if not real: # Crappy generated .gtx file :/
- print("")
- print("This program doesn't support creating a .gtx file of this type!!")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- head1 = bytearray(head1)
- head1[0x50:0x54] = bytes(bytearray.fromhex("00000001"))
- head1[0x68:0x6C] = bytes(bytearray.fromhex("00000000"))
- head2 = bytes(bytearray.fromhex("424C4B7B00000020000000010000000000000001000000000000000000000000"))
- return bytes(head1) + swizzled_data[0] + head2
-
-# ----------\/-Start of the swizzling section-\/---------- #
-def swizzle(width, height, depth, format_, tileMode, swizzle, pitch, data, dataSize, toGFD=False):
- result = bytearray(dataSize)
-
- for y in range(height):
- for x in range(width):
- bitPos = 0
- bpp = surfaceGetBitsPerPixel(format_)
- pipeSwizzle = (swizzle >> 8) & 1
- bankSwizzle = (swizzle >> 9) & 3
-
- if (tileMode == 0 or tileMode == 1):
- pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, 0, 0, bpp, pitch, height, depth, bitPos)
- elif (tileMode == 2 or tileMode == 3):
- pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, 0, bpp, pitch, height, tileMode, 0, 0, 0, bitPos)
- else:
- pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, 0, 0, bpp, pitch, height, 1*1, tileMode, 0, 0, 0, pipeSwizzle, bankSwizzle, bitPos)
-
- pos_ = (y * width + x) * 4
-
- if toGFD:
- result[pos:pos + 4] = swapRB(data[pos_:pos_ + 4])
- else:
- result[pos_:pos_ + 4] = data[pos:pos + 4]
-
- return result
-
-def swizzle_BC(width, height, depth, format_, tileMode, swizzle, pitch, data, dataSize, toGFD=False):
- result = bytearray(dataSize)
-
- width = width // 4
- height = height // 4
-
- for y in range(height):
- for x in range(width):
- bitPos = 0
- bpp = surfaceGetBitsPerPixel(format_)
- pipeSwizzle = (swizzle >> 8) & 1
- bankSwizzle = (swizzle >> 9) & 3
-
- if (tileMode == 0 or tileMode == 1):
- pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, 0, 0, bpp, pitch, height, depth, bitPos)
- elif (tileMode == 2 or tileMode == 3):
- pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, 0, bpp, pitch, height, tileMode, 0, 0, 0, bitPos)
- else:
- pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, 0, 0, bpp, pitch, height, 1, tileMode, 0, 0, 0, pipeSwizzle, bankSwizzle, bitPos)
-
- if (format_ == 0x31 or format_ == 0x431):
- pos_ = (y * width + x) * 8
-
- if toGFD:
- result[pos:pos + 8] = data[pos_:pos_ + 8]
- else:
- result[pos_:pos_ + 8] = data[pos:pos + 8]
- else:
- pos_ = (y * width + x) * 16
-
- if toGFD:
- result[pos:pos + 16] = data[pos_:pos_ + 16]
- else:
- result[pos_:pos_ + 16] = data[pos:pos + 16]
-
- return result
-
-# I'd like to give a huge thanks to Exzap for this,
-# Thanks Exzap!
-
-formatHwInfo = b"\x00\x00\x00\x01\x08\x03\x00\x01\x08\x01\x00\x01\x00\x00\x00\x01" \
- b"\x00\x00\x00\x01\x10\x07\x00\x00\x10\x03\x00\x01\x10\x03\x00\x01" \
- b"\x10\x0B\x00\x01\x10\x01\x00\x01\x10\x03\x00\x01\x10\x03\x00\x01" \
- b"\x10\x03\x00\x01\x20\x03\x00\x00\x20\x07\x00\x00\x20\x03\x00\x00" \
- b"\x20\x03\x00\x01\x20\x05\x00\x00\x00\x00\x00\x00\x20\x03\x00\x00" \
- b"\x00\x00\x00\x00\x00\x00\x00\x01\x20\x03\x00\x01\x00\x00\x00\x01" \
- b"\x00\x00\x00\x01\x20\x0B\x00\x01\x20\x0B\x00\x01\x20\x0B\x00\x01" \
- b"\x40\x05\x00\x00\x40\x03\x00\x00\x40\x03\x00\x00\x40\x03\x00\x00" \
- b"\x40\x03\x00\x01\x00\x00\x00\x00\x80\x03\x00\x00\x80\x03\x00\x00" \
- b"\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x10\x01\x00\x00" \
- b"\x10\x01\x00\x00\x20\x01\x00\x00\x20\x01\x00\x00\x20\x01\x00\x00" \
- b"\x00\x01\x00\x01\x00\x01\x00\x00\x00\x01\x00\x00\x60\x01\x00\x00" \
- b"\x60\x01\x00\x00\x40\x01\x00\x01\x80\x01\x00\x01\x80\x01\x00\x01" \
- b"\x40\x01\x00\x01\x80\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" \
- b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
- b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-
-def surfaceGetBitsPerPixel(surfaceFormat):
- hwFormat = surfaceFormat & 0x3F
- bpp = formatHwInfo[hwFormat * 4 + 0]
- return bpp
-
-def computeSurfaceThickness(tileMode):
- if (tileMode == 3 or tileMode == 7 or tileMode == 11 or tileMode == 13 or tileMode == 15):
- thickness = 4
- elif (tileMode == 16 or tileMode == 17):
- thickness = 8
- else:
- thickness = 1
- return thickness
-
-def computePixelIndexWithinMicroTile(x, y, z, bpp, tileMode, microTileType):
- pixelBit6 = 0
- pixelBit7 = 0
- pixelBit8 = 0
- thickness = computeSurfaceThickness(tileMode)
-
- if microTileType == 3:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = z & 1
- pixelBit3 = (x & 2) >> 1
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (z & 2) >> 1
- pixelBit6 = (x & 4) >> 2
- pixelBit7 = (y & 4) >> 2
- else:
- if microTileType != 0:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (y & 2) >> 1
- pixelBit4 = (x & 4) >> 2
- pixelBit5 = (y & 4) >> 2
- else:
- if bpp == 0x08:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = (x & 4) >> 2
- pixelBit3 = (y & 2) >> 1
- pixelBit4 = y & 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x10:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = (x & 4) >> 2
- pixelBit3 = y & 1
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif (bpp == 0x20 or bpp == 0x60):
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = y & 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x40:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x80:
- pixelBit0 = y & 1
- pixelBit1 = x & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- else:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = y & 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- if thickness > 1:
- pixelBit6 = z & 1
- pixelBit7 = (z & 2) >> 1
- if thickness == 8:
- pixelBit8 = (z & 4) >> 2
- return (pixelBit8 << 8) | (pixelBit7 << 7) | (pixelBit6 << 6) | 32 * pixelBit5 | 16 * pixelBit4 | 8 * pixelBit3 | 4 * pixelBit2 | pixelBit0 | 2 * pixelBit1
-
-def computePipeFromCoordWoRotation(x, y):
- # hardcoded to assume 2 pipes
- pipe = ((y >> 3) ^ (x >> 3)) & 1
- return pipe
-
-def computeBankFromCoordWoRotation(x, y):
- numPipes = m_pipes
- numBanks = m_banks
- bankOpt = 0
- if numBanks == 4:
- bankBit0 = ((y // (16 * numPipes)) ^ (x >> 3)) & 1
- if (bankOpt == 1 and numPipes == 8):
- bankBit0 ^= x // 0x20 & 1
- bank = bankBit0 | 2 * (((y // (8 * numPipes)) ^ (x >> 4)) & 1)
- elif numBanks == 8:
- bankBit0a = ((y // (32 * numPipes)) ^ (x >> 3)) & 1
- if (bankOpt == 1 and numPipes == 8):
- bankBit0a ^= x // (8 * numBanks) & 1
- bank = bankBit0a | 2 * (((y // (32 * numPipes)) ^ (y // (16 * numPipes) ^ (x >> 4))) & 1) | 4 * (((y // (8 * numPipes)) ^ (x >> 5)) & 1)
- else:
- bank = 0
-
- return bank
-
-def computeSurfaceRotationFromTileMode(tileMode):
- pipes = m_pipes
- if (tileMode == 4 or tileMode == 5 or tileMode == 6 or tileMode == 7 or tileMode == 8 or tileMode == 9 or tileMode == 10 or tileMode == 11):
- result = pipes * ((m_banks >> 1) - 1)
- elif (tileMode == 12 or tileMode == 13 or tileMode == 14 or tileMode == 15):
- if (pipes > 4 or pipes == 4):
- result = (pipes >> 1) - 1
- else:
- result = 1
- else:
- result = 0
- return result
-
-def isThickMacroTiled(tileMode):
- thickMacroTiled = 0
- if (tileMode == 7 or tileMode == 11 or tileMode == 13 or tileMode == 15):
- thickMacroTiled = 1
- else:
- thickMacroTiled = thickMacroTiled
- return thickMacroTiled
-
-def isBankSwappedTileMode(tileMode):
- bankSwapped = 0
- if (tileMode == 8 or tileMode == 9 or tileMode == 10 or tileMode == 11 or tileMode == 14 or tileMode == 15):
- bankSwapped = 1
- else:
- bankSwapped = bankSwapped
- return bankSwapped
-
-def computeMacroTileAspectRatio(tileMode):
- ratio = 1
- if (tileMode == 8 or tileMode == 12 or tileMode == 14):
- ratio = 1
- elif (tileMode == 5 or tileMode == 9):
- ratio = 2
- elif (tileMode == 6 or tileMode == 10):
- ratio = 4
- else:
- ratio = ratio
- return ratio
-
-def computeSurfaceBankSwappedWidth(tileMode, bpp, numSamples, pitch, pSlicesPerTile):
- bankSwapWidth = 0
- numBanks = m_banks
- numPipes = m_pipes
- swapSize = m_swapSize
- rowSize = m_rowSize
- splitSize = m_splitSize
- groupSize = m_pipeInterleaveBytes
- slicesPerTile = 1
- bytesPerSample = 8 * bpp & 0x1FFFFFFF
- samplesPerTile = splitSize // bytesPerSample
- if (splitSize // bytesPerSample) != 0:
- slicesPerTile = numSamples // samplesPerTile
- if not ((numSamples // samplesPerTile) != 0):
- slicesPerTile = 1
- if pSlicesPerTile != 0:
- pSlicesPerTile = slicesPerTile
- if isThickMacroTiled(tileMode) == 1:
- numSamples = 4
- bytesPerTileSlice = numSamples * bytesPerSample // slicesPerTile
- if isBankSwappedTileMode(tileMode) != 0:
- factor = computeMacroTileAspectRatio(tileMode)
- swapTiles = (swapSize >> 1) // bpp
- if swapTiles != 0:
- v9 = swapTiles
- else:
- v9 = 1
- swapWidth = v9 * 8 * numBanks
- heightBytes = numSamples * factor * numPipes * bpp // slicesPerTile
- swapMax = numPipes * numBanks * rowSize // heightBytes
- swapMin = groupSize * 8 * numBanks // bytesPerTileSlice
- if (swapMax > swapWidth or swapMax == swapWidth):
- if (swapMin < swapWidth or swapMin == swapWidth):
- v7 = swapWidth
- else:
- v7 = swapMin
- v8 = v7
- else:
- v8 = swapMax
- bankSwapWidth = v8
- while bankSwapWidth >= (2 * pitch): # Let's wish this works :P
- bankSwapWidth >>= 1
- return bankSwapWidth
-
-bankSwapOrder = bytes([0, 1, 3, 2])
-
-def AddrLib_getTileType(isDepth):
- return (1 if isDepth != 0 else 0)
-
-def AddrLib_computePixelIndexWithinMicroTile(x, y, z, bpp, tileMode, microTileType):
- pixelBit6 = 0
- pixelBit7 = 0
- pixelBit8 = 0
- thickness = computeSurfaceThickness(tileMode)
- if microTileType == 3:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = z & 1
- pixelBit3 = (x & 2) >> 1
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (z & 2) >> 1
- pixelBit6 = (x & 4) >> 2
- pixelBit7 = (y & 4) >> 2
- else:
- if microTileType != 0:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (y & 2) >> 1
- pixelBit4 = (x & 4) >> 2
- pixelBit5 = (y & 4) >> 2
- else:
- v8 = bpp - 8
- if bpp == 0x08:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = (x & 4) >> 2
- pixelBit3 = (y & 2) >> 1
- pixelBit4 = y & 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x10:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = (x & 4) >> 2
- pixelBit3 = y & 1
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif (bpp == 0x20 or bpp == 0x60):
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = y & 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x40:
- pixelBit0 = x & 1
- pixelBit1 = y & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- elif bpp == 0x80:
- pixelBit0 = y & 1
- pixelBit1 = x & 1
- pixelBit2 = (x & 2) >> 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- else:
- pixelBit0 = x & 1
- pixelBit1 = (x & 2) >> 1
- pixelBit2 = y & 1
- pixelBit3 = (x & 4) >> 2
- pixelBit4 = (y & 2) >> 1
- pixelBit5 = (y & 4) >> 2
- if thickness > 1:
- pixelBit6 = z & 1
- pixelBit7 = (z & 2) >> 1
- if thickness == 8:
- pixelBit8 = (z & 4) >> 2
- return (pixelBit8 << 8) | (pixelBit7 << 7) | (pixelBit6 << 6) | 32 * pixelBit5 | 16 * pixelBit4 | 8 * pixelBit3 | 4 * pixelBit2 | pixelBit0 | 2 * pixelBit1
-
-def AddrLib_computeSurfaceAddrFromCoordLinear(x, y, slice, sample, bpp, pitch, height, numSlices, pBitPosition):
- v9 = x + pitch * y + (slice + numSlices * sample) * height * pitch
-
- addr = v9 * bpp
-
- pBitPosition = v9 * bpp % 8
- return addr // 8
-
-def AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, slice, bpp, pitch, height, tileMode, isDepth, tileBase, compBits, pBitPosition):
- v14 = tileMode
- if tileMode == 3:
- microTileThickness = 4
- else:
- microTileThickness = 1
- microTileBytes = microTileThickness * (((bpp << 6) + 7) >> 3)
- microTilesPerRow = pitch >> 3
- microTileIndexX = x >> 3
- microTileIndexY = y >> 3
- microTileOffset = microTileThickness * (((bpp << 6) + 7) >> 3) * (x >> 3 + (pitch >> 3) * (y >> 3))
- sliceBytes = (height * pitch * microTileThickness * bpp + 7) // 8
- sliceOffset = sliceBytes * (slice // microTileThickness)
- v12 = AddrLib_getTileType(isDepth)
- pixelIndex = AddrLib_computePixelIndexWithinMicroTile(x, y, slice, bpp, tileMode, v12)
- if (compBits != 0 and compBits != bpp and isDepth!= 0):
- pixelOffset = tileBase + compBits * pixelIndex
- else:
- pixelOffset = bpp * pixelIndex
- pBitPosition = pixelOffset % 8
- pixelOffset >>= 3
- return pixelOffset + microTileOffset + sliceOffset
-
-def AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, slice, sample, bpp, pitch, height, numSamples, tileMode, isDepth, tileBase, compBits, pipeSwizzle, bankSwizzle, pBitPosition):
- # numSamples is used for AA surfaces and can be set to 1 for all others
- numPipes = m_pipes
- numBanks = m_banks
- numGroupBits = m_pipeInterleaveBytesBitcount
- numPipeBits = m_pipesBitcount
- numBankBits = m_banksBitcount
- microTileThickness = computeSurfaceThickness(tileMode)
- microTileBits = numSamples * bpp * (microTileThickness * (8*8))
- microTileBytes = microTileBits >> 3
- microTileType = (1 if isDepth != 0 else 0)
- pixelIndex = computePixelIndexWithinMicroTile(x, y, slice, bpp, tileMode, microTileType)
- if isDepth != 0:
- if (compBits != 0 and compBits != bpp):
- sampleOffset = tileBase + compBits * sample
- pixelOffset = numSamples * compBits * pixelIndex
- else:
- sampleOffset = bpp * sample
- pixelOffset = numSamples * bpp * pixelIndex
- else:
- sampleOffset = sample * (microTileBits // numSamples)
- pixelOffset = bpp * pixelIndex
- elemOffset = pixelOffset + sampleOffset
- pBitPosition = (pixelOffset + sampleOffset) % 8
- bytesPerSample = microTileBytes // numSamples
- if (numSamples <= 1 or microTileBytes <= m_splitSize):
- samplesPerSlice = numSamples
- numSampleSplits = 1
- sampleSlice = 0
- else:
- samplesPerSlice = m_splitSize // bytesPerSample
- numSampleSplits = numSamples // samplesPerSlice
- numSamples = samplesPerSlice
- tileSliceBits = microTileBits // numSampleSplits
- sampleSlice = elemOffset // (microTileBits // numSampleSplits)
- elemOffset %= microTileBits // numSampleSplits
- elemOffset >>= 3
- pipe = computePipeFromCoordWoRotation(x, y)
- bank = computeBankFromCoordWoRotation(x, y)
- bankPipe = pipe + numPipes * bank
- rotation = computeSurfaceRotationFromTileMode(tileMode)
- swizzle = pipeSwizzle + numPipes * bankSwizzle
- sliceIn = slice
- if isThickMacroTiled(tileMode) != 0:
- sliceIn >>= 2
- bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ (swizzle + sliceIn * rotation)
- bankPipe %= numPipes * numBanks
- pipe = bankPipe % numPipes
- bank = bankPipe // numPipes
- sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) // 8
- sliceOffset = sliceBytes * ((sampleSlice + numSampleSplits * slice) // microTileThickness)
- macroTilePitch = 8 * m_banks
- macroTileHeight = 8 * m_pipes
- v18 = tileMode - 5
- if (tileMode == 5 or tileMode == 9): # GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2
- macroTilePitch >>= 1
- macroTileHeight *= 2
- elif (tileMode == 6 or tileMode == 10): # GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4
- macroTilePitch >>= 2
- macroTileHeight *= 4
- macroTilesPerRow = pitch // macroTilePitch
- macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) >> 3
- macroTileIndexX = x // macroTilePitch
- macroTileIndexY = y // macroTileHeight
- macroTileOffset = (x // macroTilePitch + pitch // macroTilePitch * (y // macroTileHeight)) * macroTileBytes
- if (tileMode == 8 or tileMode == 9 or tileMode == 10 or tileMode == 11 or tileMode == 14 or tileMode == 15):
- bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, numSamples, pitch, 0)
- swapIndex = macroTilePitch * macroTileIndexX // bankSwapWidth
- if m_banks > 4:
- import pywin.debugger; pywin.debugger.brk() # todo
- bankMask = m_banks-1
- bank ^= bankSwapOrder[swapIndex & bankMask]
- p4 = (pipe << numGroupBits)
- p5 = (bank << (numPipeBits + numGroupBits))
- numSwizzleBits = (numBankBits + numPipeBits)
- ukn1 = ((macroTileOffset + sliceOffset) >> numSwizzleBits)
- ukn2 = ~((1 << numGroupBits) - 1)
- ukn3 = ((elemOffset + ukn1) & ukn2)
- groupMask = ((1 << numGroupBits) - 1)
- offset1 = (macroTileOffset + sliceOffset)
- ukn4 = (elemOffset + (offset1 >> numSwizzleBits))
-
- subOffset1 = (ukn3 << numSwizzleBits)
- subOffset2 = groupMask & ukn4
-
- return subOffset1 | subOffset2 | p4 | p5
-
-def addrLib_computeTileDataWidthAndHeight(bpp, cacheBits, pTileInfo, pMacroWidth, pMacroHeight):
- height = 1
- width = cacheBits // bpp
- pipes = m_pipes
- while (width > (pipes * 2 * height) and (width & 1) == 0):
- width >>= 1
- height *= 2
- pMacroWidth = 8 * width
- pMacroHeight = pipes * 8 * height
-
-def AddrLib_computeCmaskBytes(pitch, height, numSlices):
- return (4 * height * pitch * numSlices + 7) // 8 // 64
-
-def AddrLib__ComputeCmaskBaseAlign(pTileInfo):
- print("AddrLib__ComputeCmaskBaseAlign(): Uknown")
- v2 = 1 # uknown
- return m_pipeInterleaveBytes * v2
-
-def AddrLib_computeCmaskInfo(pitchIn, heightIn, numSlices, isLinear, pTileInfo, pPitchOut, pHeightOut, pCmaskBytes, pMacroWidth, pMacroHeight, pBaseAlign, pBlockMax):
- bpp = 4
- cacheBits = 1024
- returnCode = 0
- if isLinear != 0:
- import pywin.debugger; pywin.debugger.brk()
- else:
- addrLib_computeTileDataWidthAndHeight(bpp, cacheBits, pTileInfo, macroWidth, macroHeight)
- pPitchOut = ~(macroWidth - 1) & (pitchIn + macroWidth - 1)
- pHeightOut = ~(macroHeight - 1) & (heightIn + macroHeight - 1)
- sliceBytes = AddrLib_computeCmaskBytes(pPitchOut, pHeightOut, 1)
- baseAlign = AddrLib__ComputeCmaskBaseAlign(pTileInfo)
- while 1:
- v14 = sliceBytes % baseAlign
- if not (sliceBytes % baseAlign != 0):
- break
- pHeightOut += macroHeight
- sliceBytes = AddrLib_computeCmaskBytes(pPitchOut, pHeightOut, 1)
- surfBytes = sliceBytes * numSlices
- pCmaskBytes = surfBytes
- pMacroWidth = macroWidth
- pMacroHeight = macroHeight
- pBaseAlign = baseAlign
- slice = pHeightOut * pPitchOut
- blockMax = (slice >> 14) - 1
- # uknown part possibly missing here
- pBlockMax = blockMax
- return returnCode
-
-def main():
- """
- This place is a mess...
- """
- print("GTX Extractor v2.2.1")
- print("(C) 2014 Treeki, 2015-2016 AboodXD")
-
- if len(sys.argv) != 2:
- if len(sys.argv) != 3:
- print("")
- print("Usage (If converting from .gtx to .png, and using source code): python gtx_extract.py input")
- print("Usage (If converting from .gtx to .png, and using exe): gtx_extract.exe input")
- print("Usage (If converting from .png to .gtx, and using source code): python gtx_extract.py input(.png) input(.gtx)")
- print("Usage (If converting from .png to .gtx, and using exe): gtx_extract.exe input(.png) input(.gtx)")
- print("")
- print("Exiting in 5 seconds...")
- time.sleep(5)
- sys.exit(1)
-
- if sys.argv[1].endswith('.gtx'):
- with open(sys.argv[1], "rb") as inf:
- print('Converting: ' + sys.argv[1])
- inb = inf.read()
- inf.close()
- elif sys.argv[1].endswith('.png'):
- with open(sys.argv[2], "rb") as inf:
- print('Converting: ' + sys.argv[1])
- inb = inf.read()
- inf.close()
-
- data = readGFD(inb)
-
- print("")
- print("// ----- GX2Surface Info ----- ")
- print(" dim = " + str(data.dim))
- print(" width = " + str(data.width))
- print(" height = " + str(data.height))
- print(" depth = " + str(data.depth))
- print(" numMips = " + str(data.numMips))
- if data.format in formats:
- print(" format = " + formats[data.format])
- else:
- print(" format = " + hex(data.format))
- print(" aa = " + str(data.aa))
- print(" use = " + str(data.use))
- print(" imageSize = " + str(data.imageSize))
- print(" mipSize = " + str(data.mipSize))
- print(" tileMode = " + str(data.tileMode))
- print(" swizzle = " + str(data.swizzle) + ", " + hex(data.swizzle))
- print(" alignment = " + str(data.alignment))
- print(" pitch = " + str(data.pitch))
-
- name = os.path.splitext(sys.argv[1])[0]
-
- if sys.argv[1].endswith('.gtx'):
- for img in writePNG(data):
- img.save(name + ".png")
- print('')
- print('Finished converting: ' + sys.argv[1])
-
- elif sys.argv[1].endswith('.png'):
- if os.path.isfile(name + ".gtx"):
- output = open(name + "2.gtx", 'wb+')
- else:
- output = open(name + ".gtx", 'wb+')
- output.write(writeGFD(data, inb))
- output.close()
- print('')
- print('Finished converting: ' + sys.argv[1])
-
-if __name__ == '__main__': main()