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.
141 lines
3.8 KiB
Markdown
141 lines
3.8 KiB
Markdown
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
|
|
------
|
|
|
|
```c++
|
|
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.
|
|
|
|
```c++
|
|
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:
|
|
```c++
|
|
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:
|
|
```c++
|
|
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:
|
|
```c
|
|
[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:
|
|
```c
|
|
[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
|
|
```
|