-
-
Save Rob7045713/2f838ad66237f87c86d5396af573b71c to your computer and use it in GitHub Desktop.
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 argparse | |
import json | |
import os | |
import struct | |
import sys | |
from io import BytesIO | |
HEADER = b'GVAS' | |
HEADER_LEN = 1073 | |
def read(filename, output=sys.stdout): | |
data = None | |
with open(filename, 'rb') as stream: | |
data = stream.read() | |
if data[:4] != HEADER: | |
print("Bad Header") | |
save = deserialize_save(BytesIO(data[HEADER_LEN:])) | |
print(json.dumps(save, indent=2), file=output) | |
def deserialize_save(stream): | |
save = {} | |
save['script'] = read_str(stream) | |
save['properties'] = read_properties(stream) | |
return save | |
def read_properties(stream): | |
try: | |
data = [] | |
while True: | |
next = read_str(stream) | |
if next == 'None': | |
break | |
data.append(read_property(stream, next)) | |
return data | |
except Exception as e: | |
print(e) | |
print(data) | |
def read_str(stream): | |
length = read_int(stream) | |
ret = '' | |
for i in range(length - 1): | |
char = stream.read(1).decode() | |
ret += char | |
if stream.read(1).decode() != '\x00': | |
raise Exception(f"Bad String ({length}): '{ret}'") | |
return ret | |
def read_property(stream, name): | |
#name = read_str(stream) | |
type = read_str(stream) | |
if type not in deserializers: | |
raise Exception(f"No deserializer for {type}") | |
if type == 'StructProperty': | |
length = read_int(stream) | |
read_padding(stream, 4) | |
struct_type = read_str(stream) | |
read_padding(stream, 17) | |
data = stream.read(length) | |
value = read_struct(BytesIO(data), struct_type) | |
elif type == 'ArrayProperty': | |
length = read_int(stream) | |
read_padding(stream, 4) | |
data = stream.read(length + 20) | |
value = deserializers[type](BytesIO(data)) | |
elif type == 'BoolProperty': | |
read_padding(stream, 8) | |
data = stream.read(2) | |
value = deserializers[type](BytesIO(data)) | |
else: | |
length = read_int(stream) | |
stream.read(1) | |
read_padding(stream, 4) | |
data = stream.read(length) | |
value = deserializers[type](BytesIO(data)) | |
return { 'name': name, 'type': type, 'value': value } | |
def read_padding(stream, count): | |
ret = '' | |
for i in range(count): | |
byte = stream.read(1) | |
if byte != b'\x00': | |
print(f"Bad Padding: {byte}") | |
ret += '.' | |
return ret | |
def read_int(stream): | |
return int.from_bytes(stream.read(4), byteorder='little') | |
def read_float(stream): | |
return struct.unpack('f', stream.read(4))[0] | |
def read_bool(stream): | |
return struct.unpack('?', stream.read(1))[0] | |
def read_struct(stream, type): | |
return { 'type': type, 'properties': read_properties(stream) } | |
def read_array(stream): | |
read_str(stream) | |
read_int(stream) | |
read_padding(stream, 1) | |
read_str(stream) | |
read_str(stream) | |
length = read_int(stream) | |
read_padding(stream, 4) | |
type = read_str(stream) | |
read_padding(stream, 17) | |
data_stream = BytesIO(stream.read()) | |
data = [] | |
while data_stream.tell() < length: | |
props = read_properties(data_stream) | |
if props: | |
data.append(props) | |
else: | |
break | |
return { 'type': type, 'data': data } | |
deserializers = {} | |
deserializers['FloatProperty'] = read_float | |
deserializers['IntProperty'] = read_int | |
deserializers['StrProperty'] = read_str | |
deserializers['ObjectProperty'] = read_str | |
deserializers['SoftObjectProperty'] = read_str | |
deserializers['StructProperty'] = read_struct | |
deserializers['ArrayProperty'] = read_array | |
deserializers['BoolProperty'] = read_bool | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="UE4 Save file serializer") | |
parser.add_argument(metavar='mode', dest='mode', choices=['sav_to_json'], help='Conversion to perform') | |
parser.add_argument(metavar='input', dest='input', help='File to read') | |
parser.add_argument('-o', '--output', dest='output', default=None, help='File to write to') | |
args = parser.parse_args() | |
output = sys.stdout | |
if args.output: | |
output = open(args.output, 'w') | |
if args.mode == 'sav_to_json': | |
read(args.input, output=output) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment