.CMF - nbreeze/closers-rev GitHub Wiki

Introduction

.CMF is an archive format used exclusively in Closers to store all the game's files. If you have snooped around the game's files then you'll notice thousands of these archives right away. The format is public knowledge at this point and it hasn't changed much over the years aside from a signature change.

HEADER.CMF does not follow this format.

Signature

The file begins with a signature wide string. This signature can be one of the following:

// N.K. .E.N.G.I.N.E. .C.O.M.B.I.N.E. .F.I.L.E. .v.e.r... .1...0...
4E 00 4B 00 20 00 45 00 4E 00 47 00 49 00 4E 00
45 00 20 00 43 00 4F 00 4D 00 42 00 49 00 4E 00
45 00 20 00 46 00 49 00 4C 00 45 00 20 00 76 00
65 00 72 00 2E 00 20 00 31 00 2E 00 30 00 00 00

// C.O.M.B.I.N.E. .F.I.L.E. .v.e.r...2.
43 00 4F 00 4D 00 42 00 49 00 4E 00 45 00 20 00
46 00 49 00 4C 00 45 00 20 00 76 00 65 00 72 00
2E 00 32 00

Entries

At file offset 0x64 (100) begins the list of entries stored in the file.

Next, there is an int32 specifying how many files are stored in the .CMF archive. We'll call this the entry count. After that, there is an array of file entry structures, with each structure specifying the filename, offset in data section of the archive, compression flag, and both compressed and uncompressed sizes.

struct CMFFileEntry {
    wchar_t[256] fileName; // 512
    unsigned int size_uncompressed; // 516
    unsigned int size_compressed; // 520
    unsigned int data_offset; // 524
    unsigned int compressed; // 528
};

If compressed is non-zero, then the file has been compressed with Zlib INFLATE.

:warning: The entry count and the entries themselves are encrypted. See the Decompiling page for more details.

Data

After the entries comes the actual files stored in the archive. If you want to be safe, you can easily determine the starting offset of the data section of the archive by doing this method:

DATA_SECTION_OFFSET = SIGNATURE_SECTION_SIZE + 4 + ( entry_count * sizeof(CMFFileEntry) )

...where SIGNATURE_SECTION_SIZE is 100, or 0x64.

To get to a specific file, you need to use the data_offset field of the CMFFileEntry structure defined earlier. data_offset is an offset that points to the start offset of the file, relative to DATA_SECTION_OFFSET. Use data_offset + size_compressed to get the ending offset of the file.