You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
neptools/doc/formats/gbin.md

3.4 KiB

GBIN and GSTR

GBIN (stored inside .gbin files in SYSTEM/database, and inside .cl3s) and GSTR (stored in .gstr files in SYSTEM/database) are quite similar. All integers are little-endian.

The sections described below (Header/Footer, Type descriptor, Struct and String table) are aligned to 16 bytes in GBIN, but unaligned in GSTR...

struct HeaderFooter
{
    char magic[3];
    char endian;
    uint16_t field_04; // always 1
    uint16_t field_06; // always 0
    uint32_t field_08; // always 16
    uint32_t field_0c; // always 4
    uint32_t flags;
    uint32_t struct_offset;
    uint32_t struct_count;
    uint32_t struct_size;
    uint32_t types_count;
    uint32_t types_offset;
    uint32_t field_28; // ??
    uint32_t string_offset;
    uint32_t field_30; // 0 or 4
    uint32_t padding[3]; // always 0
};
sizeof(HeaderFooter) == 0x40

In GSTR the above struct is a header, at the beginning of the file, magic string is GST. In GBIN it's a footer (so it starts at EOF-sizeof(HeaderFooter)), and the magic is GBN... Endian is denoted by endian, if L means little-endian and B means big-endian.

flags is 1 if there's any string stored in the file (see Type descriptor below), 0 otherwise. struct_offset is 0 in GBIN and 0x40 in GSTR files (but other values should work too). Offsets are relative to the beginning of the file.

Type descriptor

This file contains a list of some structure. However the structure is actually defined inside the file, so you can figure out the types of each fields.

struct TypeDescriptor
{
    uint16_t type;
    uint16_t offset;
};
sizeof(TypeDescriptor) == 4;

They start at header.types_offset, and there's header.types_count of them. They're sorted into ascending order by offset. Each entry describes a field inside the struct, offset is an offset from the beginning of the struct. The valid values for type are:

enum Type
{
    UINT32 = 0,
    UINT8  = 1,
    UINT16 = 2,
    FLOAT  = 3,
    STRING = 5,
};

UINT16, and UINT32 are standard 16 bit and 32 bit integers (not sure if they're signed or unsigned though, probably the format makes no difference between them), FLOAT is a standard 32 bit float (as used by x86 CPUs).

UINT8 is a bit special: it's normally a single byte, but can also mean a fixed length zero terminated string. They both end up as type=1. This editor currently uses a heuristic: if the offset of the next entry is this entry's offset + 1 (taking aligning into account), it's probably a single byte, otherwise a fixed length string.

STRING is a 32 bit integer, an offset into the string table. Sometimes it'll be -1 (0xffffffff), that's an invalid string (maybe NULL pointer originally?).

The fields are aligned: UINT32, FLOAT and STRING are aligned to 4 bytes and UINT16 to 2 bytes.

Struct

The structs start at header.struct_offset and there are header.struct_count of them. Every struct is header.struct_size bytes length, the fields are described by the type descriptor.

String table

This is optional, it only exists if header.flags == 1. It starts at header.string_offset. Each contains a bunch of zero terminated strings sequentially, and referenced inside STRING type fields.

There's an optimization in the original files: if the same string appears multiple times, it's only stored once in the table, the identical STRING fields will get the same offset.