From 4247763e4b64e07c1c5de571962aec440fed1201 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Sun, 21 May 2017 18:58:16 +0400 Subject: [PATCH 01/19] Revived the project, added a new swizzling algorithm --- README.markdown | 21 +- gtx_extract.c | 784 ++++++++++++++++++++++++++++++++++++++++-------- txc_dxtn.h | 50 +++ 3 files changed, 723 insertions(+), 132 deletions(-) create mode 100644 txc_dxtn.h diff --git a/README.markdown b/README.markdown index cbd591d..1e73923 100644 --- a/README.markdown +++ b/README.markdown @@ -9,14 +9,19 @@ And it's not Wii-specific any more. :p Wii U GTX Extractor ------------------- -Extracts textures in RGBA8 and DXT5 formats from the 'Gfx2' (.gtx file -extension) format used in Wii U games. A bit of work could get it to extract -.bflim files, too. - -Somewhat unfinished, pretty buggy. Use at your own risk. It's not great, but I -figured I'd throw it out there to save other people the work I already did. - -More details on compilation and usage in the comments inside the file. +Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format used in Wii U games, and saves them as BMP. + +Supported formats: +* RGBA8_UNORM / RGBA8_SRGB +* BC3_UNORM / BC3_SRGB (DXT5) + +Credits: +* Treeki - Original developer. +* AboodXD - Reviver, Improved swizzling. +* Exzap, AddrLib - Helping with swizzling. +* libtxc_dxtn + +More details on compilation and usage in the comments inside the file. LH Decompressor diff --git a/gtx_extract.c b/gtx_extract.c index d71ad73..4bcee95 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -2,69 +2,255 @@ * Wii U 'GTX' Texture Extractor * Created by Ninji Vahran / Treeki; 2014-10-31 * ( https://github.com/Treeki ) + * Updated by AboodXD; 2017-05-21 + * ( https://github.com/aboood40091 ) * This software is released into the public domain. * - * Dependencies: libtxc_dxtn, libpng - * Tested with GCC on Arch Linux. May fail elsewhere. - * Expect it to fail anywhere, really ;) + * Special thanks to: libtxc_dxtn developers + * Tested with TDM-GCC-64 on Windows 10 Pro x64. * - * gcc -lpng -ltxc_dxtn -o gtx_extract gtx_extract.c + * How to build: + * gcc -m64 -o gtx_extract gtx_extract.c * * This tool currently supports RGBA8 (format 0x1A) and DXT5 (format 0x33) * textures. - * The former is known to work with 2048x512 textures. - * The latter has been tested successfully with 512x320 and 2048x512 textures, - * and is known to be broken with 384x256 textures. * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' * algorithm, presumably for faster access. * - * With no publicly known details that I could find, I had to attempt to - * recreate it myself - with a limited set of sample data to examine. + * TODO: + * Add more formats (I'm lazy... :P) + * Export as DDS. * - * This tool's implementation is sufficient to unpack the textures I wanted, - * but it's likely to fail on others. * Feel free to throw a pull request at me if you improve it! */ -#include -#include +#include "txc_dxtn.h" #include #include #include #include #include -typedef struct _GTXData { - uint32_t width, height; - uint32_t format; +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define min(x, y) (((x) < (y)) ? (x) : (y)) + + +static const uint32_t formats[] = { 0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2 }; // I'll add the rest later on +static const uint32_t BCn_formats[] = { 0x31, 0x431, 0x32, 0x432, 0x33, 0x433 }; // I'll add the rest later on + + +/* Start of libtxc_dxtn section */ +#define EXP5TO8R(packedcol) \ + ((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x7)) + +#define EXP6TO8G(packedcol) \ + ((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x3)) + +#define EXP5TO8B(packedcol) \ + ((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x7)) + +#define EXP4TO8(col) \ + ((col) | ((col) << 4)) + + +/* inefficient. To be efficient, it would be necessary to decode 16 pixels at once */ + +static void dxt135_decode_imageblock ( const GLubyte *img_block_src, + GLint i, GLint j, GLuint dxt_type, GLvoid *texel ) { + GLchan *rgba = (GLchan *) texel; + const GLushort color0 = img_block_src[0] | (img_block_src[1] << 8); + const GLushort color1 = img_block_src[2] | (img_block_src[3] << 8); + const GLuint bits = img_block_src[4] | (img_block_src[5] << 8) | + (img_block_src[6] << 16) | (img_block_src[7] << 24); + /* What about big/little endian? */ + GLubyte bit_pos = 2 * (j * 4 + i) ; + GLubyte code = (GLubyte) ((bits >> bit_pos) & 3); + + rgba[ACOMP] = CHAN_MAX; + switch (code) { + case 0: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color0) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color0) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color0) ); + break; + case 1: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color1) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color1) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color1) ); + break; + case 2: + if (color0 > color1) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) / 3) ); + } + else { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1)) / 2) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1)) / 2) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1)) / 2) ); + } + break; + case 3: + if ((dxt_type > 1) || (color0 > color1)) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) / 3) ); + } + else { + rgba[RCOMP] = 0; + rgba[GCOMP] = 0; + rgba[BCOMP] = 0; + if (dxt_type == 1) rgba[ACOMP] = UBYTE_TO_CHAN(0); + } + break; + default: + /* CANNOT happen (I hope) */ + break; + } +} + + +void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) { + + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + GLchan *rgba = (GLchan *) texel; + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); + const GLubyte alpha0 = blksrc[0]; + const GLubyte alpha1 = blksrc[1]; +#if 0 + const GLubyte bit_pos = 3 * ((j&3) * 4 + (i&3)); + /* simple 32bit version */ + const GLuint bits_low = blksrc[2] | (blksrc[3] << 8) | (blksrc[4] << 16) | (blksrc[5] << 24); + const GLuint bits_high = blksrc[6] | (blksrc[7] << 8); + GLubyte code; + + if (bit_pos < 30) + code = (GLubyte) ((bits_low >> bit_pos) & 7); + else if (bit_pos == 30) + code = (GLubyte) ((bits_low >> 30) & 3) | ((bits_high << 2) & 4); + else + code = (GLubyte) ((bits_high >> (bit_pos - 32)) & 7); +#endif +#if 1 +/* TODO test this! */ + const GLubyte bit_pos = ((j&3) * 4 + (i&3)) * 3; + const GLubyte acodelow = blksrc[2 + bit_pos / 8]; + const GLubyte acodehigh = blksrc[3 + bit_pos / 8]; + const GLubyte code = (acodelow >> (bit_pos & 0x7) | + (acodehigh << (8 - (bit_pos & 0x7)))) & 0x7; +#endif + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); +#if 0 + if (alpha0 > alpha1) { + switch (code) { + case 0: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + break; + case 1: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); + break; + } + } + else { + switch (code) { + case 0: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + break; + case 1: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + break; + case 2: + case 3: + case 4: + case 5: + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); + break; + case 6: + rgba[ACOMP] = 0; + break; + case 7: + rgba[ACOMP] = CHAN_MAX; + break; + } + } +#endif +/* not sure. Which version is faster? */ +#if 1 +/* TODO test this */ + if (code == 0) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + else if (code == 1) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + else if (alpha0 > alpha1) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); + else if (code < 6) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); + else if (code == 6) + rgba[ACOMP] = 0; + else + rgba[ACOMP] = CHAN_MAX; +#endif +} + +/* Start of GTX Extractor section */ +typedef struct _GFDData { + uint32_t dim; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t numMips; + uint32_t format; + uint32_t aa; + uint32_t use; + uint32_t imageSize; + uint32_t imagePtr; + uint32_t mipSize; + uint32_t mipPtr; + uint32_t tileMode; + uint32_t swizzle; + uint32_t alignment; + uint32_t pitch; uint32_t dataSize; uint8_t *data; -} GTXData; +} GFDData; -typedef struct _GTXRawHeader { +typedef struct _GFDHeader { char magic[4]; - uint32_t _4, _8, _C, _10, _14, _18, _1C; -} GTXRawHeader; -typedef struct _GTXRawSectionHeader { + uint32_t size_, majorVersion, minorVersion; + uint32_t gpuVersion, alignMode, reserved1, reserved2; +} GFDHeader; +typedef struct _GFDBlockHeader { char magic[4]; - uint32_t _4, _8, _C, _10; - uint32_t size; - uint32_t _18, _1C; -} GTXRawSectionHeader; -typedef struct _GTXRawTextureInfo { - uint32_t _0, width, height, _C; - uint32_t _10, formatMaybe, _18, _1C; - uint32_t sizeMaybe, _24, _28, _2C; - uint32_t _30, _34, _38, _3C; + uint32_t size_, majorVersion, minorVersion, type_; + uint32_t dataSize; + uint32_t id, typeIdx; +} GFDBlockHeader; +typedef struct _GFDSurface { + uint32_t dim, width, height, depth; + uint32_t numMips, format_, aa, use; + uint32_t imageSize, imagePtr, mipSize, mipPtr; + uint32_t tileMode, swizzle, alignment, pitch; uint32_t _40, _44, _48, _4C; uint32_t _50, _54, _58, _5C; uint32_t _60, _64, _68, _6C; uint32_t _70, _74, _78, _7C; uint32_t _80, _84, _88, _8C; uint32_t _90, _94, _98; -} GTXRawTextureInfo; +} GFDSurface; uint32_t swap32(uint32_t v) { uint32_t a = (v & 0xFF000000) >> 24; @@ -117,13 +303,13 @@ void writeBMPHeader(FILE *f, int width, int height) { u32 = 0; fwrite(&u32, 1, 4, f); } -int readGTX(GTXData *gtx, FILE *f) { - GTXRawHeader header; +int readGTX(GFDData *gfd, FILE *f) { + GFDHeader header; // This is kinda bad. Don't really care right now >.> - gtx->width = 0; - gtx->height = 0; - gtx->data = NULL; + gfd->width = 0; + gfd->height = 0; + gfd->data = NULL; if (fread(&header, 1, sizeof(header), f) != sizeof(header)) return -1; @@ -132,162 +318,507 @@ int readGTX(GTXData *gtx, FILE *f) { return -2; while (!feof(f)) { - GTXRawSectionHeader section; + GFDBlockHeader section; if (fread(§ion, 1, sizeof(section), f) != sizeof(section)) break; if (memcmp(section.magic, "BLK{", 4) != 0) return -100; - if (swap32(section._10) == 0xB) { - GTXRawTextureInfo info; + if (swap32(section.type_) == 0xB) { + GFDSurface info; - if (swap32(section.size) != 0x9C) + if (swap32(section.dataSize) != 0x9C) return -200; if (fread(&info, 1, sizeof(info), f) != sizeof(info)) return -201; - gtx->width = swap32(info.width); - gtx->height = swap32(info.height); - gtx->format = swap32(info.formatMaybe); - - } else if (swap32(section._10) == 0xC && gtx->data == NULL) { - gtx->dataSize = swap32(section.size); - gtx->data = malloc(gtx->dataSize); - if (!gtx->data) + gfd->dim = swap32(info.dim); + gfd->width = swap32(info.width); + gfd->height = swap32(info.height); + gfd->depth = swap32(info.depth); + gfd->numMips = swap32(info.numMips); + gfd->format = swap32(info.format_); + gfd->aa = swap32(info.aa); + gfd->use = swap32(info.use); + gfd->imageSize = swap32(info.imageSize); + gfd->imagePtr = swap32(info.imagePtr); + gfd->mipSize = swap32(info.mipSize); + gfd->mipPtr = swap32(info.mipPtr); + gfd->tileMode = swap32(info.tileMode); + gfd->swizzle = swap32(info.swizzle); + gfd->alignment = swap32(info.alignment); + gfd->pitch = swap32(info.pitch); + + } else if (swap32(section.type_) == 0xC && gfd->data == NULL) { + gfd->dataSize = swap32(section.dataSize); + gfd->data = malloc(gfd->dataSize); + if (!gfd->data) return -300; - if (fread(gtx->data, 1, gtx->dataSize, f) != gtx->dataSize) + if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) return -301; } else { - fseek(f, swap32(section.size), SEEK_CUR); + fseek(f, swap32(section.dataSize), SEEK_CUR); } } return 1; } -void writeFile(FILE *f, int width, int height, uint8_t *output, int isPNG) { +void writeFile(FILE *f, int width, int height, uint8_t *output) { int row; - if (isPNG == 0) { - writeBMPHeader(f, width, height); + writeBMPHeader(f, width, height); - for (row = height - 1; row >= 0; row--) { - fwrite(&output[row * width * 4], 1, width * 4, f); - } - } else { - png_structp png_ptr; - png_infop info_ptr; + for (row = height - 1; row >= 0; row--) { + fwrite(&output[row * width * 4], 1, width * 4, f); + } +} - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - info_ptr = png_create_info_struct(png_ptr); +/* Start of swizzling section */ + +/* Credits: + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up +*/ + +static uint32_t m_banks = 4; +static uint32_t m_banksBitcount = 2; +static uint32_t m_pipes = 2; +static uint32_t m_pipesBitcount = 1; +static uint32_t m_pipeInterleaveBytes = 256; +static uint32_t m_pipeInterleaveBytesBitcount = 8; +static uint32_t m_rowSize = 2048; +static uint32_t m_swapSize = 256; +static uint32_t m_splitSize = 2048; + +static uint32_t m_chipFamily = 2; + +static uint32_t MicroTilePixels = 8 * 8; + +uint8_t formatHwInfo[0x40*4] = +{ + // todo: Convert to struct + // each entry is 4 bytes + 0x00,0x00,0x00,0x01,0x08,0x03,0x00,0x01,0x08,0x01,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x10,0x07,0x00,0x00,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x0B,0x00,0x01,0x10,0x01,0x00,0x01,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x03,0x00,0x01,0x20,0x03,0x00,0x00,0x20,0x07,0x00,0x00,0x20,0x03,0x00,0x00, + 0x20,0x03,0x00,0x01,0x20,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x03,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01, + 0x40,0x05,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00, + 0x40,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x03,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x10,0x01,0x00,0x00, + 0x10,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00, + 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x60,0x01,0x00,0x00, + 0x60,0x01,0x00,0x00,0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x80,0x01,0x00,0x01, + 0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) +{ + uint32_t hwFormat = surfaceFormat&0x3F; + uint32_t bpp = formatHwInfo[hwFormat*4+0]; + return bpp; +} - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - return; - } +uint32_t computeSurfaceThickness(uint32_t tileMode) +{ + uint32_t thickness = 1; - png_init_io(png_ptr, f); - png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_write_info(png_ptr, info_ptr); - png_set_bgr(png_ptr); + if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickness = 4; - for (row = 0; row < height; row++) { - png_write_row(png_ptr, &output[row * width * 4]); - } + else if (tileMode == 16 || tileMode == 17) + thickness = 8; - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - } + return thickness; +} + +uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t tileMode) +{ + uint32_t z = 0; + uint32_t thickness; + uint32_t pixelBit8; + uint32_t pixelBit7; + uint32_t pixelBit6; + uint32_t pixelBit5; + uint32_t pixelBit4; + uint32_t pixelBit3; + uint32_t pixelBit2; + uint32_t pixelBit1; + uint32_t pixelBit0; + pixelBit6 = 0; + pixelBit7 = 0; + pixelBit8 = 0; + thickness = computeSurfaceThickness(tileMode); + + 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; + } + + else if (bpp == 0x10) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x20 || bpp == 0x60) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x40) { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (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); +} + +uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { + // hardcoded to assume 2 pipes + uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; +} + +uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t bankBit0; + uint32_t bankBit0a; + uint32_t bank = 0; + + if (numBanks == 4) { + bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); + } + + else if (numBanks == 8) { + bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1); + } + + return bank; } -void export_RGBA8(GTXData *gtx, FILE *f, int isPNG) { - uint32_t pos, x, y; +uint32_t computeSurfaceRotationFromTileMode(uint32_t tileMode) { + uint32_t pipes = m_pipes; + uint32_t result = 0; + + if (tileMode == 4 || tileMode == 5 || tileMode == 6 || tileMode == 7 || tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11) + result = pipes * ((m_banks >> 1) - 1); + + else if (tileMode == 12 || tileMode == 13 || tileMode == 14 || tileMode == 15) { + if (pipes >= 4) + result = (pipes >> 1) - 1; + + else + result = 1; + } + + return result; +} + +uint32_t isThickMacroTiled(uint32_t tileMode) { + uint32_t thickMacroTiled = 0; + + if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickMacroTiled = 1; + + return thickMacroTiled; +} + +uint32_t isBankSwappedTileMode(uint32_t tileMode) { + uint32_t bankSwapped = 0; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) + bankSwapped = 1; + + return bankSwapped; +} + +uint32_t computeMacroTileAspectRatio(uint32_t tileMode) { + uint32_t ratio = 1; + + if (tileMode == 5 || tileMode == 9) + ratio = 2; + + else if (tileMode == 6 || tileMode == 10) + ratio = 4; + + return ratio; +} + +uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_t pitch) { + if (isBankSwappedTileMode(tileMode) == 0) + return 0; + + uint32_t numSamples = 1; + uint32_t numBanks = m_banks; + uint32_t numPipes = m_pipes; + uint32_t swapSize = m_swapSize; + uint32_t rowSize = m_rowSize; + uint32_t splitSize = m_splitSize; + uint32_t groupSize = m_pipeInterleaveBytes; + uint32_t bytesPerSample = 8 * bpp; + + uint32_t samplesPerTile = splitSize / bytesPerSample; + uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); + + if (isThickMacroTiled(tileMode) != 0) + numSamples = 4; + + uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + + uint32_t factor = computeMacroTileAspectRatio(tileMode); + uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); + + uint32_t swapWidth = swapTiles * 8 * numBanks; + uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; + uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + + uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); + + while (bankSwapWidth >= (2 * pitch)) + bankSwapWidth >>= 1; + + return bankSwapWidth; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height) { + uint32_t rowOffset = y * pitch; + uint32_t pixOffset = x; + + uint32_t addr = (rowOffset + pixOffset) * bpp; + addr /= 8; + + return addr; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode) { + uint64_t microTileThickness = 1; + + if (tileMode == 3) + microTileThickness = 4; + + uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; + uint64_t microTilesPerRow = pitch >> 3; + uint64_t microTileIndexX = x >> 3; + uint64_t microTileIndexY = y >> 3; + + uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t pixelOffset = bpp * pixelIndex; + + pixelOffset >>= 3; + + return pixelOffset + microTileOffset; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; + uint32_t numPipeBits = m_pipesBitcount; + uint32_t numBankBits = m_banksBitcount; + + uint32_t microTileThickness = computeSurfaceThickness(tileMode); + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t elemOffset = (bpp * pixelIndex) >> 3; + + uint64_t pipe = computePipeFromCoordWoRotation(x, y); + uint64_t bank = computeBankFromCoordWoRotation(x, y); + + uint64_t bankPipe = pipe + numPipes * bank; + uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; + + uint64_t macroTilePitch = 8 * m_banks; + uint64_t macroTileHeight = 8 * m_pipes; + + if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 + macroTilePitch >>= 1; + macroTileHeight *= 2; + } + + else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 + macroTilePitch >>= 2; + macroTileHeight *= 4; + } + + uint64_t macroTilesPerRow = pitch / macroTilePitch; + uint64_t macroTileBytes = (microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileIndexX = x / macroTilePitch; + uint64_t macroTileIndexY = y / macroTileHeight; + uint64_t macroTileOffset = macroTileBytes * (macroTileIndexX + macroTilesPerRow * macroTileIndexY); + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { + static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; + uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); + uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; + } + + uint64_t group_mask = (1 << numGroupBits) - 1; + uint64_t total_offset = elemOffset + (macroTileOffset >> (numBankBits + numPipeBits)); + + uint64_t offset_high = (total_offset & ~(group_mask)) << (numBankBits + numPipeBits); + uint64_t offset_low = total_offset & group_mask; + uint64_t bank_bits = bank << (numPipeBits + numGroupBits); + uint64_t pipe_bits = pipe << numGroupBits; + + return bank_bits | pipe_bits | offset_low | offset_high; +} + +void export_RGBA8(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y; uint32_t *source, *output; - source = (uint32_t *)gtx->data; - output = malloc(gtx->width * gtx->height * 4); + source = (uint32_t *)gfd->data; + output = malloc(gfd->width * gfd->height * 4); pos = 0; - for (y = 0; y < gtx->height; y++) { - for (x = 0; x < gtx->width; x++) { - pos = (y & ~15) * gtx->width; - pos ^= (x & 3); - pos ^= ((x >> 2) & 1) << 3; - pos ^= ((x >> 3) & 1) << 6; - pos ^= ((x >> 3) & 1) << 7; - pos ^= (x & ~0xF) << 4; - pos ^= (y & 1) << 2; - pos ^= ((y >> 1) & 7) << 4; - pos ^= (y & 0x10) << 4; - pos ^= (y & 0x20) << 2; - output[y * gtx->width + x] = swapRB(source[pos]); + for (y = 0; y < gfd->height; y++) { + for (x = 0; x < gfd->width; x++) { + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * gfd->width + x] = swapRB(source[pos / bpp]); } } - writeFile(f, gtx->width, gtx->height, (uint8_t *)output, isPNG); + writeFile(f, gfd->width, gfd->height, (uint8_t *)output); free(output); } -void export_DXT5(GTXData *gtx, FILE *f, int isPNG) { - uint32_t pos, x, y; +void export_DXT5(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y; uint32_t *output, outValue; - uint32_t blobWidth = gtx->width / 4, blobHeight = gtx->height / 4; + uint32_t blobWidth = gfd->width / 4, blobHeight = gfd->height / 4; uint8_t bits[4]; - __uint128_t *src = (__uint128_t *)gtx->data; - __uint128_t *work = malloc(gtx->width * gtx->height); + __uint128_t *src = (__uint128_t *)gfd->data; + __uint128_t *work = malloc(gfd->width * gfd->height); for (y = 0; y < blobHeight; y++) { for (x = 0; x < blobWidth; x++) { - pos = (y >> 4) * (blobWidth * 16); - pos ^= (y & 1); - pos ^= (x & 7) << 1; - pos ^= (x & 8) << 1; - pos ^= (x & 8) << 2; - pos ^= (x & 0x10) << 2; - pos ^= (x & ~0x1F) << 4; - pos ^= (y & 2) << 6; - pos ^= (y & 4) << 6; - pos ^= (y & 8) << 1; - pos ^= (y & 0x10) << 2; - pos ^= (y & 0x20); - - work[(y*blobWidth)+x] = src[pos]; + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + work[(y*blobWidth)+x] = src[pos / bpp]; } } - output = malloc(gtx->width * gtx->height * 4); + output = malloc(gfd->width * gfd->height * 4); - for (y = 0; y < gtx->height; y++) { - for (x = 0; x < gtx->width; x++) { - fetch_2d_texel_rgba_dxt5(gtx->width, (uint8_t *)work, x, y, bits); + for (y = 0; y < gfd->height; y++) { + for (x = 0; x < gfd->width; x++) { + fetch_2d_texel_rgba_dxt5(gfd->width, (uint8_t *)work, x, y, bits); outValue = (bits[ACOMP] << 24); outValue |= (bits[RCOMP] << 16); outValue |= (bits[GCOMP] << 8); outValue |= bits[BCOMP]; - output[(y * gtx->width) + x] = outValue; + output[(y * gfd->width) + x] = outValue; } } - writeFile(f, gtx->width, gtx->height, (uint8_t *)output, isPNG); + writeFile(f, gfd->width, gfd->height, (uint8_t *)output); free(output); free(work); } int main(int argc, char **argv) { - GTXData data; + GFDData data; FILE *f; int result; if (argc != 3) { - fprintf(stderr, "Usage: %s [input.gtx] [output.png]\n", argv[0]); + fprintf(stderr, "Usage: %s [input.gtx] [output.bmp]\n", argv[0]); return EXIT_FAILURE; } @@ -314,10 +845,15 @@ int main(int argc, char **argv) { data.height = (data.height + 63) & ~63; printf("Padded Width: %d - Padded Height: %d\n", data.width, data.height); - if (data.format == 0x1A) - export_RGBA8(&data, f, 1); - else if (data.format == 0x33) - export_DXT5(&data, f, 1); + /* if (data.format in formats) + export_RGB(&data, f); + else if (data.format in BCn_formats) <- doesn't even work, fixing it later + export_DXT(&data, f); + */ + if (data.format == 0x1a || data.format == 0x41a) + export_RGBA8(&data, f); + else if (data.format == 0x33 || data.format == 0x433) + export_DXT5(&data, f); fclose(f); return EXIT_SUCCESS; diff --git a/txc_dxtn.h b/txc_dxtn.h new file mode 100644 index 0000000..31d7930 --- /dev/null +++ b/txc_dxtn.h @@ -0,0 +1,50 @@ +/* + * libtxc_dxtn + * Version: 0.1 + * + * Copyright (C) 2004 Roland Scheidegger All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __APPLE__ +#include +#else +#include +#endif + +typedef GLubyte GLchan; +#define UBYTE_TO_CHAN(b) (b) +#define CHAN_MAX 255 +#define RCOMP 0 +#define GCOMP 1 +#define BCOMP 2 +#define ACOMP 3 + +void fetch_2d_texel_rgb_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt3(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); + +void tx_compress_dxtn(GLint srccomps, GLint width, GLint height, + const GLubyte *srcPixData, GLenum destformat, + GLubyte *dest, GLint dstRowStride); From 0ecb0bbadfc669396893776ff9071b67e99280e6 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Sun, 21 May 2017 19:03:35 +0400 Subject: [PATCH 02/19] add more info --- README.markdown | 4 ++++ gtx_extract.c | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 1e73923..b6b2452 100644 --- a/README.markdown +++ b/README.markdown @@ -15,6 +15,10 @@ Supported formats: * RGBA8_UNORM / RGBA8_SRGB * BC3_UNORM / BC3_SRGB (DXT5) +TODO: +* Add more formats (I'm lazy... :P) +* Export as DDS. (to add more formats, obviously) + Credits: * Treeki - Original developer. * AboodXD - Reviver, Improved swizzling. diff --git a/gtx_extract.c b/gtx_extract.c index 4bcee95..eda90b7 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -11,9 +11,10 @@ * * How to build: * gcc -m64 -o gtx_extract gtx_extract.c + * (You need an x64 version of gcc!) * - * This tool currently supports RGBA8 (format 0x1A) and DXT5 (format 0x33) - * textures. + * This tool currently supports RGBA8 (format 0x1a and 0x41a) and + * BC3/DXT5 (format 0x33 and 0x433) textures. * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' @@ -21,7 +22,7 @@ * * TODO: * Add more formats (I'm lazy... :P) - * Export as DDS. + * Export as DDS. (to add more formats, obviously) * * Feel free to throw a pull request at me if you improve it! */ From 83de6ed82a664b8d804a6d7ca8ef534dbe3ecc45 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 00:40:07 +0400 Subject: [PATCH 03/19] Added more formats and the ability to export as DDS This took way more time than the swizzling code... ;( --- README.markdown | 14 +- gtx_extract.c | 579 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 461 insertions(+), 132 deletions(-) diff --git a/README.markdown b/README.markdown index b6b2452..a370a7e 100644 --- a/README.markdown +++ b/README.markdown @@ -13,11 +13,21 @@ Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format use Supported formats: * RGBA8_UNORM / RGBA8_SRGB +* RGB10A2_UNORM +* RGB565_UNORM +* RGB5A1_UNORM +* RGBA4_UNORM +* R8_UNORM +* RG8_UNORM +* RG4_UNORM +* BC1_UNORM / BC1_SRGB (DXT1) +* BC2_UNORM / BC2_SRGB (DXT3) * BC3_UNORM / BC3_SRGB (DXT5) +* BC4_UNORM / BC4_SNORM (ATI1) +* BC5_UNORM / BC5_SNORM (ATI2) TODO: -* Add more formats (I'm lazy... :P) -* Export as DDS. (to add more formats, obviously) +* Make a x86 version (Would probably force me to rewrite the program?) Credits: * Treeki - Original developer. diff --git a/gtx_extract.c b/gtx_extract.c index eda90b7..8840820 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -21,13 +21,13 @@ * algorithm, presumably for faster access. * * TODO: - * Add more formats (I'm lazy... :P) - * Export as DDS. (to add more formats, obviously) + * Make a x86 version (Would probably force me to rewrite the program?) * * Feel free to throw a pull request at me if you improve it! */ #include "txc_dxtn.h" +#include #include #include #include @@ -38,8 +38,47 @@ #define min(x, y) (((x) < (y)) ? (x) : (y)) -static const uint32_t formats[] = { 0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2 }; // I'll add the rest later on -static const uint32_t BCn_formats[] = { 0x31, 0x431, 0x32, 0x432, 0x33, 0x433 }; // I'll add the rest later on +static int BCn_formats[10] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; + + +bool isvalueinarray(int val, int *arr, int size){ + int i; + for (i=0; i < size; i++) { + if (arr[i] == val) + return true; + } + return false; +} + + +uint8_t formatHwInfo[0x40*4] = +{ + // todo: Convert to struct + // each entry is 4 bytes + 0x00,0x00,0x00,0x01,0x08,0x03,0x00,0x01,0x08,0x01,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x10,0x07,0x00,0x00,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x0B,0x00,0x01,0x10,0x01,0x00,0x01,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x03,0x00,0x01,0x20,0x03,0x00,0x00,0x20,0x07,0x00,0x00,0x20,0x03,0x00,0x00, + 0x20,0x03,0x00,0x01,0x20,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x03,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01, + 0x40,0x05,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00, + 0x40,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x03,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x10,0x01,0x00,0x00, + 0x10,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00, + 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x60,0x01,0x00,0x00, + 0x60,0x01,0x00,0x00,0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x80,0x01,0x00,0x01, + 0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) +{ + uint32_t hwFormat = surfaceFormat&0x3F; + uint32_t bpp = formatHwInfo[hwFormat*4+0]; + return bpp; +} /* Start of libtxc_dxtn section */ @@ -225,6 +264,7 @@ typedef struct _GFDData { uint32_t swizzle; uint32_t alignment; uint32_t pitch; + uint32_t realSize; uint32_t dataSize; uint8_t *data; } GFDData; @@ -261,47 +301,176 @@ uint32_t swap32(uint32_t v) { return a|b|c|d; } -uint32_t swapRB(uint32_t argb) { - uint32_t r = (argb & 0x00FF0000) >> 16; - uint32_t b = (argb & 0x000000FF) << 16; - uint32_t ag = (argb & 0xFF00FF00); - return ag|r|b; -} +void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t format_, bool compressed) { + uint32_t fmtbpp = 0; + uint32_t has_alpha; + uint32_t rmask = 0; + uint32_t gmask = 0; + uint32_t bmask = 0; + uint32_t amask = 0; + uint32_t flags; + uint32_t pflags; + uint32_t caps; + uint32_t size; + uint32_t u32; + + if (format_ == 28) { // RGBA8 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + } + + else if (format_ == 24) { // RGB10A2 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000003ff; + gmask = 0x000ffc00; + bmask = 0x3ff00000; + amask = 0xc0000000; + } + + else if (format_ == 85) { // RGB565 + fmtbpp = 2; + has_alpha = 0; + rmask = 0x0000001f; + gmask = 0x000007e0; + bmask = 0x0000f800; + amask = 0x00000000; + } + + else if (format_ == 86) { // RGB5A1 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000001f; + gmask = 0x000003e0; + bmask = 0x00007c00; + amask = 0x00008000; + } + + else if (format_ == 115) { // RGBA4 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x000000f0; + bmask = 0x00000f00; + amask = 0x0000f000; + } + + else if (format_ == 61) { // L8 + fmtbpp = 1; + has_alpha = 0; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x00000000; + } + + else if (format_ == 49) { // L8A8 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x0000ff00; + } + + else if (format_ == 112) { // L4A4 + fmtbpp = 1; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x0000000f; + bmask = 0x0000000f; + amask = 0x000000f0; + } + + fmtbpp <<= 3; + + flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); -void writeBMPHeader(FILE *f, int width, int height) { - uint16_t u16; - uint32_t u32; - - fwrite("BM", 1, 2, f); - u32 = 122 + (width*height*4); fwrite(&u32, 1, 4, f); - u16 = 0; fwrite(&u16, 1, 2, f); - u16 = 0; fwrite(&u16, 1, 2, f); - u32 = 122; fwrite(&u32, 1, 4, f); - - u32 = 108; fwrite(&u32, 1, 4, f); - u32 = width; fwrite(&u32, 1, 4, f); - u32 = height; fwrite(&u32, 1, 4, f); - u16 = 1; fwrite(&u16, 1, 2, f); - u16 = 32; fwrite(&u16, 1, 2, f); - u32 = 3; fwrite(&u32, 1, 4, f); - u32 = width*height*4; fwrite(&u32, 1, 4, f); - u32 = 2835; fwrite(&u32, 1, 4, f); - u32 = 2835; fwrite(&u32, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); - u32 = 0xFF0000; fwrite(&u32, 1, 4, f); - u32 = 0xFF00; fwrite(&u32, 1, 4, f); - u32 = 0xFF; fwrite(&u32, 1, 4, f); - u32 = 0xFF000000; fwrite(&u32, 1, 4, f); - u32 = 0x57696E20; fwrite(&u32, 1, 4, f); - - uint8_t thing[0x24]; - memset(thing, 0, 0x24); - fwrite(thing, 1, 0x24, f); - - u32 = 0; fwrite(&u32, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); + caps = (0x00001000); + + if (num_mipmaps == 0) + num_mipmaps = 1; + if (num_mipmaps != 1) { + flags |= (0x00020000); + caps |= ((0x00000008) | (0x00400000)); + } + + if (!(compressed)) { + flags |= (0x00000008); + + if (fmtbpp == 1 || format_ == 49) // LUMINANCE + pflags = (0x00020000); + + else // RGB + pflags = (0x00000040); + + if (has_alpha != 0) + pflags |= (0x00000001); + + size = (w * fmtbpp); + } + + else { + flags |= (0x00080000); + pflags = (0x00000004); + + size = ((w + 3) >> 2) * ((h + 3) >> 2); + if (format_ == 71 || format_ == 80 || format_ == 81) + size *= 8; + else + size *= 16; + } + + fwrite("DDS ", 1, 4, f); + u32 = 124; fwrite(&u32, 1, 4, f); + fwrite(&flags, 1, 4, f); + fwrite(&h, 1, 4, f); + fwrite(&w, 1, 4, f); + fwrite(&size, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + fwrite(&num_mipmaps, 1, 4, f); + + uint8_t thing[0x2C]; + memset(thing, 0, 0x2C); + fwrite(thing, 1, 0x2C, f); + + u32 = 32; fwrite(&u32, 1, 4, f); + fwrite(&pflags, 1, 4, f); + + if (!(compressed)) { + u32 = 0; fwrite(&u32, 1, 4, f); + } + else { + if (format_ == 71) + fwrite("DXT1", 1, 4, f); + else if (format_ == 74) + fwrite("DXT3", 1, 4, f); + else if (format_ == 77) + fwrite("DXT5", 1, 4, f); + else if (format_ == 80) + fwrite("BC4U", 1, 4, f); + else if (format_ == 81) + fwrite("BC4S", 1, 4, f); + else if (format_ == 83) + fwrite("BC5U", 1, 4, f); + else if (format_ == 84) + fwrite("BC5S", 1, 4, f); + } + + fwrite(&fmtbpp, 1, 4, f); + fwrite(&rmask, 1, 4, f); + fwrite(&gmask, 1, 4, f); + fwrite(&bmask, 1, 4, f); + fwrite(&amask, 1, 4, f); + fwrite(&caps, 1, 4, f); + + uint8_t thing2[0x10]; + memset(thing2, 0, 0x10); + fwrite(thing2, 1, 0x10, f); } int readGTX(GFDData *gfd, FILE *f) { @@ -353,8 +522,16 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->pitch = swap32(info.pitch); } else if (swap32(section.type_) == 0xC && gfd->data == NULL) { - gfd->dataSize = swap32(section.dataSize); - gfd->data = malloc(gfd->dataSize); + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + bpp /= 8; + if (isvalueinarray(gfd->format, BCn_formats, 10)) + gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; + + else + gfd->realSize = gfd->width * gfd->height * bpp; + + gfd->dataSize = swap32(section.dataSize); + gfd->data = malloc(gfd->dataSize); if (!gfd->data) return -300; @@ -369,16 +546,6 @@ int readGTX(GFDData *gfd, FILE *f) { return 1; } -void writeFile(FILE *f, int width, int height, uint8_t *output) { - int row; - - writeBMPHeader(f, width, height); - - for (row = height - 1; row >= 0; row--) { - fwrite(&output[row * width * 4], 1, width * 4, f); - } -} - /* Start of swizzling section */ /* Credits: @@ -401,35 +568,6 @@ static uint32_t m_chipFamily = 2; static uint32_t MicroTilePixels = 8 * 8; -uint8_t formatHwInfo[0x40*4] = -{ - // todo: Convert to struct - // each entry is 4 bytes - 0x00,0x00,0x00,0x01,0x08,0x03,0x00,0x01,0x08,0x01,0x00,0x01,0x00,0x00,0x00,0x01, - 0x00,0x00,0x00,0x01,0x10,0x07,0x00,0x00,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, - 0x10,0x0B,0x00,0x01,0x10,0x01,0x00,0x01,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, - 0x10,0x03,0x00,0x01,0x20,0x03,0x00,0x00,0x20,0x07,0x00,0x00,0x20,0x03,0x00,0x00, - 0x20,0x03,0x00,0x01,0x20,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x03,0x00,0x01,0x00,0x00,0x00,0x01, - 0x00,0x00,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01, - 0x40,0x05,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00, - 0x40,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x03,0x00,0x00, - 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x10,0x01,0x00,0x00, - 0x10,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00, - 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x60,0x01,0x00,0x00, - 0x60,0x01,0x00,0x00,0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x80,0x01,0x00,0x01, - 0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - -uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) -{ - uint32_t hwFormat = surfaceFormat&0x3F; - uint32_t bpp = formatHwInfo[hwFormat*4+0]; - return bpp; -} - uint32_t computeSurfaceThickness(uint32_t tileMode) { uint32_t thickness = 1; @@ -731,17 +869,158 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u return bank_bits | pipe_bits | offset_low | offset_high; } -void export_RGBA8(GFDData *gfd, FILE *f) { +void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { + int y; + uint32_t format; + + if (gfd->format == 0x1a || gfd->format == 0x41a) + format = 28; + else if (gfd->format == 0x19) + format = 24; + else if (gfd->format == 0x8) + format = 85; + else if (gfd->format == 0xa) + format = 86; + else if (gfd->format == 0xb) + format = 115; + else if (gfd->format == 0x1) + format = 61; + else if (gfd->format == 0x7) + format = 49; + else if (gfd->format == 0x2) + format = 112; + else if (gfd->format == 0x31 || gfd->format == 0x431) + format = 71; + else if (gfd->format == 0x32 || gfd->format == 0x432) + format = 74; + else if (gfd->format == 0x33 || gfd->format == 0x433) + format = 77; + else if (gfd->format == 0x34) + format = 80; + else if (gfd->format == 0x234) + format = 81; + else if (gfd->format == 0x35) + format = 83; + else if (gfd->format == 0x235) + format = 84; + + writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); + + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + bpp /= 8; + + for (y = 0; y < gfd->height; y++) { + if ((y * gfd->width * bpp) >= gfd->realSize) + break; + + fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); + } +} + +void swizzle_8(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + uint8_t *source, *output; + + source = (uint8_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + width = gfd->width / 4; + height = gfd->height / 4; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +void swizzle_16(GFDData *gfd, FILE *f) { uint64_t pos; - uint32_t x, y; + uint32_t x, y, width, height; + uint16_t *source, *output; + + source = (uint16_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + width = gfd->width / 4; + height = gfd->height / 4; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +void swizzle_32(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; uint32_t *source, *output; source = (uint32_t *)gfd->data; - output = malloc(gfd->width * gfd->height * 4); - pos = 0; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + width = gfd->width / 4; + height = gfd->height / 4; + } - for (y = 0; y < gfd->height; y++) { - for (x = 0; x < gfd->width; x++) { + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -755,26 +1034,35 @@ void export_RGBA8(GFDData *gfd, FILE *f) { bpp /= 8; - output[y * gfd->width + x] = swapRB(source[pos / bpp]); + output[y * width + x] = source[pos / bpp]; } } - writeFile(f, gfd->width, gfd->height, (uint8_t *)output); + writeFile(f, gfd, (uint8_t *)output); free(output); } -void export_DXT5(GFDData *gfd, FILE *f) { +void swizzle_64(GFDData *gfd, FILE *f) { uint64_t pos; - uint32_t x, y; - uint32_t *output, outValue; - uint32_t blobWidth = gfd->width / 4, blobHeight = gfd->height / 4; - uint8_t bits[4]; - __uint128_t *src = (__uint128_t *)gfd->data; - __uint128_t *work = malloc(gfd->width * gfd->height); - - for (y = 0; y < blobHeight; y++) { - for (x = 0; x < blobWidth; x++) { + uint32_t x, y, width, height; + uint64_t *source, *output; + + source = (uint64_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + width = gfd->width / 4; + height = gfd->height / 4; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -788,29 +1076,56 @@ void export_DXT5(GFDData *gfd, FILE *f) { bpp /= 8; - work[(y*blobWidth)+x] = src[pos / bpp]; + output[y * width + x] = source[pos / bpp]; } } - output = malloc(gfd->width * gfd->height * 4); + writeFile(f, gfd, (uint8_t *)output); - for (y = 0; y < gfd->height; y++) { - for (x = 0; x < gfd->width; x++) { - fetch_2d_texel_rgba_dxt5(gfd->width, (uint8_t *)work, x, y, bits); + free(output); + +} - outValue = (bits[ACOMP] << 24); - outValue |= (bits[RCOMP] << 16); - outValue |= (bits[GCOMP] << 8); - outValue |= bits[BCOMP]; +void swizzle_128(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + __uint128_t *source, *output; + + source = (__uint128_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + width = gfd->width / 4; + height = gfd->height / 4; + } - output[(y * gfd->width) + x] = outValue; + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; } } - writeFile(f, gfd->width, gfd->height, (uint8_t *)output); + writeFile(f, gfd, (uint8_t *)output); free(output); - free(work); } int main(int argc, char **argv) { @@ -819,7 +1134,7 @@ int main(int argc, char **argv) { int result; if (argc != 3) { - fprintf(stderr, "Usage: %s [input.gtx] [output.bmp]\n", argv[0]); + fprintf(stderr, "Usage: %s [input.gtx] [output.dds]\n", argv[0]); return EXIT_FAILURE; } @@ -840,21 +1155,25 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - printf("Width: %d - Height: %d - Format: 0x%x - Size: %d (%x)\n", data.width, data.height, data.format, data.dataSize, data.dataSize); - - data.width = (data.width + 63) & ~63; - data.height = (data.height + 63) & ~63; - printf("Padded Width: %d - Padded Height: %d\n", data.width, data.height); + printf("Width: %d - Height: %d - Format: 0x%x\n", data.width, data.height, data.format); + printf("Size: %d (%x)\n", data.imageSize, data.imageSize); + printf("Real size: %d (%x)\n", data.dataSize, data.dataSize); + + uint32_t bpp = surfaceGetBitsPerPixel(data.format); + + if (bpp == 8) + swizzle_8(&data, f); + else if (bpp == 16) + swizzle_16(&data, f); + else if (bpp == 32) + swizzle_32(&data, f); + else if (bpp == 64) + swizzle_64(&data, f); + else if (bpp == 128) + swizzle_128(&data, f); + else + printf("Unsupported format: 0x%x\n", data.format); - /* if (data.format in formats) - export_RGB(&data, f); - else if (data.format in BCn_formats) <- doesn't even work, fixing it later - export_DXT(&data, f); - */ - if (data.format == 0x1a || data.format == 0x41a) - export_RGBA8(&data, f); - else if (data.format == 0x33 || data.format == 0x433) - export_DXT5(&data, f); fclose(f); return EXIT_SUCCESS; From 0c2a0feeecf9f232c18abacdb1e6a05b1a914848 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 00:41:53 +0400 Subject: [PATCH 04/19] fix typo and add more info --- README.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index a370a7e..f73deed 100644 --- a/README.markdown +++ b/README.markdown @@ -6,10 +6,10 @@ doesn't really need one repo. Currently, not that much, but it could change.. And it's not Wii-specific any more. :p -Wii U GTX Extractor +GTX Extractor (C version) ------------------- -Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format used in Wii U games, and saves them as BMP. +Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format used in Wii U games, and saves them as DDS. Supported formats: * RGBA8_UNORM / RGBA8_SRGB @@ -31,7 +31,7 @@ TODO: Credits: * Treeki - Original developer. -* AboodXD - Reviver, Improved swizzling. +* AboodXD - Reviver, Added new features, Improved swizzling. * Exzap, AddrLib - Helping with swizzling. * libtxc_dxtn From a8d74e40d3b05e04a49726d963e71c88d9533043 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 00:57:08 +0400 Subject: [PATCH 05/19] removed libtxc_dxtn --- README.markdown | 1 - gtx_extract.c | 167 ------------------------------------------------ txc_dxtn.h | 50 --------------- 3 files changed, 218 deletions(-) delete mode 100644 txc_dxtn.h diff --git a/README.markdown b/README.markdown index f73deed..3cca31d 100644 --- a/README.markdown +++ b/README.markdown @@ -33,7 +33,6 @@ Credits: * Treeki - Original developer. * AboodXD - Reviver, Added new features, Improved swizzling. * Exzap, AddrLib - Helping with swizzling. -* libtxc_dxtn More details on compilation and usage in the comments inside the file. diff --git a/gtx_extract.c b/gtx_extract.c index 8840820..b0ccb9c 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -6,7 +6,6 @@ * ( https://github.com/aboood40091 ) * This software is released into the public domain. * - * Special thanks to: libtxc_dxtn developers * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: @@ -26,7 +25,6 @@ * Feel free to throw a pull request at me if you improve it! */ -#include "txc_dxtn.h" #include #include #include @@ -81,171 +79,6 @@ uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) } -/* Start of libtxc_dxtn section */ -#define EXP5TO8R(packedcol) \ - ((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x7)) - -#define EXP6TO8G(packedcol) \ - ((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x3)) - -#define EXP5TO8B(packedcol) \ - ((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x7)) - -#define EXP4TO8(col) \ - ((col) | ((col) << 4)) - - -/* inefficient. To be efficient, it would be necessary to decode 16 pixels at once */ - -static void dxt135_decode_imageblock ( const GLubyte *img_block_src, - GLint i, GLint j, GLuint dxt_type, GLvoid *texel ) { - GLchan *rgba = (GLchan *) texel; - const GLushort color0 = img_block_src[0] | (img_block_src[1] << 8); - const GLushort color1 = img_block_src[2] | (img_block_src[3] << 8); - const GLuint bits = img_block_src[4] | (img_block_src[5] << 8) | - (img_block_src[6] << 16) | (img_block_src[7] << 24); - /* What about big/little endian? */ - GLubyte bit_pos = 2 * (j * 4 + i) ; - GLubyte code = (GLubyte) ((bits >> bit_pos) & 3); - - rgba[ACOMP] = CHAN_MAX; - switch (code) { - case 0: - rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color0) ); - rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color0) ); - rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color0) ); - break; - case 1: - rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color1) ); - rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color1) ); - rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color1) ); - break; - case 2: - if (color0 > color1) { - rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) / 3) ); - rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) / 3) ); - rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) / 3) ); - } - else { - rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1)) / 2) ); - rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1)) / 2) ); - rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1)) / 2) ); - } - break; - case 3: - if ((dxt_type > 1) || (color0 > color1)) { - rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) / 3) ); - rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) / 3) ); - rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) / 3) ); - } - else { - rgba[RCOMP] = 0; - rgba[GCOMP] = 0; - rgba[BCOMP] = 0; - if (dxt_type == 1) rgba[ACOMP] = UBYTE_TO_CHAN(0); - } - break; - default: - /* CANNOT happen (I hope) */ - break; - } -} - - -void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, - GLint i, GLint j, GLvoid *texel) { - - /* Extract the (i,j) pixel from pixdata and return it - * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. - */ - - GLchan *rgba = (GLchan *) texel; - const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); - const GLubyte alpha0 = blksrc[0]; - const GLubyte alpha1 = blksrc[1]; -#if 0 - const GLubyte bit_pos = 3 * ((j&3) * 4 + (i&3)); - /* simple 32bit version */ - const GLuint bits_low = blksrc[2] | (blksrc[3] << 8) | (blksrc[4] << 16) | (blksrc[5] << 24); - const GLuint bits_high = blksrc[6] | (blksrc[7] << 8); - GLubyte code; - - if (bit_pos < 30) - code = (GLubyte) ((bits_low >> bit_pos) & 7); - else if (bit_pos == 30) - code = (GLubyte) ((bits_low >> 30) & 3) | ((bits_high << 2) & 4); - else - code = (GLubyte) ((bits_high >> (bit_pos - 32)) & 7); -#endif -#if 1 -/* TODO test this! */ - const GLubyte bit_pos = ((j&3) * 4 + (i&3)) * 3; - const GLubyte acodelow = blksrc[2 + bit_pos / 8]; - const GLubyte acodehigh = blksrc[3 + bit_pos / 8]; - const GLubyte code = (acodelow >> (bit_pos & 0x7) | - (acodehigh << (8 - (bit_pos & 0x7)))) & 0x7; -#endif - dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); -#if 0 - if (alpha0 > alpha1) { - switch (code) { - case 0: - rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); - break; - case 1: - rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); - break; - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); - break; - } - } - else { - switch (code) { - case 0: - rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); - break; - case 1: - rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); - break; - case 2: - case 3: - case 4: - case 5: - rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); - break; - case 6: - rgba[ACOMP] = 0; - break; - case 7: - rgba[ACOMP] = CHAN_MAX; - break; - } - } -#endif -/* not sure. Which version is faster? */ -#if 1 -/* TODO test this */ - if (code == 0) - rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); - else if (code == 1) - rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); - else if (alpha0 > alpha1) - rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); - else if (code < 6) - rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); - else if (code == 6) - rgba[ACOMP] = 0; - else - rgba[ACOMP] = CHAN_MAX; -#endif -} - /* Start of GTX Extractor section */ typedef struct _GFDData { uint32_t dim; diff --git a/txc_dxtn.h b/txc_dxtn.h deleted file mode 100644 index 31d7930..0000000 --- a/txc_dxtn.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * libtxc_dxtn - * Version: 0.1 - * - * Copyright (C) 2004 Roland Scheidegger All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifdef __APPLE__ -#include -#else -#include -#endif - -typedef GLubyte GLchan; -#define UBYTE_TO_CHAN(b) (b) -#define CHAN_MAX 255 -#define RCOMP 0 -#define GCOMP 1 -#define BCOMP 2 -#define ACOMP 3 - -void fetch_2d_texel_rgb_dxt1(GLint srcRowStride, const GLubyte *pixdata, - GLint i, GLint j, GLvoid *texel); -void fetch_2d_texel_rgba_dxt1(GLint srcRowStride, const GLubyte *pixdata, - GLint i, GLint j, GLvoid *texel); -void fetch_2d_texel_rgba_dxt3(GLint srcRowStride, const GLubyte *pixdata, - GLint i, GLint j, GLvoid *texel); -void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, - GLint i, GLint j, GLvoid *texel); - -void tx_compress_dxtn(GLint srccomps, GLint width, GLint height, - const GLubyte *srcPixData, GLenum destformat, - GLubyte *dest, GLint dstRowStride); From 3a2fb78878936f9b1c02a0ac9f6576f5a89fa590 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 01:03:51 +0400 Subject: [PATCH 06/19] remove false info --- gtx_extract.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index b0ccb9c..efb505b 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -12,9 +12,6 @@ * gcc -m64 -o gtx_extract gtx_extract.c * (You need an x64 version of gcc!) * - * This tool currently supports RGBA8 (format 0x1a and 0x41a) and - * BC3/DXT5 (format 0x33 and 0x433) textures. - * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' * algorithm, presumably for faster access. From 0de0d0e6d44c318bd3dfc353c3dae24b7ffc9e03 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 15:56:20 +0400 Subject: [PATCH 07/19] Fix issue with writing file, fix typo --- gtx_extract.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gtx_extract.c b/gtx_extract.c index efb505b..e5396a7 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -739,6 +739,11 @@ void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); bpp /= 8; + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + bpp /= 2; + bpp = max(1, bpp); + } + for (y = 0; y < gfd->height; y++) { if ((y * gfd->width * bpp) >= gfd->realSize) break; @@ -987,7 +992,7 @@ int main(int argc, char **argv) { printf("Width: %d - Height: %d - Format: 0x%x\n", data.width, data.height, data.format); printf("Size: %d (%x)\n", data.imageSize, data.imageSize); - printf("Real size: %d (%x)\n", data.dataSize, data.dataSize); + printf("Real size: %d (%x)\n", data.realSize, data.realSize); uint32_t bpp = surfaceGetBitsPerPixel(data.format); From 501128e336234bcc6b6399d7b58b7ed0a62aae91 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 18:23:50 +0400 Subject: [PATCH 08/19] make the program fancier --- gtx_extract.c | 192 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 155 insertions(+), 37 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index e5396a7..e47cd1d 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -28,11 +28,13 @@ #include #include #include +#include #define max(x, y) (((x) > (y)) ? (x) : (y)) #define min(x, y) (((x) < (y)) ? (x) : (y)) +static int formats[] = {0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2, 0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; // Supported formats static int BCn_formats[10] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; @@ -78,6 +80,7 @@ uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) /* Start of GTX Extractor section */ typedef struct _GFDData { + uint32_t numImages; uint32_t dim; uint32_t width; uint32_t height; @@ -94,6 +97,7 @@ typedef struct _GFDData { uint32_t swizzle; uint32_t alignment; uint32_t pitch; + uint32_t bpp; uint32_t realSize; uint32_t dataSize; uint8_t *data; @@ -306,17 +310,14 @@ void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t int readGTX(GFDData *gfd, FILE *f) { GFDHeader header; - // This is kinda bad. Don't really care right now >.> - gfd->width = 0; - gfd->height = 0; - gfd->data = NULL; - if (fread(&header, 1, sizeof(header), f) != sizeof(header)) return -1; if (memcmp(header.magic, "Gfx2", 4) != 0) return -2; + gfd->numImages = 0; + while (!feof(f)) { GFDBlockHeader section; if (fread(§ion, 1, sizeof(section), f) != sizeof(section)) @@ -350,9 +351,10 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->swizzle = swap32(info.swizzle); gfd->alignment = swap32(info.alignment); gfd->pitch = swap32(info.pitch); + gfd->bpp = surfaceGetBitsPerPixel(gfd->format); - } else if (swap32(section.type_) == 0xC && gfd->data == NULL) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + } else if (swap32(section.type_) == 0xC) { + uint32_t bpp = gfd->bpp; bpp /= 8; if (isvalueinarray(gfd->format, BCn_formats, 10)) gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; @@ -368,6 +370,8 @@ int readGTX(GFDData *gfd, FILE *f) { if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) return -301; + gfd->numImages += 1; + } else { fseek(f, swap32(section.dataSize), SEEK_CUR); } @@ -736,7 +740,7 @@ void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; bpp /= 8; if (isvalueinarray(gfd->format, BCn_formats, 10)) { @@ -772,7 +776,7 @@ void swizzle_8(GFDData *gfd, FILE *f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -814,7 +818,7 @@ void swizzle_16(GFDData *gfd, FILE *f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -856,7 +860,7 @@ void swizzle_32(GFDData *gfd, FILE *f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -898,7 +902,7 @@ void swizzle_64(GFDData *gfd, FILE *f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -941,7 +945,7 @@ void swizzle_128(GFDData *gfd, FILE *f) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - uint32_t bpp = surfaceGetBitsPerPixel(gfd->format); + uint32_t bpp = gfd->bpp; uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; @@ -963,51 +967,165 @@ void swizzle_128(GFDData *gfd, FILE *f) { free(output); } +char *remove_three(const char *filename) { + size_t len = strlen(filename); + char *newfilename = malloc(len-2); + if (!newfilename) /* handle error */; + memcpy(newfilename, filename, len-3); + newfilename[len - 3] = 0; + return newfilename; +} + int main(int argc, char **argv) { GFDData data; FILE *f; int result; - if (argc != 3) { - fprintf(stderr, "Usage: %s [input.gtx] [output.dds]\n", argv[0]); + printf("GTX Extractor - C ver.\n"); + printf("(C) 2014 Treeki, 2017 AboodXD\n"); + + if (argc != 2) { + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported formats:\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } if (!(f = fopen(argv[1], "rb"))) { - fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } if ((result = readGTX(&data, f)) != 1) { - fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); + fprintf(stderr, "\n"); + fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); fclose(f); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } fclose(f); - if (!(f = fopen(argv[2], "wb"))) { - fprintf(stderr, "Cannot open %s for writing\n", argv[2]); + char *str = remove_three(argv[1]); + char c = 'd'; + char c1 = 'd'; + char c2 = 's'; + + size_t len = strlen(str); + char *str2 = malloc(len + 3 + 1 ); + strcpy(str2, str); + str2[len] = c; + str2[len + 1] = c1; + str2[len + 2] = c2; + str2[len + 3] = '\0'; + + if (!(f = fopen(str2, "wb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for writing\n", str2); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + free(str2); + + if (data.numImages > 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + else if (data.numImages == 0) { + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } - printf("Width: %d - Height: %d - Format: 0x%x\n", data.width, data.height, data.format); - printf("Size: %d (%x)\n", data.imageSize, data.imageSize); - printf("Real size: %d (%x)\n", data.realSize, data.realSize); - - uint32_t bpp = surfaceGetBitsPerPixel(data.format); - - if (bpp == 8) - swizzle_8(&data, f); - else if (bpp == 16) - swizzle_16(&data, f); - else if (bpp == 32) - swizzle_32(&data, f); - else if (bpp == 64) - swizzle_64(&data, f); - else if (bpp == 128) - swizzle_128(&data, f); - else - printf("Unsupported format: 0x%x\n", data.format); + printf("\n"); + printf("// ----- GX2Surface Info ----- \n"); + printf(" dim = %d\n", data.dim); + printf(" width = %d\n", data.width); + printf(" height = %d\n", data.height); + printf(" depth = %d\n", data.depth); + printf(" numMips = %d\n", data.numMips); + printf(" format = 0x%x\n", data.format); + printf(" aa = %d\n", data.aa); + printf(" use = %d\n", data.use); + printf(" imageSize = %d\n", data.imageSize); + printf(" mipSize = %d\n", data.mipSize); + printf(" tileMode = %d\n", data.tileMode); + printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); + printf(" alignment = %d\n", data.alignment); + printf(" pitch = %d\n", data.pitch); + printf("\n"); + printf(" bits per pixel = %d\n", data.bpp); + printf(" bytes per pixel = %d\n", data.bpp / 8); + printf(" realSize = %d\n", data.realSize); + + uint32_t bpp = data.bpp; + + if (isvalueinarray(data.format, formats, 19)) { + if (bpp == 8) + swizzle_8(&data, f); + else if (bpp == 16) + swizzle_16(&data, f); + else if (bpp == 32) + swizzle_32(&data, f); + else if (bpp == 64) + swizzle_64(&data, f); + else if (bpp == 128) + swizzle_128(&data, f); + } + + else { + fprintf(stderr, "Unsupported format: 0x%x\n", data.format); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } fclose(f); From dfac3fe9076708ba80e063257a0e2de284e1a823 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Mon, 22 May 2017 18:52:57 +0400 Subject: [PATCH 09/19] prevent it from creating an empty file when numImages != 1 --- gtx_extract.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index e47cd1d..7e6511b 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -1025,6 +1025,9 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + else + printf("\nConverting: %s\n", argv[1]); + if ((result = readGTX(&data, f)) != 1) { fprintf(stderr, "\n"); fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); @@ -1037,6 +1040,26 @@ int main(int argc, char **argv) { } fclose(f); + if (data.numImages > 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + else if (data.numImages == 0) { + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + char *str = remove_three(argv[1]); char c = 'd'; char c1 = 'd'; @@ -1062,26 +1085,6 @@ int main(int argc, char **argv) { free(str2); - if (data.numImages > 1) { - fprintf(stderr, "\n"); - fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); - return EXIT_FAILURE; - } - - else if (data.numImages == 0) { - fprintf(stderr, "\n"); - fprintf(stderr, "No images were found in this GTX file\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); - return EXIT_FAILURE; - } - printf("\n"); printf("// ----- GX2Surface Info ----- \n"); printf(" dim = %d\n", data.dim); @@ -1129,6 +1132,8 @@ int main(int argc, char **argv) { fclose(f); + printf("\nFinished converting: %s\n", argv[1]); + return EXIT_SUCCESS; } From db081422db0a17d4079bf3854b620848429713f1 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Sat, 27 May 2017 11:18:05 +0400 Subject: [PATCH 10/19] Fix indent blocks --- gtx_extract.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index 7e6511b..64b40c7 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -22,6 +22,7 @@ * Feel free to throw a pull request at me if you improve it! */ +/* General stuff and imports */ #include #include #include @@ -135,6 +136,31 @@ uint32_t swap32(uint32_t v) { return a|b|c|d; } +/* Start of DDS writer section */ + +/* + * Copyright © 2016-2017 AboodXD + * + * Supported formats: + -RGBA8 + -RGB10A2 + -RGB565 + -RGB5A1 + -RGBA4 + -L8 + -L8A8 + -L4A4 + -BC1_UNORM + -BC2_UNORM + -BC3_UNORM + -BC4_UNORM + -BC4_SNORM + -BC5_UNORM + -BC5_SNORM + * + * Feel free to include this in your own program if you want, just give credits. :) + */ + void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t format_, bool compressed) { uint32_t fmtbpp = 0; uint32_t has_alpha; @@ -383,9 +409,9 @@ int readGTX(GFDData *gfd, FILE *f) { /* Start of swizzling section */ /* Credits: - -AddrLib: actual code - -Exzap: modifying code to apply to Wii U textures - -AboodXD: porting, code improvements and cleaning up + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up */ static uint32_t m_banks = 4; From 8392fc3fd5799cecb1d42e2bfd38503becc61e6c Mon Sep 17 00:00:00 2001 From: AboodXD Date: Tue, 6 Jun 2017 15:55:48 +0400 Subject: [PATCH 11/19] Fixed calculating the width and height needed for swizzling, added no5 ver --- gtx_extract.c | 71 ++- gtx_extract_no5.c | 1161 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1222 insertions(+), 10 deletions(-) create mode 100644 gtx_extract_no5.c diff --git a/gtx_extract.c b/gtx_extract.c index 7e6511b..7a4bb08 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -22,6 +22,7 @@ * Feel free to throw a pull request at me if you improve it! */ +#include #include #include #include @@ -760,13 +761,23 @@ void swizzle_8(GFDData *gfd, FILE *f) { uint64_t pos; uint32_t x, y, width, height; uint8_t *source, *output; + double frac, whole; source = (uint8_t *)gfd->data; output = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - width = gfd->width / 4; - height = gfd->height / 4; + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } else { @@ -802,13 +813,23 @@ void swizzle_16(GFDData *gfd, FILE *f) { uint64_t pos; uint32_t x, y, width, height; uint16_t *source, *output; + double frac, whole; source = (uint16_t *)gfd->data; output = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - width = gfd->width / 4; - height = gfd->height / 4; + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } else { @@ -844,13 +865,23 @@ void swizzle_32(GFDData *gfd, FILE *f) { uint64_t pos; uint32_t x, y, width, height; uint32_t *source, *output; + double frac, whole; source = (uint32_t *)gfd->data; output = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - width = gfd->width / 4; - height = gfd->height / 4; + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } else { @@ -886,13 +917,23 @@ void swizzle_64(GFDData *gfd, FILE *f) { uint64_t pos; uint32_t x, y, width, height; uint64_t *source, *output; + double frac, whole; source = (uint64_t *)gfd->data; output = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - width = gfd->width / 4; - height = gfd->height / 4; + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } else { @@ -929,13 +970,23 @@ void swizzle_128(GFDData *gfd, FILE *f) { uint64_t pos; uint32_t x, y, width, height; __uint128_t *source, *output; + double frac, whole; source = (__uint128_t *)gfd->data; output = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - width = gfd->width / 4; - height = gfd->height / 4; + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } else { diff --git a/gtx_extract_no5.c b/gtx_extract_no5.c new file mode 100644 index 0000000..73eeec9 --- /dev/null +++ b/gtx_extract_no5.c @@ -0,0 +1,1161 @@ +/* + * Wii U 'GTX' Texture Extractor + * Created by Ninji Vahran / Treeki; 2014-10-31 + * ( https://github.com/Treeki ) + * Updated by AboodXD; 2017-05-21 + * ( https://github.com/aboood40091 ) + * This software is released into the public domain. + * + * Tested with TDM-GCC-64 on Windows 10 Pro x64. + * + * How to build: + * gcc -m64 -o gtx_extract gtx_extract.c + * (You need an x64 version of gcc!) + * + * Why so complex? + * Wii U textures appear to be packed using a complex 'texture swizzling' + * algorithm, presumably for faster access. + * + * TODO: + * Make a x86 version (Would probably force me to rewrite the program?) + * + * Feel free to throw a pull request at me if you improve it! + */ + +#include +#include +#include +#include +#include +#include +#include + +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define min(x, y) (((x) < (y)) ? (x) : (y)) + + +static int formats[] = {0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2, 0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; // Supported formats +static int BCn_formats[10] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; + + +bool isvalueinarray(int val, int *arr, int size){ + int i; + for (i=0; i < size; i++) { + if (arr[i] == val) + return true; + } + return false; +} + + +uint8_t formatHwInfo[0x40*4] = +{ + // todo: Convert to struct + // each entry is 4 bytes + 0x00,0x00,0x00,0x01,0x08,0x03,0x00,0x01,0x08,0x01,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x10,0x07,0x00,0x00,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x0B,0x00,0x01,0x10,0x01,0x00,0x01,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x03,0x00,0x01,0x20,0x03,0x00,0x00,0x20,0x07,0x00,0x00,0x20,0x03,0x00,0x00, + 0x20,0x03,0x00,0x01,0x20,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x03,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01, + 0x40,0x05,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00, + 0x40,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x03,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x10,0x01,0x00,0x00, + 0x10,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00, + 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x60,0x01,0x00,0x00, + 0x60,0x01,0x00,0x00,0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x80,0x01,0x00,0x01, + 0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) +{ + uint32_t hwFormat = surfaceFormat&0x3F; + uint32_t bpp = formatHwInfo[hwFormat*4+0]; + return bpp; +} + + +/* Start of GTX Extractor section */ +typedef struct _GFDData { + uint32_t numImages; + uint32_t dim; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t numMips; + uint32_t format; + uint32_t aa; + uint32_t use; + uint32_t imageSize; + uint32_t imagePtr; + uint32_t mipSize; + uint32_t mipPtr; + uint32_t tileMode; + uint32_t swizzle; + uint32_t alignment; + uint32_t pitch; + uint32_t bpp; + uint32_t realSize; + uint32_t dataSize; + uint8_t *data; +} GFDData; + +typedef struct _GFDHeader { + char magic[4]; + uint32_t size_, majorVersion, minorVersion; + uint32_t gpuVersion, alignMode, reserved1, reserved2; +} GFDHeader; +typedef struct _GFDBlockHeader { + char magic[4]; + uint32_t size_, majorVersion, minorVersion, type_; + uint32_t dataSize; + uint32_t id, typeIdx; +} GFDBlockHeader; +typedef struct _GFDSurface { + uint32_t dim, width, height, depth; + uint32_t numMips, format_, aa, use; + uint32_t imageSize, imagePtr, mipSize, mipPtr; + uint32_t tileMode, swizzle, alignment, pitch; + uint32_t _40, _44, _48, _4C; + uint32_t _50, _54, _58, _5C; + uint32_t _60, _64, _68, _6C; + uint32_t _70, _74, _78, _7C; + uint32_t _80, _84, _88, _8C; + uint32_t _90, _94, _98; +} GFDSurface; + +uint32_t swap32(uint32_t v) { + uint32_t a = (v & 0xFF000000) >> 24; + uint32_t b = (v & 0x00FF0000) >> 8; + uint32_t c = (v & 0x0000FF00) << 8; + uint32_t d = (v & 0x000000FF) << 24; + return a|b|c|d; +} + +void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t format_, bool compressed) { + uint32_t fmtbpp = 0; + uint32_t has_alpha; + uint32_t rmask = 0; + uint32_t gmask = 0; + uint32_t bmask = 0; + uint32_t amask = 0; + uint32_t flags; + uint32_t pflags; + uint32_t caps; + uint32_t size; + uint32_t u32; + + if (format_ == 28) { // RGBA8 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + } + + else if (format_ == 24) { // RGB10A2 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000003ff; + gmask = 0x000ffc00; + bmask = 0x3ff00000; + amask = 0xc0000000; + } + + else if (format_ == 85) { // RGB565 + fmtbpp = 2; + has_alpha = 0; + rmask = 0x0000001f; + gmask = 0x000007e0; + bmask = 0x0000f800; + amask = 0x00000000; + } + + else if (format_ == 86) { // RGB5A1 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000001f; + gmask = 0x000003e0; + bmask = 0x00007c00; + amask = 0x00008000; + } + + else if (format_ == 115) { // RGBA4 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x000000f0; + bmask = 0x00000f00; + amask = 0x0000f000; + } + + else if (format_ == 61) { // L8 + fmtbpp = 1; + has_alpha = 0; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x00000000; + } + + else if (format_ == 49) { // L8A8 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x0000ff00; + } + + else if (format_ == 112) { // L4A4 + fmtbpp = 1; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x0000000f; + bmask = 0x0000000f; + amask = 0x000000f0; + } + + fmtbpp <<= 3; + + flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); + + caps = (0x00001000); + + if (num_mipmaps == 0) + num_mipmaps = 1; + if (num_mipmaps != 1) { + flags |= (0x00020000); + caps |= ((0x00000008) | (0x00400000)); + } + + if (!(compressed)) { + flags |= (0x00000008); + + if (fmtbpp == 1 || format_ == 49) // LUMINANCE + pflags = (0x00020000); + + else // RGB + pflags = (0x00000040); + + if (has_alpha != 0) + pflags |= (0x00000001); + + size = (w * fmtbpp); + } + + else { + flags |= (0x00080000); + pflags = (0x00000004); + + size = ((w + 3) >> 2) * ((h + 3) >> 2); + if (format_ == 71 || format_ == 80 || format_ == 81) + size *= 8; + else + size *= 16; + } + + fwrite("DDS ", 1, 4, f); + u32 = 124; fwrite(&u32, 1, 4, f); + fwrite(&flags, 1, 4, f); + fwrite(&h, 1, 4, f); + fwrite(&w, 1, 4, f); + fwrite(&size, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + fwrite(&num_mipmaps, 1, 4, f); + + uint8_t thing[0x2C]; + memset(thing, 0, 0x2C); + fwrite(thing, 1, 0x2C, f); + + u32 = 32; fwrite(&u32, 1, 4, f); + fwrite(&pflags, 1, 4, f); + + if (!(compressed)) { + u32 = 0; fwrite(&u32, 1, 4, f); + } + else { + if (format_ == 71) + fwrite("DXT1", 1, 4, f); + else if (format_ == 74) + fwrite("DXT3", 1, 4, f); + else if (format_ == 77) + fwrite("DXT5", 1, 4, f); + else if (format_ == 80) + fwrite("BC4U", 1, 4, f); + else if (format_ == 81) + fwrite("BC4S", 1, 4, f); + else if (format_ == 83) + fwrite("BC5U", 1, 4, f); + else if (format_ == 84) + fwrite("BC5S", 1, 4, f); + } + + fwrite(&fmtbpp, 1, 4, f); + fwrite(&rmask, 1, 4, f); + fwrite(&gmask, 1, 4, f); + fwrite(&bmask, 1, 4, f); + fwrite(&amask, 1, 4, f); + fwrite(&caps, 1, 4, f); + + uint8_t thing2[0x10]; + memset(thing2, 0, 0x10); + fwrite(thing2, 1, 0x10, f); +} + +int readGTX(GFDData *gfd, FILE *f) { + GFDHeader header; + + if (fread(&header, 1, sizeof(header), f) != sizeof(header)) + return -1; + + if (memcmp(header.magic, "Gfx2", 4) != 0) + return -2; + + gfd->numImages = 0; + + while (!feof(f)) { + GFDBlockHeader section; + if (fread(§ion, 1, sizeof(section), f) != sizeof(section)) + break; + + if (memcmp(section.magic, "BLK{", 4) != 0) + return -100; + + if (swap32(section.type_) == 0xB) { + GFDSurface info; + + if (swap32(section.dataSize) != 0x9C) + return -200; + + if (fread(&info, 1, sizeof(info), f) != sizeof(info)) + return -201; + + gfd->dim = swap32(info.dim); + gfd->width = swap32(info.width); + gfd->height = swap32(info.height); + gfd->depth = swap32(info.depth); + gfd->numMips = swap32(info.numMips); + gfd->format = swap32(info.format_); + gfd->aa = swap32(info.aa); + gfd->use = swap32(info.use); + gfd->imageSize = swap32(info.imageSize); + gfd->imagePtr = swap32(info.imagePtr); + gfd->mipSize = swap32(info.mipSize); + gfd->mipPtr = swap32(info.mipPtr); + gfd->tileMode = swap32(info.tileMode); + gfd->swizzle = swap32(info.swizzle); + gfd->alignment = swap32(info.alignment); + gfd->pitch = swap32(info.pitch); + gfd->bpp = surfaceGetBitsPerPixel(gfd->format); + + } else if (swap32(section.type_) == 0xC) { + uint32_t bpp = gfd->bpp; + bpp /= 8; + if (isvalueinarray(gfd->format, BCn_formats, 10)) + gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; + + else + gfd->realSize = gfd->width * gfd->height * bpp; + + gfd->dataSize = swap32(section.dataSize); + gfd->data = malloc(gfd->dataSize); + if (!gfd->data) + return -300; + + if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) + return -301; + + gfd->numImages += 1; + + } else { + fseek(f, swap32(section.dataSize), SEEK_CUR); + } + } + + return 1; +} + +/* Start of swizzling section */ + +/* Credits: + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up +*/ + +static uint32_t m_banks = 4; +static uint32_t m_banksBitcount = 2; +static uint32_t m_pipes = 2; +static uint32_t m_pipesBitcount = 1; +static uint32_t m_pipeInterleaveBytes = 256; +static uint32_t m_pipeInterleaveBytesBitcount = 8; +static uint32_t m_rowSize = 2048; +static uint32_t m_swapSize = 256; +static uint32_t m_splitSize = 2048; + +static uint32_t m_chipFamily = 2; + +static uint32_t MicroTilePixels = 8 * 8; + +uint32_t computeSurfaceThickness(uint32_t tileMode) +{ + uint32_t thickness = 1; + + if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickness = 4; + + else if (tileMode == 16 || tileMode == 17) + thickness = 8; + + return thickness; +} + +uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t tileMode) +{ + uint32_t z = 0; + uint32_t thickness; + uint32_t pixelBit8; + uint32_t pixelBit7; + uint32_t pixelBit6; + uint32_t pixelBit5; + uint32_t pixelBit4; + uint32_t pixelBit3; + uint32_t pixelBit2; + uint32_t pixelBit1; + uint32_t pixelBit0; + pixelBit6 = 0; + pixelBit7 = 0; + pixelBit8 = 0; + thickness = computeSurfaceThickness(tileMode); + + 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; + } + + else if (bpp == 0x10) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x20 || bpp == 0x60) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x40) { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (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); +} + +uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { + // hardcoded to assume 2 pipes + uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; +} + +uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t bankBit0; + uint32_t bankBit0a; + uint32_t bank = 0; + + if (numBanks == 4) { + bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); + } + + else if (numBanks == 8) { + bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1); + } + + return bank; +} + +uint32_t computeSurfaceRotationFromTileMode(uint32_t tileMode) { + uint32_t pipes = m_pipes; + uint32_t result = 0; + + if (tileMode == 4 || tileMode == 5 || tileMode == 6 || tileMode == 7 || tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11) + result = pipes * ((m_banks >> 1) - 1); + + else if (tileMode == 12 || tileMode == 13 || tileMode == 14 || tileMode == 15) { + if (pipes >= 4) + result = (pipes >> 1) - 1; + + else + result = 1; + } + + return result; +} + +uint32_t isThickMacroTiled(uint32_t tileMode) { + uint32_t thickMacroTiled = 0; + + if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickMacroTiled = 1; + + return thickMacroTiled; +} + +uint32_t isBankSwappedTileMode(uint32_t tileMode) { + uint32_t bankSwapped = 0; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) + bankSwapped = 1; + + return bankSwapped; +} + +uint32_t computeMacroTileAspectRatio(uint32_t tileMode) { + uint32_t ratio = 1; + + if (tileMode == 5 || tileMode == 9) + ratio = 2; + + else if (tileMode == 6 || tileMode == 10) + ratio = 4; + + return ratio; +} + +uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_t pitch) { + if (isBankSwappedTileMode(tileMode) == 0) + return 0; + + uint32_t numSamples = 1; + uint32_t numBanks = m_banks; + uint32_t numPipes = m_pipes; + uint32_t swapSize = m_swapSize; + uint32_t rowSize = m_rowSize; + uint32_t splitSize = m_splitSize; + uint32_t groupSize = m_pipeInterleaveBytes; + uint32_t bytesPerSample = 8 * bpp; + + uint32_t samplesPerTile = splitSize / bytesPerSample; + uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); + + if (isThickMacroTiled(tileMode) != 0) + numSamples = 4; + + uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + + uint32_t factor = computeMacroTileAspectRatio(tileMode); + uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); + + uint32_t swapWidth = swapTiles * 8 * numBanks; + uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; + uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + + uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); + + while (bankSwapWidth >= (2 * pitch)) + bankSwapWidth >>= 1; + + return bankSwapWidth; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height) { + uint32_t rowOffset = y * pitch; + uint32_t pixOffset = x; + + uint32_t addr = (rowOffset + pixOffset) * bpp; + addr /= 8; + + return addr; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode) { + uint64_t microTileThickness = 1; + + if (tileMode == 3) + microTileThickness = 4; + + uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; + uint64_t microTilesPerRow = pitch >> 3; + uint64_t microTileIndexX = x >> 3; + uint64_t microTileIndexY = y >> 3; + + uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t pixelOffset = bpp * pixelIndex; + + pixelOffset >>= 3; + + return pixelOffset + microTileOffset; +} + +uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; + uint32_t numPipeBits = m_pipesBitcount; + uint32_t numBankBits = m_banksBitcount; + + uint32_t microTileThickness = computeSurfaceThickness(tileMode); + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t elemOffset = (bpp * pixelIndex) >> 3; + + uint64_t pipe = computePipeFromCoordWoRotation(x, y); + uint64_t bank = computeBankFromCoordWoRotation(x, y); + + uint64_t bankPipe = pipe + numPipes * bank; + uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; + + uint64_t macroTilePitch = 8 * m_banks; + uint64_t macroTileHeight = 8 * m_pipes; + + if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 + macroTilePitch >>= 1; + macroTileHeight *= 2; + } + + else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 + macroTilePitch >>= 2; + macroTileHeight *= 4; + } + + uint64_t macroTilesPerRow = pitch / macroTilePitch; + uint64_t macroTileBytes = (microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileIndexX = x / macroTilePitch; + uint64_t macroTileIndexY = y / macroTileHeight; + uint64_t macroTileOffset = macroTileBytes * (macroTileIndexX + macroTilesPerRow * macroTileIndexY); + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { + static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; + uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); + uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; + } + + uint64_t group_mask = (1 << numGroupBits) - 1; + uint64_t total_offset = elemOffset + (macroTileOffset >> (numBankBits + numPipeBits)); + + uint64_t offset_high = (total_offset & ~(group_mask)) << (numBankBits + numPipeBits); + uint64_t offset_low = total_offset & group_mask; + uint64_t bank_bits = bank << (numPipeBits + numGroupBits); + uint64_t pipe_bits = pipe << numGroupBits; + + return bank_bits | pipe_bits | offset_low | offset_high; +} + +void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { + int y; + uint32_t format; + + if (gfd->format == 0x1a || gfd->format == 0x41a) + format = 28; + else if (gfd->format == 0x19) + format = 24; + else if (gfd->format == 0x8) + format = 85; + else if (gfd->format == 0xa) + format = 86; + else if (gfd->format == 0xb) + format = 115; + else if (gfd->format == 0x1) + format = 61; + else if (gfd->format == 0x7) + format = 49; + else if (gfd->format == 0x2) + format = 112; + else if (gfd->format == 0x31 || gfd->format == 0x431) + format = 71; + else if (gfd->format == 0x32 || gfd->format == 0x432) + format = 74; + else if (gfd->format == 0x33 || gfd->format == 0x433) + format = 77; + else if (gfd->format == 0x34) + format = 80; + else if (gfd->format == 0x234) + format = 81; + else if (gfd->format == 0x35) + format = 83; + else if (gfd->format == 0x235) + format = 84; + + writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); + + uint32_t bpp = gfd->bpp; + bpp /= 8; + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + bpp /= 2; + bpp = max(1, bpp); + } + + for (y = 0; y < gfd->height; y++) { + if ((y * gfd->width * bpp) >= gfd->realSize) + break; + + fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); + } +} + +void swizzle_8(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + uint8_t *source, *output; + double frac, whole; + + source = (uint8_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +void swizzle_16(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + uint16_t *source, *output; + double frac, whole; + + source = (uint16_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +void swizzle_32(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + uint32_t *source, *output; + double frac, whole; + + source = (uint32_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +void swizzle_64(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + uint64_t *source, *output; + double frac, whole; + + source = (uint64_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); + +} + +void swizzle_128(GFDData *gfd, FILE *f) { + uint64_t pos; + uint32_t x, y, width, height; + __uint128_t *source, *output; + double frac, whole; + + source = (__uint128_t *)gfd->data; + output = malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, BCn_formats, 10)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + output[y * width + x] = source[pos / bpp]; + } + } + + writeFile(f, gfd, (uint8_t *)output); + + free(output); +} + +char *remove_three(const char *filename) { + size_t len = strlen(filename); + char *newfilename = malloc(len-2); + if (!newfilename) /* handle error */; + memcpy(newfilename, filename, len-3); + newfilename[len - 3] = 0; + return newfilename; +} + +int main(int argc, char **argv) { + GFDData data; + FILE *f; + int result; + + printf("GTX Extractor - C ver.\n"); + printf("(C) 2014 Treeki, 2017 AboodXD\n"); + + if (argc != 2) { + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported formats:\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); + return EXIT_FAILURE; + } + + if (!(f = fopen(argv[1], "rb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + return EXIT_FAILURE; + } + + else + printf("\nConverting: %s\n", argv[1]); + + if ((result = readGTX(&data, f)) != 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); + fclose(f); + return EXIT_FAILURE; + } + fclose(f); + + if (data.numImages > 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO + return EXIT_FAILURE; + } + + else if (data.numImages == 0) { + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); + return EXIT_FAILURE; + } + + char *str = remove_three(argv[1]); + char c = 'd'; + char c1 = 'd'; + char c2 = 's'; + + size_t len = strlen(str); + char *str2 = malloc(len + 3 + 1 ); + strcpy(str2, str); + str2[len] = c; + str2[len + 1] = c1; + str2[len + 2] = c2; + str2[len + 3] = '\0'; + + if (!(f = fopen(str2, "wb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for writing\n", str2); + return EXIT_FAILURE; + } + + free(str2); + + printf("\n"); + printf("// ----- GX2Surface Info ----- \n"); + printf(" dim = %d\n", data.dim); + printf(" width = %d\n", data.width); + printf(" height = %d\n", data.height); + printf(" depth = %d\n", data.depth); + printf(" numMips = %d\n", data.numMips); + printf(" format = 0x%x\n", data.format); + printf(" aa = %d\n", data.aa); + printf(" use = %d\n", data.use); + printf(" imageSize = %d\n", data.imageSize); + printf(" mipSize = %d\n", data.mipSize); + printf(" tileMode = %d\n", data.tileMode); + printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); + printf(" alignment = %d\n", data.alignment); + printf(" pitch = %d\n", data.pitch); + printf("\n"); + printf(" bits per pixel = %d\n", data.bpp); + printf(" bytes per pixel = %d\n", data.bpp / 8); + printf(" realSize = %d\n", data.realSize); + + uint32_t bpp = data.bpp; + + if (isvalueinarray(data.format, formats, 19)) { + if (bpp == 8) + swizzle_8(&data, f); + else if (bpp == 16) + swizzle_16(&data, f); + else if (bpp == 32) + swizzle_32(&data, f); + else if (bpp == 64) + swizzle_64(&data, f); + else if (bpp == 128) + swizzle_128(&data, f); + } + + else { + fprintf(stderr, "Unsupported format: 0x%x\n", data.format); + return EXIT_FAILURE; + } + + fclose(f); + + printf("\nFinished converting: %s\n", argv[1]); + + return EXIT_SUCCESS; +} + From 1ebd78562eefd8395e8c7347d6f678faba4d1901 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Tue, 6 Jun 2017 21:24:22 +0400 Subject: [PATCH 12/19] Calculate new pitch --- gtx_extract.c | 27 ++++++++++++++++++++++++++- gtx_extract_no5.c | 27 ++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index 2651c18..e1b2665 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -334,6 +334,31 @@ void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t fwrite(thing2, 1, 0x10, f); } +uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { + uint32_t bpp = surfaceGetBitsPerPixel(format_) >> 3; + double frac, whole; + + if (isvalueinarray(format_, BCn_formats, 10)) { + double width2 = (double)width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + } + + uint32_t pitch = 1; + uint32_t z = 1; + while ((pitch < width) || (pitch < org_pitch)) { + pitch = bpp*z; + z += 1; + } + + if (pitch < 1) + pitch = 1; + + return pitch; +} + int readGTX(GFDData *gfd, FILE *f) { GFDHeader header; @@ -377,7 +402,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->tileMode = swap32(info.tileMode); gfd->swizzle = swap32(info.swizzle); gfd->alignment = swap32(info.alignment); - gfd->pitch = swap32(info.pitch); + gfd->pitch = cal_pitch(gfd->width, gfd->format, swap32(info.pitch)); gfd->bpp = surfaceGetBitsPerPixel(gfd->format); } else if (swap32(section.type_) == 0xC) { diff --git a/gtx_extract_no5.c b/gtx_extract_no5.c index 55baf2a..791e52f 100644 --- a/gtx_extract_no5.c +++ b/gtx_extract_no5.c @@ -333,6 +333,31 @@ void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t fwrite(thing2, 1, 0x10, f); } +uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { + uint32_t bpp = surfaceGetBitsPerPixel(format_) >> 3; + double frac, whole; + + if (isvalueinarray(format_, BCn_formats, 10)) { + double width2 = (double)width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + } + + uint32_t pitch = 1; + uint32_t z = 1; + while ((pitch < width) || (pitch < org_pitch)) { + pitch = bpp*z; + z += 1; + } + + if (pitch < 1) + pitch = 1; + + return pitch; +} + int readGTX(GFDData *gfd, FILE *f) { GFDHeader header; @@ -376,7 +401,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->tileMode = swap32(info.tileMode); gfd->swizzle = swap32(info.swizzle); gfd->alignment = swap32(info.alignment); - gfd->pitch = swap32(info.pitch); + gfd->pitch = cal_pitch(gfd->width, gfd->format, swap32(info.pitch)); gfd->bpp = surfaceGetBitsPerPixel(gfd->format); } else if (swap32(section.type_) == 0xC) { From 7437188c8c446f884247068295c2efbc4424afa9 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Wed, 7 Jun 2017 15:16:35 +0400 Subject: [PATCH 13/19] Fix a bug in swizzling code, and pitch calculator --- gtx_extract.c | 69 ++++++++++++++++++++++++++++++++++++----------- gtx_extract_no5.c | 69 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 108 insertions(+), 30 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index e1b2665..fede652 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -334,11 +334,17 @@ void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t fwrite(thing2, 1, 0x10, f); } -uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { - uint32_t bpp = surfaceGetBitsPerPixel(format_) >> 3; +uint32_t cal_pitch(uint32_t width, uint32_t format_) { + uint32_t bpp = surfaceGetBitsPerPixel(format_); double frac, whole; if (isvalueinarray(format_, BCn_formats, 10)) { + double bpp2 = (double)bpp / 4.0; + frac = modf(bpp2, &whole); + bpp = (uint32_t)whole; + if (frac == 0.5) + bpp += 1; + double width2 = (double)width / 4.0; frac = modf(width2, &whole); width = (uint32_t)whole; @@ -348,7 +354,7 @@ uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { uint32_t pitch = 1; uint32_t z = 1; - while ((pitch < width) || (pitch < org_pitch)) { + while (pitch < width) { pitch = bpp*z; z += 1; } @@ -402,7 +408,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->tileMode = swap32(info.tileMode); gfd->swizzle = swap32(info.swizzle); gfd->alignment = swap32(info.alignment); - gfd->pitch = cal_pitch(gfd->width, gfd->format, swap32(info.pitch)); + gfd->pitch = cal_pitch(gfd->width, gfd->format); gfd->bpp = surfaceGetBitsPerPixel(gfd->format); } else if (swap32(section.type_) == 0xC) { @@ -637,7 +643,7 @@ uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_ uint32_t rowSize = m_rowSize; uint32_t splitSize = m_splitSize; uint32_t groupSize = m_pipeInterleaveBytes; - uint32_t bytesPerSample = 8 * bpp; + uint32_t bytesPerSample = 8 * bpp & 0x1FFFFFFF; uint32_t samplesPerTile = splitSize / bytesPerSample; uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); @@ -696,6 +702,9 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, u } uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { + uint64_t samplesPerSlice, numSampleSplits; + uint64_t numSamples, sampleSlice; + uint32_t numPipes = m_pipes; uint32_t numBanks = m_banks; uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; @@ -704,9 +713,29 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u uint32_t microTileThickness = computeSurfaceThickness(tileMode); + uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); + uint64_t microTileBytes = (microTileBits + 7) / 8; + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - uint64_t elemOffset = (bpp * pixelIndex) >> 3; + uint64_t pixelOffset = bpp * pixelIndex; + + uint64_t elemOffset = pixelOffset; + + uint64_t bytesPerSample = microTileBytes; + if (microTileBytes <= m_splitSize) { + numSamples = 1; + sampleSlice = 0; + } + else { + samplesPerSlice = m_splitSize / bytesPerSample; + numSampleSplits = max(1, 1 / samplesPerSlice); + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + elemOffset += 7; + elemOffset /= 8; uint64_t pipe = computePipeFromCoordWoRotation(x, y); uint64_t bank = computeBankFromCoordWoRotation(x, y); @@ -714,10 +743,16 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u uint64_t bankPipe = pipe + numPipes * bank; uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + uint64_t swizzle = pipeSwizzle + numPipes * bankSwizzle; + + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle; bankPipe %= numPipes * numBanks; pipe = bankPipe % numPipes; bank = bankPipe / numPipes; + uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); + uint64_t macroTilePitch = 8 * m_banks; uint64_t macroTileHeight = 8 * m_pipes; @@ -732,10 +767,10 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u } uint64_t macroTilesPerRow = pitch / macroTilePitch; - uint64_t macroTileBytes = (microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; uint64_t macroTileIndexX = x / macroTilePitch; uint64_t macroTileIndexY = y / macroTileHeight; - uint64_t macroTileOffset = macroTileBytes * (macroTileIndexX + macroTilesPerRow * macroTileIndexY); + uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * (macroTileIndexY)) * macroTileBytes; if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; @@ -744,15 +779,19 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; } - uint64_t group_mask = (1 << numGroupBits) - 1; - uint64_t total_offset = elemOffset + (macroTileOffset >> (numBankBits + numPipeBits)); + uint64_t groupMask = ((1 << numGroupBits) - 1); + + uint64_t numSwizzleBits = (numBankBits + numPipeBits); + + uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); + + uint64_t offsetHigh = (totalOffset & ~(groupMask)) << numSwizzleBits; + uint64_t offsetLow = groupMask & totalOffset; - uint64_t offset_high = (total_offset & ~(group_mask)) << (numBankBits + numPipeBits); - uint64_t offset_low = total_offset & group_mask; - uint64_t bank_bits = bank << (numPipeBits + numGroupBits); - uint64_t pipe_bits = pipe << numGroupBits; + uint64_t pipeBits = pipe << numGroupBits; + uint64_t bankBits = bank << (numPipeBits + numGroupBits); - return bank_bits | pipe_bits | offset_low | offset_high; + return bankBits | pipeBits | offsetLow | offsetHigh; } void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { diff --git a/gtx_extract_no5.c b/gtx_extract_no5.c index 791e52f..45333be 100644 --- a/gtx_extract_no5.c +++ b/gtx_extract_no5.c @@ -333,11 +333,17 @@ void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t fwrite(thing2, 1, 0x10, f); } -uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { - uint32_t bpp = surfaceGetBitsPerPixel(format_) >> 3; +uint32_t cal_pitch(uint32_t width, uint32_t format_) { + uint32_t bpp = surfaceGetBitsPerPixel(format_); double frac, whole; if (isvalueinarray(format_, BCn_formats, 10)) { + double bpp2 = (double)bpp / 4.0; + frac = modf(bpp2, &whole); + bpp = (uint32_t)whole; + if (frac == 0.5) + bpp += 1; + double width2 = (double)width / 4.0; frac = modf(width2, &whole); width = (uint32_t)whole; @@ -347,7 +353,7 @@ uint32_t cal_pitch(uint32_t width, uint32_t format_, uint32_t org_pitch) { uint32_t pitch = 1; uint32_t z = 1; - while ((pitch < width) || (pitch < org_pitch)) { + while (pitch < width) { pitch = bpp*z; z += 1; } @@ -401,7 +407,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->tileMode = swap32(info.tileMode); gfd->swizzle = swap32(info.swizzle); gfd->alignment = swap32(info.alignment); - gfd->pitch = cal_pitch(gfd->width, gfd->format, swap32(info.pitch)); + gfd->pitch = cal_pitch(gfd->width, gfd->format); gfd->bpp = surfaceGetBitsPerPixel(gfd->format); } else if (swap32(section.type_) == 0xC) { @@ -636,7 +642,7 @@ uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_ uint32_t rowSize = m_rowSize; uint32_t splitSize = m_splitSize; uint32_t groupSize = m_pipeInterleaveBytes; - uint32_t bytesPerSample = 8 * bpp; + uint32_t bytesPerSample = 8 * bpp & 0x1FFFFFFF; uint32_t samplesPerTile = splitSize / bytesPerSample; uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); @@ -695,6 +701,9 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, u } uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { + uint64_t samplesPerSlice, numSampleSplits; + uint64_t numSamples, sampleSlice; + uint32_t numPipes = m_pipes; uint32_t numBanks = m_banks; uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; @@ -703,9 +712,29 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u uint32_t microTileThickness = computeSurfaceThickness(tileMode); + uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); + uint64_t microTileBytes = (microTileBits + 7) / 8; + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - uint64_t elemOffset = (bpp * pixelIndex) >> 3; + uint64_t pixelOffset = bpp * pixelIndex; + + uint64_t elemOffset = pixelOffset; + + uint64_t bytesPerSample = microTileBytes; + if (microTileBytes <= m_splitSize) { + numSamples = 1; + sampleSlice = 0; + } + else { + samplesPerSlice = m_splitSize / bytesPerSample; + numSampleSplits = max(1, 1 / samplesPerSlice); + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + elemOffset += 7; + elemOffset /= 8; uint64_t pipe = computePipeFromCoordWoRotation(x, y); uint64_t bank = computeBankFromCoordWoRotation(x, y); @@ -713,10 +742,16 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u uint64_t bankPipe = pipe + numPipes * bank; uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + uint64_t swizzle = pipeSwizzle + numPipes * bankSwizzle; + + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle; bankPipe %= numPipes * numBanks; pipe = bankPipe % numPipes; bank = bankPipe / numPipes; + uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); + uint64_t macroTilePitch = 8 * m_banks; uint64_t macroTileHeight = 8 * m_pipes; @@ -731,10 +766,10 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u } uint64_t macroTilesPerRow = pitch / macroTilePitch; - uint64_t macroTileBytes = (microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; uint64_t macroTileIndexX = x / macroTilePitch; uint64_t macroTileIndexY = y / macroTileHeight; - uint64_t macroTileOffset = macroTileBytes * (macroTileIndexX + macroTilesPerRow * macroTileIndexY); + uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * (macroTileIndexY)) * macroTileBytes; if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; @@ -743,15 +778,19 @@ uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, u bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; } - uint64_t group_mask = (1 << numGroupBits) - 1; - uint64_t total_offset = elemOffset + (macroTileOffset >> (numBankBits + numPipeBits)); + uint64_t groupMask = ((1 << numGroupBits) - 1); + + uint64_t numSwizzleBits = (numBankBits + numPipeBits); + + uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); + + uint64_t offsetHigh = (totalOffset & ~(groupMask)) << numSwizzleBits; + uint64_t offsetLow = groupMask & totalOffset; - uint64_t offset_high = (total_offset & ~(group_mask)) << (numBankBits + numPipeBits); - uint64_t offset_low = total_offset & group_mask; - uint64_t bank_bits = bank << (numPipeBits + numGroupBits); - uint64_t pipe_bits = pipe << numGroupBits; + uint64_t pipeBits = pipe << numGroupBits; + uint64_t bankBits = bank << (numPipeBits + numGroupBits); - return bank_bits | pipe_bits | offset_low | offset_high; + return bankBits | pipeBits | offsetLow | offsetHigh; } void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { From 94a2e1c517465277abba1de32bd64f69a9a39cba Mon Sep 17 00:00:00 2001 From: AboodXD Date: Fri, 14 Jul 2017 21:59:54 +0400 Subject: [PATCH 14/19] Fix indent blocks, add 32bit support --- gtx_extract.c | 1526 +++++++++++++++++++-------------------------- gtx_extract_no5.c | 1478 +++++++++++++++++++------------------------ 2 files changed, 1292 insertions(+), 1712 deletions(-) diff --git a/gtx_extract.c b/gtx_extract.c index fede652..ca0bdeb 100644 --- a/gtx_extract.c +++ b/gtx_extract.c @@ -39,14 +39,13 @@ static int formats[] = {0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2, 0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; // Supported formats static int BCn_formats[10] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; - +// isvalueinarray(): find if a certain value is in a certain array bool isvalueinarray(int val, int *arr, int size){ - int i; - for (i=0; i < size; i++) { - if (arr[i] == val) - return true; - } - return false; + for (int i = 0; i < size; i++) { + if (arr[i] == val) + return true; + } + return false; } @@ -72,63 +71,68 @@ uint8_t formatHwInfo[0x40*4] = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) { - uint32_t hwFormat = surfaceFormat&0x3F; - uint32_t bpp = formatHwInfo[hwFormat*4+0]; + uint32_t hwFormat = (surfaceFormat & 0x3F) * 4; + uint32_t bpp = formatHwInfo[hwFormat]; return bpp; } /* Start of GTX Extractor section */ typedef struct _GFDData { - uint32_t numImages; + uint32_t numImages; uint32_t dim; uint32_t width; uint32_t height; uint32_t depth; uint32_t numMips; - uint32_t format; - uint32_t aa; - uint32_t use; - uint32_t imageSize; - uint32_t imagePtr; - uint32_t mipSize; - uint32_t mipPtr; - uint32_t tileMode; - uint32_t swizzle; - uint32_t alignment; - uint32_t pitch; - uint32_t bpp; + uint32_t format; + uint32_t aa; + uint32_t use; + uint32_t imageSize; + uint32_t imagePtr; + uint32_t mipSize; + uint32_t mipPtr; + uint32_t tileMode; + uint32_t swizzle; + uint32_t alignment; + uint32_t pitch; + uint32_t bpp; uint32_t realSize; uint32_t dataSize; uint8_t *data; } GFDData; + typedef struct _GFDHeader { char magic[4]; uint32_t size_, majorVersion, minorVersion; uint32_t gpuVersion, alignMode, reserved1, reserved2; } GFDHeader; + + typedef struct _GFDBlockHeader { char magic[4]; uint32_t size_, majorVersion, minorVersion, type_; uint32_t dataSize; uint32_t id, typeIdx; } GFDBlockHeader; + + typedef struct _GFDSurface { uint32_t dim, width, height, depth; uint32_t numMips, format_, aa, use; uint32_t imageSize, imagePtr, mipSize, mipPtr; uint32_t tileMode, swizzle, alignment, pitch; - uint32_t _40, _44, _48, _4C; - uint32_t _50, _54, _58, _5C; - uint32_t _60, _64, _68, _6C; - uint32_t _70, _74, _78, _7C; - uint32_t _80, _84, _88, _8C; - uint32_t _90, _94, _98; + uint32_t mipOffset[13]; + uint32_t mip1, numMips_, slice1, numSlices; + uint8_t compSel[4]; + uint32_t texReg[5]; } GFDSurface; +// swap32(): swaps the endianness uint32_t swap32(uint32_t v) { uint32_t a = (v & 0xFF000000) >> 24; uint32_t b = (v & 0x00FF0000) >> 8; @@ -137,6 +141,8 @@ uint32_t swap32(uint32_t v) { return a|b|c|d; } + + /* Start of DDS writer section */ /* @@ -162,209 +168,187 @@ uint32_t swap32(uint32_t v) { * Feel free to include this in your own program if you want, just give credits. :) */ + // writeHeader(): writes a DDS header according to the given values void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t format_, bool compressed) { - uint32_t fmtbpp = 0; - uint32_t has_alpha; - uint32_t rmask = 0; - uint32_t gmask = 0; - uint32_t bmask = 0; - uint32_t amask = 0; - uint32_t flags; - uint32_t pflags; - uint32_t caps; - uint32_t size; - uint32_t u32; - - if (format_ == 28) { // RGBA8 - fmtbpp = 4; - has_alpha = 1; - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0xff000000; - } - - else if (format_ == 24) { // RGB10A2 - fmtbpp = 4; - has_alpha = 1; - rmask = 0x000003ff; - gmask = 0x000ffc00; - bmask = 0x3ff00000; - amask = 0xc0000000; - } - - else if (format_ == 85) { // RGB565 - fmtbpp = 2; - has_alpha = 0; - rmask = 0x0000001f; - gmask = 0x000007e0; - bmask = 0x0000f800; - amask = 0x00000000; - } - - else if (format_ == 86) { // RGB5A1 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x0000001f; - gmask = 0x000003e0; - bmask = 0x00007c00; - amask = 0x00008000; - } - - else if (format_ == 115) { // RGBA4 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x0000000f; - gmask = 0x000000f0; - bmask = 0x00000f00; - amask = 0x0000f000; - } - - else if (format_ == 61) { // L8 - fmtbpp = 1; - has_alpha = 0; - rmask = 0x000000ff; - gmask = 0x000000ff; - bmask = 0x000000ff; - amask = 0x00000000; - } - - else if (format_ == 49) { // L8A8 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x000000ff; - gmask = 0x000000ff; - bmask = 0x000000ff; - amask = 0x0000ff00; - } - - else if (format_ == 112) { // L4A4 - fmtbpp = 1; - has_alpha = 1; - rmask = 0x0000000f; - gmask = 0x0000000f; - bmask = 0x0000000f; - amask = 0x000000f0; - } - - fmtbpp <<= 3; - - flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); - - caps = (0x00001000); - - if (num_mipmaps == 0) - num_mipmaps = 1; - if (num_mipmaps != 1) { - flags |= (0x00020000); - caps |= ((0x00000008) | (0x00400000)); - } - - if (!(compressed)) { - flags |= (0x00000008); - - if (fmtbpp == 1 || format_ == 49) // LUMINANCE - pflags = (0x00020000); - - else // RGB - pflags = (0x00000040); - - if (has_alpha != 0) - pflags |= (0x00000001); - - size = (w * fmtbpp); - } - - else { - flags |= (0x00080000); - pflags = (0x00000004); - - size = ((w + 3) >> 2) * ((h + 3) >> 2); - if (format_ == 71 || format_ == 80 || format_ == 81) - size *= 8; - else - size *= 16; - } - - fwrite("DDS ", 1, 4, f); - u32 = 124; fwrite(&u32, 1, 4, f); - fwrite(&flags, 1, 4, f); - fwrite(&h, 1, 4, f); - fwrite(&w, 1, 4, f); - fwrite(&size, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); - fwrite(&num_mipmaps, 1, 4, f); - - uint8_t thing[0x2C]; + uint32_t fmtbpp = 0; + uint32_t has_alpha; + uint32_t rmask = 0; + uint32_t gmask = 0; + uint32_t bmask = 0; + uint32_t amask = 0; + uint32_t flags; + uint32_t pflags; + uint32_t caps; + uint32_t size; + uint32_t u32; + + if (format_ == 28) { // RGBA8 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + } + + else if (format_ == 24) { // RGB10A2 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000003ff; + gmask = 0x000ffc00; + bmask = 0x3ff00000; + amask = 0xc0000000; + } + + else if (format_ == 85) { // RGB565 + fmtbpp = 2; + has_alpha = 0; + rmask = 0x0000001f; + gmask = 0x000007e0; + bmask = 0x0000f800; + amask = 0x00000000; + } + + else if (format_ == 86) { // RGB5A1 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000001f; + gmask = 0x000003e0; + bmask = 0x00007c00; + amask = 0x00008000; + } + + else if (format_ == 115) { // RGBA4 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x000000f0; + bmask = 0x00000f00; + amask = 0x0000f000; + } + + else if (format_ == 61) { // L8 + fmtbpp = 1; + has_alpha = 0; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x00000000; + } + + else if (format_ == 49) { // L8A8 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x0000ff00; + } + + else if (format_ == 112) { // L4A4 + fmtbpp = 1; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x0000000f; + bmask = 0x0000000f; + amask = 0x000000f0; + } + + fmtbpp <<= 3; + + flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); + + caps = (0x00001000); + + if (num_mipmaps == 0) + num_mipmaps = 1; + if (num_mipmaps != 1) { + flags |= (0x00020000); + caps |= ((0x00000008) | (0x00400000)); + } + + if (!(compressed)) { + flags |= (0x00000008); + + if (fmtbpp == 1 || format_ == 49) // LUMINANCE + pflags = (0x00020000); + + else // RGB + pflags = (0x00000040); + + if (has_alpha != 0) + pflags |= (0x00000001); + + size = (w * fmtbpp); + } + + else { + flags |= (0x00080000); + pflags = (0x00000004); + + size = ((w + 3) >> 2) * ((h + 3) >> 2); + if (format_ == 71 || format_ == 80 || format_ == 81) + size *= 8; + else + size *= 16; + } + + fwrite("DDS ", 1, 4, f); + u32 = 124; fwrite(&u32, 1, 4, f); + fwrite(&flags, 1, 4, f); + fwrite(&h, 1, 4, f); + fwrite(&w, 1, 4, f); + fwrite(&size, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + fwrite(&num_mipmaps, 1, 4, f); + + uint8_t thing[0x2C]; memset(thing, 0, 0x2C); fwrite(thing, 1, 0x2C, f); u32 = 32; fwrite(&u32, 1, 4, f); - fwrite(&pflags, 1, 4, f); - - if (!(compressed)) { - u32 = 0; fwrite(&u32, 1, 4, f); - } - else { - if (format_ == 71) - fwrite("DXT1", 1, 4, f); - else if (format_ == 74) - fwrite("DXT3", 1, 4, f); - else if (format_ == 77) - fwrite("DXT5", 1, 4, f); - else if (format_ == 80) - fwrite("BC4U", 1, 4, f); - else if (format_ == 81) - fwrite("BC4S", 1, 4, f); - else if (format_ == 83) - fwrite("BC5U", 1, 4, f); - else if (format_ == 84) - fwrite("BC5S", 1, 4, f); - } - - fwrite(&fmtbpp, 1, 4, f); - fwrite(&rmask, 1, 4, f); - fwrite(&gmask, 1, 4, f); - fwrite(&bmask, 1, 4, f); - fwrite(&amask, 1, 4, f); - fwrite(&caps, 1, 4, f); - - uint8_t thing2[0x10]; + fwrite(&pflags, 1, 4, f); + + if (!(compressed)) { + u32 = 0; fwrite(&u32, 1, 4, f); + } + + else { + if (format_ == 71) + fwrite("DXT1", 1, 4, f); + + else if (format_ == 74) + fwrite("DXT3", 1, 4, f); + + else if (format_ == 77) + fwrite("DXT5", 1, 4, f); + + else if (format_ == 80) + fwrite("BC4U", 1, 4, f); + + else if (format_ == 81) + fwrite("BC4S", 1, 4, f); + + else if (format_ == 83) + fwrite("BC5U", 1, 4, f); + + else if (format_ == 84) + fwrite("BC5S", 1, 4, f); + } + + fwrite(&fmtbpp, 1, 4, f); + fwrite(&rmask, 1, 4, f); + fwrite(&gmask, 1, 4, f); + fwrite(&bmask, 1, 4, f); + fwrite(&amask, 1, 4, f); + fwrite(&caps, 1, 4, f); + + uint8_t thing2[0x10]; memset(thing2, 0, 0x10); fwrite(thing2, 1, 0x10, f); } -uint32_t cal_pitch(uint32_t width, uint32_t format_) { - uint32_t bpp = surfaceGetBitsPerPixel(format_); - double frac, whole; - - if (isvalueinarray(format_, BCn_formats, 10)) { - double bpp2 = (double)bpp / 4.0; - frac = modf(bpp2, &whole); - bpp = (uint32_t)whole; - if (frac == 0.5) - bpp += 1; - - double width2 = (double)width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - } - - uint32_t pitch = 1; - uint32_t z = 1; - while (pitch < width) { - pitch = bpp*z; - z += 1; - } - - if (pitch < 1) - pitch = 1; - - return pitch; -} - +// readGTX(): reads the GTX file int readGTX(GFDData *gfd, FILE *f) { GFDHeader header; @@ -374,7 +358,7 @@ int readGTX(GFDData *gfd, FILE *f) { if (memcmp(header.magic, "Gfx2", 4) != 0) return -2; - gfd->numImages = 0; + gfd->numImages = 0; while (!feof(f)) { GFDBlockHeader section; @@ -393,44 +377,50 @@ int readGTX(GFDData *gfd, FILE *f) { if (fread(&info, 1, sizeof(info), f) != sizeof(info)) return -201; + gfd->dim = swap32(info.dim); - gfd->width = swap32(info.width); - gfd->height = swap32(info.height); - gfd->depth = swap32(info.depth); - gfd->numMips = swap32(info.numMips); - gfd->format = swap32(info.format_); - gfd->aa = swap32(info.aa); - gfd->use = swap32(info.use); - gfd->imageSize = swap32(info.imageSize); - gfd->imagePtr = swap32(info.imagePtr); - gfd->mipSize = swap32(info.mipSize); - gfd->mipPtr = swap32(info.mipPtr); - gfd->tileMode = swap32(info.tileMode); - gfd->swizzle = swap32(info.swizzle); - gfd->alignment = swap32(info.alignment); - gfd->pitch = cal_pitch(gfd->width, gfd->format); - gfd->bpp = surfaceGetBitsPerPixel(gfd->format); - - } else if (swap32(section.type_) == 0xC) { - uint32_t bpp = gfd->bpp; - bpp /= 8; - if (isvalueinarray(gfd->format, BCn_formats, 10)) - gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; - - else - gfd->realSize = gfd->width * gfd->height * bpp; - - gfd->dataSize = swap32(section.dataSize); - gfd->data = malloc(gfd->dataSize); + gfd->width = swap32(info.width); + gfd->height = swap32(info.height); + gfd->depth = swap32(info.depth); + gfd->numMips = swap32(info.numMips); + gfd->format = swap32(info.format_); + gfd->aa = swap32(info.aa); + gfd->use = swap32(info.use); + gfd->imageSize = swap32(info.imageSize); + gfd->imagePtr = swap32(info.imagePtr); + gfd->mipSize = swap32(info.mipSize); + gfd->mipPtr = swap32(info.mipPtr); + gfd->tileMode = swap32(info.tileMode); + gfd->swizzle = swap32(info.swizzle); + gfd->alignment = swap32(info.alignment); + gfd->pitch = swap32(info.pitch); + gfd->bpp = surfaceGetBitsPerPixel(gfd->format); + + } + + else if (swap32(section.type_) == 0xC) { + uint32_t bpp = gfd->bpp; + bpp /= 8; + + if (isvalueinarray(gfd->format, BCn_formats, 10)) + gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; + + else + gfd->realSize = gfd->width * gfd->height * bpp; + + gfd->dataSize = swap32(section.dataSize); + gfd->data = malloc(gfd->dataSize); if (!gfd->data) return -300; if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) return -301; - gfd->numImages += 1; + gfd->numImages += 1; + + } - } else { + else { fseek(f, swap32(section.dataSize), SEEK_CUR); } } @@ -438,12 +428,13 @@ int readGTX(GFDData *gfd, FILE *f) { return 1; } + /* Start of swizzling section */ /* Credits: - -AddrLib: actual code - -Exzap: modifying code to apply to Wii U textures - -AboodXD: porting, code improvements and cleaning up + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up */ static uint32_t m_banks = 4; @@ -458,25 +449,26 @@ static uint32_t m_splitSize = 2048; static uint32_t m_chipFamily = 2; -static uint32_t MicroTilePixels = 8 * 8; +static uint32_t MicroTilePixels = 64; uint32_t computeSurfaceThickness(uint32_t tileMode) { - uint32_t thickness = 1; + uint32_t thickness = 1; - if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) - thickness = 4; + if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickness = 4; - else if (tileMode == 16 || tileMode == 17) - thickness = 8; + else if (tileMode == 16 || tileMode == 17) + thickness = 8; return thickness; } + uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t tileMode) { - uint32_t z = 0; - uint32_t thickness; + uint32_t z = 0; + uint32_t thickness; uint32_t pixelBit8; uint32_t pixelBit7; uint32_t pixelBit6; @@ -486,788 +478,587 @@ uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pixelBit2; uint32_t pixelBit1; uint32_t pixelBit0; - pixelBit6 = 0; - pixelBit7 = 0; - pixelBit8 = 0; - thickness = computeSurfaceThickness(tileMode); - - 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; - } - - else if (bpp == 0x10) { - pixelBit0 = x & 1; - pixelBit1 = (x & 2) >> 1; - pixelBit2 = (x & 4) >> 2; - pixelBit3 = y & 1; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (bpp == 0x20 || bpp == 0x60) { - pixelBit0 = x & 1; - pixelBit1 = (x & 2) >> 1; - pixelBit2 = y & 1; - pixelBit3 = (x & 4) >> 2; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (bpp == 0x40) { - pixelBit0 = x & 1; - pixelBit1 = y & 1; - pixelBit2 = (x & 2) >> 1; - pixelBit3 = (x & 4) >> 2; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (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); -} + pixelBit6 = 0; + pixelBit7 = 0; + pixelBit8 = 0; + thickness = computeSurfaceThickness(tileMode); + + 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; + } -uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { - // hardcoded to assume 2 pipes - uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; - return pipe; -} + else if (bpp == 0x10) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } -uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { - uint32_t numPipes = m_pipes; - uint32_t numBanks = m_banks; - uint32_t bankBit0; - uint32_t bankBit0a; - uint32_t bank = 0; - - if (numBanks == 4) { - bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; - bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); - } - - else if (numBanks == 8) { - bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; - bank = bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1); - } - - return bank; + else if (bpp == 0x20 || bpp == 0x60) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x40) { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (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); } -uint32_t computeSurfaceRotationFromTileMode(uint32_t tileMode) { - uint32_t pipes = m_pipes; - uint32_t result = 0; - if (tileMode == 4 || tileMode == 5 || tileMode == 6 || tileMode == 7 || tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11) - result = pipes * ((m_banks >> 1) - 1); +uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { + // hardcoded to assume 2 pipes + uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; +} + - else if (tileMode == 12 || tileMode == 13 || tileMode == 14 || tileMode == 15) { - if (pipes >= 4) - result = (pipes >> 1) - 1; +uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t bankBit0; + uint32_t bankBit0a; + uint32_t bank = 0; + + if (numBanks == 4) { + bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); + } - else - result = 1; - } + else if (numBanks == 8) { + bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; + bank = (bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | + 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1)); + } - return result; + return bank; } + uint32_t isThickMacroTiled(uint32_t tileMode) { - uint32_t thickMacroTiled = 0; + uint32_t thickMacroTiled = 0; - if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) - thickMacroTiled = 1; + if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickMacroTiled = 1; - return thickMacroTiled; + return thickMacroTiled; } + uint32_t isBankSwappedTileMode(uint32_t tileMode) { - uint32_t bankSwapped = 0; + uint32_t bankSwapped = 0; - if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) - bankSwapped = 1; + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) + bankSwapped = 1; - return bankSwapped; + return bankSwapped; } + uint32_t computeMacroTileAspectRatio(uint32_t tileMode) { - uint32_t ratio = 1; + uint32_t ratio = 1; - if (tileMode == 5 || tileMode == 9) - ratio = 2; + if (tileMode == 5 || tileMode == 9) + ratio = 2; - else if (tileMode == 6 || tileMode == 10) - ratio = 4; + else if (tileMode == 6 || tileMode == 10) + ratio = 4; - return ratio; + return ratio; } + uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_t pitch) { - if (isBankSwappedTileMode(tileMode) == 0) - return 0; + if (isBankSwappedTileMode(tileMode) == 0) + return 0; - uint32_t numSamples = 1; - uint32_t numBanks = m_banks; - uint32_t numPipes = m_pipes; - uint32_t swapSize = m_swapSize; - uint32_t rowSize = m_rowSize; - uint32_t splitSize = m_splitSize; - uint32_t groupSize = m_pipeInterleaveBytes; - uint32_t bytesPerSample = 8 * bpp & 0x1FFFFFFF; + uint32_t numSamples = 1; + uint32_t numBanks = m_banks; + uint32_t numPipes = m_pipes; + uint32_t swapSize = m_swapSize; + uint32_t rowSize = m_rowSize; + uint32_t splitSize = m_splitSize; + uint32_t groupSize = m_pipeInterleaveBytes; + uint32_t bytesPerSample = 8 * bpp; - uint32_t samplesPerTile = splitSize / bytesPerSample; - uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); + uint32_t samplesPerTile = splitSize / bytesPerSample; + uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); - if (isThickMacroTiled(tileMode) != 0) - numSamples = 4; + if (isThickMacroTiled(tileMode) != 0) + numSamples = 4; - uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; - uint32_t factor = computeMacroTileAspectRatio(tileMode); - uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); + uint32_t factor = computeMacroTileAspectRatio(tileMode); + uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); - uint32_t swapWidth = swapTiles * 8 * numBanks; - uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; - uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; - uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + uint32_t swapWidth = swapTiles * 8 * numBanks; + uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; + uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; - uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); + uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); - while (bankSwapWidth >= (2 * pitch)) - bankSwapWidth >>= 1; + while (bankSwapWidth >= (2 * pitch)) + bankSwapWidth >>= 1; - return bankSwapWidth; + return bankSwapWidth; } -uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height) { - uint32_t rowOffset = y * pitch; - uint32_t pixOffset = x; - uint32_t addr = (rowOffset + pixOffset) * bpp; - addr /= 8; +uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch) { + uint32_t rowOffset = y * pitch; + uint32_t pixOffset = x; + + uint32_t addr = (rowOffset + pixOffset) * bpp; + addr /= 8; - return addr; + return addr; } -uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode) { - uint64_t microTileThickness = 1; - if (tileMode == 3) - microTileThickness = 4; +uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t tileMode) { + uint64_t microTileThickness = 1; - uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; - uint64_t microTilesPerRow = pitch >> 3; - uint64_t microTileIndexX = x >> 3; - uint64_t microTileIndexY = y >> 3; + if (tileMode == 3) + microTileThickness = 4; - uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); + uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; + uint64_t microTilesPerRow = pitch >> 3; + uint64_t microTileIndexX = x >> 3; + uint64_t microTileIndexY = y >> 3; - uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); - uint64_t pixelOffset = bpp * pixelIndex; + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - pixelOffset >>= 3; + uint64_t pixelOffset = bpp * pixelIndex; - return pixelOffset + microTileOffset; + pixelOffset >>= 3; + + return pixelOffset + microTileOffset; } + uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { - uint64_t samplesPerSlice, numSampleSplits; - uint64_t numSamples, sampleSlice; + uint64_t sampleSlice, numSamples; - uint32_t numPipes = m_pipes; - uint32_t numBanks = m_banks; - uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; - uint32_t numPipeBits = m_pipesBitcount; - uint32_t numBankBits = m_banksBitcount; + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; + uint32_t numPipeBits = m_pipesBitcount; + uint32_t numBankBits = m_banksBitcount; - uint32_t microTileThickness = computeSurfaceThickness(tileMode); + uint32_t microTileThickness = computeSurfaceThickness(tileMode); - uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); - uint64_t microTileBytes = (microTileBits + 7) / 8; + uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); + uint64_t microTileBytes = (microTileBits + 7) / 8; - uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - uint64_t pixelOffset = bpp * pixelIndex; + uint64_t pixelOffset = bpp * pixelIndex; - uint64_t elemOffset = pixelOffset; + uint64_t elemOffset = pixelOffset; - uint64_t bytesPerSample = microTileBytes; - if (microTileBytes <= m_splitSize) { - numSamples = 1; - sampleSlice = 0; - } - else { - samplesPerSlice = m_splitSize / bytesPerSample; - numSampleSplits = max(1, 1 / samplesPerSlice); - numSamples = samplesPerSlice; - sampleSlice = elemOffset / (microTileBits / numSampleSplits); - elemOffset %= microTileBits / numSampleSplits; - } - elemOffset += 7; - elemOffset /= 8; + uint64_t bytesPerSample = microTileBytes; + if (microTileBytes <= m_splitSize) { + numSamples = 1; + sampleSlice = 0; + } - uint64_t pipe = computePipeFromCoordWoRotation(x, y); - uint64_t bank = computeBankFromCoordWoRotation(x, y); + else { + uint64_t samplesPerSlice = m_splitSize / bytesPerSample; + uint64_t numSampleSplits = max(1, 1 / samplesPerSlice); + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + elemOffset += 7; + elemOffset /= 8; - uint64_t bankPipe = pipe + numPipes * bank; - uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + uint64_t pipe = computePipeFromCoordWoRotation(x, y); + uint64_t bank = computeBankFromCoordWoRotation(x, y); - uint64_t swizzle = pipeSwizzle + numPipes * bankSwizzle; + uint64_t bankPipe = pipe + numPipes * bank; - bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle; - bankPipe %= numPipes * numBanks; - pipe = bankPipe % numPipes; - bank = bankPipe / numPipes; + uint64_t swizzle_ = pipeSwizzle + numPipes * bankSwizzle; - uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; - uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle_; + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; - uint64_t macroTilePitch = 8 * m_banks; - uint64_t macroTileHeight = 8 * m_pipes; + uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); - if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 - macroTilePitch >>= 1; - macroTileHeight *= 2; - } + uint64_t macroTilePitch = 8 * m_banks; + uint64_t macroTileHeight = 8 * m_pipes; - else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 - macroTilePitch >>= 2; - macroTileHeight *= 4; - } + if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 + macroTilePitch >>= 1; + macroTileHeight *= 2; + } - uint64_t macroTilesPerRow = pitch / macroTilePitch; - uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; - uint64_t macroTileIndexX = x / macroTilePitch; - uint64_t macroTileIndexY = y / macroTileHeight; - uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * (macroTileIndexY)) * macroTileBytes; + else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 + macroTilePitch >>= 2; + macroTileHeight *= 4; + } - if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { - static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; - uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); - uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; - bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; - } + uint64_t macroTilesPerRow = pitch / macroTilePitch; + uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileIndexX = x / macroTilePitch; + uint64_t macroTileIndexY = y / macroTileHeight; + uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * macroTileIndexY) * macroTileBytes; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { + static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; + uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); + uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; + } - uint64_t groupMask = ((1 << numGroupBits) - 1); + uint64_t groupMask = ((1 << numGroupBits) - 1); - uint64_t numSwizzleBits = (numBankBits + numPipeBits); + uint64_t numSwizzleBits = (numBankBits + numPipeBits); - uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); + uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); - uint64_t offsetHigh = (totalOffset & ~(groupMask)) << numSwizzleBits; - uint64_t offsetLow = groupMask & totalOffset; + uint64_t offsetHigh = (totalOffset & ~groupMask) << numSwizzleBits; + uint64_t offsetLow = groupMask & totalOffset; - uint64_t pipeBits = pipe << numGroupBits; - uint64_t bankBits = bank << (numPipeBits + numGroupBits); + uint64_t pipeBits = pipe << numGroupBits; + uint64_t bankBits = bank << (numPipeBits + numGroupBits); - return bankBits | pipeBits | offsetLow | offsetHigh; + return bankBits | pipeBits | offsetLow | offsetHigh; } +// writeFile(): writes the DDS file void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { int y; uint32_t format; if (gfd->format == 0x1a || gfd->format == 0x41a) - format = 28; - else if (gfd->format == 0x19) - format = 24; - else if (gfd->format == 0x8) - format = 85; - else if (gfd->format == 0xa) - format = 86; - else if (gfd->format == 0xb) - format = 115; - else if (gfd->format == 0x1) - format = 61; - else if (gfd->format == 0x7) - format = 49; - else if (gfd->format == 0x2) - format = 112; - else if (gfd->format == 0x31 || gfd->format == 0x431) - format = 71; - else if (gfd->format == 0x32 || gfd->format == 0x432) - format = 74; - else if (gfd->format == 0x33 || gfd->format == 0x433) - format = 77; - else if (gfd->format == 0x34) - format = 80; - else if (gfd->format == 0x234) - format = 81; - else if (gfd->format == 0x35) - format = 83; - else if (gfd->format == 0x235) - format = 84; + format = 28; - writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); + else if (gfd->format == 0x19) + format = 24; - uint32_t bpp = gfd->bpp; - bpp /= 8; + else if (gfd->format == 0x8) + format = 85; - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - bpp /= 2; - bpp = max(1, bpp); - } + else if (gfd->format == 0xa) + format = 86; - for (y = 0; y < gfd->height; y++) { - if ((y * gfd->width * bpp) >= gfd->realSize) - break; + else if (gfd->format == 0xb) + format = 115; - fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); - } -} + else if (gfd->format == 0x1) + format = 61; -void swizzle_8(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint8_t *source, *output; - double frac, whole; + else if (gfd->format == 0x7) + format = 49; - source = (uint8_t *)gfd->data; - output = malloc(gfd->dataSize); + else if (gfd->format == 0x2) + format = 112; - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else if (gfd->format == 0x31 || gfd->format == 0x431) + format = 71; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + else if (gfd->format == 0x32 || gfd->format == 0x432) + format = 74; - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + else if (gfd->format == 0x33 || gfd->format == 0x433) + format = 77; - bpp /= 8; + else if (gfd->format == 0x34) + format = 80; - output[y * width + x] = source[pos / bpp]; - } - } + else if (gfd->format == 0x234) + format = 81; - writeFile(f, gfd, (uint8_t *)output); + else if (gfd->format == 0x35) + format = 83; - free(output); -} + else if (gfd->format == 0x235) + format = 84; -void swizzle_16(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint16_t *source, *output; - double frac, whole; + writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); - source = (uint16_t *)gfd->data; - output = malloc(gfd->dataSize); + uint32_t bpp = gfd->bpp; + bpp /= 8; if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - - bpp /= 8; - - output[y * width + x] = source[pos / bpp]; - } + bpp /= 2; + bpp = max(1, bpp); } - writeFile(f, gfd, (uint8_t *)output); + for (y = 0; y < gfd->height; y++) { + if ((y * gfd->width * bpp) >= gfd->realSize) + break; - free(output); + fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); + } } -void swizzle_32(GFDData *gfd, FILE *f) { - uint64_t pos; +// deswizzle(): deswizzles the image +void deswizzle(GFDData *gfd, FILE *f) { + uint64_t pos, pos_; uint32_t x, y, width, height; - uint32_t *source, *output; + uint8_t *data, *result; double frac, whole; - source = (uint32_t *)gfd->data; - output = malloc(gfd->dataSize); + data = (uint8_t *)gfd->data; + result = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - - bpp /= 8; - - output[y * width + x] = source[pos / bpp]; - } + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } - writeFile(f, gfd, (uint8_t *)output); - - free(output); -} - -void swizzle_64(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint64_t *source, *output; - double frac, whole; - - source = (uint64_t *)gfd->data; - output = malloc(gfd->dataSize); - - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else { + width = gfd->width; + height = gfd->height; + } for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - - bpp /= 8; - - output[y * width + x] = source[pos / bpp]; - } - } - - writeFile(f, gfd, (uint8_t *)output); - - free(output); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; -} - -void swizzle_128(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - __uint128_t *source, *output; - double frac, whole; - - source = (__uint128_t *)gfd->data; - output = malloc(gfd->dataSize); + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch); - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->tileMode); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + bpp /= 8; - bpp /= 8; + pos_ = (y * width + x) * bpp; - output[y * width + x] = source[pos / bpp]; + for (int i = 0; i < bpp; i++) { + if (pos + i < gfd->dataSize && pos_ + i < gfd->dataSize) + result[pos_ + i] = data[pos + i]; + } } } - writeFile(f, gfd, (uint8_t *)output); + writeFile(f, gfd, result); - free(output); + free(result); } +// remove_three(): removes the file extension from a string char *remove_three(const char *filename) { - size_t len = strlen(filename); - char *newfilename = malloc(len-2); - if (!newfilename) /* handle error */; - memcpy(newfilename, filename, len-3); - newfilename[len - 3] = 0; - return newfilename; + size_t len = strlen(filename); + char *newfilename = malloc(len-2); + if (!newfilename) /* handle error */; + memcpy(newfilename, filename, len-3); + newfilename[len - 3] = 0; + return newfilename; } +// main(): the main function int main(int argc, char **argv) { GFDData data; FILE *f; int result; printf("GTX Extractor - C ver.\n"); - printf("(C) 2014 Treeki, 2017 AboodXD\n"); + printf("(C) 2014 Treeki, 2017 AboodXD\n"); if (argc != 2) { - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); - fprintf(stderr, "\n"); - fprintf(stderr, "Supported formats:\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported formats:\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } if (!(f = fopen(argv[1], "rb"))) { fprintf(stderr, "\n"); - fprintf(stderr, "Cannot open %s for reading\n", argv[1]); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } else - printf("\nConverting: %s\n", argv[1]); + printf("\nConverting: %s\n", argv[1]); if ((result = readGTX(&data, f)) != 1) { fprintf(stderr, "\n"); - fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); + fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); fclose(f); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } + fclose(f); if (data.numImages > 1) { - fprintf(stderr, "\n"); - fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } else if (data.numImages == 0) { - fprintf(stderr, "\n"); - fprintf(stderr, "No images were found in this GTX file\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } char *str = remove_three(argv[1]); - char c = 'd'; - char c1 = 'd'; - char c2 = 's'; - - size_t len = strlen(str); - char *str2 = malloc(len + 3 + 1 ); - strcpy(str2, str); - str2[len] = c; - str2[len + 1] = c1; - str2[len + 2] = c2; - str2[len + 3] = '\0'; - - if (!(f = fopen(str2, "wb"))) { + char c = 'd'; + char c1 = 'd'; + char c2 = 's'; + + size_t len = strlen(str); + char *str2 = malloc(len + 3 + 1 ); + strcpy(str2, str); + str2[len] = c; + str2[len + 1] = c1; + str2[len + 2] = c2; + str2[len + 3] = '\0'; + + if (!(f = fopen(str2, "wb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for writing\n", str2); fprintf(stderr, "\n"); - fprintf(stderr, "Cannot open %s for writing\n", str2); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } free(str2); printf("\n"); - printf("// ----- GX2Surface Info ----- \n"); - printf(" dim = %d\n", data.dim); - printf(" width = %d\n", data.width); - printf(" height = %d\n", data.height); - printf(" depth = %d\n", data.depth); - printf(" numMips = %d\n", data.numMips); - printf(" format = 0x%x\n", data.format); - printf(" aa = %d\n", data.aa); - printf(" use = %d\n", data.use); - printf(" imageSize = %d\n", data.imageSize); - printf(" mipSize = %d\n", data.mipSize); - printf(" tileMode = %d\n", data.tileMode); - printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); - printf(" alignment = %d\n", data.alignment); - printf(" pitch = %d\n", data.pitch); - printf("\n"); - printf(" bits per pixel = %d\n", data.bpp); - printf(" bytes per pixel = %d\n", data.bpp / 8); - printf(" realSize = %d\n", data.realSize); + printf("// ----- GX2Surface Info ----- \n"); + printf(" dim = %d\n", data.dim); + printf(" width = %d\n", data.width); + printf(" height = %d\n", data.height); + printf(" depth = %d\n", data.depth); + printf(" numMips = %d\n", data.numMips); + printf(" format = 0x%x\n", data.format); + printf(" aa = %d\n", data.aa); + printf(" use = %d\n", data.use); + printf(" imageSize = %d\n", data.imageSize); + printf(" mipSize = %d\n", data.mipSize); + printf(" tileMode = %d\n", data.tileMode); + printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); + printf(" alignment = %d\n", data.alignment); + printf(" pitch = %d\n", data.pitch); + printf("\n"); + printf(" bits per pixel = %d\n", data.bpp); + printf(" bytes per pixel = %d\n", data.bpp / 8); + printf(" realSize = %d\n", data.realSize); uint32_t bpp = data.bpp; if (isvalueinarray(data.format, formats, 19)) { - if (bpp == 8) - swizzle_8(&data, f); - else if (bpp == 16) - swizzle_16(&data, f); - else if (bpp == 32) - swizzle_32(&data, f); - else if (bpp == 64) - swizzle_64(&data, f); - else if (bpp == 128) - swizzle_128(&data, f); + deswizzle(&data, f); } else { - fprintf(stderr, "Unsupported format: 0x%x\n", data.format); - fprintf(stderr, "\n"); - fprintf(stderr, "Exiting in 5 seconds...\n"); - unsigned int retTime = time(0) + 5; - while (time(0) < retTime); + fprintf(stderr, "Unsupported format: 0x%x\n", data.format); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); return EXIT_FAILURE; } @@ -1277,4 +1068,3 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - diff --git a/gtx_extract_no5.c b/gtx_extract_no5.c index 45333be..53c3c2c 100644 --- a/gtx_extract_no5.c +++ b/gtx_extract_no5.c @@ -2,22 +2,22 @@ * Wii U 'GTX' Texture Extractor * Created by Ninji Vahran / Treeki; 2014-10-31 * ( https://github.com/Treeki ) - * Updated by AboodXD; 2017-05-21 + * Updated by AboodXD; 2017-07-14 * ( https://github.com/aboood40091 ) * This software is released into the public domain. * * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: - * gcc -m64 -o gtx_extract gtx_extract.c - * (You need an x64 version of gcc!) + * gcc -o gtx_extract gtx_extract.c * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' * algorithm, presumably for faster access. * * TODO: - * Make a x86 version (Would probably force me to rewrite the program?) + * Implement creating GTX files. + * Add BFLIM support. * * Feel free to throw a pull request at me if you improve it! */ @@ -38,14 +38,13 @@ static int formats[] = {0x1a, 0x41a, 0x19, 0x8, 0xa, 0xb, 0x1, 0x7, 0x2, 0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; // Supported formats static int BCn_formats[10] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433, 0x34, 0x234, 0x35, 0x235}; - +// isvalueinarray(): find if a certain value is in a certain array bool isvalueinarray(int val, int *arr, int size){ - int i; - for (i=0; i < size; i++) { - if (arr[i] == val) - return true; - } - return false; + for (int i = 0; i < size; i++) { + if (arr[i] == val) + return true; + } + return false; } @@ -71,63 +70,68 @@ uint8_t formatHwInfo[0x40*4] = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) { - uint32_t hwFormat = surfaceFormat&0x3F; - uint32_t bpp = formatHwInfo[hwFormat*4+0]; + uint32_t hwFormat = (surfaceFormat & 0x3F) * 4; + uint32_t bpp = formatHwInfo[hwFormat]; return bpp; } /* Start of GTX Extractor section */ typedef struct _GFDData { - uint32_t numImages; + uint32_t numImages; uint32_t dim; uint32_t width; uint32_t height; uint32_t depth; uint32_t numMips; - uint32_t format; - uint32_t aa; - uint32_t use; - uint32_t imageSize; - uint32_t imagePtr; - uint32_t mipSize; - uint32_t mipPtr; - uint32_t tileMode; - uint32_t swizzle; - uint32_t alignment; - uint32_t pitch; - uint32_t bpp; + uint32_t format; + uint32_t aa; + uint32_t use; + uint32_t imageSize; + uint32_t imagePtr; + uint32_t mipSize; + uint32_t mipPtr; + uint32_t tileMode; + uint32_t swizzle; + uint32_t alignment; + uint32_t pitch; + uint32_t bpp; uint32_t realSize; uint32_t dataSize; uint8_t *data; } GFDData; + typedef struct _GFDHeader { char magic[4]; uint32_t size_, majorVersion, minorVersion; uint32_t gpuVersion, alignMode, reserved1, reserved2; } GFDHeader; + + typedef struct _GFDBlockHeader { char magic[4]; uint32_t size_, majorVersion, minorVersion, type_; uint32_t dataSize; uint32_t id, typeIdx; } GFDBlockHeader; + + typedef struct _GFDSurface { uint32_t dim, width, height, depth; uint32_t numMips, format_, aa, use; uint32_t imageSize, imagePtr, mipSize, mipPtr; uint32_t tileMode, swizzle, alignment, pitch; - uint32_t _40, _44, _48, _4C; - uint32_t _50, _54, _58, _5C; - uint32_t _60, _64, _68, _6C; - uint32_t _70, _74, _78, _7C; - uint32_t _80, _84, _88, _8C; - uint32_t _90, _94, _98; + uint32_t mipOffset[13]; + uint32_t mip1, numMips_, slice1, numSlices; + uint8_t compSel[4]; + uint32_t texReg[5]; } GFDSurface; +// swap32(): swaps the endianness uint32_t swap32(uint32_t v) { uint32_t a = (v & 0xFF000000) >> 24; uint32_t b = (v & 0x00FF0000) >> 8; @@ -136,6 +140,8 @@ uint32_t swap32(uint32_t v) { return a|b|c|d; } + + /* Start of DDS writer section */ /* @@ -161,209 +167,187 @@ uint32_t swap32(uint32_t v) { * Feel free to include this in your own program if you want, just give credits. :) */ + // writeHeader(): writes a DDS header according to the given values void writeHeader(FILE *f, uint32_t num_mipmaps, uint32_t w, uint32_t h, uint32_t format_, bool compressed) { - uint32_t fmtbpp = 0; - uint32_t has_alpha; - uint32_t rmask = 0; - uint32_t gmask = 0; - uint32_t bmask = 0; - uint32_t amask = 0; - uint32_t flags; - uint32_t pflags; - uint32_t caps; - uint32_t size; - uint32_t u32; - - if (format_ == 28) { // RGBA8 - fmtbpp = 4; - has_alpha = 1; - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0xff000000; - } - - else if (format_ == 24) { // RGB10A2 - fmtbpp = 4; - has_alpha = 1; - rmask = 0x000003ff; - gmask = 0x000ffc00; - bmask = 0x3ff00000; - amask = 0xc0000000; - } - - else if (format_ == 85) { // RGB565 - fmtbpp = 2; - has_alpha = 0; - rmask = 0x0000001f; - gmask = 0x000007e0; - bmask = 0x0000f800; - amask = 0x00000000; - } - - else if (format_ == 86) { // RGB5A1 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x0000001f; - gmask = 0x000003e0; - bmask = 0x00007c00; - amask = 0x00008000; - } - - else if (format_ == 115) { // RGBA4 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x0000000f; - gmask = 0x000000f0; - bmask = 0x00000f00; - amask = 0x0000f000; - } - - else if (format_ == 61) { // L8 - fmtbpp = 1; - has_alpha = 0; - rmask = 0x000000ff; - gmask = 0x000000ff; - bmask = 0x000000ff; - amask = 0x00000000; - } - - else if (format_ == 49) { // L8A8 - fmtbpp = 2; - has_alpha = 1; - rmask = 0x000000ff; - gmask = 0x000000ff; - bmask = 0x000000ff; - amask = 0x0000ff00; - } - - else if (format_ == 112) { // L4A4 - fmtbpp = 1; - has_alpha = 1; - rmask = 0x0000000f; - gmask = 0x0000000f; - bmask = 0x0000000f; - amask = 0x000000f0; - } - - fmtbpp <<= 3; - - flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); - - caps = (0x00001000); - - if (num_mipmaps == 0) - num_mipmaps = 1; - if (num_mipmaps != 1) { - flags |= (0x00020000); - caps |= ((0x00000008) | (0x00400000)); - } - - if (!(compressed)) { - flags |= (0x00000008); - - if (fmtbpp == 1 || format_ == 49) // LUMINANCE - pflags = (0x00020000); - - else // RGB - pflags = (0x00000040); - - if (has_alpha != 0) - pflags |= (0x00000001); - - size = (w * fmtbpp); - } - - else { - flags |= (0x00080000); - pflags = (0x00000004); - - size = ((w + 3) >> 2) * ((h + 3) >> 2); - if (format_ == 71 || format_ == 80 || format_ == 81) - size *= 8; - else - size *= 16; - } - - fwrite("DDS ", 1, 4, f); - u32 = 124; fwrite(&u32, 1, 4, f); - fwrite(&flags, 1, 4, f); - fwrite(&h, 1, 4, f); - fwrite(&w, 1, 4, f); - fwrite(&size, 1, 4, f); - u32 = 0; fwrite(&u32, 1, 4, f); - fwrite(&num_mipmaps, 1, 4, f); - - uint8_t thing[0x2C]; + uint32_t fmtbpp = 0; + uint32_t has_alpha; + uint32_t rmask = 0; + uint32_t gmask = 0; + uint32_t bmask = 0; + uint32_t amask = 0; + uint32_t flags; + uint32_t pflags; + uint32_t caps; + uint32_t size; + uint32_t u32; + + if (format_ == 28) { // RGBA8 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + } + + else if (format_ == 24) { // RGB10A2 + fmtbpp = 4; + has_alpha = 1; + rmask = 0x000003ff; + gmask = 0x000ffc00; + bmask = 0x3ff00000; + amask = 0xc0000000; + } + + else if (format_ == 85) { // RGB565 + fmtbpp = 2; + has_alpha = 0; + rmask = 0x0000001f; + gmask = 0x000007e0; + bmask = 0x0000f800; + amask = 0x00000000; + } + + else if (format_ == 86) { // RGB5A1 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000001f; + gmask = 0x000003e0; + bmask = 0x00007c00; + amask = 0x00008000; + } + + else if (format_ == 115) { // RGBA4 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x000000f0; + bmask = 0x00000f00; + amask = 0x0000f000; + } + + else if (format_ == 61) { // L8 + fmtbpp = 1; + has_alpha = 0; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x00000000; + } + + else if (format_ == 49) { // L8A8 + fmtbpp = 2; + has_alpha = 1; + rmask = 0x000000ff; + gmask = 0x000000ff; + bmask = 0x000000ff; + amask = 0x0000ff00; + } + + else if (format_ == 112) { // L4A4 + fmtbpp = 1; + has_alpha = 1; + rmask = 0x0000000f; + gmask = 0x0000000f; + bmask = 0x0000000f; + amask = 0x000000f0; + } + + fmtbpp <<= 3; + + flags = (0x00000001) | (0x00001000) | (0x00000004) | (0x00000002); + + caps = (0x00001000); + + if (num_mipmaps == 0) + num_mipmaps = 1; + if (num_mipmaps != 1) { + flags |= (0x00020000); + caps |= ((0x00000008) | (0x00400000)); + } + + if (!(compressed)) { + flags |= (0x00000008); + + if (fmtbpp == 1 || format_ == 49) // LUMINANCE + pflags = (0x00020000); + + else // RGB + pflags = (0x00000040); + + if (has_alpha != 0) + pflags |= (0x00000001); + + size = (w * fmtbpp); + } + + else { + flags |= (0x00080000); + pflags = (0x00000004); + + size = ((w + 3) >> 2) * ((h + 3) >> 2); + if (format_ == 71 || format_ == 80 || format_ == 81) + size *= 8; + else + size *= 16; + } + + fwrite("DDS ", 1, 4, f); + u32 = 124; fwrite(&u32, 1, 4, f); + fwrite(&flags, 1, 4, f); + fwrite(&h, 1, 4, f); + fwrite(&w, 1, 4, f); + fwrite(&size, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + fwrite(&num_mipmaps, 1, 4, f); + + uint8_t thing[0x2C]; memset(thing, 0, 0x2C); fwrite(thing, 1, 0x2C, f); u32 = 32; fwrite(&u32, 1, 4, f); - fwrite(&pflags, 1, 4, f); - - if (!(compressed)) { - u32 = 0; fwrite(&u32, 1, 4, f); - } - else { - if (format_ == 71) - fwrite("DXT1", 1, 4, f); - else if (format_ == 74) - fwrite("DXT3", 1, 4, f); - else if (format_ == 77) - fwrite("DXT5", 1, 4, f); - else if (format_ == 80) - fwrite("BC4U", 1, 4, f); - else if (format_ == 81) - fwrite("BC4S", 1, 4, f); - else if (format_ == 83) - fwrite("BC5U", 1, 4, f); - else if (format_ == 84) - fwrite("BC5S", 1, 4, f); - } - - fwrite(&fmtbpp, 1, 4, f); - fwrite(&rmask, 1, 4, f); - fwrite(&gmask, 1, 4, f); - fwrite(&bmask, 1, 4, f); - fwrite(&amask, 1, 4, f); - fwrite(&caps, 1, 4, f); - - uint8_t thing2[0x10]; + fwrite(&pflags, 1, 4, f); + + if (!(compressed)) { + u32 = 0; fwrite(&u32, 1, 4, f); + } + + else { + if (format_ == 71) + fwrite("DXT1", 1, 4, f); + + else if (format_ == 74) + fwrite("DXT3", 1, 4, f); + + else if (format_ == 77) + fwrite("DXT5", 1, 4, f); + + else if (format_ == 80) + fwrite("BC4U", 1, 4, f); + + else if (format_ == 81) + fwrite("BC4S", 1, 4, f); + + else if (format_ == 83) + fwrite("BC5U", 1, 4, f); + + else if (format_ == 84) + fwrite("BC5S", 1, 4, f); + } + + fwrite(&fmtbpp, 1, 4, f); + fwrite(&rmask, 1, 4, f); + fwrite(&gmask, 1, 4, f); + fwrite(&bmask, 1, 4, f); + fwrite(&amask, 1, 4, f); + fwrite(&caps, 1, 4, f); + + uint8_t thing2[0x10]; memset(thing2, 0, 0x10); fwrite(thing2, 1, 0x10, f); } -uint32_t cal_pitch(uint32_t width, uint32_t format_) { - uint32_t bpp = surfaceGetBitsPerPixel(format_); - double frac, whole; - - if (isvalueinarray(format_, BCn_formats, 10)) { - double bpp2 = (double)bpp / 4.0; - frac = modf(bpp2, &whole); - bpp = (uint32_t)whole; - if (frac == 0.5) - bpp += 1; - - double width2 = (double)width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - } - - uint32_t pitch = 1; - uint32_t z = 1; - while (pitch < width) { - pitch = bpp*z; - z += 1; - } - - if (pitch < 1) - pitch = 1; - - return pitch; -} - +// readGTX(): reads the GTX file int readGTX(GFDData *gfd, FILE *f) { GFDHeader header; @@ -373,7 +357,7 @@ int readGTX(GFDData *gfd, FILE *f) { if (memcmp(header.magic, "Gfx2", 4) != 0) return -2; - gfd->numImages = 0; + gfd->numImages = 0; while (!feof(f)) { GFDBlockHeader section; @@ -392,44 +376,50 @@ int readGTX(GFDData *gfd, FILE *f) { if (fread(&info, 1, sizeof(info), f) != sizeof(info)) return -201; + gfd->dim = swap32(info.dim); - gfd->width = swap32(info.width); - gfd->height = swap32(info.height); - gfd->depth = swap32(info.depth); - gfd->numMips = swap32(info.numMips); - gfd->format = swap32(info.format_); - gfd->aa = swap32(info.aa); - gfd->use = swap32(info.use); - gfd->imageSize = swap32(info.imageSize); - gfd->imagePtr = swap32(info.imagePtr); - gfd->mipSize = swap32(info.mipSize); - gfd->mipPtr = swap32(info.mipPtr); - gfd->tileMode = swap32(info.tileMode); - gfd->swizzle = swap32(info.swizzle); - gfd->alignment = swap32(info.alignment); - gfd->pitch = cal_pitch(gfd->width, gfd->format); - gfd->bpp = surfaceGetBitsPerPixel(gfd->format); - - } else if (swap32(section.type_) == 0xC) { - uint32_t bpp = gfd->bpp; - bpp /= 8; - if (isvalueinarray(gfd->format, BCn_formats, 10)) - gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; - - else - gfd->realSize = gfd->width * gfd->height * bpp; - - gfd->dataSize = swap32(section.dataSize); - gfd->data = malloc(gfd->dataSize); + gfd->width = swap32(info.width); + gfd->height = swap32(info.height); + gfd->depth = swap32(info.depth); + gfd->numMips = swap32(info.numMips); + gfd->format = swap32(info.format_); + gfd->aa = swap32(info.aa); + gfd->use = swap32(info.use); + gfd->imageSize = swap32(info.imageSize); + gfd->imagePtr = swap32(info.imagePtr); + gfd->mipSize = swap32(info.mipSize); + gfd->mipPtr = swap32(info.mipPtr); + gfd->tileMode = swap32(info.tileMode); + gfd->swizzle = swap32(info.swizzle); + gfd->alignment = swap32(info.alignment); + gfd->pitch = swap32(info.pitch); + gfd->bpp = surfaceGetBitsPerPixel(gfd->format); + + } + + else if (swap32(section.type_) == 0xC) { + uint32_t bpp = gfd->bpp; + bpp /= 8; + + if (isvalueinarray(gfd->format, BCn_formats, 10)) + gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; + + else + gfd->realSize = gfd->width * gfd->height * bpp; + + gfd->dataSize = swap32(section.dataSize); + gfd->data = malloc(gfd->dataSize); if (!gfd->data) return -300; if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) return -301; - gfd->numImages += 1; + gfd->numImages += 1; + + } - } else { + else { fseek(f, swap32(section.dataSize), SEEK_CUR); } } @@ -437,12 +427,13 @@ int readGTX(GFDData *gfd, FILE *f) { return 1; } + /* Start of swizzling section */ /* Credits: - -AddrLib: actual code - -Exzap: modifying code to apply to Wii U textures - -AboodXD: porting, code improvements and cleaning up + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up */ static uint32_t m_banks = 4; @@ -457,25 +448,26 @@ static uint32_t m_splitSize = 2048; static uint32_t m_chipFamily = 2; -static uint32_t MicroTilePixels = 8 * 8; +static uint32_t MicroTilePixels = 64; uint32_t computeSurfaceThickness(uint32_t tileMode) { - uint32_t thickness = 1; + uint32_t thickness = 1; - if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) - thickness = 4; + if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickness = 4; - else if (tileMode == 16 || tileMode == 17) - thickness = 8; + else if (tileMode == 16 || tileMode == 17) + thickness = 8; return thickness; } + uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t tileMode) { - uint32_t z = 0; - uint32_t thickness; + uint32_t z = 0; + uint32_t thickness; uint32_t pixelBit8; uint32_t pixelBit7; uint32_t pixelBit6; @@ -485,760 +477,559 @@ uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pixelBit2; uint32_t pixelBit1; uint32_t pixelBit0; - pixelBit6 = 0; - pixelBit7 = 0; - pixelBit8 = 0; - thickness = computeSurfaceThickness(tileMode); - - 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; - } - - else if (bpp == 0x10) { - pixelBit0 = x & 1; - pixelBit1 = (x & 2) >> 1; - pixelBit2 = (x & 4) >> 2; - pixelBit3 = y & 1; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (bpp == 0x20 || bpp == 0x60) { - pixelBit0 = x & 1; - pixelBit1 = (x & 2) >> 1; - pixelBit2 = y & 1; - pixelBit3 = (x & 4) >> 2; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (bpp == 0x40) { - pixelBit0 = x & 1; - pixelBit1 = y & 1; - pixelBit2 = (x & 2) >> 1; - pixelBit3 = (x & 4) >> 2; - pixelBit4 = (y & 2) >> 1; - pixelBit5 = (y & 4) >> 2; - } - - else if (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); -} + pixelBit6 = 0; + pixelBit7 = 0; + pixelBit8 = 0; + thickness = computeSurfaceThickness(tileMode); + + 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; + } -uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { - // hardcoded to assume 2 pipes - uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; - return pipe; -} + else if (bpp == 0x10) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } -uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { - uint32_t numPipes = m_pipes; - uint32_t numBanks = m_banks; - uint32_t bankBit0; - uint32_t bankBit0a; - uint32_t bank = 0; - - if (numBanks == 4) { - bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; - bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); - } - - else if (numBanks == 8) { - bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; - bank = bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1); - } - - return bank; + else if (bpp == 0x20 || bpp == 0x60) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x40) { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (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); } -uint32_t computeSurfaceRotationFromTileMode(uint32_t tileMode) { - uint32_t pipes = m_pipes; - uint32_t result = 0; - if (tileMode == 4 || tileMode == 5 || tileMode == 6 || tileMode == 7 || tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11) - result = pipes * ((m_banks >> 1) - 1); +uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { + // hardcoded to assume 2 pipes + uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; +} - else if (tileMode == 12 || tileMode == 13 || tileMode == 14 || tileMode == 15) { - if (pipes >= 4) - result = (pipes >> 1) - 1; - else - result = 1; - } +uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t bankBit0; + uint32_t bankBit0a; + uint32_t bank = 0; + + if (numBanks == 4) { + bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); + } + + else if (numBanks == 8) { + bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; + bank = (bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | + 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1)); + } - return result; + return bank; } + uint32_t isThickMacroTiled(uint32_t tileMode) { - uint32_t thickMacroTiled = 0; + uint32_t thickMacroTiled = 0; - if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) - thickMacroTiled = 1; + if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickMacroTiled = 1; - return thickMacroTiled; + return thickMacroTiled; } + uint32_t isBankSwappedTileMode(uint32_t tileMode) { - uint32_t bankSwapped = 0; + uint32_t bankSwapped = 0; - if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) - bankSwapped = 1; + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) + bankSwapped = 1; - return bankSwapped; + return bankSwapped; } + uint32_t computeMacroTileAspectRatio(uint32_t tileMode) { - uint32_t ratio = 1; + uint32_t ratio = 1; - if (tileMode == 5 || tileMode == 9) - ratio = 2; + if (tileMode == 5 || tileMode == 9) + ratio = 2; - else if (tileMode == 6 || tileMode == 10) - ratio = 4; + else if (tileMode == 6 || tileMode == 10) + ratio = 4; - return ratio; + return ratio; } + uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_t pitch) { - if (isBankSwappedTileMode(tileMode) == 0) - return 0; + if (isBankSwappedTileMode(tileMode) == 0) + return 0; - uint32_t numSamples = 1; - uint32_t numBanks = m_banks; - uint32_t numPipes = m_pipes; - uint32_t swapSize = m_swapSize; - uint32_t rowSize = m_rowSize; - uint32_t splitSize = m_splitSize; - uint32_t groupSize = m_pipeInterleaveBytes; - uint32_t bytesPerSample = 8 * bpp & 0x1FFFFFFF; + uint32_t numSamples = 1; + uint32_t numBanks = m_banks; + uint32_t numPipes = m_pipes; + uint32_t swapSize = m_swapSize; + uint32_t rowSize = m_rowSize; + uint32_t splitSize = m_splitSize; + uint32_t groupSize = m_pipeInterleaveBytes; + uint32_t bytesPerSample = 8 * bpp; - uint32_t samplesPerTile = splitSize / bytesPerSample; - uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); + uint32_t samplesPerTile = splitSize / bytesPerSample; + uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); - if (isThickMacroTiled(tileMode) != 0) - numSamples = 4; + if (isThickMacroTiled(tileMode) != 0) + numSamples = 4; - uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; - uint32_t factor = computeMacroTileAspectRatio(tileMode); - uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); + uint32_t factor = computeMacroTileAspectRatio(tileMode); + uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); - uint32_t swapWidth = swapTiles * 8 * numBanks; - uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; - uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; - uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + uint32_t swapWidth = swapTiles * 8 * numBanks; + uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; + uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; - uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); + uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); - while (bankSwapWidth >= (2 * pitch)) - bankSwapWidth >>= 1; + while (bankSwapWidth >= (2 * pitch)) + bankSwapWidth >>= 1; - return bankSwapWidth; + return bankSwapWidth; } -uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height) { - uint32_t rowOffset = y * pitch; - uint32_t pixOffset = x; - uint32_t addr = (rowOffset + pixOffset) * bpp; - addr /= 8; +uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch) { + uint32_t rowOffset = y * pitch; + uint32_t pixOffset = x; + + uint32_t addr = (rowOffset + pixOffset) * bpp; + addr /= 8; - return addr; + return addr; } -uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode) { - uint64_t microTileThickness = 1; - if (tileMode == 3) - microTileThickness = 4; +uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t tileMode) { + uint64_t microTileThickness = 1; - uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; - uint64_t microTilesPerRow = pitch >> 3; - uint64_t microTileIndexX = x >> 3; - uint64_t microTileIndexY = y >> 3; + if (tileMode == 3) + microTileThickness = 4; - uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); + uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; + uint64_t microTilesPerRow = pitch >> 3; + uint64_t microTileIndexX = x >> 3; + uint64_t microTileIndexY = y >> 3; - uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); - uint64_t pixelOffset = bpp * pixelIndex; + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - pixelOffset >>= 3; + uint64_t pixelOffset = bpp * pixelIndex; - return pixelOffset + microTileOffset; + pixelOffset >>= 3; + + return pixelOffset + microTileOffset; } + uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { - uint64_t samplesPerSlice, numSampleSplits; - uint64_t numSamples, sampleSlice; + uint64_t sampleSlice, numSamples; - uint32_t numPipes = m_pipes; - uint32_t numBanks = m_banks; - uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; - uint32_t numPipeBits = m_pipesBitcount; - uint32_t numBankBits = m_banksBitcount; + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; + uint32_t numPipeBits = m_pipesBitcount; + uint32_t numBankBits = m_banksBitcount; - uint32_t microTileThickness = computeSurfaceThickness(tileMode); + uint32_t microTileThickness = computeSurfaceThickness(tileMode); - uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); - uint64_t microTileBytes = (microTileBits + 7) / 8; + uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); + uint64_t microTileBytes = (microTileBits + 7) / 8; - uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); - uint64_t pixelOffset = bpp * pixelIndex; + uint64_t pixelOffset = bpp * pixelIndex; - uint64_t elemOffset = pixelOffset; + uint64_t elemOffset = pixelOffset; - uint64_t bytesPerSample = microTileBytes; - if (microTileBytes <= m_splitSize) { - numSamples = 1; - sampleSlice = 0; - } - else { - samplesPerSlice = m_splitSize / bytesPerSample; - numSampleSplits = max(1, 1 / samplesPerSlice); - numSamples = samplesPerSlice; - sampleSlice = elemOffset / (microTileBits / numSampleSplits); - elemOffset %= microTileBits / numSampleSplits; - } - elemOffset += 7; - elemOffset /= 8; + uint64_t bytesPerSample = microTileBytes; + if (microTileBytes <= m_splitSize) { + numSamples = 1; + sampleSlice = 0; + } - uint64_t pipe = computePipeFromCoordWoRotation(x, y); - uint64_t bank = computeBankFromCoordWoRotation(x, y); + else { + uint64_t samplesPerSlice = m_splitSize / bytesPerSample; + uint64_t numSampleSplits = max(1, 1 / samplesPerSlice); + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + elemOffset += 7; + elemOffset /= 8; - uint64_t bankPipe = pipe + numPipes * bank; - uint64_t rotation = computeSurfaceRotationFromTileMode(tileMode); + uint64_t pipe = computePipeFromCoordWoRotation(x, y); + uint64_t bank = computeBankFromCoordWoRotation(x, y); - uint64_t swizzle = pipeSwizzle + numPipes * bankSwizzle; + uint64_t bankPipe = pipe + numPipes * bank; - bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle; - bankPipe %= numPipes * numBanks; - pipe = bankPipe % numPipes; - bank = bankPipe / numPipes; + uint64_t swizzle_ = pipeSwizzle + numPipes * bankSwizzle; - uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; - uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle_; + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; - uint64_t macroTilePitch = 8 * m_banks; - uint64_t macroTileHeight = 8 * m_pipes; + uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); - if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 - macroTilePitch >>= 1; - macroTileHeight *= 2; - } + uint64_t macroTilePitch = 8 * m_banks; + uint64_t macroTileHeight = 8 * m_pipes; - else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 - macroTilePitch >>= 2; - macroTileHeight *= 4; - } + if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 + macroTilePitch >>= 1; + macroTileHeight *= 2; + } - uint64_t macroTilesPerRow = pitch / macroTilePitch; - uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; - uint64_t macroTileIndexX = x / macroTilePitch; - uint64_t macroTileIndexY = y / macroTileHeight; - uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * (macroTileIndexY)) * macroTileBytes; + else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 + macroTilePitch >>= 2; + macroTileHeight *= 4; + } - if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { - static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; - uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); - uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; - bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; - } + uint64_t macroTilesPerRow = pitch / macroTilePitch; + uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileIndexX = x / macroTilePitch; + uint64_t macroTileIndexY = y / macroTileHeight; + uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * macroTileIndexY) * macroTileBytes; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { + static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; + uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); + uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; + } - uint64_t groupMask = ((1 << numGroupBits) - 1); + uint64_t groupMask = ((1 << numGroupBits) - 1); - uint64_t numSwizzleBits = (numBankBits + numPipeBits); + uint64_t numSwizzleBits = (numBankBits + numPipeBits); - uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); + uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); - uint64_t offsetHigh = (totalOffset & ~(groupMask)) << numSwizzleBits; - uint64_t offsetLow = groupMask & totalOffset; + uint64_t offsetHigh = (totalOffset & ~groupMask) << numSwizzleBits; + uint64_t offsetLow = groupMask & totalOffset; - uint64_t pipeBits = pipe << numGroupBits; - uint64_t bankBits = bank << (numPipeBits + numGroupBits); + uint64_t pipeBits = pipe << numGroupBits; + uint64_t bankBits = bank << (numPipeBits + numGroupBits); - return bankBits | pipeBits | offsetLow | offsetHigh; + return bankBits | pipeBits | offsetLow | offsetHigh; } +// writeFile(): writes the DDS file void writeFile(FILE *f, GFDData *gfd, uint8_t *output) { int y; uint32_t format; if (gfd->format == 0x1a || gfd->format == 0x41a) - format = 28; - else if (gfd->format == 0x19) - format = 24; - else if (gfd->format == 0x8) - format = 85; - else if (gfd->format == 0xa) - format = 86; - else if (gfd->format == 0xb) - format = 115; - else if (gfd->format == 0x1) - format = 61; - else if (gfd->format == 0x7) - format = 49; - else if (gfd->format == 0x2) - format = 112; - else if (gfd->format == 0x31 || gfd->format == 0x431) - format = 71; - else if (gfd->format == 0x32 || gfd->format == 0x432) - format = 74; - else if (gfd->format == 0x33 || gfd->format == 0x433) - format = 77; - else if (gfd->format == 0x34) - format = 80; - else if (gfd->format == 0x234) - format = 81; - else if (gfd->format == 0x35) - format = 83; - else if (gfd->format == 0x235) - format = 84; + format = 28; - writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); + else if (gfd->format == 0x19) + format = 24; - uint32_t bpp = gfd->bpp; - bpp /= 8; + else if (gfd->format == 0x8) + format = 85; - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - bpp /= 2; - bpp = max(1, bpp); - } + else if (gfd->format == 0xa) + format = 86; - for (y = 0; y < gfd->height; y++) { - if ((y * gfd->width * bpp) >= gfd->realSize) - break; + else if (gfd->format == 0xb) + format = 115; - fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); - } -} + else if (gfd->format == 0x1) + format = 61; -void swizzle_8(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint8_t *source, *output; - double frac, whole; + else if (gfd->format == 0x7) + format = 49; - source = (uint8_t *)gfd->data; - output = malloc(gfd->dataSize); + else if (gfd->format == 0x2) + format = 112; - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else if (gfd->format == 0x31 || gfd->format == 0x431) + format = 71; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + else if (gfd->format == 0x32 || gfd->format == 0x432) + format = 74; - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + else if (gfd->format == 0x33 || gfd->format == 0x433) + format = 77; - bpp /= 8; + else if (gfd->format == 0x34) + format = 80; - output[y * width + x] = source[pos / bpp]; - } - } + else if (gfd->format == 0x234) + format = 81; - writeFile(f, gfd, (uint8_t *)output); + else if (gfd->format == 0x35) + format = 83; - free(output); -} + else if (gfd->format == 0x235) + format = 84; -void swizzle_16(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint16_t *source, *output; - double frac, whole; + writeHeader(f, 1, gfd->width, gfd->height, format, isvalueinarray(gfd->format, BCn_formats, 10)); - source = (uint16_t *)gfd->data; - output = malloc(gfd->dataSize); + uint32_t bpp = gfd->bpp; + bpp /= 8; if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - - bpp /= 8; - - output[y * width + x] = source[pos / bpp]; - } + bpp /= 2; + bpp = max(1, bpp); } - writeFile(f, gfd, (uint8_t *)output); + for (y = 0; y < gfd->height; y++) { + if ((y * gfd->width * bpp) >= gfd->realSize) + break; - free(output); + fwrite(&output[y * gfd->width * bpp], 1, gfd->width * bpp, f); + } } -void swizzle_32(GFDData *gfd, FILE *f) { - uint64_t pos; +// deswizzle(): deswizzles the image +void deswizzle(GFDData *gfd, FILE *f) { + uint64_t pos, pos_; uint32_t x, y, width, height; - uint32_t *source, *output; + uint8_t *data, *result; double frac, whole; - source = (uint32_t *)gfd->data; - output = malloc(gfd->dataSize); + data = (uint8_t *)gfd->data; + result = malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - - bpp /= 8; - - output[y * width + x] = source[pos / bpp]; - } + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; } - writeFile(f, gfd, (uint8_t *)output); - - free(output); -} - -void swizzle_64(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - uint64_t *source, *output; - double frac, whole; - - source = (uint64_t *)gfd->data; - output = malloc(gfd->dataSize); - - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else { + width = gfd->width; + height = gfd->height; + } for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; - bpp /= 8; + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch); - output[y * width + x] = source[pos / bpp]; - } - } - - writeFile(f, gfd, (uint8_t *)output); - - free(output); - -} - -void swizzle_128(GFDData *gfd, FILE *f) { - uint64_t pos; - uint32_t x, y, width, height; - __uint128_t *source, *output; - double frac, whole; - - source = (__uint128_t *)gfd->data; - output = malloc(gfd->dataSize); - - if (isvalueinarray(gfd->format, BCn_formats, 10)) { - double width2 = (double)gfd->width / 4.0; - frac = modf(width2, &whole); - width = (uint32_t)whole; - if (frac == 0.5) - width += 1; - - double height2 = (double)gfd->height / 4.0; - frac = modf(height2, &whole); - height = (uint32_t)whole; - if (frac == 0.5) - height += 1; - } - - else { - width = gfd->width; - height = gfd->height; - } + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->tileMode); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint32_t bpp = gfd->bpp; - uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; - uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); - if (gfd->tileMode == 0 || gfd->tileMode == 1) - pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch, gfd->height); - else if (gfd->tileMode == 2 || gfd->tileMode == 3) - pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode); - else - pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + bpp /= 8; - bpp /= 8; + pos_ = (y * width + x) * bpp; - output[y * width + x] = source[pos / bpp]; + for (int i = 0; i < bpp; i++) { + if (pos + i < gfd->dataSize && pos_ + i < gfd->dataSize) + result[pos_ + i] = data[pos + i]; + } } } - writeFile(f, gfd, (uint8_t *)output); + writeFile(f, gfd, result); - free(output); + free(result); } +// remove_three(): removes the file extension from a string char *remove_three(const char *filename) { - size_t len = strlen(filename); - char *newfilename = malloc(len-2); - if (!newfilename) /* handle error */; - memcpy(newfilename, filename, len-3); - newfilename[len - 3] = 0; - return newfilename; + size_t len = strlen(filename); + char *newfilename = malloc(len-2); + if (!newfilename) /* handle error */; + memcpy(newfilename, filename, len-3); + newfilename[len - 3] = 0; + return newfilename; } +// main(): the main function int main(int argc, char **argv) { GFDData data; FILE *f; int result; printf("GTX Extractor - C ver.\n"); - printf("(C) 2014 Treeki, 2017 AboodXD\n"); + printf("(C) 2014 Treeki, 2017 AboodXD\n"); if (argc != 2) { - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); - fprintf(stderr, "\n"); - fprintf(stderr, "Supported formats:\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); - fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported formats:\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R10_G10_B10_A2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_B4_A4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R8_G8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TC_R4_G4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC4_SNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC5_SNORM\n"); return EXIT_FAILURE; } if (!(f = fopen(argv[1], "rb"))) { fprintf(stderr, "\n"); - fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + fprintf(stderr, "Cannot open %s for reading\n", argv[1]); return EXIT_FAILURE; } else - printf("\nConverting: %s\n", argv[1]); + printf("\nConverting: %s\n", argv[1]); if ((result = readGTX(&data, f)) != 1) { fprintf(stderr, "\n"); - fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); + fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); fclose(f); return EXIT_FAILURE; } + fclose(f); if (data.numImages > 1) { - fprintf(stderr, "\n"); - fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO return EXIT_FAILURE; } else if (data.numImages == 0) { - fprintf(stderr, "\n"); - fprintf(stderr, "No images were found in this GTX file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); return EXIT_FAILURE; } char *str = remove_three(argv[1]); - char c = 'd'; - char c1 = 'd'; - char c2 = 's'; - - size_t len = strlen(str); - char *str2 = malloc(len + 3 + 1 ); - strcpy(str2, str); - str2[len] = c; - str2[len + 1] = c1; - str2[len + 2] = c2; - str2[len + 3] = '\0'; - - if (!(f = fopen(str2, "wb"))) { + char c = 'd'; + char c1 = 'd'; + char c2 = 's'; + + size_t len = strlen(str); + char *str2 = malloc(len + 3 + 1 ); + strcpy(str2, str); + str2[len] = c; + str2[len + 1] = c1; + str2[len + 2] = c2; + str2[len + 3] = '\0'; + + if (!(f = fopen(str2, "wb"))) { fprintf(stderr, "\n"); - fprintf(stderr, "Cannot open %s for writing\n", str2); + fprintf(stderr, "Cannot open %s for writing\n", str2); return EXIT_FAILURE; } free(str2); printf("\n"); - printf("// ----- GX2Surface Info ----- \n"); - printf(" dim = %d\n", data.dim); - printf(" width = %d\n", data.width); - printf(" height = %d\n", data.height); - printf(" depth = %d\n", data.depth); - printf(" numMips = %d\n", data.numMips); - printf(" format = 0x%x\n", data.format); - printf(" aa = %d\n", data.aa); - printf(" use = %d\n", data.use); - printf(" imageSize = %d\n", data.imageSize); - printf(" mipSize = %d\n", data.mipSize); - printf(" tileMode = %d\n", data.tileMode); - printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); - printf(" alignment = %d\n", data.alignment); - printf(" pitch = %d\n", data.pitch); - printf("\n"); - printf(" bits per pixel = %d\n", data.bpp); - printf(" bytes per pixel = %d\n", data.bpp / 8); - printf(" realSize = %d\n", data.realSize); + printf("// ----- GX2Surface Info ----- \n"); + printf(" dim = %d\n", data.dim); + printf(" width = %d\n", data.width); + printf(" height = %d\n", data.height); + printf(" depth = %d\n", data.depth); + printf(" numMips = %d\n", data.numMips); + printf(" format = 0x%x\n", data.format); + printf(" aa = %d\n", data.aa); + printf(" use = %d\n", data.use); + printf(" imageSize = %d\n", data.imageSize); + printf(" mipSize = %d\n", data.mipSize); + printf(" tileMode = %d\n", data.tileMode); + printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); + printf(" alignment = %d\n", data.alignment); + printf(" pitch = %d\n", data.pitch); + printf("\n"); + printf(" bits per pixel = %d\n", data.bpp); + printf(" bytes per pixel = %d\n", data.bpp / 8); + printf(" realSize = %d\n", data.realSize); uint32_t bpp = data.bpp; if (isvalueinarray(data.format, formats, 19)) { - if (bpp == 8) - swizzle_8(&data, f); - else if (bpp == 16) - swizzle_16(&data, f); - else if (bpp == 32) - swizzle_32(&data, f); - else if (bpp == 64) - swizzle_64(&data, f); - else if (bpp == 128) - swizzle_128(&data, f); + deswizzle(&data, f); } else { - fprintf(stderr, "Unsupported format: 0x%x\n", data.format); + fprintf(stderr, "Unsupported format: 0x%x\n", data.format); return EXIT_FAILURE; } @@ -1248,4 +1039,3 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - From db2248b28566376d36782c2c036ab92e0e208c2e Mon Sep 17 00:00:00 2001 From: AboodXD Date: Fri, 14 Jul 2017 22:06:58 +0400 Subject: [PATCH 15/19] Switch to C++ --- gtx_extract.c => gtx_extract.cpp | 8 ++++---- gtx_extract_no5.c => gtx_extract_no5.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) rename gtx_extract.c => gtx_extract.cpp (99%) rename gtx_extract_no5.c => gtx_extract_no5.cpp (99%) diff --git a/gtx_extract.c b/gtx_extract.cpp similarity index 99% rename from gtx_extract.c rename to gtx_extract.cpp index ca0bdeb..e08fc93 100644 --- a/gtx_extract.c +++ b/gtx_extract.cpp @@ -409,7 +409,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->realSize = gfd->width * gfd->height * bpp; gfd->dataSize = swap32(section.dataSize); - gfd->data = malloc(gfd->dataSize); + gfd->data = (uint8_t*)malloc(gfd->dataSize); if (!gfd->data) return -300; @@ -853,7 +853,7 @@ void deswizzle(GFDData *gfd, FILE *f) { double frac, whole; data = (uint8_t *)gfd->data; - result = malloc(gfd->dataSize); + result = (uint8_t*)malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { double width2 = (double)gfd->width / 4.0; @@ -908,7 +908,7 @@ void deswizzle(GFDData *gfd, FILE *f) { // remove_three(): removes the file extension from a string char *remove_three(const char *filename) { size_t len = strlen(filename); - char *newfilename = malloc(len-2); + char *newfilename = (char*)malloc(len-2); if (!newfilename) /* handle error */; memcpy(newfilename, filename, len-3); newfilename[len - 3] = 0; @@ -1007,7 +1007,7 @@ int main(int argc, char **argv) { char c2 = 's'; size_t len = strlen(str); - char *str2 = malloc(len + 3 + 1 ); + char *str2 = (char*)malloc(len + 3 + 1 ); strcpy(str2, str); str2[len] = c; str2[len + 1] = c1; diff --git a/gtx_extract_no5.c b/gtx_extract_no5.cpp similarity index 99% rename from gtx_extract_no5.c rename to gtx_extract_no5.cpp index 53c3c2c..7e9a8c7 100644 --- a/gtx_extract_no5.c +++ b/gtx_extract_no5.cpp @@ -408,7 +408,7 @@ int readGTX(GFDData *gfd, FILE *f) { gfd->realSize = gfd->width * gfd->height * bpp; gfd->dataSize = swap32(section.dataSize); - gfd->data = malloc(gfd->dataSize); + gfd->data = (uint8_t*)malloc(gfd->dataSize); if (!gfd->data) return -300; @@ -852,7 +852,7 @@ void deswizzle(GFDData *gfd, FILE *f) { double frac, whole; data = (uint8_t *)gfd->data; - result = malloc(gfd->dataSize); + result = (uint8_t*)malloc(gfd->dataSize); if (isvalueinarray(gfd->format, BCn_formats, 10)) { double width2 = (double)gfd->width / 4.0; @@ -907,7 +907,7 @@ void deswizzle(GFDData *gfd, FILE *f) { // remove_three(): removes the file extension from a string char *remove_three(const char *filename) { size_t len = strlen(filename); - char *newfilename = malloc(len-2); + char *newfilename = (char*)malloc(len-2); if (!newfilename) /* handle error */; memcpy(newfilename, filename, len-3); newfilename[len - 3] = 0; @@ -986,7 +986,7 @@ int main(int argc, char **argv) { char c2 = 's'; size_t len = strlen(str); - char *str2 = malloc(len + 3 + 1 ); + char *str2 = (char*)malloc(len + 3 + 1 ); strcpy(str2, str); str2[len] = c; str2[len + 1] = c1; From 2f0d8e15d78f25576abe28cc491a26e614fd3787 Mon Sep 17 00:00:00 2001 From: AboodXD Date: Fri, 14 Jul 2017 23:16:10 +0400 Subject: [PATCH 16/19] Implemented saving as BMP --- .gitignore | 2 + README.markdown | 23 +- gtx_extract.cpp | 8 +- gtx_extract_bmp.cpp | 1088 +++++++++++++++++++++++++++++++++++++++++++ txc_dxtn.h | 50 ++ 5 files changed, 1156 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 gtx_extract_bmp.cpp create mode 100644 txc_dxtn.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ce788f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.exe +*.o \ No newline at end of file diff --git a/README.markdown b/README.markdown index 3cca31d..46b87a2 100644 --- a/README.markdown +++ b/README.markdown @@ -9,30 +9,31 @@ And it's not Wii-specific any more. :p GTX Extractor (C version) ------------------- -Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format used in Wii U games, and saves them as DDS. +Extracts textures from the GX2 Texture ('Gfx2' / .gtx file extension) format used in Wii U games, and saves them as DDS/BMP. Supported formats: * RGBA8_UNORM / RGBA8_SRGB -* RGB10A2_UNORM -* RGB565_UNORM -* RGB5A1_UNORM -* RGBA4_UNORM -* R8_UNORM -* RG8_UNORM -* RG4_UNORM +* RGB10A2_UNORM (Not supported in BMP ver) +* RGB565_UNORM (Not supported in BMP ver) +* RGB5A1_UNORM (Not supported in BMP ver) +* RGBA4_UNORM (Not supported in BMP ver) +* R8_UNORM (Not supported in BMP ver) +* RG8_UNORM (Not supported in BMP ver) +* RG4_UNORM (Not supported in BMP ver) * BC1_UNORM / BC1_SRGB (DXT1) * BC2_UNORM / BC2_SRGB (DXT3) * BC3_UNORM / BC3_SRGB (DXT5) -* BC4_UNORM / BC4_SNORM (ATI1) -* BC5_UNORM / BC5_SNORM (ATI2) +* BC4_UNORM / BC4_SNORM (ATI1 / Not supported in BMP ver) +* BC5_UNORM / BC5_SNORM (ATI2 / Not supported in BMP ver) TODO: * Make a x86 version (Would probably force me to rewrite the program?) Credits: * Treeki - Original developer. -* AboodXD - Reviver, Added new features, Improved swizzling. +* AboodXD - Reviver, Added more formats and new features, Improved swizzling. * Exzap, AddrLib - Helping with swizzling. +* libtxc_dxtn - DXTn compressor. More details on compilation and usage in the comments inside the file. diff --git a/gtx_extract.cpp b/gtx_extract.cpp index e08fc93..5bd6f61 100644 --- a/gtx_extract.cpp +++ b/gtx_extract.cpp @@ -2,22 +2,22 @@ * Wii U 'GTX' Texture Extractor * Created by Ninji Vahran / Treeki; 2014-10-31 * ( https://github.com/Treeki ) - * Updated by AboodXD; 2017-05-21 + * Updated by AboodXD; 2017-07-14 * ( https://github.com/aboood40091 ) * This software is released into the public domain. * * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: - * gcc -m64 -o gtx_extract gtx_extract.c - * (You need an x64 version of gcc!) + * gcc -o gtx_extract gtx_extract.c * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' * algorithm, presumably for faster access. * * TODO: - * Make a x86 version (Would probably force me to rewrite the program?) + * Implement creating GTX files. + * Add BFLIM support. * * Feel free to throw a pull request at me if you improve it! */ diff --git a/gtx_extract_bmp.cpp b/gtx_extract_bmp.cpp new file mode 100644 index 0000000..aaa753b --- /dev/null +++ b/gtx_extract_bmp.cpp @@ -0,0 +1,1088 @@ +/* + * Wii U 'GTX' Texture Extractor + * Created by Ninji Vahran / Treeki; 2014-10-31 + * ( https://github.com/Treeki ) + * Updated by AboodXD; 2017-07-14 + * ( https://github.com/aboood40091 ) + * This software is released into the public domain. + * + * Special thanks to: libtxc_dxtn developers + * + * Tested with TDM-GCC-64 on Windows 10 Pro x64. + * + * How to build: + * gcc -o gtx_extract gtx_extract.c + * + * Why so complex? + * Wii U textures appear to be packed using a complex 'texture swizzling' + * algorithm, presumably for faster access. + * + * TODO: + * Implement creating GTX files. + * Add BFLIM support. + * + * Feel free to throw a pull request at me if you improve it! + */ + +/* General stuff and imports */ +#include "txc_dxtn.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define min(x, y) (((x) < (y)) ? (x) : (y)) + + +static int formats[8] = {0x1a, 0x41a, 0x31, 0x431, 0x32, 0x432, 0x33, 0x433}; // Supported formats +static int DXTn_formats[6] = {0x31, 0x431, 0x32, 0x432, 0x33, 0x433}; + +// isvalueinarray(): find if a certain value is in a certain array +bool isvalueinarray(int val, int *arr, int size){ + for (int i = 0; i < size; i++) { + if (arr[i] == val) + return true; + } + return false; +} + + +uint8_t formatHwInfo[0x40*4] = +{ + // todo: Convert to struct + // each entry is 4 bytes + 0x00,0x00,0x00,0x01,0x08,0x03,0x00,0x01,0x08,0x01,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x10,0x07,0x00,0x00,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x0B,0x00,0x01,0x10,0x01,0x00,0x01,0x10,0x03,0x00,0x01,0x10,0x03,0x00,0x01, + 0x10,0x03,0x00,0x01,0x20,0x03,0x00,0x00,0x20,0x07,0x00,0x00,0x20,0x03,0x00,0x00, + 0x20,0x03,0x00,0x01,0x20,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x03,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x00,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01,0x20,0x0B,0x00,0x01, + 0x40,0x05,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x00,0x00, + 0x40,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x80,0x03,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x10,0x01,0x00,0x00, + 0x10,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x20,0x01,0x00,0x00, + 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x60,0x01,0x00,0x00, + 0x60,0x01,0x00,0x00,0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x80,0x01,0x00,0x01, + 0x40,0x01,0x00,0x01,0x80,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + + +uint32_t surfaceGetBitsPerPixel(uint32_t surfaceFormat) +{ + uint32_t hwFormat = (surfaceFormat & 0x3F) * 4; + uint32_t bpp = formatHwInfo[hwFormat]; + return bpp; +} + + +/* Start of libtxc_dxtn section */ +#define EXP5TO8R(packedcol) \ + ((((packedcol) >> 8) & 0xf8) | (((packedcol) >> 13) & 0x7)) + +#define EXP6TO8G(packedcol) \ + ((((packedcol) >> 3) & 0xfc) | (((packedcol) >> 9) & 0x3)) + +#define EXP5TO8B(packedcol) \ + ((((packedcol) << 3) & 0xf8) | (((packedcol) >> 2) & 0x7)) + +#define EXP4TO8(col) \ + ((col) | ((col) << 4)) + + +/* inefficient. To be efficient, it would be necessary to decode 16 pixels at once */ + +static void dxt135_decode_imageblock ( const GLubyte *img_block_src, + GLint i, GLint j, GLuint dxt_type, GLvoid *texel ) { + GLchan *rgba = (GLchan *) texel; + const GLushort color0 = img_block_src[0] | (img_block_src[1] << 8); + const GLushort color1 = img_block_src[2] | (img_block_src[3] << 8); + const GLuint bits = img_block_src[4] | (img_block_src[5] << 8) | + (img_block_src[6] << 16) | (img_block_src[7] << 24); + /* What about big/little endian? */ + GLubyte bit_pos = 2 * (j * 4 + i) ; + GLubyte code = (GLubyte) ((bits >> bit_pos) & 3); + + rgba[ACOMP] = CHAN_MAX; + switch (code) { + case 0: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color0) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color0) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color0) ); + break; + case 1: + rgba[RCOMP] = UBYTE_TO_CHAN( EXP5TO8R(color1) ); + rgba[GCOMP] = UBYTE_TO_CHAN( EXP6TO8G(color1) ); + rgba[BCOMP] = UBYTE_TO_CHAN( EXP5TO8B(color1) ); + break; + case 2: + if (color0 > color1) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) * 2 + EXP5TO8R(color1)) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) * 2 + EXP6TO8G(color1)) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) * 2 + EXP5TO8B(color1)) / 3) ); + } + else { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1)) / 2) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1)) / 2) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1)) / 2) ); + } + break; + case 3: + if ((dxt_type > 1) || (color0 > color1)) { + rgba[RCOMP] = UBYTE_TO_CHAN( ((EXP5TO8R(color0) + EXP5TO8R(color1) * 2) / 3) ); + rgba[GCOMP] = UBYTE_TO_CHAN( ((EXP6TO8G(color0) + EXP6TO8G(color1) * 2) / 3) ); + rgba[BCOMP] = UBYTE_TO_CHAN( ((EXP5TO8B(color0) + EXP5TO8B(color1) * 2) / 3) ); + } + else { + rgba[RCOMP] = 0; + rgba[GCOMP] = 0; + rgba[BCOMP] = 0; + if (dxt_type == 1) rgba[ACOMP] = UBYTE_TO_CHAN(0); + } + break; + default: + /* CANNOT happen (I hope) */ + break; + } +} + + +void fetch_2d_texel_rgba_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) +{ + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 8); + dxt135_decode_imageblock(blksrc, (i&3), (j&3), 1, texel); +} + + +void fetch_2d_texel_rgba_dxt3(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) { + + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + GLchan *rgba = (GLchan *) texel; + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); +#if 0 + /* Simple 32bit version. */ +/* that's pretty brain-dead for a single pixel, isn't it? */ + const GLubyte bit_pos = 4 * ((j&3) * 4 + (i&3)); + const GLuint alpha_low = blksrc[0] | (blksrc[1] << 8) | (blksrc[2] << 16) | (blksrc[3] << 24); + const GLuint alpha_high = blksrc[4] | (blksrc[5] << 8) | (blksrc[6] << 16) | (blksrc[7] << 24); + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); + if (bit_pos < 32) + rgba[ACOMP] = UBYTE_TO_CHAN( (GLubyte)(EXP4TO8((alpha_low >> bit_pos) & 15)) ); + else + rgba[ACOMP] = UBYTE_TO_CHAN( (GLubyte)(EXP4TO8((alpha_high >> (bit_pos - 32)) & 15)) ); +#endif +#if 1 +/* TODO test this! */ + const GLubyte anibble = (blksrc[((j&3) * 4 + (i&3)) / 2] >> (4 * (i&1))) & 0xf; + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); + rgba[ACOMP] = UBYTE_TO_CHAN( (GLubyte)(EXP4TO8(anibble)) ); +#endif + +} + + +void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel) { + + /* Extract the (i,j) pixel from pixdata and return it + * in texel[RCOMP], texel[GCOMP], texel[BCOMP], texel[ACOMP]. + */ + + GLchan *rgba = (GLchan *) texel; + const GLubyte *blksrc = (pixdata + ((srcRowStride + 3) / 4 * (j / 4) + (i / 4)) * 16); + const GLubyte alpha0 = blksrc[0]; + const GLubyte alpha1 = blksrc[1]; +#if 0 + const GLubyte bit_pos = 3 * ((j&3) * 4 + (i&3)); + /* simple 32bit version */ + const GLuint bits_low = blksrc[2] | (blksrc[3] << 8) | (blksrc[4] << 16) | (blksrc[5] << 24); + const GLuint bits_high = blksrc[6] | (blksrc[7] << 8); + GLubyte code; + if (bit_pos < 30) + code = (GLubyte) ((bits_low >> bit_pos) & 7); + else if (bit_pos == 30) + code = (GLubyte) ((bits_low >> 30) & 3) | ((bits_high << 2) & 4); + else + code = (GLubyte) ((bits_high >> (bit_pos - 32)) & 7); +#endif +#if 1 +/* TODO test this! */ + const GLubyte bit_pos = ((j&3) * 4 + (i&3)) * 3; + const GLubyte acodelow = blksrc[2 + bit_pos / 8]; + const GLubyte acodehigh = blksrc[3 + bit_pos / 8]; + const GLubyte code = (acodelow >> (bit_pos & 0x7) | + (acodehigh << (8 - (bit_pos & 0x7)))) & 0x7; +#endif + dxt135_decode_imageblock(blksrc + 8, (i&3), (j&3), 2, texel); +#if 0 + if (alpha0 > alpha1) { + switch (code) { + case 0: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + break; + case 1: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); + break; + } + } + else { + switch (code) { + case 0: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + break; + case 1: + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + break; + case 2: + case 3: + case 4: + case 5: + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); + break; + case 6: + rgba[ACOMP] = 0; + break; + case 7: + rgba[ACOMP] = CHAN_MAX; + break; + } + } +#endif +/* not sure. Which version is faster? */ +#if 1 +/* TODO test this */ + if (code == 0) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha0 ); + else if (code == 1) + rgba[ACOMP] = UBYTE_TO_CHAN( alpha1 ); + else if (alpha0 > alpha1) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (8 - code) + (alpha1 * (code - 1))) / 7) ); + else if (code < 6) + rgba[ACOMP] = UBYTE_TO_CHAN( ((alpha0 * (6 - code) + (alpha1 * (code - 1))) / 5) ); + else if (code == 6) + rgba[ACOMP] = 0; + else + rgba[ACOMP] = CHAN_MAX; +#endif +} + + +/* Start of GTX Extractor section */ +typedef struct _GFDData { + uint32_t numImages; + uint32_t dim; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t numMips; + uint32_t format; + uint32_t aa; + uint32_t use; + uint32_t imageSize; + uint32_t imagePtr; + uint32_t mipSize; + uint32_t mipPtr; + uint32_t tileMode; + uint32_t swizzle; + uint32_t alignment; + uint32_t pitch; + uint32_t bpp; + uint32_t realSize; + uint32_t dataSize; + uint8_t *data; +} GFDData; + + +typedef struct _GFDHeader { + char magic[4]; + uint32_t size_, majorVersion, minorVersion; + uint32_t gpuVersion, alignMode, reserved1, reserved2; +} GFDHeader; + + +typedef struct _GFDBlockHeader { + char magic[4]; + uint32_t size_, majorVersion, minorVersion, type_; + uint32_t dataSize; + uint32_t id, typeIdx; +} GFDBlockHeader; + + +typedef struct _GFDSurface { + uint32_t dim, width, height, depth; + uint32_t numMips, format_, aa, use; + uint32_t imageSize, imagePtr, mipSize, mipPtr; + uint32_t tileMode, swizzle, alignment, pitch; + uint32_t mipOffset[13]; + uint32_t mip1, numMips_, slice1, numSlices; + uint8_t compSel[4]; + uint32_t texReg[5]; +} GFDSurface; + +// swap32(): swaps the endianness +uint32_t swap32(uint32_t v) { + uint32_t a = (v & 0xFF000000) >> 24; + uint32_t b = (v & 0x00FF0000) >> 8; + uint32_t c = (v & 0x0000FF00) << 8; + uint32_t d = (v & 0x000000FF) << 24; + return a|b|c|d; +} + +// swapRB(): swaps the R and B channels +uint32_t swapRB(uint32_t argb) { + uint32_t r = (argb & 0x00FF0000) >> 16; + uint32_t b = (argb & 0x000000FF) << 16; + uint32_t ag = (argb & 0xFF00FF00); + return ag|r|b; +} + +// writeBMPHeader(): writes a BMP header according to the given values +void writeBMPHeader(FILE *f, int width, int height) { + uint16_t u16; + uint32_t u32; + + fwrite("BM", 1, 2, f); + u32 = 122 + (width*height*4); fwrite(&u32, 1, 4, f); + u16 = 0; fwrite(&u16, 1, 2, f); + u16 = 0; fwrite(&u16, 1, 2, f); + u32 = 122; fwrite(&u32, 1, 4, f); + + u32 = 108; fwrite(&u32, 1, 4, f); + u32 = width; fwrite(&u32, 1, 4, f); + u32 = height; fwrite(&u32, 1, 4, f); + u16 = 1; fwrite(&u16, 1, 2, f); + u16 = 32; fwrite(&u16, 1, 2, f); + u32 = 3; fwrite(&u32, 1, 4, f); + u32 = width*height*4; fwrite(&u32, 1, 4, f); + u32 = 2835; fwrite(&u32, 1, 4, f); + u32 = 2835; fwrite(&u32, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + u32 = 0xFF0000; fwrite(&u32, 1, 4, f); + u32 = 0xFF00; fwrite(&u32, 1, 4, f); + u32 = 0xFF; fwrite(&u32, 1, 4, f); + u32 = 0xFF000000; fwrite(&u32, 1, 4, f); + u32 = 0x57696E20; fwrite(&u32, 1, 4, f); + + uint8_t thing[0x24]; + memset(thing, 0, 0x24); + fwrite(thing, 1, 0x24, f); + + u32 = 0; fwrite(&u32, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); + u32 = 0; fwrite(&u32, 1, 4, f); +} + + +// readGTX(): reads the GTX file +int readGTX(GFDData *gfd, FILE *f) { + GFDHeader header; + + if (fread(&header, 1, sizeof(header), f) != sizeof(header)) + return -1; + + if (memcmp(header.magic, "Gfx2", 4) != 0) + return -2; + + gfd->numImages = 0; + + while (!feof(f)) { + GFDBlockHeader section; + if (fread(§ion, 1, sizeof(section), f) != sizeof(section)) + break; + + if (memcmp(section.magic, "BLK{", 4) != 0) + return -100; + + if (swap32(section.type_) == 0xB) { + GFDSurface info; + + if (swap32(section.dataSize) != 0x9C) + return -200; + + if (fread(&info, 1, sizeof(info), f) != sizeof(info)) + return -201; + + + gfd->dim = swap32(info.dim); + gfd->width = swap32(info.width); + gfd->height = swap32(info.height); + gfd->depth = swap32(info.depth); + gfd->numMips = swap32(info.numMips); + gfd->format = swap32(info.format_); + gfd->aa = swap32(info.aa); + gfd->use = swap32(info.use); + gfd->imageSize = swap32(info.imageSize); + gfd->imagePtr = swap32(info.imagePtr); + gfd->mipSize = swap32(info.mipSize); + gfd->mipPtr = swap32(info.mipPtr); + gfd->tileMode = swap32(info.tileMode); + gfd->swizzle = swap32(info.swizzle); + gfd->alignment = swap32(info.alignment); + gfd->pitch = swap32(info.pitch); + gfd->bpp = surfaceGetBitsPerPixel(gfd->format); + + } + + else if (swap32(section.type_) == 0xC) { + uint32_t bpp = gfd->bpp; + bpp /= 8; + + if (isvalueinarray(gfd->format, DXTn_formats, 6)) + gfd->realSize = ((gfd->width + 3) >> 2) * ((gfd->height + 3) >> 2) * bpp; + + else + gfd->realSize = gfd->width * gfd->height * bpp; + + gfd->dataSize = swap32(section.dataSize); + gfd->data = (uint8_t*)malloc(gfd->dataSize); + if (!gfd->data) + return -300; + + if (fread(gfd->data, 1, gfd->dataSize, f) != gfd->dataSize) + return -301; + + gfd->numImages += 1; + + } + + else { + fseek(f, swap32(section.dataSize), SEEK_CUR); + } + } + + return 1; +} + + +/* Start of swizzling section */ + +/* Credits: + -AddrLib: actual code + -Exzap: modifying code to apply to Wii U textures + -AboodXD: porting, code improvements and cleaning up +*/ + +static uint32_t m_banks = 4; +static uint32_t m_banksBitcount = 2; +static uint32_t m_pipes = 2; +static uint32_t m_pipesBitcount = 1; +static uint32_t m_pipeInterleaveBytes = 256; +static uint32_t m_pipeInterleaveBytesBitcount = 8; +static uint32_t m_rowSize = 2048; +static uint32_t m_swapSize = 256; +static uint32_t m_splitSize = 2048; + +static uint32_t m_chipFamily = 2; + +static uint32_t MicroTilePixels = 64; + +uint32_t computeSurfaceThickness(uint32_t tileMode) +{ + uint32_t thickness = 1; + + if (tileMode == 3 || tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickness = 4; + + else if (tileMode == 16 || tileMode == 17) + thickness = 8; + + return thickness; +} + + +uint32_t computePixelIndexWithinMicroTile(uint32_t x, uint32_t y, uint32_t bpp, uint32_t tileMode) +{ + uint32_t z = 0; + uint32_t thickness; + uint32_t pixelBit8; + uint32_t pixelBit7; + uint32_t pixelBit6; + uint32_t pixelBit5; + uint32_t pixelBit4; + uint32_t pixelBit3; + uint32_t pixelBit2; + uint32_t pixelBit1; + uint32_t pixelBit0; + pixelBit6 = 0; + pixelBit7 = 0; + pixelBit8 = 0; + thickness = computeSurfaceThickness(tileMode); + + 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; + } + + else if (bpp == 0x10) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = (x & 4) >> 2; + pixelBit3 = y & 1; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x20 || bpp == 0x60) { + pixelBit0 = x & 1; + pixelBit1 = (x & 2) >> 1; + pixelBit2 = y & 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (bpp == 0x40) { + pixelBit0 = x & 1; + pixelBit1 = y & 1; + pixelBit2 = (x & 2) >> 1; + pixelBit3 = (x & 4) >> 2; + pixelBit4 = (y & 2) >> 1; + pixelBit5 = (y & 4) >> 2; + } + + else if (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); +} + + +uint32_t computePipeFromCoordWoRotation(uint32_t x, uint32_t y) { + // hardcoded to assume 2 pipes + uint32_t pipe = ((y >> 3) ^ (x >> 3)) & 1; + return pipe; +} + + +uint32_t computeBankFromCoordWoRotation(uint32_t x, uint32_t y) { + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t bankBit0; + uint32_t bankBit0a; + uint32_t bank = 0; + + if (numBanks == 4) { + bankBit0 = ((y / (16 * numPipes)) ^ (x >> 3)) & 1; + bank = bankBit0 | 2 * (((y / (8 * numPipes)) ^ (x >> 4)) & 1); + } + + else if (numBanks == 8) { + bankBit0a = ((y / (32 * numPipes)) ^ (x >> 3)) & 1; + bank = (bankBit0a | 2 * (((y / (32 * numPipes)) ^ (y / (16 * numPipes) ^ (x >> 4))) & 1) | + 4 * (((y / (8 * numPipes)) ^ (x >> 5)) & 1)); + } + + return bank; +} + + +uint32_t isThickMacroTiled(uint32_t tileMode) { + uint32_t thickMacroTiled = 0; + + if (tileMode == 7 || tileMode == 11 || tileMode == 13 || tileMode == 15) + thickMacroTiled = 1; + + return thickMacroTiled; +} + + +uint32_t isBankSwappedTileMode(uint32_t tileMode) { + uint32_t bankSwapped = 0; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) + bankSwapped = 1; + + return bankSwapped; +} + + +uint32_t computeMacroTileAspectRatio(uint32_t tileMode) { + uint32_t ratio = 1; + + if (tileMode == 5 || tileMode == 9) + ratio = 2; + + else if (tileMode == 6 || tileMode == 10) + ratio = 4; + + return ratio; +} + + +uint32_t computeSurfaceBankSwappedWidth(uint32_t tileMode, uint32_t bpp, uint32_t pitch) { + if (isBankSwappedTileMode(tileMode) == 0) + return 0; + + uint32_t numSamples = 1; + uint32_t numBanks = m_banks; + uint32_t numPipes = m_pipes; + uint32_t swapSize = m_swapSize; + uint32_t rowSize = m_rowSize; + uint32_t splitSize = m_splitSize; + uint32_t groupSize = m_pipeInterleaveBytes; + uint32_t bytesPerSample = 8 * bpp; + + uint32_t samplesPerTile = splitSize / bytesPerSample; + uint32_t slicesPerTile = max(1, numSamples / samplesPerTile); + + if (isThickMacroTiled(tileMode) != 0) + numSamples = 4; + + uint32_t bytesPerTileSlice = numSamples * bytesPerSample / slicesPerTile; + + uint32_t factor = computeMacroTileAspectRatio(tileMode); + uint32_t swapTiles = max(1, (swapSize >> 1) / bpp); + + uint32_t swapWidth = swapTiles * 8 * numBanks; + uint32_t heightBytes = numSamples * factor * numPipes * bpp / slicesPerTile; + uint32_t swapMax = numPipes * numBanks * rowSize / heightBytes; + uint32_t swapMin = groupSize * 8 * numBanks / bytesPerTileSlice; + + uint32_t bankSwapWidth = min(swapMax, max(swapMin, swapWidth)); + + while (bankSwapWidth >= (2 * pitch)) + bankSwapWidth >>= 1; + + return bankSwapWidth; +} + + +uint64_t AddrLib_computeSurfaceAddrFromCoordLinear(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch) { + uint32_t rowOffset = y * pitch; + uint32_t pixOffset = x; + + uint32_t addr = (rowOffset + pixOffset) * bpp; + addr /= 8; + + return addr; +} + + +uint64_t AddrLib_computeSurfaceAddrFromCoordMicroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t tileMode) { + uint64_t microTileThickness = 1; + + if (tileMode == 3) + microTileThickness = 4; + + uint64_t microTileBytes = (MicroTilePixels * microTileThickness * bpp + 7) / 8; + uint64_t microTilesPerRow = pitch >> 3; + uint64_t microTileIndexX = x >> 3; + uint64_t microTileIndexY = y >> 3; + + uint64_t microTileOffset = microTileBytes * (microTileIndexX + microTileIndexY * microTilesPerRow); + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t pixelOffset = bpp * pixelIndex; + + pixelOffset >>= 3; + + return pixelOffset + microTileOffset; +} + + +uint64_t AddrLib_computeSurfaceAddrFromCoordMacroTiled(uint32_t x, uint32_t y, uint32_t bpp, uint32_t pitch, uint32_t height, uint32_t tileMode, uint32_t pipeSwizzle, uint32_t bankSwizzle) { + uint64_t sampleSlice, numSamples; + + uint32_t numPipes = m_pipes; + uint32_t numBanks = m_banks; + uint32_t numGroupBits = m_pipeInterleaveBytesBitcount; + uint32_t numPipeBits = m_pipesBitcount; + uint32_t numBankBits = m_banksBitcount; + + uint32_t microTileThickness = computeSurfaceThickness(tileMode); + + uint64_t microTileBits = bpp * (microTileThickness * MicroTilePixels); + uint64_t microTileBytes = (microTileBits + 7) / 8; + + uint64_t pixelIndex = computePixelIndexWithinMicroTile(x, y, bpp, tileMode); + + uint64_t pixelOffset = bpp * pixelIndex; + + uint64_t elemOffset = pixelOffset; + + uint64_t bytesPerSample = microTileBytes; + if (microTileBytes <= m_splitSize) { + numSamples = 1; + sampleSlice = 0; + } + + else { + uint64_t samplesPerSlice = m_splitSize / bytesPerSample; + uint64_t numSampleSplits = max(1, 1 / samplesPerSlice); + numSamples = samplesPerSlice; + sampleSlice = elemOffset / (microTileBits / numSampleSplits); + elemOffset %= microTileBits / numSampleSplits; + } + elemOffset += 7; + elemOffset /= 8; + + uint64_t pipe = computePipeFromCoordWoRotation(x, y); + uint64_t bank = computeBankFromCoordWoRotation(x, y); + + uint64_t bankPipe = pipe + numPipes * bank; + + uint64_t swizzle_ = pipeSwizzle + numPipes * bankSwizzle; + + bankPipe ^= numPipes * sampleSlice * ((numBanks >> 1) + 1) ^ swizzle_; + bankPipe %= numPipes * numBanks; + pipe = bankPipe % numPipes; + bank = bankPipe / numPipes; + + uint64_t sliceBytes = (height * pitch * microTileThickness * bpp * numSamples + 7) / 8; + uint64_t sliceOffset = sliceBytes * (sampleSlice / microTileThickness); + + uint64_t macroTilePitch = 8 * m_banks; + uint64_t macroTileHeight = 8 * m_pipes; + + if (tileMode == 5 || tileMode == 9) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN2 + macroTilePitch >>= 1; + macroTileHeight *= 2; + } + + else if (tileMode == 6 || tileMode == 10) { // GX2_TILE_MODE_2D_TILED_THIN4 and GX2_TILE_MODE_2B_TILED_THIN4 + macroTilePitch >>= 2; + macroTileHeight *= 4; + } + + uint64_t macroTilesPerRow = pitch / macroTilePitch; + uint64_t macroTileBytes = (numSamples * microTileThickness * bpp * macroTileHeight * macroTilePitch + 7) / 8; + uint64_t macroTileIndexX = x / macroTilePitch; + uint64_t macroTileIndexY = y / macroTileHeight; + uint64_t macroTileOffset = (macroTileIndexX + macroTilesPerRow * macroTileIndexY) * macroTileBytes; + + if (tileMode == 8 || tileMode == 9 || tileMode == 10 || tileMode == 11 || tileMode == 14 || tileMode == 15) { + static const uint32_t bankSwapOrder[] = { 0, 1, 3, 2, 6, 7, 5, 4, 0, 0 }; + uint64_t bankSwapWidth = computeSurfaceBankSwappedWidth(tileMode, bpp, pitch); + uint64_t swapIndex = macroTilePitch * macroTileIndexX / bankSwapWidth; + bank ^= bankSwapOrder[swapIndex & (m_banks - 1)]; + } + + uint64_t groupMask = ((1 << numGroupBits) - 1); + + uint64_t numSwizzleBits = (numBankBits + numPipeBits); + + uint64_t totalOffset = (elemOffset + ((macroTileOffset + sliceOffset) >> numSwizzleBits)); + + uint64_t offsetHigh = (totalOffset & ~groupMask) << numSwizzleBits; + uint64_t offsetLow = groupMask & totalOffset; + + uint64_t pipeBits = pipe << numGroupBits; + uint64_t bankBits = bank << (numPipeBits + numGroupBits); + + return bankBits | pipeBits | offsetLow | offsetHigh; +} + +// writeFile(): writes the BMP file +void writeFile(FILE *f, int width, int height, uint8_t *output) { + int row; + + writeBMPHeader(f, width, height); + + for (row = height - 1; row >= 0; row--) { + fwrite(&output[row * width * 4], 1, width * 4, f); + } +} + +// deswizzle(): deswizzles the image +void deswizzle(GFDData *gfd, FILE *f) { + uint64_t pos, pos_; + uint32_t x, y, width, height; + uint8_t *data, *result; + uint32_t *output, outValue; + double frac, whole; + + data = (uint8_t *)gfd->data; + result = (uint8_t*)malloc(gfd->dataSize); + + if (isvalueinarray(gfd->format, DXTn_formats, 6)) { + double width2 = (double)gfd->width / 4.0; + frac = modf(width2, &whole); + width = (uint32_t)whole; + if (frac == 0.5) + width += 1; + + double height2 = (double)gfd->height / 4.0; + frac = modf(height2, &whole); + height = (uint32_t)whole; + if (frac == 0.5) + height += 1; + } + + else { + width = gfd->width; + height = gfd->height; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint32_t bpp = gfd->bpp; + uint32_t pipeSwizzle = (gfd->swizzle >> 8) & 1; + uint32_t bankSwizzle = (gfd->swizzle >> 9) & 3; + + if (gfd->tileMode == 0 || gfd->tileMode == 1) + pos = AddrLib_computeSurfaceAddrFromCoordLinear(x, y, bpp, gfd->pitch); + + else if (gfd->tileMode == 2 || gfd->tileMode == 3) + pos = AddrLib_computeSurfaceAddrFromCoordMicroTiled(x, y, bpp, gfd->pitch, gfd->tileMode); + + else + pos = AddrLib_computeSurfaceAddrFromCoordMacroTiled(x, y, bpp, gfd->pitch, gfd->height, gfd->tileMode, pipeSwizzle, bankSwizzle); + + bpp /= 8; + + pos_ = (y * width + x) * bpp; + + for (int i = 0; i < bpp; i++) { + if (pos + i < gfd->dataSize && pos_ + i < gfd->dataSize) + result[pos_ + i] = data[pos + i]; + } + } + } + + output = (uint32_t*)malloc(gfd->width * gfd->height * 4); + + for (y = 0; y < gfd->height; y++) { + for (x = 0; x < gfd->width; x++) { + if (isvalueinarray(gfd->format, DXTn_formats, 6)) { + uint8_t bits[4]; + + if (gfd->format == 0x31 || gfd->format == 0x431) + fetch_2d_texel_rgba_dxt1(gfd->width, result, x, y, bits); + else if (gfd->format == 0x32 || gfd->format == 0x432) + fetch_2d_texel_rgba_dxt3(gfd->width, result, x, y, bits); + else if (gfd->format == 0x33 || gfd->format == 0x433) + fetch_2d_texel_rgba_dxt5(gfd->width, result, x, y, bits); + + outValue = (bits[ACOMP] << 24); + outValue |= (bits[RCOMP] << 16); + outValue |= (bits[GCOMP] << 8); + outValue |= bits[BCOMP]; + } + + else { + pos_ = (y * width + x) * 4; + + outValue = (result[pos_ + 3] << 24); + outValue |= (result[pos_] << 16); + outValue |= (result[pos_ + 1] << 8); + outValue |= result[pos_ + 2]; + } + + output[(y * gfd->width) + x] = outValue; + } + } + + writeFile(f, gfd->width, gfd->height, (uint8_t *)output); + + free(result); + free(output); +} + +// remove_three(): removes the file extension from a string +char *remove_three(const char *filename) { + size_t len = strlen(filename); + char *newfilename = (char*)malloc(len-2); + if (!newfilename) /* handle error */; + memcpy(newfilename, filename, len-3); + newfilename[len - 3] = 0; + return newfilename; +} + +// main(): the main function +int main(int argc, char **argv) { + GFDData data; + FILE *f; + int result; + + printf("GTX Extractor - C++ ver.\n"); + printf("BMP ver.\n"); + printf("(C) 2014 Treeki, 2017 AboodXD\n"); + + if (argc != 2) { + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: %s [input.gtx]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Supported formats:\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC1_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC2_SRGB\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_UNORM\n"); + fprintf(stderr, " - GX2_SURFACE_FORMAT_T_BC3_SRGB\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + if (!(f = fopen(argv[1], "rb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for reading\n", argv[1]); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + else + printf("\nConverting: %s\n", argv[1]); + + if ((result = readGTX(&data, f)) != 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "Error %d while parsing GTX file %s\n", result, argv[1]); + fclose(f); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + fclose(f); + + if (data.numImages > 1) { + fprintf(stderr, "\n"); + fprintf(stderr, "This program doesn't support converting GTX files with multiple images\n"); // TODO + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + else if (data.numImages == 0) { + fprintf(stderr, "\n"); + fprintf(stderr, "No images were found in this GTX file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + char *str = remove_three(argv[1]); + char c = 'b'; + char c1 = 'm'; + char c2 = 'p'; + + size_t len = strlen(str); + char *str2 = (char*)malloc(len + 3 + 1 ); + strcpy(str2, str); + str2[len] = c; + str2[len + 1] = c1; + str2[len + 2] = c2; + str2[len + 3] = '\0'; + + if (!(f = fopen(str2, "wb"))) { + fprintf(stderr, "\n"); + fprintf(stderr, "Cannot open %s for writing\n", str2); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + free(str2); + + printf("\n"); + printf("// ----- GX2Surface Info ----- \n"); + printf(" dim = %d\n", data.dim); + printf(" width = %d\n", data.width); + printf(" height = %d\n", data.height); + printf(" depth = %d\n", data.depth); + printf(" numMips = %d\n", data.numMips); + printf(" format = 0x%x\n", data.format); + printf(" aa = %d\n", data.aa); + printf(" use = %d\n", data.use); + printf(" imageSize = %d\n", data.imageSize); + printf(" mipSize = %d\n", data.mipSize); + printf(" tileMode = %d\n", data.tileMode); + printf(" swizzle = %d, 0x%x\n", data.swizzle, data.swizzle); + printf(" alignment = %d\n", data.alignment); + printf(" pitch = %d\n", data.pitch); + printf("\n"); + printf(" bits per pixel = %d\n", data.bpp); + printf(" bytes per pixel = %d\n", data.bpp / 8); + printf(" realSize = %d\n", data.realSize); + + uint32_t bpp = data.bpp; + + if (isvalueinarray(data.format, formats, 8)) { + deswizzle(&data, f); + } + + else { + fprintf(stderr, "Unsupported format: 0x%x\n", data.format); + fprintf(stderr, "\n"); + fprintf(stderr, "Exiting in 5 seconds...\n"); + unsigned int retTime = time(0) + 5; + while (time(0) < retTime); + return EXIT_FAILURE; + } + + fclose(f); + + printf("\nFinished converting: %s\n", argv[1]); + + return EXIT_SUCCESS; +} diff --git a/txc_dxtn.h b/txc_dxtn.h new file mode 100644 index 0000000..31d7930 --- /dev/null +++ b/txc_dxtn.h @@ -0,0 +1,50 @@ +/* + * libtxc_dxtn + * Version: 0.1 + * + * Copyright (C) 2004 Roland Scheidegger All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __APPLE__ +#include +#else +#include +#endif + +typedef GLubyte GLchan; +#define UBYTE_TO_CHAN(b) (b) +#define CHAN_MAX 255 +#define RCOMP 0 +#define GCOMP 1 +#define BCOMP 2 +#define ACOMP 3 + +void fetch_2d_texel_rgb_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt1(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt3(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); +void fetch_2d_texel_rgba_dxt5(GLint srcRowStride, const GLubyte *pixdata, + GLint i, GLint j, GLvoid *texel); + +void tx_compress_dxtn(GLint srccomps, GLint width, GLint height, + const GLubyte *srcPixData, GLenum destformat, + GLubyte *dest, GLint dstRowStride); From 21b5f57cd1847691c46d2525f4d9e1787064f03d Mon Sep 17 00:00:00 2001 From: AboodXD Date: Fri, 14 Jul 2017 23:19:10 +0400 Subject: [PATCH 17/19] whoops --- gtx_extract.cpp | 2 +- gtx_extract_no5.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gtx_extract.cpp b/gtx_extract.cpp index 5bd6f61..08c4b6e 100644 --- a/gtx_extract.cpp +++ b/gtx_extract.cpp @@ -921,7 +921,7 @@ int main(int argc, char **argv) { FILE *f; int result; - printf("GTX Extractor - C ver.\n"); + printf("GTX Extractor - C++ ver.\n"); printf("(C) 2014 Treeki, 2017 AboodXD\n"); if (argc != 2) { diff --git a/gtx_extract_no5.cpp b/gtx_extract_no5.cpp index 7e9a8c7..9c7f578 100644 --- a/gtx_extract_no5.cpp +++ b/gtx_extract_no5.cpp @@ -920,7 +920,7 @@ int main(int argc, char **argv) { FILE *f; int result; - printf("GTX Extractor - C ver.\n"); + printf("GTX Extractor - C++ ver.\n"); printf("(C) 2014 Treeki, 2017 AboodXD\n"); if (argc != 2) { From e3addbaaf006e14ed40e740bc4e77de12f3858aa Mon Sep 17 00:00:00 2001 From: AboodXD Date: Sat, 15 Jul 2017 13:50:10 +0400 Subject: [PATCH 18/19] Update the compiler --- gtx_extract.cpp | 2 +- gtx_extract_bmp.cpp | 2 +- gtx_extract_no5.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gtx_extract.cpp b/gtx_extract.cpp index 08c4b6e..bd3abeb 100644 --- a/gtx_extract.cpp +++ b/gtx_extract.cpp @@ -9,7 +9,7 @@ * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: - * gcc -o gtx_extract gtx_extract.c + * g++ -o gtx_extract gtx_extract.c * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' diff --git a/gtx_extract_bmp.cpp b/gtx_extract_bmp.cpp index aaa753b..7cbd63f 100644 --- a/gtx_extract_bmp.cpp +++ b/gtx_extract_bmp.cpp @@ -11,7 +11,7 @@ * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: - * gcc -o gtx_extract gtx_extract.c + * g++ -o gtx_extract gtx_extract.c * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' diff --git a/gtx_extract_no5.cpp b/gtx_extract_no5.cpp index 9c7f578..2892f54 100644 --- a/gtx_extract_no5.cpp +++ b/gtx_extract_no5.cpp @@ -9,7 +9,7 @@ * Tested with TDM-GCC-64 on Windows 10 Pro x64. * * How to build: - * gcc -o gtx_extract gtx_extract.c + * g++ -o gtx_extract gtx_extract.c * * Why so complex? * Wii U textures appear to be packed using a complex 'texture swizzling' From 36b710a1c08f166fa8cf14b24f26eaa576950faf Mon Sep 17 00:00:00 2001 From: AboodXD Date: Sat, 15 Jul 2017 14:30:09 +0400 Subject: [PATCH 19/19] Fix indent blocks --- gtx_extract_bmp.cpp | 56 +++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/gtx_extract_bmp.cpp b/gtx_extract_bmp.cpp index 7cbd63f..db6521d 100644 --- a/gtx_extract_bmp.cpp +++ b/gtx_extract_bmp.cpp @@ -896,34 +896,36 @@ void deswizzle(GFDData *gfd, FILE *f) { output = (uint32_t*)malloc(gfd->width * gfd->height * 4); - for (y = 0; y < gfd->height; y++) { + for (y = 0; y < gfd->height; y++) { for (x = 0; x < gfd->width; x++) { - if (isvalueinarray(gfd->format, DXTn_formats, 6)) { - uint8_t bits[4]; - - if (gfd->format == 0x31 || gfd->format == 0x431) - fetch_2d_texel_rgba_dxt1(gfd->width, result, x, y, bits); - else if (gfd->format == 0x32 || gfd->format == 0x432) - fetch_2d_texel_rgba_dxt3(gfd->width, result, x, y, bits); - else if (gfd->format == 0x33 || gfd->format == 0x433) - fetch_2d_texel_rgba_dxt5(gfd->width, result, x, y, bits); - - outValue = (bits[ACOMP] << 24); - outValue |= (bits[RCOMP] << 16); - outValue |= (bits[GCOMP] << 8); - outValue |= bits[BCOMP]; - } - - else { - pos_ = (y * width + x) * 4; - - outValue = (result[pos_ + 3] << 24); - outValue |= (result[pos_] << 16); - outValue |= (result[pos_ + 1] << 8); - outValue |= result[pos_ + 2]; - } - - output[(y * gfd->width) + x] = outValue; + if (isvalueinarray(gfd->format, DXTn_formats, 6)) { + uint8_t bits[4]; + + if (gfd->format == 0x31 || gfd->format == 0x431) + fetch_2d_texel_rgba_dxt1(gfd->width, result, x, y, bits); + + else if (gfd->format == 0x32 || gfd->format == 0x432) + fetch_2d_texel_rgba_dxt3(gfd->width, result, x, y, bits); + + else if (gfd->format == 0x33 || gfd->format == 0x433) + fetch_2d_texel_rgba_dxt5(gfd->width, result, x, y, bits); + + outValue = (bits[ACOMP] << 24); + outValue |= (bits[RCOMP] << 16); + outValue |= (bits[GCOMP] << 8); + outValue |= bits[BCOMP]; + } + + else { + pos_ = (y * width + x) * 4; + + outValue = (result[pos_ + 3] << 24); + outValue |= (result[pos_] << 16); + outValue |= (result[pos_ + 1] << 8); + outValue |= result[pos_ + 2]; + } + + output[(y * gfd->width) + x] = outValue; } }