Skip to content

Instantly share code, notes, and snippets.

@bunyk
Last active February 12, 2022 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bunyk/f17e991cd9566b7fe6dc82ff2d5f9992 to your computer and use it in GitHub Desktop.
Save bunyk/f17e991cd9566b7fe6dc82ff2d5f9992 to your computer and use it in GitHub Desktop.
London Racer: World Challenge unpacker
'''
Unpacker for game archive of London Racer: World Challenge
Expects to run in the folder which contains files WR.idx and WR.img, creates
folder unpacked with the unpacked game resources.
See https://bunyk.github.io/posts/reverse_engineering_game_archive to know how this was made.
'''
import os
import struct
def main():
items = read_index('WR.idx')
with open('WR.img', 'rb') as img:
create_folder('unpacked', items, img)
def read_index(filename):
with open(filename, 'rb') as f:
f.seek(48) # skip header
return read_folder(f)
def read_folder(f):
n = read_int(f)
return [read_item(f) for i in range(n)]
def read_int(f):
data = f.read(4)
decoded = struct.unpack('<l', data) # unpack it as little-endian int
return decoded[0]
def read_item(f):
is_file = f.read(1)[0] # type of item
f.read(3) # unknown bytes
f.read(8) # zeroes
name = read_string(f)
if is_file == 1:
size, offset = read_file_fields(f)
return (name, size, offset)
else:
return (name, read_folder(f))
def read_string(f):
res = []
while True:
c = f.read(1)[0]
if c == 0:
pad = f.tell() % 4
if pad > 0:
f.read(4 - pad) # skip padding
return ''.join(res)
res.append(chr(c))
return res
def read_file_fields(f):
data = f.read(16)
size1, size2, fsize, offset = struct.unpack('<llll', data)
f.read((fsize - 1) * 4) # skip some unknown values
return size1, offset
def create_folder(path, items, img):
os.mkdir(path)
print("created folder", path)
for i in items:
np = path + '/' + i[0]
if len(i) == 2: # another folder
create_folder(np, i[1], img)
if len(i) == 3:
data = read_slice(img, i[1], i[2])
create_file(np, data)
def read_slice(f, size, offset):
f.seek(offset)
return f.read(size)
def create_file(filename, data):
with open(filename, 'wb') as f:
f.write(data)
print("created file ", filename)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment