3.8 KiB
CL3
CL3 is an archive format, that contains uncompressed files plus links between them. All integers are little-endian. Offsets are 0x40 bytes aligned in the original files (this is not a requirement).
Header
struct Header
{
char magic[3]; // always "CL3"
char endian;
uint32_t field_04; // always 0
uint32_t field_08; // always 3
uint32_t sections_count;
uint32_t sections_offset;
uint32_t field_14;
// maybe more?
};
sizeof(Header) == 0x18
endian
is L
if the file is little-endian, B
if big-endian. field_14
is
either 0, 1 or 2 (at least in RB3). Script .cl3
s use 1, effect .cl3
s use 2.
Sections
Starting at sections_offset
(relative to file beginning), there are
sections_count
sections.
struct Section
{
char name[0x20]; // '\0' terminated
uint32_t count;
uint32_t data_size;
uint32_t data_offset;
uint32_t padding[9]; // always 0
};
sizeof(Section) == 0x50
There are two known sections: FILE_COLLECTION
and FILE_LINK
. Each has a
payload of data_size
bytes starting at data_offset
.
FILE_COLLECTION
This is used to store the actual files. It begins with count
entries:
struct FileEntry
{
char name[0x200]; // '\0' terminated
uint32_t field_200;
uint32_t data_offset;
uint32_t data_size;
uint32_t link_start;
uint32_t link_count;
uint32_t padding[7]; // always 0
};
sizeof(FileEntry) == 0x230
Section::data_size
includes both the size of these entries and the actual file
data size (it's last_entry.offset + last_entry.size + pad
).
name
is the name of the file. field_200
is either 0 for all files, or they
contain the index of the file (?). The file contents start at data_offset
,
(unlike other offsets) relative to the beginning of FILE_COLLECTION, and
it's data_size
bytes long. link_count
contains the number of links this file
has (see below), link_start
is an index inside the FILE_LINK
.
FILE_LINK
It contains count
entries:
struct LinkEntry
{
uint32_t field_00; // always 0
uint32_t linked_file_id;
uint32_t link_id;
uint32_t padding[5]; // always 0
};
sizeof(LinkEntry) == 0x20;
Section::data_size
is count*sizeof(LinkEntry)
. linked_file_id
contains the
index of the link destination (inside FILE_COLLECTION
). link_id
counts the
index of the current file's link (it's an incrementing counter reset with every
new file).
In the original files, files and the corresponding link entries are in the same
order, if a file has 0 links, the link_start
is still set to where it would
began if it'd actually have links.
Example
Probably the way I described this whole link thing is not clear enough, so here's an example:
FILE_COLLECTION
contains this:
[0] = { .name = "a.bin", .link_start = 0, .link_count = 2 },
[1] = { .name = "b.bin", .link_start = 2, .link_count = 1 },
[2] = { .name = "c.bin", .link_start = 3, .link_count = 0 },
[3] = { .name = "d.bin", .link_start = 3, .link_count = 1 },
And FILE_LINK
contains this:
[0] = { .linked_file_id = 1, link_id = 0 },
[1] = { .linked_file_id = 2, link_id = 1 },
[2] = { .linked_file_id = 2, link_id = 0 },
[3] = { .linked_file_id = 1, link_id = 0 },
This means that a.bin
has two links, starting at 0 in FILE_LINK
. link[0]
says it's a link to file[1]
(i.e. b.bin
), and link[1]
says it's a link to
file[2]
(i.e. c.bin
).
b.bin
only has one link, at link[2]
. It's a link to file[2]
(i.e.
c.bin
).
c.bin
has zero links, but link_start
still contains 3, because the links
would be there if there were one. Finally d.bin
contains a single link, still
starting at 3, since 3+0=0
.
The actual link graph looks like this:
a.bin ---> b.bin <--- d.bin
| |
| v
+------> c.bin