Skip to content

Instantly share code, notes, and snippets.

@RenolY2
Last active June 12, 2016 07:12
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 RenolY2/6cacd79581ebd8beae1410140f27e8fb to your computer and use it in GitHub Desktop.
Save RenolY2/6cacd79581ebd8beae1410140f27e8fb to your computer and use it in GitHub Desktop.
Put bw_archive_base.py, dump_res_scripts.py, and rebuild_res_scripts.py in the same directory somewhere where you want to work with res files and lua script and set the paths up in dump_res_scripts.py and rebuild_res_scripts. If you have the decompiled scripts and the lua 5.0.2 compiler, put the compiler in the same directory as the .py scripts …
import io
import struct
from array import array
def read_uint32(fileobj):
return struct.unpack("I", fileobj.read(4))[0]
class BWResource(object):
def __init__(self, name, size, memview):
self.name = name
self._size = size
self._data = memview
self._fileobj = io.BytesIO(self._data)
@property
def fileobj(self):
return self._fileobj
# File object and data object should be kept up to date together when
# one of them is changed.
@fileobj.setter
def fileobj(self, fobj):
self._fileobj.close()
self._fileobj = fobj
self._data = fobj.getbuffer()
@property
def data(self):
return self._data
@data.setter
def data(self, data):
self._fileobj.close()
self._data = data
self._fileobj = io.BytesIO(self._data)
def pack(self):
#data = self.fileobj.read()
data = self._data#self.fileobj.getbuffer()
#print(self.name, len(data))
return self.name, len(data), data
# Interpret a data entry as a section. If cls is given, an instance of that will be returned.
# When using cls, offset is unused.
def as_section(self, offset=0, cls=None):
if cls is None:
return BWSection(self.name, self._size, self._data, section_offset=offset)
else:
return cls(self.name, self._size, self._data)
class BWSection(BWResource):
def __init__(self, name, size, memview, section_offset=0):
super().__init__(name, size, memview)
self.entries = []
self._header = self._data[0:section_offset]
self._fileobj.seek(section_offset)
while self._fileobj.tell() < self._size:
name, size, entry_memview = read_section(self._fileobj, memview)
res_obj = BWResource(name, size, entry_memview)
self.entries.append(res_obj)
def pack(self):
packed = io.BytesIO()
packed.write(self._header)
section_size = len(self._header)
for entry in self.entries:
name, size, data = entry.pack()
packed.write(name)
assert size == len(data)
packed.write(struct.pack("I", size))
packed.write(data)
# 4 bytes for the ID, 4 bytes for the length, and the rest is from the data
section_size += 4 + 4 + len(data)
packed_data = packed.getvalue()
packed.close()
return self.name, section_size, packed_data
def as_section(self, offset=0):
return self
class BWArchiveBase(BWSection):
# f should be a file open in binary mode
def __init__(self, f):
# We read the content of the file into memory and put it in a bytearray,
# which is necessary so the content can be modified.
file_content = bytearray(f.read())
#file_content = array("B", f.read())
super().__init__(name=None, size=len(file_content), memview=file_content)
def write(self, f):
unused, size, data = self.pack()
f.write(data)
def read_section(f, memview):
name = f.read(4)
size = read_uint32(f)
offset = f.tell()
data = memoryview(memview[offset:(offset+size)])#f.read(data_len)
f.seek(size, io.SEEK_CUR)
#print(len(memview), len(f.getbuffer()))
return name, size, data
import os
import struct
import io
from bw_archive_base import BWArchiveBase, BWResource
def unpack_uint32(data, offset):
return struct.unpack("I", data[offset:offset+4])[0]
class ScriptEntry(BWResource):
def __init__(self, name, size, memview):
super().__init__(name, size, memview)
strlength = unpack_uint32(self._data, 0)
self.res_name = self._data[4:4+strlength]
self.script_data = self._data[4+strlength:]
def pack(self):
self._fileobj = io.BytesIO()
self._fileobj.write(struct.pack("I", len(self.res_name)))
self._fileobj.write(self.res_name)
self._fileobj.write(self.script_data)
self._data = self._fileobj.getbuffer()
return super().pack()
if __name__ == "__main__":
inputfile = "C1_Bonus_Level.res" # The file from which the lua scripts should be dumped
outputdir = "data" # the directory into which the lua scripts will be dumped
os.makedirs(outputdir, exist_ok=True)
with open(inputfile, "rb") as f:
res = BWArchiveBase(f)
for i, entry in enumerate(res.entries):
if entry.name == b"PRCS":
script = entry.as_section(cls=ScriptEntry)
scriptname = str(script.res_name, encoding="ascii")
data = script.script_data
with open(os.path.join(outputdir, scriptname+".luap"), "wb") as f:
f.write(data)
print("wrote", os.path.join(outputdir, scriptname+".luap"))
import os
import struct
import io
import subprocess
from bw_archive_base import BWArchiveBase, BWResource
from dump_res_scripts import ScriptEntry
if __name__ == "__main__":
inputdir = "C1_OnPatrol_Level.resData" # The directory that contains the changed files
inputfile = "C1_OnPatrol_Level.res" # The original file that will be used to rebuild the res archive
outputfile = "C1_OnPatrol_Levelchanged.res" # The output name of the new res archive
compilerpath = "luac5.0.2.exe" # Path to the compiler. Leave empty (i.e. compilerpath = "") if no compiler
if compilerpath:
scriptfiles = filter(lambda x: x.endswith(".lua"), os.listdir(inputdir))
for scriptfile in scriptfiles:
sourcefile = os.path.join(inputdir, scriptfile)
compiledfile = os.path.join(inputdir, scriptfile+"p")
print("compiling", scriptfile, "to", compiledfile)
returncode = subprocess.call([compilerpath, "-o", compiledfile, sourcefile])
print("compiled with return code", returncode) # return code 0 means it went well
print("finished compiling")
with open(inputfile, "rb") as f:
res = BWArchiveBase(f)
for i, entry in enumerate(res.entries):
if entry.name == b"PRCS":
script = entry.as_section(cls=ScriptEntry)
scriptname = str(script.res_name, encoding="ascii")
scriptpath = os.path.join(inputdir, scriptname+".luap")
with open(scriptpath, "rb") as f:
data = f.read()
script.script_data = data
res.entries[i] = script
with open(outputfile, "wb") as f:
res.write(f)
print("wrote", outputfile)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment