wsldecoded Posted August 28 Share Posted August 28 (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 by wsldecoded Link to comment Share on other sites More sharing options...
piken Posted September 4 Share Posted September 4 (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 by piken Link to comment Share on other sites More sharing options...
wsldecoded Posted September 10 Author Share Posted September 10 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