Formats/Common:BIG

From SSX Modding Wiki
Jump to navigation Jump to search

Big File

Overview

A Big file is an archive format used to pack game assets into a single file, with some member files being Refpack or ChunkZip compressed.

There are 4 Big file types/versions used throughout the series even thought they use the same .big file extension. You can tell what is their type by looking at the first bytes of the file in a hex editor.

  • COFB (Unofficial name) Used by SSX 2000 and SSX Tricky.
  • BIGF Used by SSX Tricky, SSX 3, SSX On Tour, and SSX Blur.
  • BIG4 Used by SSX Tricky, SSX 3, SSX On Tour, and SSX Blur.
  • NewBig Used by SSX 2012.

File Structures

If the first word of the member file data starts with 0x10FB, then the file is Refpack compressed. If it starts with the string "chunkzip" then it's ChunkZip compressed.

Null terminated paths can be forward slashes or backslashes, but forward slashes is the standard.

COFB

#include <std/mem.pat>

struct MemberFileHeader {
    be u24 offset; // position of file data, 128 byte aligned.
    be u24 size; // size of file data
    char path[]; // null terminated
    
    // For visualizing on the Hex Editor.
    // Note that this doesnt highlight the 128 byte alignment.
    u8 data[size] @ offset;
};

struct COFBHeader {
    be u16 fileCount;
    MemberFileHeader files[fileCount];
    std::mem::AlignTo<128>; // This is only here to skip the first alignment in ImHex.
};

struct COFB {
    u8 magic[2]; // 0xCOFB
    be u16 headerSize;
    COFBHeader header;
    // Member file data goes here.
    // Offsets are 128 byte aligned like so:
    // std::mem::AlignTo<128>;
    // Data
    // std::mem::AlignTo<128>;
    // Data
    // ...etc
};

COFB big @ 0x0;

BIGF and BIG4

BigF and Big4 have similar file structures. The only difference is the signature at the start of the files.

#include <std/mem.pat>

struct LumpyDebugInfo {
    char version[4];
    be u32 flags; // Unknown use
};

struct MemberFileHeader {
    be u32 offset; // Position of file data, 128 byte aligned.
    be u32 size; // Size of file data
    char path[]; // Null terminated
    
    // For visualizing on the Hex Editor.
    // Note that this doesnt highlight the 128 byte alignment.
    u8 data[size] @ offset;
};

struct BIGF4Header {
    le u32 fileSize;
    be u32 fileCount;
    be u32 headerSize;
    MemberFileHeader files[fileCount];
    LumpyDebugInfo debugInfo;
    std::mem::AlignTo<128>; // This is only here to skip the first alignment in ImHex.
};

struct BIGF4 {
    u8 magic[4]; // "BIGF" or "BIG4" depending on type
    BIGF4Header header;
    // Member file data goes here.
    // Offsets are 128 byte aligned like so:
    // std::mem::AlignTo<128>;
    // Data
    // std::mem::AlignTo<128>;
    // Data
    // ...etc
};

BIGF4 big @ 0x0;

Lumpy is the program the SSX team used to handle big files.

NewBig

#include <std/core.pat>
#include <std/mem.pat>

struct NewBigHeader {
    be u16 signature; // "EB" (Electronic Arts Bigfile)
    be u16 headerVersion; // version number
    be u32 fileCount; // number of files in this archive
    be u16 flags; // bitflags
    u8 alignment; // power of two on which the files are aligned (default = 4, 16 bytes)
    u8 reserved; // not used
    be u32 baseHeaderSize; // file header + hash index + index flags
    be u32 nameHeaderSize; // size of the Path and Directory entries, in bytes
    u8 pathEntrySize; // size of each PathEntry in bytes
    u8 directoryEntrySize; // size of each DirectoryEntry in bytes
    be u16 uniqueDirectoryEntryCount; // total number of unique directories
    be u64 fileSize; // total size of the bigfile
};

struct HashIndex {
    // offset divided by the alignment to the start of the file.
    // To get to the file location, multiply this by the alignment (16 by default)
    be u32 offset; 
    be u32 zSize; // size of the file data if its Refpack compressed
    be u32 size; // size of the file data if its Chunkzip compressed or uncompressed
    be u32 hash; // 32-bit DJB2 hash

    // For visualizing on the Hex Editor.
    // If the data is refpack compressed then use zSize.
    be u32 realSize = size;
    if ($[offset * 16] == 0x10 && $[offset * 16 + 1] == 0xFB) {
        realSize = zSize;
    }
    u8 data[realSize] @ offset * 16;
};

struct PathEntry<auto structSize> {
    be u16 directoryIndex; // the DirectoryEntry entry index for this path
    char filename[structSize-2];
};

struct DirectoryEntry<auto length> {
    char directory[length];
};

struct NewBig {
    NewBigHeader header;
    padding[16];
    HashIndex hashIndices[header.fileCount];
    u8 indexFlags[header.fileCount];
    std::mem::AlignTo<16>;
    PathEntry<header.pathEntrySize> pathEntries[header.fileCount];
    std::mem::AlignTo<16>;
    DirectoryEntry<header.directoryEntrySize> directoryEntries[header.uniqueDirectoryEntryCount];
    std::mem::AlignTo<16>;
    // All file data actually starts here. The data field in HashIndex is just for visualizing in ImHex.
    // All file data pointed by HashIndex.Offset is 16 byte aligned. That includes the end of the file.
};

NewBig big @ 0x0;

NewBig introduced the ChunkZip compression format. Member files can be Refpack compressed, ChunkZip compressed, or just raw uncompressed data. The size of member file data depends on the compression type, that explain the conditional check on line 30.

Trivia

  • Official packing tools identify C0FB as "old 24bit" and BIGF as "old 32bit" respectively.