wsldecoded Posted August 28, 2024 Share Posted August 28, 2024 (edited) Hi everyone, i'm trying to translate a Bleach japanese game from PS2, worked on the text routine and now i want to change the font, i don't quite like it and doesn't fit the game IMO. BUT, i can't edit it, tried Console Texture Explorer (Where i can edit 8bpp textures) but 4bpp i can't... anyone knows how can i edit it? font.zip Edited August 28, 2024 by wsldecoded Link to comment Share on other sites More sharing options...
piken Posted September 4, 2024 Share Posted September 4, 2024 (edited) Yeah, Console Texture Explorer's 4-bpp swizzled format is different than this 4-bpp swizzled format. I think you could export/import it by calling some Python functions in ReverseBox or Noesis's imageUntwiddlePS2. Note, I used the C++ code below to convert it, just because that's what I'm familiar with. Ikskoks also had a list of potential leads here: It appears to have 4 256x256 textures at different offsets (unsure where the pointers are). Here is the first one at 0x200. ... uint32_t rrw = width / 2; uint32_t rrh = height / 4; std::vector<uint8_t> tempInput(inputData.size_in_bytes()); std::vector<uint8> outputData(inputData.size_in_bytes()); writeTexPSMCT32(0, rrw / 64, 0, 0, rrw, rrh, inputData, tempInput); readTexPSMT4(0, width / 64, 0, 0, width, height, tempInput, outputData); ... int ps2_32bpp_mediumBlockIndices[32] = { 0, 1, 4, 5, 16, 17, 20, 21, 2, 3, 6, 7, 18, 19, 22, 23, 8, 9, 12, 13, 24, 25, 28, 29, 10, 11, 14, 15, 26, 27, 30, 31 }; int ps2_32bpp_smallBlockWordIndices[16] = { 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15 }; void writeTexPSMCT32( uint32_t basePointer, uint32_t pageBufferWidth, // in pages (2 = 16KB stride per page) uint32_t xStart, uint32_t yStart, uint32_t width, uint32_t height, std::span<uint8_t const> inputData, std::span<uint8> outputData ) { uint32_t const* src = reinterpret_cast<uint32_t const*>(inputData.data()); uint32_t startBlockPos = basePointer * 64; for (uint32_t y = yStart; y < yStart + height; ++y) { uint32_t pageY = y / 32; uint32_t py = y - (pageY * 32); uint32_t blockY = py / 8; uint32_t by = py - blockY * 8; uint32_t column = by / 2; uint32_t cy = by - column * 2; for (uint32_t x = xStart; x < xStart + width; ++x) { uint32_t pageX = x / 64; uint32_t page = pageX + pageY * pageBufferWidth; uint32_t px = x - (pageX * 64); uint32_t blockX = px / 8; uint32_t block = ps2_32bpp_mediumBlockIndices[blockX + blockY * 8]; uint32_t bx = px - blockX * 8; uint32_t cx = bx; uint32_t cw = ps2_32bpp_smallBlockWordIndices[cx + cy * 8]; uint32* dest = (uint32*)outputData.data(); uint32_t const inputWordOffset = startBlockPos + page * 2048 + block * 64 + column * 16 + cw; dest[inputWordOffset] = *src; src++; } } } // 128x128 pixel blocks contain 32 32x16 blocks. constexpr uint8_t ps2_4bpp_mediumBlockIndices[32] = { 0, 2, 8, 10, 1, 3, 9, 11, 4, 6, 12, 14, 5, 7, 13, 15, 16, 18, 24, 26, 17, 19, 25, 27, 20, 22, 28, 30, 21, 23, 29, 31 }; // Maps the pixel x,y within a 32x4 block to uint32_t element index in VRAM. constexpr uint8_t ps2_4bpp_smallBlockWordIndices[2][128] = { { 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7 }, { 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15 } }; // Combine this 32x4 array with the array above (no point in having both arrays). constexpr uint8_t ps2_4bpp_smallBlockNybbleIndices[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7 }; // Adapted from: https://github.com/neko68k/FusionTool/blob/3c90521fec34b9025baaa1bf9306736f289bdd66/PS2Textures.cpp#L784 void readTexPSMT4( uint32_t basePointer, uint32_t pageBufferWidth, // in pages (2 = 16KB stride per page) uint32_t xStart, uint32_t yStart, uint32_t width, uint32_t height, std::span<uint8_t const> inputData, std::span<uint8> outputData ) { uint32_t constexpr largeBlockByteSize = 8192; // called "pages" uint32_t constexpr mediumBlockByteSize = 256; // called "blocks" uint32_t constexpr smallBlockByteSize = 64; // called "columns", even though they are rows. uint32_t constexpr largeBlockSizeX = 128; uint32_t constexpr largeBlockSizeY = 128; uint32_t constexpr mediumBlockSizeX = 32; uint32_t constexpr mediumBlockSizeY = 16; uint32_t constexpr smallBlockSizeX = 32; uint32_t constexpr smallBlockSizeY = 4; uint32_t constexpr mediumBlocksPerLargeBlockX = largeBlockSizeX / mediumBlockSizeX; // 4 = 128 / 32 uint32_t constexpr smallBlocksPerMediumBlockY = mediumBlockSizeY / smallBlockSizeY; // 4 = 16 / 4 uint32_t const pixelCount = width * height; uint32_t const xEnd = xStart + width; uint32_t const yEnd = yStart + height; assert(inputData.size() * 2 >= pixelCount); assert(outputData.size() * 2 >= pixelCount); pageBufferWidth >>= 1; uint8* outputPtr = outputData.data(); uint32_t startBlockByteOffset = basePointer * mediumBlockByteSize; uint32_t outputNybbleShift = 0; uint32_t outputNybbleMask = 0x0F; for (uint32_t y = yStart; y < yEnd; ++y) { uint32_t pageY = y & (largeBlockSizeY - 1); uint32_t blockY = y & (mediumBlockSizeY - 1); uint32_t columnY = y & (smallBlocksPerMediumBlockY - 1); uint32_t pageIndexY = y / largeBlockSizeY; uint32_t blockIndexY = pageY / mediumBlockSizeY; uint32_t columnIndex = blockY / smallBlocksPerMediumBlockY; for (uint32_t x = xStart; x < xEnd; ++x) { uint32_t pageIndexX = x / largeBlockSizeX; uint32_t pageIndex = pageIndexX + pageIndexY * pageBufferWidth; uint32_t pageX = x & (largeBlockSizeX - 1); uint32_t blockIndexX = pageX / mediumBlockSizeX; uint32_t blockIndex = ps2_4bpp_mediumBlockIndices[blockIndexX + blockIndexY * mediumBlocksPerLargeBlockX]; uint32_t blockX = x & (mediumBlockSizeX - 1); uint32_t columnX = blockX; uint32_t columnWord = ps2_4bpp_smallBlockWordIndices[columnIndex & 1][columnX + columnY * smallBlockSizeX]; uint32_t columnNybble = ps2_4bpp_smallBlockNybbleIndices[columnX + columnY * smallBlockSizeX]; uint32_t const inputByteOffset = startBlockByteOffset + pageIndex * largeBlockByteSize + blockIndex * mediumBlockByteSize + columnIndex * smallBlockByteSize + columnWord * sizeof(uint32) + (columnNybble >> 1); // nybble index / 2 to get byte offset assert(inputByteOffset < inputData.size_in_bytes()); uint8_t inputValue = inputData[inputByteOffset]; uint32_t inputNybbleShift = (columnNybble & 1) << 2; // Select high nybble (<<4) or low nybble (<<0). uint32_t newValue = (inputValue >> inputNybbleShift) << outputNybbleShift; uint8_t previousOutputValue = *outputPtr; *outputPtr = (previousOutputValue & ~outputNybbleMask) | (newValue & outputNybbleMask); // Flush the new value. if (outputNybbleShift) { outputPtr++; } outputNybbleShift ^= 4; outputNybbleMask ^= 0xFF; } } } Edited September 4, 2024 by piken Link to comment Share on other sites More sharing options...
wsldecoded Posted September 10, 2024 Author Share Posted September 10, 2024 On 9/4/2024 at 5:41 AM, piken said: Yeah, Console Texture Explorer's 4-bpp swizzled format is different than this 4-bpp swizzled format. I think you could export/import it by calling some Python functions in ReverseBox or Noesis's imageUntwiddlePS2. Note, I used the C++ code below to convert it, just because that's what I'm familiar with. Ikskoks also had a list of potential leads here: It appears to have 4 256x256 textures at different offsets (unsure where the pointers are). Here is the first one at 0x200. ... uint32_t rrw = width / 2; uint32_t rrh = height / 4; std::vector<uint8_t> tempInput(inputData.size_in_bytes()); std::vector<uint8> outputData(inputData.size_in_bytes()); writeTexPSMCT32(0, rrw / 64, 0, 0, rrw, rrh, inputData, tempInput); readTexPSMT4(0, width / 64, 0, 0, width, height, tempInput, outputData); ... int ps2_32bpp_mediumBlockIndices[32] = { 0, 1, 4, 5, 16, 17, 20, 21, 2, 3, 6, 7, 18, 19, 22, 23, 8, 9, 12, 13, 24, 25, 28, 29, 10, 11, 14, 15, 26, 27, 30, 31 }; int ps2_32bpp_smallBlockWordIndices[16] = { 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15 }; void writeTexPSMCT32( uint32_t basePointer, uint32_t pageBufferWidth, // in pages (2 = 16KB stride per page) uint32_t xStart, uint32_t yStart, uint32_t width, uint32_t height, std::span<uint8_t const> inputData, std::span<uint8> outputData ) { uint32_t const* src = reinterpret_cast<uint32_t const*>(inputData.data()); uint32_t startBlockPos = basePointer * 64; for (uint32_t y = yStart; y < yStart + height; ++y) { uint32_t pageY = y / 32; uint32_t py = y - (pageY * 32); uint32_t blockY = py / 8; uint32_t by = py - blockY * 8; uint32_t column = by / 2; uint32_t cy = by - column * 2; for (uint32_t x = xStart; x < xStart + width; ++x) { uint32_t pageX = x / 64; uint32_t page = pageX + pageY * pageBufferWidth; uint32_t px = x - (pageX * 64); uint32_t blockX = px / 8; uint32_t block = ps2_32bpp_mediumBlockIndices[blockX + blockY * 8]; uint32_t bx = px - blockX * 8; uint32_t cx = bx; uint32_t cw = ps2_32bpp_smallBlockWordIndices[cx + cy * 8]; uint32* dest = (uint32*)outputData.data(); uint32_t const inputWordOffset = startBlockPos + page * 2048 + block * 64 + column * 16 + cw; dest[inputWordOffset] = *src; src++; } } } // 128x128 pixel blocks contain 32 32x16 blocks. constexpr uint8_t ps2_4bpp_mediumBlockIndices[32] = { 0, 2, 8, 10, 1, 3, 9, 11, 4, 6, 12, 14, 5, 7, 13, 15, 16, 18, 24, 26, 17, 19, 25, 27, 20, 22, 28, 30, 21, 23, 29, 31 }; // Maps the pixel x,y within a 32x4 block to uint32_t element index in VRAM. constexpr uint8_t ps2_4bpp_smallBlockWordIndices[2][128] = { { 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7 }, { 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15 } }; // Combine this 32x4 array with the array above (no point in having both arrays). constexpr uint8_t ps2_4bpp_smallBlockNybbleIndices[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7 }; // Adapted from: https://github.com/neko68k/FusionTool/blob/3c90521fec34b9025baaa1bf9306736f289bdd66/PS2Textures.cpp#L784 void readTexPSMT4( uint32_t basePointer, uint32_t pageBufferWidth, // in pages (2 = 16KB stride per page) uint32_t xStart, uint32_t yStart, uint32_t width, uint32_t height, std::span<uint8_t const> inputData, std::span<uint8> outputData ) { uint32_t constexpr largeBlockByteSize = 8192; // called "pages" uint32_t constexpr mediumBlockByteSize = 256; // called "blocks" uint32_t constexpr smallBlockByteSize = 64; // called "columns", even though they are rows. uint32_t constexpr largeBlockSizeX = 128; uint32_t constexpr largeBlockSizeY = 128; uint32_t constexpr mediumBlockSizeX = 32; uint32_t constexpr mediumBlockSizeY = 16; uint32_t constexpr smallBlockSizeX = 32; uint32_t constexpr smallBlockSizeY = 4; uint32_t constexpr mediumBlocksPerLargeBlockX = largeBlockSizeX / mediumBlockSizeX; // 4 = 128 / 32 uint32_t constexpr smallBlocksPerMediumBlockY = mediumBlockSizeY / smallBlockSizeY; // 4 = 16 / 4 uint32_t const pixelCount = width * height; uint32_t const xEnd = xStart + width; uint32_t const yEnd = yStart + height; assert(inputData.size() * 2 >= pixelCount); assert(outputData.size() * 2 >= pixelCount); pageBufferWidth >>= 1; uint8* outputPtr = outputData.data(); uint32_t startBlockByteOffset = basePointer * mediumBlockByteSize; uint32_t outputNybbleShift = 0; uint32_t outputNybbleMask = 0x0F; for (uint32_t y = yStart; y < yEnd; ++y) { uint32_t pageY = y & (largeBlockSizeY - 1); uint32_t blockY = y & (mediumBlockSizeY - 1); uint32_t columnY = y & (smallBlocksPerMediumBlockY - 1); uint32_t pageIndexY = y / largeBlockSizeY; uint32_t blockIndexY = pageY / mediumBlockSizeY; uint32_t columnIndex = blockY / smallBlocksPerMediumBlockY; for (uint32_t x = xStart; x < xEnd; ++x) { uint32_t pageIndexX = x / largeBlockSizeX; uint32_t pageIndex = pageIndexX + pageIndexY * pageBufferWidth; uint32_t pageX = x & (largeBlockSizeX - 1); uint32_t blockIndexX = pageX / mediumBlockSizeX; uint32_t blockIndex = ps2_4bpp_mediumBlockIndices[blockIndexX + blockIndexY * mediumBlocksPerLargeBlockX]; uint32_t blockX = x & (mediumBlockSizeX - 1); uint32_t columnX = blockX; uint32_t columnWord = ps2_4bpp_smallBlockWordIndices[columnIndex & 1][columnX + columnY * smallBlockSizeX]; uint32_t columnNybble = ps2_4bpp_smallBlockNybbleIndices[columnX + columnY * smallBlockSizeX]; uint32_t const inputByteOffset = startBlockByteOffset + pageIndex * largeBlockByteSize + blockIndex * mediumBlockByteSize + columnIndex * smallBlockByteSize + columnWord * sizeof(uint32) + (columnNybble >> 1); // nybble index / 2 to get byte offset assert(inputByteOffset < inputData.size_in_bytes()); uint8_t inputValue = inputData[inputByteOffset]; uint32_t inputNybbleShift = (columnNybble & 1) << 2; // Select high nybble (<<4) or low nybble (<<0). uint32_t newValue = (inputValue >> inputNybbleShift) << outputNybbleShift; uint8_t previousOutputValue = *outputPtr; *outputPtr = (previousOutputValue & ~outputNybbleMask) | (newValue & outputNybbleMask); // Flush the new value. if (outputNybbleShift) { outputPtr++; } outputNybbleShift ^= 4; outputNybbleMask ^= 0xFF; } } } hey thank you for the reply, i will test this tomorrow, sadly my PC broke, when i get it back i'll update :) 1 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