-
-
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 …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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