Alpha 1001 Posted April 29 Share Posted April 29 Hi everyone, I'm developing a tool to edit the textures of the NFS Carbon and Most Wanted games in your PlayStation 2 releases. Until now, this tool supports to open and rebuild the .tpk archives with the textures, visualize and export to the tm2 format all the textures with 4bpp and 8bpp, and also import a tm2 to a 8bpp texture replacing the original texture in the tpk archive. My goal now is to find an algorithm to swizzle again the 4bpp textures, so I can import a 4bpp texture to the tpk archive. I've found some C codes to swizzle 4bpp textures, but they won't work, only the 8bpp swizzling is working fine. There is a video in attachment where I can show the progress of tool. Thanks in advance! 20240429_112840.zip Link to comment Share on other sites More sharing options...
Solution ikskoks Posted April 29 Solution Share Posted April 29 Try some of these: https://gist.github.com/Fireboyd78/1546f5c86ebce52ce05e7837c697dc72#file-unswizzle-cs https://github.com/leeao/PS2Textures/blob/main/PS2Textures.py https://ps2linux.no-ip.info/playstation2-linux.com/docs/howto/display_docef7c.html?docid=75 https://github.com/neko68k/FusionTool/blob/master/SubaSwizzle/PS2Textures.cpp https://gta.nick7.com/ps2/swizzling/unswizzle_delphi.txt https://sourceforge.net/projects/kernelloader/files/Sony Linux Toolkit/Package Update Files/ezswizzle/ 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted April 29 Author Share Posted April 29 Thanks, I'll try here Link to comment Share on other sites More sharing options...
Alpha 1001 Posted April 29 Author Share Posted April 29 Hi IksKoks, I've tested these codes and them only do the unswizzling, some of them I've already have here and unfortunately there is none to swizzle properly the 4bpp textures again. If I can send a sample of a 4bpp swizzled texture and a 4bpp unswizzled texture, can you create a swizzling algorithm to swizzle and leave it identical with the swizzled texture? Link to comment Share on other sites More sharing options...
piken Posted April 30 Share Posted April 30 (edited) Swizzling and unswizzling are bijective mirror operations. So if you have to code to successfully unswizzle, then you already have the code to reswizzle - just swap the reads and the writes of the linear offset and swizzled/twiddled offset. That said, I would like to see more real world 4-bpp samples either way, as I am coincidentally trying to figure out some issues in my swizzled texture diagnostics C++ app with non-square textures, seeing odd horizontal bands occurring vertically every 128 pixels (and both the linux no ip and FusionTool implementations appear to suffer from it). Edited May 13 by piken Link to comment Share on other sites More sharing options...
Alpha 1001 Posted April 30 Author Share Posted April 30 The problem to me is, I don't know how to swap the read and the writes, the unswizzling is a complicated code, but in attachment there is 4 samples of textures with swizzle and without swizzle. The last 64 bytes of each sample are the colors pallete. The only data that need to be swizzled is the texture data. In this sample there is also the unswizzling code that I use to unswizzle the textures. If you can modify this code to swizzle again, I'll be grateful samples.rar Link to comment Share on other sites More sharing options...
piken Posted April 30 Share Posted April 30 1 hour ago, Alpha 1001 said: If you can modify this code to swizzle again, I'll be grateful. Just swap the read and write: out[swizzleid] = buffer[y * width + x]; ---> out[y * width + x] = buffer[swizzleid]; Thanks for the extra test data. 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted April 30 Author Share Posted April 30 14 minutes ago, piken said: Just swap the read and write: out[swizzleid] = buffer[y * width + x]; ---> out[y * width + x] = buffer[swizzleid]; Thanks for the extra test data. Thank you very much! If you want, I can send more test data and samples. Link to comment Share on other sites More sharing options...
piken Posted April 30 Share Posted April 30 > If you want, I can send more test data and samples. Sure, especially desired are non-square sizes. Cheers (and good night :b). Link to comment Share on other sites More sharing options...
Alpha 1001 Posted April 30 Author Share Posted April 30 1 hour ago, piken said: > If you want, I can send more test data and samples. Sure, especially desired are non-square sizes. Cheers (and good night :b). Hi, almost all the textures of nfs mw are power of two, so, if I've understood good, a non-square size must be different. Well, I've found a texture with dimensions of 640x480, this texture is in attachment. About the swizzling code, only the texture with dimensions of 256x64 has been swizzled again after the swap of read and the writes. The textures with different dimensions won't work, unfortunatelly. I think it is needed to make more changes in that function. unswizzled_640x480_8bpp.zip 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 1 Author Share Posted May 1 Do you can make more changes in that function to swizzle 4bpp, piken? Link to comment Share on other sites More sharing options...
piken Posted May 1 Share Posted May 1 (edited) On 4/30/2024 at 6:20 AM, Alpha 1001 said: if I've understood good, a non-square size must be different. A square texture has the same width and height. So I just hoped you had more 4-bpp swizzled cases that were not square, like the rectangular 4bpp_swizzled_256x64. The 640x480 one is simple linear: On 4/30/2024 at 6:20 AM, Alpha 1001 said: The textures with different dimensions won't work, unfortunatelly Which file are you having difficulty with specifically? This is 4bpp_swizzled_256x256: Edited May 1 by piken 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 1 Author Share Posted May 1 3 hours ago, piken said: I hoped you had more 4-bpp swizzled cases that were not square, like the rectangular 4bpp_swizzled_256x64. The 640x480 one is simple linear: Which file are you having difficulty with specifically? 4bpp_swizzled_256x256? I am having difficult with all the unswizzled 4_bpp textures, I can't swizzle them again, only unswizzle them. I'll be going soon send more 4bpp samples for you, I don't know if these textures are non-square size because I didn't understood very well this concept, but I'll send. Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 1 Author Share Posted May 1 Here more samples of 4bpp textures without swizzle in attachment new samples - 4bpp.rar 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 2 Author Share Posted May 2 Hi piken, can you help me yet with swizzle? Link to comment Share on other sites More sharing options...
piken Posted May 5 Share Posted May 5 (edited) On 5/1/2024 at 8:20 AM, Alpha 1001 said: I am having difficult with all the unswizzled 4_bpp textures Let's focus on one specific file that you're having trouble with, because the ones I tried work. e.g. 4bpp_swizzled_256x64_original -> unswizzled -> reswizzled. 9_256x64 4bpp unswizzled -> reswizzled -> unswizzled: --> --> 5_128x128 4bpp unswizzled -> reswizzled -> unswizzled: #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <memory.h> #include <stdbool.h> #include <stdint.h> #include <string.h> #include <limits.h> void Unswizzle4( uint8_t const* inputBuffer, // (width * height + 1) / 2 bytes uint8_t* outputBuffer, // (width * height + 1) / 2 bytes uint32_t width, uint32_t height, bool swizzle // reswizzle instead of unswizzle ); void Unswizzle8( uint8_t const* inputBuffer, // width * height bytes uint8_t* outputBuffer, // width * height bytes uint32_t width, uint32_t height, bool swizzle // reswizzle instead of unswizzle ); // nullptr was added in C23, but most compilers do not support it yet. #define nullptr NULL int main(int argc, char* argv[]) { if (argc < 8) { printf( "Usage:\n" " action bitdepth width height byteOffset inputFilename outputFilename\n" " action: swizzle, unswizzle\n" " bitdepth: 4, 8\n" " byte offset: 0 to any valid position in file\n" " file names: raw binary data\n" "\n" "e.g.:\n" " swizzle 4 256 64 0 input.dat output.dat\n" " unswizzle 8 128 128 0x40 input.bin output.bin\n" ); return EXIT_FAILURE; } char const* action = argv[1]; char const* bitDepthString = argv[2]; char const* widthString = argv[3]; char const* heightString = argv[4]; char const* byteOffsetString = argv[5]; char const* inputFilename = argv[6]; char const* outputFilename = argv[7]; bool swizzle = false; if (strcmp(action, "swizzle") == 0) { swizzle = true; } else if (strcmp(action, "unswizzle") == 0) { swizzle = false; } else { printf("Expect action \"swizzle\" or \"unswizzle\", not %s.", action); return EXIT_FAILURE; } uint32_t bitDepth = atoi(bitDepthString); if (bitDepth != 4 && bitDepth != 8) { printf("Expect bit depth of 4 or 8, not %s.", bitDepthString); return EXIT_FAILURE; } uint32_t width = atoi(widthString); if (width <= 0) { printf("Invalid width: %s.", widthString); return EXIT_FAILURE; } uint32_t height = atoi(heightString); if (height <= 0) { printf("Invalid height: %s.", heightString); return EXIT_FAILURE; } uint32_t byteOffset = strtoul(byteOffsetString, nullptr, 0); if (byteOffset == 0 && byteOffsetString[0] != '0') { printf("Invalid byte offset: %s.", byteOffsetString); return EXIT_FAILURE; } FILE* inputFile = fopen(inputFilename, "rb"); if (inputFile == nullptr) { printf("Could not open input file: \"%s\"", inputFilename); return EXIT_FAILURE; } FILE* outputFile = fopen(outputFilename, "wb"); if (outputFile == nullptr) { printf("Could not open output file: \"%s\"", outputFilename); return EXIT_FAILURE; } printf( "action: %s\n" "bit depth: %s\n" "width: %s\n" "height: %s\n" "byte offset: %s\n" "input: %s\n" "output: %s\n", action, bitDepthString, widthString, heightString, byteOffsetString, inputFilename, outputFilename ); // Allocate buffers. fseek(inputFile, 0, SEEK_END); long inputFileSize = ftell(inputFile); fseek(inputFile, 0, SEEK_SET); uint32_t minimumBufferSize = bitDepth * width * height / CHAR_BIT; if ((uint32_t)inputFileSize < byteOffset + minimumBufferSize) { inputFileSize = byteOffset + minimumBufferSize; } uint8_t* inputFileData = (uint8_t*)malloc(inputFileSize); uint8_t* outputFileData = (uint8_t*)malloc(inputFileSize); // Read input data, perform conversion, write output data. fread(inputFileData, 1, inputFileSize, inputFile); fclose(inputFile); // Copy data before and after the pixels. memcpy(outputFileData, inputFileData, byteOffset); uint32_t byteOffsetAfterPixels = byteOffset + minimumBufferSize; memcpy(outputFileData + byteOffsetAfterPixels, inputFileData + byteOffsetAfterPixels, inputFileSize - byteOffsetAfterPixels); switch (bitDepth) { case 4: Unswizzle4(inputFileData + byteOffset, outputFileData + byteOffset, width, height, swizzle); break; case 8: Unswizzle8(inputFileData + byteOffset, outputFileData + byteOffset, width, height, swizzle); break; } fwrite(outputFileData, 1, inputFileSize, outputFile); fclose(outputFile); free(inputFileData); free(outputFileData); printf("Converted\n"); return EXIT_SUCCESS; } void Unswizzle4( uint8_t const* inputBuffer, // (width * height + 1) / 2 bytes uint8_t* outputBuffer, // (width * height + 1) / 2 bytes uint32_t width, uint32_t height, bool swizzle ) { uint8_t* inputPixels8bpp = (uint8_t*)malloc(width * height); uint8_t* outputPixels8bpp = (uint8_t*)malloc(width * height); const uint32_t bufferByteSize = (width * height + 1) / 2; // Unpack 4-bpp pixels to 8-bpp. for (uint32_t i = 0; i < bufferByteSize; ++i) { uint8_t byteValue = inputBuffer[i]; uint8_t nybbleLow = byteValue & 0xF; uint8_t nybbleHigh = byteValue >> 4; inputPixels8bpp[i * 2] = nybbleLow; inputPixels8bpp[i * 2 + 1] = nybbleHigh; } // Swizzle/Unswizzle them as if 8-bpp. Unswizzle8(inputPixels8bpp, outputPixels8bpp, width, height, swizzle); // Repack 8-bpp pixels to 4-bpp. for (uint32_t i = 0; i < bufferByteSize; ++i) { uint8_t nybbleLow = outputPixels8bpp[i * 2 + 0]; uint8_t nybbleHigh = outputPixels8bpp[i * 2 + 1]; uint8_t byteValue = (nybbleHigh << 4) | nybbleLow; outputBuffer[i] = byteValue; } free(inputPixels8bpp); free(outputPixels8bpp); } void Unswizzle8( uint8_t const* inputBuffer, // width * height bytes uint8_t* outputBuffer, // width * height bytes uint32_t width, uint32_t height, bool swizzle ) { uint32_t pixelCount = width * height; for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { uint32_t blockLocation = (y & (~0xF)) * width + (x & (~0xF)) * 2; uint32_t swapSelector = (((y + 2) >> 2) & 0x1) * 4; uint32_t posY = (((y & (~3)) >> 1) + (y & 1)) & 0x7; uint32_t columnLocation = posY * width * 2 + ((x + swapSelector) & 0x7) * 4; uint32_t byteNumber = ((y >> 1) & 1) + ((x >> 2) & 2); uint32_t swizzleOffset = blockLocation + columnLocation + byteNumber; uint32_t linearOffset = y * width + x; if (linearOffset < pixelCount && swizzleOffset < pixelCount) { if (swizzle) { outputBuffer[swizzleOffset] = inputBuffer[linearOffset]; } else { outputBuffer[linearOffset] = inputBuffer[swizzleOffset]; } } } } } Edited May 12 by piken 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 6 Author Share Posted May 6 Thankyou very much! I managed to do it a little while ago with that small change in the code that you suggested, changing the reading and writing operations, I think that before that I was entering some wrong parameter for the function, but now it's working fine, thanks for all the help. 1 Link to comment Share on other sites More sharing options...
piken Posted May 6 Share Posted May 6 (edited) 2 hours ago, Alpha 1001 said: Thankyou very much! Welcome, and thank you for sharing the samples, as they were a new memory layout for me (evidently there are two different ways of storing swizzled 4bpp inside PS2 textures, the one like Dageron's Console Texture Explorer where 4bpp is swizzled as if it was 8bpp, and the true hardware 4bpp 🤔). I think I'll publish the C code above as a small CLI utility, after adding the other 4bpp layout (plus the 16bpp and 32bpp layouts too, after finding some sample data ⏳). Cheers. Edited May 6 by piken 1 Link to comment Share on other sites More sharing options...
gledson999 Posted May 9 Share Posted May 9 On 5/5/2024 at 10:52 AM, piken said: Let's focus on one specific file that you're having trouble with, because the ones I tried work. e.g. 4bpp_swizzled_256x64_original -> unswizzled -> reswizzled. 9_256x64 4bpp unswizzled -> reswizzled -> unswizzled: --> --> 5_128x128 4bpp unswizzled -> reswizzled -> unswizzled: #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <memory.h> #include <stdbool.h> #include <stdint.h> #include <string.h> void Unswizzle4( uint8_t const* inputBuffer, // (width * height + 1) / 2 bytes uint8_t* outputBuffer, // (width * height + 1) / 2 bytes uint32_t width, uint32_t height, bool swizzle // reswizzle instead of unswizzle ); void Unswizzle8( uint8_t const* inputBuffer, // width * height bytes uint8_t* outputBuffer, // width * height bytes uint32_t width, uint32_t height, bool swizzle // reswizzle instead of unswizzle ); // nullptr was added in C23, but most compilers do not support it yet. #define nullptr NULL int main(int argc, char* argv[]) { if (argc < 8) { printf( "Usage:\n" " action bitdepth width height byteOffset inputFilename outputFilename\n" " action: swizzle, unswizzle\n" " bitdepth: 4, 8\n" " byte offset: 0 to any valid position in file\n" " file names: raw binary data\n" "\n" "e.g.:\n" " swizzle 4 256 64 0 input.dat output.dat\n" " unswizzle 8 128 128 0x40 input.bin output.bin\n" ); return EXIT_FAILURE; } char const* action = argv[1]; char const* bitDepthString = argv[2]; char const* widthString = argv[3]; char const* heightString = argv[4]; char const* byteOffsetString = argv[5]; char const* inputFilename = argv[6]; char const* outputFilename = argv[7]; bool swizzle = false; if (strcmp(action, "swizzle") == 0) { swizzle = true; } else if (strcmp(action, "unswizzle") == 0) { swizzle = false; } else { printf("Expect action \"swizzle\" or \"unswizzle\", not %s.", action); return EXIT_FAILURE; } uint32_t bitDepth = atoi(bitDepthString); if (bitDepth != 4 && bitDepth != 8) { printf("Expect bit depth of 4 or 8, not %s.", bitDepthString); return EXIT_FAILURE; } uint32_t width = atoi(widthString); if (width <= 0) { printf("Invalid width: %s.", widthString); return EXIT_FAILURE; } uint32_t height = atoi(heightString); if (height <= 0) { printf("Invalid height: %s.", heightString); return EXIT_FAILURE; } uint32_t byteOffset = strtoul(byteOffsetString, nullptr, 0); if (byteOffset == 0 && byteOffsetString[0] != '0') { printf("Invalid byte offset: %s.", byteOffsetString); return EXIT_FAILURE; } FILE* inputFile = fopen(inputFilename, "rb"); if (inputFile == nullptr) { printf("Could not open input file: \"%s\"", inputFilename); return EXIT_FAILURE; } FILE* outputFile = fopen(outputFilename, "wb"); if (outputFile == nullptr) { printf("Could not open output file: \"%s\"", outputFilename); return EXIT_FAILURE; } printf( "action: %s\n" "bit depth: %s\n" "width: %s\n" "height: %s\n" "byte offset: %s\n" "input: %s\n" "output: %s\n", action, bitDepthString, widthString, heightString, byteOffsetString, inputFilename, outputFilename ); // Allocate buffers. fseek(inputFile, 0, SEEK_END); long inputFileSize = ftell(inputFile); fseek(inputFile, 0, SEEK_SET); uint32_t minimumBufferSize = bitDepth * width * height / CHAR_BIT; if ((uint32_t)inputFileSize < byteOffset + minimumBufferSize) { inputFileSize = byteOffset + minimumBufferSize; } uint8_t* inputFileData = (uint8_t*)malloc(inputFileSize); uint8_t* outputFileData = (uint8_t*)malloc(inputFileSize); // Read input data, perform conversion, write output data. fread(inputFileData, 1, inputFileSize, inputFile); fclose(inputFile); // Copy data before and after the pixels. memcpy(outputFileData, inputFileData, byteOffset); uint32_t byteOffsetAfterPixels = byteOffset + minimumBufferSize; memcpy(outputFileData + byteOffsetAfterPixels, inputFileData + byteOffsetAfterPixels, inputFileSize - byteOffsetAfterPixels); switch (bitDepth) { case 4: Unswizzle4(inputFileData + byteOffset, outputFileData + byteOffset, width, height, swizzle); break; case 8: Unswizzle8(inputFileData + byteOffset, outputFileData + byteOffset, width, height, swizzle); break; } fwrite(outputFileData, 1, inputFileSize, outputFile); fclose(outputFile); free(inputFileData); free(outputFileData); printf("Converted\n"); return EXIT_SUCCESS; } void Unswizzle4( uint8_t const* inputBuffer, // (width * height + 1) / 2 bytes uint8_t* outputBuffer, // (width * height + 1) / 2 bytes uint32_t width, uint32_t height, bool swizzle ) { uint8_t* inputPixels8bpp = (uint8_t*)malloc(width * height); uint8_t* outputPixels8bpp = (uint8_t*)malloc(width * height); const uint32_t bufferByteSize = (width * height + 1) / 2; // Unpack 4-bpp pixels to 8-bpp. for (uint32_t i = 0; i < bufferByteSize; ++i) { uint8_t byteValue = inputBuffer[i]; uint8_t nybbleLow = byteValue & 0xF; uint8_t nybbleHigh = byteValue >> 4; inputPixels8bpp[i * 2] = nybbleLow; inputPixels8bpp[i * 2 + 1] = nybbleHigh; } // Swizzle/Unswizzle them as if 8-bpp. Unswizzle8(inputPixels8bpp, outputPixels8bpp, width, height, swizzle); // Repack 8-bpp pixels to 4-bpp. for (uint32_t i = 0; i < bufferByteSize; ++i) { uint8_t nybbleLow = outputPixels8bpp[i * 2 + 0]; uint8_t nybbleHigh = outputPixels8bpp[i * 2 + 1]; uint8_t byteValue = (nybbleHigh << 4) | nybbleLow; outputBuffer[i] = byteValue; } free(inputPixels8bpp); free(outputPixels8bpp); } void Unswizzle8( uint8_t const* inputBuffer, // width * height bytes uint8_t* outputBuffer, // width * height bytes uint32_t width, uint32_t height, bool swizzle ) { uint32_t pixelCount = width * height; for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { uint32_t blockLocation = (y & (~0xF)) * width + (x & (~0xF)) * 2; uint32_t swapSelector = (((y + 2) >> 2) & 0x1) * 4; uint32_t posY = (((y & (~3)) >> 1) + (y & 1)) & 0x7; uint32_t columnLocation = posY * width * 2 + ((x + swapSelector) & 0x7) * 4; uint32_t byteNumber = ((y >> 1) & 1) + ((x >> 2) & 2); uint32_t swizzleOffset = blockLocation + columnLocation + byteNumber; uint32_t linearOffset = y * width + x; if (linearOffset < pixelCount && swizzleOffset < pixelCount) { if (swizzle) { outputBuffer[swizzleOffset] = inputBuffer[linearOffset]; } else { outputBuffer[linearOffset] = inputBuffer[swizzleOffset]; } } } } } Can you compile this code for me? thanks 1 Link to comment Share on other sites More sharing options...
Alpha 1001 Posted May 10 Author Share Posted May 10 Hmm, asre you also creating a version with mods for the NFS MW? Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now