Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # python 2.7
- # Rename characters in LoG save files.
- import argparse
- import struct
- import zlib
- import re
- import io
- MAX_CHAR_NAME_SZ = 20 # Max character name size is set to 20 characters
- # Savegame format:
- # ----------------
- # 4752 494D 0400 0000 F14E 0B00 789C EC7D ... ...
- # ^^^^ ^^^^ Block name = "GRIM"
- # ^^^^ ^^^^ Block size = 4 (little-endian)
- # ^^^^ ^^^^ Uncompressed data size in bytes (little-endian)
- # ^^^^ Zlib data header (always 789C)
- # ^^^^ ^^^^ ^^^^ Zlib data
- def parse_savegame(filename, keep_uncompressed = False):
- savefile_in, savefile_out, uncompfile_out = None, None, None
- try:
- savefile_in = open(filename,'rb')
- print "Parsing %s:" % filename
- out_filename = re.sub(r'\.sav', '.edited.sav', filename)
- if out_filename == filename:
- raise Exception("Input savefiles must have the .sav extension")
- uncomp_data_out = ""
- filetype = savefile_in.read(4)
- if filetype != "GRIM":
- raise Exception("Not a valid savefile")
- block_size= savefile_in.read(8) # Skip the next 8 bytes
- zlib_data = savefile_in.read(-1) # Read all the rest (= the compressed data)
- data_stream = io.BytesIO(zlib.decompress(zlib_data))
- while True:
- skip_flag = False
- block_name = data_stream.read(4)
- if block_name == "":
- break
- block_size = struct.unpack("<L",data_stream.read(4))[0]
- block_data = data_stream.read(block_size)
- block_data_edit = bytearray(block_data)
- delta_sz = 0
- if block_name == "CHAR":
- char_id = struct.unpack("<H",block_data[10:12])[0]
- name_tag = block_data[12:16]
- name_block_sz = struct.unpack("<L",block_data[16:20])[0]
- name_sz = struct.unpack("<L",block_data[24:28])[0]
- if name_tag == "CHAM":
- print " Found character %s" % str(block_data[28:28+name_sz])
- name_too_long = True
- while name_too_long:
- new_name = raw_input(" New name (press Enter to keep identical)? ")
- name_too_long = (len(new_name) > MAX_CHAR_NAME_SZ)
- if name_too_long:
- print " Name too long (%d chars max)" % MAX_CHAR_NAME_SZ
- if len(new_name) != 0:
- delta_sz = len(new_name) - name_sz
- block_data_edit[16:20] = struct.pack("<L", name_block_sz + delta_sz)
- block_data_edit[24:28] = struct.pack("<L", len(new_name))
- block_data_edit[28:28+name_sz] = bytearray(new_name)
- uncomp_data_out += block_name + struct.pack("<L", block_size + delta_sz) + str(block_data_edit)
- savefile_out = open(out_filename, 'wb')
- savefile_out.write("GRIM" + struct.pack("<LL", 4, len(uncomp_data_out)))
- savefile_out.write(zlib.compress(uncomp_data_out))
- print "\nEdited savegame is: %s" % out_filename
- if keep_uncompressed:
- uncomp_filename = re.sub(r'\.sav', '.uncompressed.sav', filename)
- if uncomp_filename == filename:
- raise Exception("Input savefiles must have the .sav extension")
- uncompfile_out = open(uncomp_filename, 'wb')
- uncompfile_out.write(uncomp_data_out)
- finally:
- if savefile_in:
- savefile_in.close()
- if savefile_out:
- savefile_out.close()
- if uncompfile_out:
- uncompfile_out.close()
- #
- # Main()
- #
- def main():
- parser = argparse.ArgumentParser(description='Rename characters LoG save files')
- parser.add_argument('savefiles', metavar='savefile', nargs=1,
- help='LoG save files (v1.1.4 or above) with .sav extension')
- parser.add_argument('-k', dest='keep_uncompressed', action='store_true',
- help='Keep the uncompressed data (with extension .uncompressed.sav)')
- args = parser.parse_args()
- # Parse the first savefile and rename characters
- parse_savegame(args.savefiles[0], keep_uncompressed = args.keep_uncompressed)
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement