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()