LIB Format (Mythos Software)
|File Allocation Table (FAT)||Beginning|
Mythos Software's LIB Format is very similar to the LIB format used by Electronic Arts, which is fitting since EA was the publisher for Mythos Software. It is efficient in the sense that it only stores file names and starting positions in the header, so file lengths must be inferred. It does not support compression. There are two versions of LIB files. An earlier version used in The Lost Files of Sherlock Holmes: The Case of the Serrated Scalpel, and a later version used in Bodyworks Voyager: Missions in Anatomy and The Lost Files of Sherlock Holmes: The Case of the Rose Tattoo. The second version has a slightly different layout, which is not yet fully understood, but it seems the added data in the second format is not actually needed by the games.
Version 1 LIB files begin with "LIB", then character 0x1A, then the number of files. In version 2, the string part is "LIC". You actually have to read one more file entry than the header suggests, as there is an empty file entry at the end of the list to indicate where the final file ends.
|char||Signature||"LIB" plus 0x1A in version 1. Version 2 uses "LIC" plus 0x1A.|
|UINT16LE||FileCount||Number of files|
In version 1, the signature is followed immediately by the file entry index, a list of the file entry structure described below, repeated
In version 2, there is a kind of table with a length of
FileCount * 8 between this header and the file entries, but besides that, the format is completely identical. The values in this table are unknown, but tests in both Bodyworks Voyager and Rose Tattoo showed that the games didn't seem to care if data in the .lib file was modified without any change to these header value, or even if all these table values were blanked out to 0x00 bytes, so even if the table values would be some sort of file checksums, they don't prevent the game from accepting the archive and its contents.
Each file entry consists of the file name and its offset in the file. Even though the file name is fixed-width, it appears to be null-terminated.
|char||filename||File name (8.3 style), padded with nulls to 13 characters|
|UINT32LE||offset||File's offset from the start of the LIB archive|
Each file's size must be calculated by subtracting it from the next file's offset. There is an additional null-named file at the end with an offset equal to the size of the file so you don't need any special code to get the final file's size. This entry is not included in fileCount in the header.
While no files appear to use this, it is possible to hide data after the list of file entries and before the first file.
As there is an extra file entry to hold the size of the archive file, it is also possible to hide data at the end of the file as long as it comes after the offset of the final null-named file entry.
Both these methods produce valid files where the hidden data would always be ignored.
The Music.lib group affixes 12 spaces to the beginning of each file. Although this may just be part of the music format, it is also affixed to the driver files. It is unknown why these spaces are in this lib group, but they do not show up in other lib files. Perhaps it was required for the sound engine?
The following tools are able to work with files in this format.
|Name||Platform||Extract files?||Decompress on extract?||Create new?||Modify?||Compress on insert?||Access hidden data?||Edit metadata?||Notes|
This FreeBASIC code will extract all of the files from the LIB file into a folder named after the LIB file you specify.
' Specify the Mythos Software LIB file here: Dim As String LibFileName = "music.lib" Open LibFileName For Binary As #1 Print "Reading header..." ' Verify the format's magic word. Dim As String MagicWord = Space(3) Get #1, , MagicWord If MagicWord <> "LIB" Then Print "Not a Mythos Software .LIB file." End End If ' Header Structure Dim As Byte Unknown1 Get #1, , Unknown1 Dim As UShort NumberOfFiles Get #1, , NumberOfFiles ' An additional entry is made which has an empty file name and stores the length of the LIB file as the starting offset. ' This is used to calculate the length of the last file without needing special code to determine it based on the file size. NumberOfFiles = NumberOfFiles + 1 Print "Valid LIB File." ' Load the header filenames and their offsets. Dim FileName(1 To NumberOfFiles) As String Dim FileStart(1 To NumberOfFiles) As UInteger Dim As UShort FileNo For FileNo = 1 To NumberOfFiles FileName(FileNo) = Space(13) Get #1, , FileName(FileNo) FileName(FileNo) = Left(FileName(FileNo), InStr(FileName(FileNo), Chr(0)) - 1) Get #1, , FileStart(FileNo) Next FileNo ' Extract the files. Print "Extracting..." Dim As String LibFolder = Left(LibFileName, InStr(LibFileName, ".") - 1) MkDir("./" + LibFolder) Dim As ULongInt FileLength For FileNo = 1 To (NumberOfFiles - 1) ' Skip the last file which is not a real file anyway. FileLength = FileStart(FileNo + 1) - FileStart(FileNo) Print " ./" + LibFolder + "/" + FileName(FileNo) + " - " + Str(FileLength) + " bytes" Dim FileContents(0 To (FileLength - 1)) As Byte Get #1, FileStart(FileNo) + 1, FileContents() Open ("./" + LibFolder + "/" + FileName(FileNo)) For Binary As #2 Put #2, , FileContents() Close #2 Next FileNo Close #1 Print : Print Str(NumberOfFiles - 1) + " file(s) extracted." Sleep
This archive format was reverse engineered by TheAlmightyGuru. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)