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 ANDed 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/0x20if 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: