Skip to content

Instantly share code, notes, and snippets.

@x1nixmzeng
Created October 8, 2019 21:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save x1nixmzeng/9f352f387e7c9159507f9d976ba66a41 to your computer and use it in GitHub Desktop.
Save x1nixmzeng/9f352f387e7c9159507f9d976ba66a41 to your computer and use it in GitHub Desktop.
Banjo Kazooie CAFF Research
//------------------------------------------------
//--- 010 Editor v9.0.1 Binary Template
//
// File: caffBanjo.bt
// Authors: WRS/x1nixmzeng
// Version: 0.01
// Purpose: CAFF files used by Banjo
// Category: Games
// File Mask: n/a
// ID Bytes: CAFF
// History:
//------------------------------------------------
char magic[4];
char version[16];
struct Header
{
BigEndian();
uint chunk1_offset;
uint checksum;
uint count_1;
uint count_2;
uint unknown_2[2];
uint count_3;
uint count_ex;
uint unknown_4[5];
ubyte byteOrder;
ubyte flag_2;
ubyte flag_3;
ubyte flag_4;
uint resType; // incorrect. loc/textures vary
uint chunk1_size;
uint unknown_12;
uint unknown_13[2];
uint chunk1_zsize;
uint chunk2_size;
uint unknown_17[3];
uint chunk2_zsize;
} header;
FSeek(header.chunk1_offset);
struct perFile
{
byte by;
uint offset; // not right. packed type?
uint b;
uint size1; // could be zsize
uint d, e, f, g;
uint size2; //
};
struct perFileHeader
{
string id; // .data or .texturegpu
Printf("Section header: %s\n", id);
};
union
{
byte chunk1[header.chunk1_size];
struct
{
// largely unknow
perFile pf [header.count_2] <optimize=false>;
perFileHeader pfh [header.count_2] <optimize=false>;
uint fnSize,bb; // not always present
char filename[fnSize];
} s;
} u;
BigEndian();
byte chunk2[header.chunk2_size];
// based on halo3/rare view
enum <byte> X360TextureFormat
{
L8 = 2,
A8L8 = 0x4a,
DXT1 = 0x52,
DXT3 = 0x53,
DXT5 = 0x54,
// https://en.wikipedia.org/wiki/3Dc
DXN = 0x71,
CTX1 = 0x7c,
A8R8G8B8 = 0x86, // uncompressed
};
struct texture
{
byte tex[8];
byte version2[16];
byte pad[3];
X360TextureFormat textureFormat;
uint unknown_0;
uint unknown_1;
ushort width;
ushort height;
uint offset;
int unknown_3; // -1
uint unknown_4; // 1
uint unknown_5; // 60 (56+4? extra data)
Assert(unknown_5==60);
byte padding[56];
} te;
Printf("Image: %ux%u\n", te.width, te.height);
Printf("Format: %s\n", EnumToString(te.textureFormat));

The CAFF files share the same rough structure as other Rare titles.

This includes the magic string "CAFF", with a version string of "07.08.06.0036"

The checksum field does a sanity-check on the first 120 bytes of the file. C++ source to read this:

void checksumByte(u32& checksum, const u8 currentByte)
{
    u32 val = (checksum << 4) + static_cast<char>(currentByte);

    u32 val_mask = val & 0xF0000000;
    if (val_mask) {
        /* copy the mask over 0xF00000F0 */
        val_mask |= ((u32)val_mask >> 24);
        val ^= val_mask;
    }

    checksum = val;
}

u32 calculateChecksum(base::stream& stream)
{
    constexpr size_t sc_length = 120;

    buffer data(sc_length);

    const auto originalPosition{ stream.getPosition() };

    stream.seek(0);
    stream.readAll(data);

    u32 checksum = 0;

    size_t index = 0;

    // Checksum the first 24 bytes (magic, version string, offset)
    while (index < 24) {
        checksumByte(checksum, data[index]);
        ++index;
    }

    // Skip the stored checksum value
    while (index < 28) {
        checksumByte(checksum, 0);
        ++index;
    }

    // Checksum the remaining header data
    while (index < sc_length) {
        checksumByte(checksum, data[index]);
        ++index;
    }

    stream.seek(originalPosition);

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