PAK Format (Westwood)

From ModdingWiki
Jump to navigation Jump to search
PAK Format (Westwood)
Format typeArchive
Max files65,536
File Allocation Table (FAT)Beginning
Filenames?Yes, 8.3
Metadata?None
Supports compression?No
Supports encryption?No
Supports subdirectories?No
Hidden data?Yes
Games

Westwood's PAK Format is a simple group file used in their games from 1991-1994. 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 three different versions of the header layouts which are nearly identical and backward-compatible with the previous version. Each update makes it slightly easier to read the header.

  • Version 1: Eye of the Beholder 1
  • Version 2: Dune II, Legend of Kyrandia 1
  • Version 3: Legend of Kyrandia 2, Lands of Lore 1, Legend of Kyrandia 3


File format

Signature

PAK files have no signature. They are usually given the extension .PAK, but not always.

File Entry

The header contains a list of files consisting of a 32-bit file offset and a null-terminating file name. Depending on the version, the header ends either when the file pointer equals the offset of the first file (version 1), or when you encounter a file offset of 0 (versions 2 and 3).

Data type Name Description
UINT32LE Offset File's starting offset in the PAK archive.
ASCIIZ Filename File name (8.3 style), null terminated, variable width.

Each file's size must be calculated by subtracting it from the next file's offset. Version 1 PAK files have a final file offset equal to the size of the file. Version 2 PAK files replace this offset with an end of header flag (0x0000), so the end of the last file must be inferred from the size of the file itself. Version 3 PAK files have an additional file offset with an empty file name (just a null) to determine the size of the last file and then feature an end of header flag (0x0000). Because of this, version 2 PAK files cannot contain hidden files after the last file.

Note that while version 1 PAK files normally have the end offset set to the file size, about half of the PAK files in Eye of the Beholder 1 have it set to complete garbage. The game doesn't actually use the end offset in any way; it has a table of file sizes hardcoded in the exe file, and relies on that to extract the files correctly. This should be taken into account when dealing with version 1 PAK files.

Tools

The following tools are able to work with files in this format.

Name PlatformExtract files? Decompress on extract? Create new? Modify? Compress on insert? Access hidden data? Edit metadata? Notes
XCC Mixer WindowsYesN/AYesYesN/ANoN/A Can open at least the Dune II type PAK files.
Librarian WindowsYesN/AYesYesN/ANoN/A

Source Code

Extractor

This FreeBASIC code will extract all of the files from a version 1, 2, or 3 PAK file into the folder you specify.

' This program will extract the files stored in a Westwood PAK file used in various games in the first half of the 1990s.
' There are three versions of the PAK file. Version 1 was used only in Eye of the Beholder 1. To determine the 
' end of the header, just wait until the file pointer equals the position of the first file. The last file offset is the 
' length of the file. Version 2 was first used in Dune II. It's header ends when the file offset is zero (0x0000). 
' Version 3 was first used in The Legend of Kyrandia 2. It has an additional empty file name with a file offset so you 
' can easily determine the size of the last file rather than relying on the length of the file, like in version 2.

Dim As String PAKFile = "H:\DOS\DUNE2\sound.pak"
Dim As String ExportFolder = "H:\DOS\DUNE2\dune"

Open PAKFile For Binary As #1

Dim As UInteger<32> FileStart(1000)
Dim As String   FileName(1000)
Dim As UInteger<32> Position
Dim As String   Char
Dim As UInteger FileCount = 0
Dim As UInteger FileNo
Dim As ULong    FileSize
Dim As UInteger ByteNo

' Loop through the header.
Do
    ' 4-byte file start position.
    Get #1, , FileStart(FileCount)

    Position = Seek(1)

    ' Trap for version 2 and 3 PAK files.
    If FileStart(FileCount) = 0 Then
        Exit Do
    Else
        ' Trap for version 1 PAK files.
        If (Position - 1) = FileStart(0) Then
            FileCount = FileCount + 1
            Exit Do
        Else
            ' Read the file name until we hit a null.
            FileName(FileCount) = ""
            Do
                Char = " "
                Get #1, , Char
                If Asc(Char) <> 0 Then
                    FileName(FileCount) = FileName(FileCount) + Char
                End If
            Loop While Asc(Char) <> 0

            FileCount = FileCount + 1
        End If
    End If
Loop

FileCount = FileCount - 1

MKDir(ExportFolder)

For FileNo = 0 To FileCount
    ' Read the previous file from the PAK.
    
    ' Get the file size.
    If FileNo = FileCount Then
        ' Trap for version 1 and 2 PAK files.
        FileSize = LoF(1) - FileStart(FileNo)
    Else
        FileSize = FileStart(FileNo + 1) - FileStart(FileNo)
    End If

    Print Using "###) File: \            \ Offset: ########,,   Size: ########,"; FileNo; FileName(FileNo); FileStart(FileNo); FileSize

    ' Trap for version 3 PAK files.
    If FileSize > 0 Then
        ' Create a buffer to store the next file.
        ReDim As Byte FileData(0 To FileSize)
        
        ' Load the file from the PAK into the buffer.
        Seek 1, FileStart(FileNo) + 1
        For ByteNo = 0 To (FileSize - 1)
            Get #1, , FileData(ByteNo)
        Next ByteNo
    
        ' Save the buffer to the export folder.
        Open ExportFolder + "\" + FileName(FileNo) For Binary As #2
        For ByteNo = 0 To (FileSize - 1)
            Put #2, , FileData(ByteNo)
        Next ByteNo
        Close #2
    End If
Next FileNo

Print "Finished."
Close #1

Sleep

Credits

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!)