January 6Jan 6 Hello, The Devilman PSX game doesn’t have much text, and I’d like to edit it. Most of the dialogue is audio and is stored in the PSOUND folder. These are .VAG files without header, so they’re easy to extract, edit, and reinsert. I need some help with the game’s images. QDATA.BIN contains many .TIM images, but I don’t see any that contain Japanese text. The game script seems to be stored in .TIM images—I’ve been able to confirm this by analyzing RAM dumps—but when I look for those images in the ISO, I can’t find them. I’ve checked the files one by one with Tile Molester, but I still can’t see the text images that do appear in RAM. They’re most likely compressed inside the HDATA or TDATA folders. I can see some data that looks like .TIM structures, but I haven’t been able to view any actual images. Any help with this would be really appreciated. Attached are game files (without sound and video) and RAM dump. https://www.mediafire.com/file/ce0sfz30qtob351/Devilman_psx_files.rar/file https://www.mediafire.com/file/k54kjfbtf2isz15/RAM_dump.rar/file Edited January 6Jan 6 by JoseKenshin
January 6Jan 6 Supporter 5 hours ago, JoseKenshin said: Hello, The Devilman PSX game doesn’t have much text, and I’d like to edit it. Most of the dialogue is audio and is stored in the PSOUND folder. These are .VAG files without header, so they’re easy to extract, edit, and reinsert. I need some help with the game’s images. QDATA.BIN contains many .TIM images, but I don’t see any that contain Japanese text. The game script seems to be stored in .TIM images—I’ve been able to confirm this by analyzing RAM dumps—but when I look for those images in the ISO, I can’t find them. I’ve checked the files one by one with Tile Molester, but I still can’t see the text images that do appear in RAM. They’re most likely compressed inside the HDATA or TDATA folders. I can see some data that looks like .TIM structures, but I haven’t been able to view any actual images. Any help with this would be really appreciated. Attached are game files (without sound and video) and RAM dump. https://www.mediafire.com/file/ce0sfz30qtob351/Devilman_psx_files.rar/file https://www.mediafire.com/file/k54kjfbtf2isz15/RAM_dump.rar/file It’s compressed using a custom LZSS algorithm with token counting. Your files are both compressed and packed, so you will need to unpack and decompress them simultaneously. Inside the TEX files, there are multiple TIM images. Edited January 6Jan 6 by Rabatini
January 6Jan 6 Author Amazing! I’ve been trying to figure out the compression for several days. Thank you! Could you explain a bit more about the custom LZSS algorithm? Also, if you’d like, you could share the tools you used. 😁 Edited January 6Jan 6 by JoseKenshin
January 6Jan 6 Supporter 3 hours ago, JoseKenshin said: Amazing! I’ve been trying to figure out the compression for several days. Thank you! Could you explain a bit more about the custom LZSS algorithm? Also, if you’d like, you could share the tools you used. 😁 Yeah, I can do that. For the translation part, I’ll need to build a compression tool as well. Which language are you planning to translate?
January 6Jan 6 Author ok, thanks! I want to translate it into Spanish. You already helped me with Granstream Saga. I owe you a bunch of coffees. 😄
January 7Jan 7 Supporter 4 hours ago, JoseKenshin said: ok, thanks! I want to translate it into Spanish. You already helped me with Granstream Saga. I owe you a bunch of coffees. 😄 Quote Thank you! Could you explain a bit more about the custom LZSS algorithm? It’s a token-based LZSS/LZ77 stream. Header (2 bytes, little-endian): this is NOT the output size. It is the number of commands/tokens to execute. After the header, the data is processed in groups of 8 tokens: Before each group, read 1 flag byte. You consume its bits from MSB to LSB (bit 7 → bit 0). Each bit decides what the next token is. If the bit = 0 (literal token): Read 1 byte from input and copy it directly to the output. If the bit = 1 (reference token): Read 2 bytes (byte1, byte2). Decode: length = (byte1 >> 2) + 1 → range 1..64 offset = ((byte1 & 0x03) << 8') | byte2 → range 0..1023 distance = offset + 1 → range 1..1024 Copy length bytes from output position: copy_pos = current_output_pos - distance Copy forward (overlap allowed), like standard LZSS. Stop condition: stops when you’ve executed exactly token_count tokens (each literal counts as 1 token, each reference also counts as 1 token), regardless of how many output bytes the references produce. ---------------------------------------------------------------------------------------------------------------------------------------------------- Good news. Compressor is working, with good rate. Edited January 7Jan 7 by Rabatini
January 7Jan 7 Author I tried to find the compression with Ghidra and Chatgpt, analyzing SLPS_022.75. I think the FUN_80013a34 function is the one that manages the compression. Thank you for your explanations, they are very helpful. Unfortunately, I don't know how to make a tool, so I need your help ;) #Function extracted from Ghidra void FUN_80013a34(ushort *param_1,undefined *param_2) { undefined *puVar1; byte bVar2; ushort uVar3; uint uVar4; ushort *puVar5; int iVar6; uint uVar7; int in_t2; uint uVar8; uVar7 = 0; uVar3 = *param_1; param_1 = param_1 + 1; do { while( true ) { uVar4 = (uint)*(byte *)param_1; puVar5 = param_1; if ((uVar7 & 7) == 0) { puVar5 = (ushort *)((int)param_1 + 1); in_t2 = uVar4 << 0x18; uVar4 = (uint)*(byte *)puVar5; } param_1 = (ushort *)((int)puVar5 + 1); if (in_t2 < 0) break; *param_2 = (char)uVar4; param_2 = param_2 + 1; uVar7 = uVar7 + 1; in_t2 = in_t2 << 1; if (uVar7 == uVar3) { return; } } uVar8 = uVar4 >> 2; bVar2 = *(byte *)param_1; param_1 = puVar5 + 1; iVar6 = (int)param_2 - ((uVar4 & 3) << 8 | (uint)bVar2); do { puVar1 = (undefined *)(iVar6 + -1); iVar6 = iVar6 + 1; *param_2 = *puVar1; uVar8 = uVar8 - 1; param_2 = param_2 + 1; } while (-1 < (int)uVar8); uVar7 = uVar7 + 1; in_t2 = in_t2 << 1; } while (uVar7 != uVar3); return; }
January 7Jan 7 Supporter Solution 43 minutes ago, JoseKenshin said: I tried to find the compression with Ghidra and Chatgpt, analyzing SLPS_022.75. I think the FUN_80013a34 function is the one that manages the compression. Thank you for your explanations, they are very helpful. Unfortunately, I don't know how to make a tool, so I need your help 😉 #Function extracted from Ghidra void FUN_80013a34(ushort *param_1,undefined *param_2) { undefined *puVar1; byte bVar2; ushort uVar3; uint uVar4; ushort *puVar5; int iVar6; uint uVar7; int in_t2; uint uVar8; uVar7 = 0; uVar3 = *param_1; param_1 = param_1 + 1; do { while( true ) { uVar4 = (uint)*(byte *)param_1; puVar5 = param_1; if ((uVar7 & 7) == 0) { puVar5 = (ushort *)((int)param_1 + 1); in_t2 = uVar4 << 0x18; uVar4 = (uint)*(byte *)puVar5; } param_1 = (ushort *)((int)puVar5 + 1); if (in_t2 < 0) break; *param_2 = (char)uVar4; param_2 = param_2 + 1; uVar7 = uVar7 + 1; in_t2 = in_t2 << 1; if (uVar7 == uVar3) { return; } } uVar8 = uVar4 >> 2; bVar2 = *(byte *)param_1; param_1 = puVar5 + 1; iVar6 = (int)param_2 - ((uVar4 & 3) << 8 | (uint)bVar2); do { puVar1 = (undefined *)(iVar6 + -1); iVar6 = iVar6 + 1; *param_2 = *puVar1; uVar8 = uVar8 - 1; param_2 = param_2 + 1; } while (-1 < (int)uVar8); uVar7 = uVar7 + 1; in_t2 = in_t2 << 1; } while (uVar7 != uVar3); return; } I've already did Tool, Just cant sente It now, because, i'am not at home.
Create an account or sign in to comment