Skip to content

Instantly share code, notes, and snippets.

@x1nixmzeng
Last active November 29, 2019 20:06
Show Gist options
  • Save x1nixmzeng/c55c248c384bf1c9df2a to your computer and use it in GitHub Desktop.
Save x1nixmzeng/c55c248c384bf1c9df2a to your computer and use it in GitHub Desktop.
Conker
#include <cstdio>
typedef signed char s8;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
u8 default_rbm[384] = {
0x43, 0x41, 0x46, 0x46, 0x32, 0x38, 0x2E, 0x30, 0x31, 0x2E, 0x30, 0x35, 0x2E, 0x30, 0x30, 0x33,
0x31, 0x00, 0x00, 0x00, 0x50, 0x5D, 0xC9, 0x02, 0x7A, 0x01, 0x00, 0x00, 0xF2, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x02, 0x00, 0x00, 0x74, 0x05, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x70, 0x02, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00,
0x2E, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00,
0x04, 0xBF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB4, 0x59, 0x00, 0x00, 0x2E, 0x67, 0x70, 0x75, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0xB2, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x9A, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* likely an extern to something automatically generated */
const char *gCurrentRevision = "28.01.05.0031";
bool checkString(const u8* a, const u8* b, u32 length)
{
for( u32 i(0); i < length; ++i, ++a, ++b ) {
/* check characters against each other */
if( *a != *b ) {
return false;
}
}
return true;
}
bool checkEndian(u8 *fileBuffer)
{
const u8 endian_flag = *(u8*)(fileBuffer + 60);
/* marker for big endian */
return ( endian_flag & 1 );
}
u32 doCaffChecksumByte(const u32 initialVal, const u8 currentByte)
{
/* the byte buffer is actually signed */
int val = (initialVal << 4) + static_cast<s8>(currentByte);
int val_mask = val & 0xF0000000;
if( val_mask ) {
/* copy the mask over 0xF00000F0 */
val_mask |= ( (u32)val_mask >> 24 );
val ^= val_mask;
}
return val;
}
u32 checkCaff(u8 *fileBuffer, u32 fileHash)
{
u32 result = 0;
/* check file magic */
if( !checkString( fileBuffer, reinterpret_cast<const u8*>("CAFF"), 4 ) ) {
result = 1;
}
/* check asset revision */
else if( !checkString( fileBuffer + 4, reinterpret_cast<const u8*>(gCurrentRevision), 13 ) ) {
result = 2;
}
/* validate file hash */
else
{
fileHash = *(u32*)(fileBuffer + 20);
/* fix file hash if file endian is marked */
if( checkEndian(fileBuffer) ) {
fileHash = ( ( fileHash >> 24 ) & 0x000000FF )
| ( ( fileHash << 24 ) & 0xFF000000 )
| ( (fileHash << 8 ) & 0x00FF0000 )
| ( ( fileHash >> 8 ) & 0x0000FF00 );
}
/* clear hash from buffer */
*(u32*)(fileBuffer + 20) = 0;
u32 chksmLocal = 0;
u8 *chkcmBuffer = fileBuffer;
/* runs over (64 * 6) bytes */
const u32 run_count = 64 * 6;
for( u32 run = 0; run < run_count; ++run, ++chkcmBuffer ) {
/* the xbe file runs though 6 bytes at a time */
chksmLocal = doCaffChecksumByte(chksmLocal, *chkcmBuffer);
}
printf("Result %08X\n", chksmLocal);
printf("Expected %08X\n", fileHash);
if( chksmLocal != fileHash ) {
result = 3;
}
}
return result;
}
void getFileInfo(u8 *data)
{
u8 count = *(u8*)(data + 61);
#pragma pack(push,1)
struct CAFF_SECTION_HEADER
{
char name[6];
char pad[30];
u32 size;
};
#pragma pack(pop)
u8 *ptr = (data + 64);
CAFF_SECTION_HEADER d;
u32 total_size = 0;
for( u8 i = 0; i < count; ++i, ptr += 40 )
{
d = *(CAFF_SECTION_HEADER*)(ptr);
printf("Found block named %s (size %u)\n", d.name, d.size);
total_size += d.size;
}
u32 data_starts = *(u32*)(data + 56);
printf("Data offset = %u\n", data_starts);
printf("Data length = %u\n", total_size);
}
int main(int, char**)
{
getFileInfo(default_rbm);
int result = checkCaff(default_rbm, 0);
switch( result )
{
case 1:
printf("Unsupported file\n");
break;
case 2:
printf("File is out of date (expected %s)\n", gCurrentRevision);
break;
case 3:
printf("Checksum failed\n");
break;
default:
printf("Valid file\n");
break;
}
return 0;
}
A quick look at 'Conker: Live & Reloaded' (xbox)
The sourcecode runs the same initial checks as the game to check the file validity (magic, asset version, and checksum)
Some of the data files are futher compressed using an unknown algorithm.
After reading the segment offsets and sizes, the compressed data is stored in chunks:
u32 chunk_size;
u32 length;
u8 data[length]
@x1nixmzeng
Copy link
Author

Revision 5:

  • Checksum calculation now works (buffer is now treated as an array of signed bytes)
  • Cleaned up the checksum algorithm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment