LIB Format (Shadowcaster)
Overview
Shadowcaster .lib archives can be considered the "ancestor" of the Doom WAD format. They are mostly similar, realistically only differing in how you are expected to find the sub-file directory.
At the high level, .lib archives consist of a data region, the directory region, and finally the number of records stored at the very end of the file.
Data Region
The data region is simply every file's contents, concatenated end-to-end. There can possibly be "hidden" sections; one file does not necessarily need to exactly start at the end of the previous, or itself end at the start of the next.
In the case of .lib files, mostly level data and music are stored; graphics are stored in DAT files.
Directory
The directory immediately precedes the record count, and consists of a number of 21-byte entries with the following format:
uint32_t Size;
uint32_t Offset; // from beginning of file
char RecordName[13];
Record Count
Determining how many records are stored in the directory is done by seeking to the end of the file and reading the final uint16_t found there.
Reading a lib file
Adapted from MapCaster GML:
// first, we get the number of records and construct a pointer to the start of the directory
buffer_seek(Lib, buffer_seek_start, filesize - 2);
NumRecords = buffer_read(Lib, buffer_u16);
// offset to the start of the directory:
Directory = filesize - 2 - (NumRecords * 21);
// seek to the start of the directory
buffer_seek(Lib, buffer_seek_start, Directory);
// Read all entries until the end
for(i = 0; i < NumRecords; i++) {
Records[i] = ds_map_create();
r = Records[i];
r[? "Size"] = buffer_read(Lib, buffer_u32);
r[? "Offset"] = buffer_read(Lib, buffer_u32);
// read the name string
r[? "Name"] = ReadString(13); // directory subfile names are always exactly 13 bytes and follow 8.3 DOS filename convention, and are null-terminated
}