Deserialize (load)

This commit is contained in:
Emily 2019-03-19 17:25:05 +01:00
parent ba738e395d
commit 1a0c63872c
3 changed files with 65 additions and 31 deletions

View File

@ -96,14 +96,13 @@ class Serializable:
self.prefix = prefix self.prefix = prefix
self.filter = filter self.filter = filter
def get_struct_fmt(self, length = 1): def get_struct_fmt(self, length = 1, uleb_length_override = -1):
struct_fmt = self.type[0] struct_fmt = self.type[0]
if length > 1: if length > 1:
struct_fmt = b"%d%b" % (length, self.type[0]) struct_fmt = b"%d%b" % (length, self.type[0])
if self.length is not None: if self.length is not None:
if self.length == TYPE_ULEB: if self.length == TYPE_ULEB:
val = self.get_value() struct_fmt = b"%d%b%b" % (len(self.get_value()[0]) if uleb_length_override == -1 else uleb_length_override, self.length[0], struct_fmt)
struct_fmt = b"%d%b%b" % (len(val[0]), self.length[0], struct_fmt)
else: else:
struct_fmt = b"%b%b" % (self.length[0], struct_fmt) struct_fmt = b"%b%b" % (self.length[0], struct_fmt)
return struct_fmt return struct_fmt
@ -111,7 +110,7 @@ class Serializable:
def get_value(self): def get_value(self):
val = [self.value] val = [self.value]
if self.filter is not None: if self.filter is not None:
val[-1] = self.filter(val[-1]) val[-1] = self.filter(val[-1], 0)
if self.length is not None: if self.length is not None:
if self.length == TYPE_ULEB: if self.length == TYPE_ULEB:
val.insert(0, encode_uleb(len(val[-1]))) val.insert(0, encode_uleb(len(val[-1])))
@ -129,10 +128,40 @@ class Serializable:
packed = self.prefix + packed packed = self.prefix + packed
return packed return packed
def unpack(self, length = -1, byte_order = BYTEORDER_LITTLE): def unpack(self, stream, byte_order = BYTEORDER_LITTLE):
length = 1
read_pos = 0
extra_read = 0
remove_struct_fmt = []
if self.type == TYPE_STRING: if self.type == TYPE_STRING:
length = len(self.value) if self.length == TYPE_ULEB:
struct_fmt = byte_order + self.get_struct_fmt(length) extra_read = 1
read_pos += 1 # Skip 0x0b
length, _read_pos = decode_uleb(stream[read_pos:])
read_pos += _read_pos - 1
else:
l = Serializable(0, self.length)
read_pos = l.unpack(stream)
length = l.get_value()[-1]
remove_struct_fmt.append(0)
struct_fmt = byte_order + self.get_struct_fmt(length, read_pos)
length += extra_read
if len(remove_struct_fmt):
for rem in remove_struct_fmt:
struct_fmt = struct_fmt[:rem + 1] + struct_fmt[rem + 2:]
data = struct.unpack(struct_fmt, stream[read_pos:read_pos + self.type[1] * length])
if self.filter is not None:
data = list(data)
data[-1] = self.filter(data[-1], 1)
self.value = data[-1]
return read_pos + self.type[1] * length
class Serializer: class Serializer:
def __init__(self, byte_order = BYTEORDER_LITTLE): def __init__(self, byte_order = BYTEORDER_LITTLE):
@ -145,26 +174,15 @@ class Serializer:
else: else:
self.stack.append(serializable) self.stack.append(serializable)
def serialize(self):
return b"".join(s.pack() for s in self.stack)
def deserialize(self, stream):
for s in self.stack:
stream = stream[s.unpack(stream):]
def flush(self): def flush(self):
return b"".join(s.pack() for s in self.stack) return b"".join(s.pack() for s in self.stack)
def __bytes__(self): def __bytes__(self):
return self.flush() return self.flush()
class Deserializer:
def __init__(self, byte_order = BYTEORDER_LITTLE):
self.stack = []
self.byte_order = byte_order
def add(self, serializable):
if type(serializable) is list:
self.stack += serializable
else:
self.stack.append(serializable)
"""
def deserialize_data(self, stream):
pos = 0
for s in self.stack:
struct.
s.value
"""

View File

@ -1,6 +1,6 @@
import struct import struct
import string import string
#import lzma import lzma
import pylzma # Required cause pythons standard library lzma has missing support import pylzma # Required cause pythons standard library lzma has missing support
from .frame import ReplayFrame from .frame import ReplayFrame
@ -34,14 +34,21 @@ def md5_file(file):
hash.update(chunk) hash.update(chunk)
return hash.hexdigest() return hash.hexdigest()
def append_and_compress(data): def append_and_compress(data, mode):
if mode == 0:
return lzma_compress(data + b"-12345|0|0|1337,") return lzma_compress(data + b"-12345|0|0|1337,")
else:
return lzma_decompress(data)[:-len(b"-12345|0|0|1337,")]
def lzma_compress(data): def lzma_compress(data):
comp = pylzma.compress(data, dictionary = 21, fastBytes = 255, eos = False) comp = pylzma.compress(data, dictionary = 21, fastBytes = 255, eos = False)
comp = comp[:5] + struct.pack(b"<Q", len(data)) + comp[5:] # Append uncompressed length comp = comp[:5] + struct.pack(b"<Q", len(data)) + comp[5:] # Append uncompressed length
return comp return comp
def lzma_decompress(data):
comp = lzma.decompress(data)
return comp
class Replay: class Replay:
_mode = Serializable(osuModes.STANDARD, TYPE_BYTE) _mode = Serializable(osuModes.STANDARD, TYPE_BYTE)
_osu_version = Serializable(20181216, TYPE_INT) _osu_version = Serializable(20181216, TYPE_INT)
@ -248,10 +255,17 @@ class Replay:
serializer.add( self.__getattribute__(attrib) ) # Add value to serializer serializer.add( self.__getattribute__(attrib) ) # Add value to serializer
with open(filename, "wb") as f: with open(filename, "wb") as f:
f.write( serializer.flush() ) f.write( serializer.serialize() )
def load(self, filename): def load(self, filename):
serializer = Serializer()
attribs = [ a for a in self.__dir__() if not a.startswith("__") ] attribs = [ a for a in self.__dir__() if not a.startswith("__") ]
for attrib in attribs: for attrib in attribs:
if attrib == "_end_of_attributes": if attrib == "_end_of_attributes":
break break
serializer.add( self.__getattribute__(attrib) ) # Add value to serializer
with open(filename, "rb") as f:
serializer.deserialize( f.read() )

View File

@ -6,3 +6,5 @@ from osuRepy.helpers import osuButtons, osuMods # Enum helpers
replay = Replay() # Create replay instance replay = Replay() # Create replay instance
replay.load("replay.osr") replay.load("replay.osr")
replay.save("test.osr")