GoodFeelArchive A file format used in Good-Feel games released on Nintendo consoles prior to their Nintendo Switch games.
General Information
- Byte order: little-endian
- Sections: 5
- archive header
- file entries
- filename list
- compression header
- compressed data
Assume padding of bytes with a value of 0 when there is a gap.
Format documentation
Archive Header
Size = 0x40
field | offset | size | data type | description |
---|---|---|---|---|
magic | 0x0 |
0x4 |
char[0x4] |
"GFAC" - GoodFeel ArChive |
version | 0x4 |
0x4 |
u32 |
Rightmost byte is version major. The byte after that is version minor. The other two bytes don't seem to be used. |
compressed | 0x8 |
0x1 |
bool |
Indicates whether or not the file has a compression header. |
entry info offset | 0xC |
0x4 |
u32 |
Offset to entry information. |
file info size | 0x10 |
0x4 |
u32 |
The size of the file information.1 |
gfcp offset | 0x14 |
0x4 |
u32 |
Offset to GFCP header. |
payload size | 0x18 |
0x4 |
u32 |
Size of the rest of the file from the start of the GFCP header onwards. |
file count | 0x2C |
0x4 |
u32 |
The number of files in the archive. |
File Entries
Size = 0x10
field | offset | size | data type | description |
---|---|---|---|---|
checksum | 0x0 |
0x4 |
u32 |
This is based on the filename, not the contents of the file.2 |
name offset | 0x4 |
0x4 |
u32 |
Offset to the filename. To process, this value is to be AND ed with 0x00FFFFFF . If this is the last entry in the archive, a flag of 0x80000000 is applied. |
size | 0x8 |
0x4 |
u32 |
Size of the file when uncompressed. |
decompressed offset | 0xC |
0x4 |
u32 |
See below.3 |
Filename Strings
Immediately after the final file entry, null-terminated strings are written consecutively. Once the final string is written, there is padding until the next multiple of 0x10
.
Compression Header
Size = 0x14
Before compression, files are concatenated.
field | offset | size | data size | description |
---|---|---|---|---|
magic | 0x0 |
0x4 |
char[0x4] |
"GFCP" - GoodFeel ComPression |
version? | 0x4 |
0x4 |
u32 |
usually 1. |
compression type | 0x8 |
0x4 |
u32 |
See chart below.4 |
decompressed size | 0xC |
0x4 |
u32 |
Size of collective decompressed data, padded to offset of 0x10 . |
compressed size | 0x10 |
0x4 |
u32 |
Size of compressed data. |
1 - Calculated this way:
uint32_t size =
4 +
(entry_count * sizeof(entry)) +
(length_of_all_strings);
// ^ this includes the null terminator for *every* string
2 - Calculated this way:
const char* filename;
uint32_t checksum = 0;
for (auto i = 0; filename[i] != 0; i++) {
char c = filename[i];
checksum = c + checksum * 137;
}
3 - This is calculated as if the file structure was this:
- archive header
- file entries
- filename
- decompressed data (padded to a size of
0x10
/0x20
if necessary)
4 - Compression types:
value | type |
---|---|
1 | Byte Pair Encoding |
2 | Speculated to be LZ10 |
3 | LZ10 |
Tools
Existing Documentation
Aside from reverse engineering certain aspects of the format myself, I referenced the following: