YPF
Used in the following games:
- Eroge! ~Sex and Games Make Sexy Games~
Structure
Header | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Size | Content | Description | ||||||||||||
4Bytes | Magic/ID | "YPF/0" | ||||||||||||
4Bytes | Unknown1 | Static? | ||||||||||||
4Bytes | File Count | Number of Files in archive | ||||||||||||
4Bytes | Index Size | Size of Index | ||||||||||||
16Bytes | Padding | |||||||||||||
Index Entry | ||||||||||||||
Size | Content | Description | ||||||||||||
4Bytes | Unknown1 | |||||||||||||
1Bytes | Filename Length | XOR 0xFF, then table lookup. | ||||||||||||
?Bytes | Filename | XOR 0xFF then XOR 0x40 | ||||||||||||
2Bytes | Unknown2 | Static? | ||||||||||||
4Bytes | FileSize1 | |||||||||||||
4Bytes | FileSize2 | Suggests compression is supported | ||||||||||||
4Bytes | File Offset | |||||||||||||
4Bytes | Unknown3 |
Comments
To decode the filenames, each byte in the filename is XOR by 0xFF and then 0x40. However, the filenames are not null terminated so 1 byte is stored before the filename that represents the the filename's length. This value is protected like the filename but is slightly different (as far as I can tell). XOR by 0xFF and then do a table lookup. The table is generated at runtime and swaps around 38 values in the table.
char lookup(char incoming)
{
switch (incoming)
{
case 0x09:
return 0x0B;
case 0x0B:
return 0x09;
case 0x0C:
return 0x10;
case 0x10:
return 0x0C;
case 0x11:
return 0x19;
case 0x13:
return 0x0D;
case 0x15:
return 0x1B;
case 0x19:
return 0x11;
case 0x1b:
return 0x15;
case 0x1C:
return 0x1E;
case 0x1E:
return 0x1C;
case 0x20:
return 0x23;
case 0x23:
return 0x20;
case 0x26:
return 0x29;
case 0x29:
return 0x26;
case 0x2C:
return 0x2F;
case 0x2F:
return 0x2C;
default:
return incoming;
}
}
Example: 0xEC ^ 0xFF = 0x13 Table lookup 0x13 -> 0x0D Which means the filename length is 13.
Nothing else appears protected (file sizes and offset) however there is still a few unknown values that will need to be researched if a packing tool is to be made.
Not the most optimized method and I could still be overlooking something or not realizing something important... oh well. It works, right? ^^; Table can even work when repacking too! :D