Skip to content

Instantly share code, notes, and snippets.

@mdavey
Created June 18, 2014 02:03
Show Gist options
  • Save mdavey/4b984e021bf484035cfa to your computer and use it in GitHub Desktop.
Save mdavey/4b984e021bf484035cfa to your computer and use it in GitHub Desktop.
Crimsonland (Steam) Pak Format
import os
import struct
"""
Crimsonland (Steam) pak file format
Header format:
50 40 4B 00 56 31 31 00 ("PAK" NUL "V11" NUL)
int32 index offset (eg: 28679361)
int32 end index offset (eg: 28885444)
Index Format:
int32 number of indexes?
Index_File format:
null terminated string
int32 absolute offset of file
int32 file length
unknown maybe always (?) equal to: FF 26 E2 50 20 00 00 00
"""
class ClPak(object):
def __init__(self, filename):
self.file = open(filename, 'rb')
def read_header(self):
self.file.seek(0)
bytes = self.file.read(16)
(magic_1, magic_2, index_start_offset, index_end_offset) = struct.unpack('3sx3sxii', bytes)
if magic_1 != 'PAK' or magic_2 != 'V11':
raise Exception('Unknown file format')
return index_start_offset, index_end_offset
def read_index(self):
start_offset, end_offset = self.read_header()
self.file.seek(start_offset)
bytes = self.file.read(end_offset - start_offset)
# index_size = struct.unpack('i', bytes[:4])
return self.read_index_file_details(bytes[4:])
def read_index_file_details(self, bytes):
details = {'name': '', 'offset': 0, 'length': 0, 'unknown': ''}
sequence = iter(bytes)
for char in sequence:
if char != chr(0):
details['name'] += char
else:
offset_bytes = sequence.next() + sequence.next() + sequence.next() + sequence.next()
length_bytes = sequence.next() + sequence.next() + sequence.next() + sequence.next()
(details['offset'],) = struct.unpack('i', offset_bytes)
(details['length'],) = struct.unpack('i', length_bytes)
for x in range(0, 8):
details['unknown'] += sequence.next()
yield details
details = {'name': '', 'offset': 0, 'length': 0, 'unknown': ''}
def dump_file(self, details, base_directory):
dest_filename = base_directory + '/' + details['name']
if not os.path.exists(os.path.dirname(dest_filename)):
os.makedirs(os.path.dirname(dest_filename))
with open(dest_filename, 'wb') as dest:
self.file.seek(details['offset'])
dest.write(self.file.read(details['length']))
if __name__ == '__main__':
cl_pak = ClPak(r'D:\Steam\steamapps\common\Crimsonland\data.pak')
for file_details in cl_pak.read_index():
print file_details
cl_pak.dump_file(file_details, 'd:/clpak_files')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment