Rewrite start
This commit is contained in:
parent
15c80aae66
commit
12ea8c4d1b
|
@ -35,6 +35,6 @@ class ReplayFrame:
|
||||||
self._float_convert(self.x),
|
self._float_convert(self.x),
|
||||||
self._float_convert(self.y),
|
self._float_convert(self.y),
|
||||||
self.buttons)
|
self.buttons)
|
||||||
|
|
||||||
def __bytes__(self):
|
def __bytes__(self):
|
||||||
return str(self).encode()
|
return str(self).encode()
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
TYPE_CHAR = b"b"
|
TYPE_CHAR = b"b", 1
|
||||||
TYPE_BYTE = b"B"
|
TYPE_BYTE = b"B", 1
|
||||||
TYPE_BOOL = b"?"
|
TYPE_BOOL = b"?", 1
|
||||||
TYPE_SHORT = b"h"
|
TYPE_SHORT = b"h", 2
|
||||||
TYPE_USHORT = b"H"
|
TYPE_USHORT = b"H", 2
|
||||||
TYPE_INT = b"i"
|
TYPE_INT = b"i", 4
|
||||||
TYPE_UINT = b"I"
|
TYPE_UINT = b"I", 4
|
||||||
TYPE_LONG = b"l"
|
TYPE_LONG = b"l", 4
|
||||||
TYPE_ULONG = b"L"
|
TYPE_ULONG = b"L", 4
|
||||||
TYPE_LLONG = b"q"
|
TYPE_LLONG = b"q", 8
|
||||||
TYPE_ULLONG = b"Q"
|
TYPE_ULLONG = b"Q", 8
|
||||||
TYPE_FLOAT = b"f"
|
TYPE_FLOAT = b"f", 4
|
||||||
TYPE_DOUBLE = b"d"
|
TYPE_DOUBLE = b"d", 8
|
||||||
TYPE_STRING = b"B%ds"
|
TYPE_STRING = b"s", 1
|
||||||
TYPE_BYTESTREAM = b"%ds"
|
|
||||||
|
TYPE_ULEB = 1 # Just so we have the enum type~ish
|
||||||
|
|
||||||
BYTEORDER_NATIVE = b"@"
|
BYTEORDER_NATIVE = b"@"
|
||||||
BYTEORDER_LITTLE = b"<"
|
BYTEORDER_LITTLE = b"<"
|
||||||
|
@ -22,7 +23,6 @@ BYTEORDER_BIG = b">"
|
||||||
|
|
||||||
def encode_uleb(val):
|
def encode_uleb(val):
|
||||||
data = b""
|
data = b""
|
||||||
pos = 0
|
|
||||||
while val != 0:
|
while val != 0:
|
||||||
b = val & 0x7f
|
b = val & 0x7f
|
||||||
val >>= 7
|
val >>= 7
|
||||||
|
@ -73,10 +73,18 @@ def guess_type(value):
|
||||||
|
|
||||||
raise Exception("Unable to guess type")
|
raise Exception("Unable to guess type")
|
||||||
|
|
||||||
class Serializable:
|
def read(value_type, stream):
|
||||||
post_serialized = None
|
#if value_type == TYPE_STRING:
|
||||||
|
# read()
|
||||||
|
|
||||||
def __init__(self, value, type = None, post = None):
|
struct.unpack(value_type[0], stream[:value_type[1]])
|
||||||
|
|
||||||
|
class Serializable:
|
||||||
|
length = None
|
||||||
|
prefix = b""
|
||||||
|
pre_serialized = None
|
||||||
|
|
||||||
|
def __init__(self, value, type = None, length = None, prefix = b"", pre = None):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
if type is not None:
|
if type is not None:
|
||||||
|
@ -84,29 +92,37 @@ class Serializable:
|
||||||
else:
|
else:
|
||||||
self.type = guess_type(type)
|
self.type = guess_type(type)
|
||||||
|
|
||||||
self.post_serialized = post
|
self.length = length
|
||||||
|
self.prefix = prefix
|
||||||
|
self.pre_serialized = pre
|
||||||
|
|
||||||
def get_struct_fmt(self):
|
def get_struct_fmt(self):
|
||||||
if self.type in [TYPE_STRING, TYPE_BYTESTREAM]:
|
if self.length is not None:
|
||||||
return self.type % len(self.get_value()[-1])
|
return b"%d%b" % (self.length.value, self.type[0])
|
||||||
return self.type
|
return self.type[0]
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
def _get_value():
|
val = self.value
|
||||||
if self.type == TYPE_STRING:
|
if self.pre_serialized is not None:
|
||||||
return [0x0b, encode_uleb(len(self.value)) + self.value]
|
val[-1] = self.pre_serialized(val[-1])
|
||||||
return [self.value]
|
|
||||||
|
|
||||||
val = _get_value()
|
|
||||||
if self.post_serialized is not None:
|
|
||||||
val[-1] = self.post_serialized(val[-1])
|
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def pack(self, byte_order = BYTEORDER_LITTLE):
|
def pack(self, byte_order = BYTEORDER_LITTLE):
|
||||||
|
if self.type == TYPE_ULEB:
|
||||||
|
return encode_uleb(self.value)
|
||||||
struct_fmt = byte_order + self.get_struct_fmt()
|
struct_fmt = byte_order + self.get_struct_fmt()
|
||||||
#print(struct_fmt, self.get_value())
|
packed = struct.pack(struct_fmt, self.get_value())
|
||||||
return struct.pack(struct_fmt, *self.get_value())
|
if self.prefix is not None:
|
||||||
|
packed = self.prefix + packed
|
||||||
|
return packed
|
||||||
|
|
||||||
|
def unpack(self, length = -1, byte_order = BYTEORDER_LITTLE):
|
||||||
|
if self.type == TYPE_ULEB:
|
||||||
|
self.value = decode_uleb(2)
|
||||||
|
return
|
||||||
|
if self.type == TYPE_STRING:
|
||||||
|
length =
|
||||||
|
struct_fmt = byte_order + self.get_struct_fmt(length)
|
||||||
|
|
||||||
class Serializer:
|
class Serializer:
|
||||||
def __init__(self, byte_order = BYTEORDER_LITTLE):
|
def __init__(self, byte_order = BYTEORDER_LITTLE):
|
||||||
|
@ -124,3 +140,21 @@ class Serializer:
|
||||||
|
|
||||||
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
|
||||||
|
"""
|
|
@ -45,30 +45,35 @@ def lzma_compress(data):
|
||||||
return struct.pack("<I", len(comp)) + comp # For some odd ass reason this doesnt use ULEB
|
return struct.pack("<I", len(comp)) + comp # For some odd ass reason this doesnt use ULEB
|
||||||
|
|
||||||
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)
|
||||||
_beatmap_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)
|
_beatmap_hash_length = Serializable(32, TYPE_ULEB)
|
||||||
_player_name = Serializable(b"osu!", TYPE_STRING)
|
_beatmap_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING, length = _beatmap_hash_length, prefix = b"\x0b")
|
||||||
_score_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)
|
_player_name_length = Serializable(4, TYPE_ULEB)
|
||||||
|
_player_name = Serializable(b"osu!", TYPE_STRING, length = _player_name_length, prefix = b"\x0b")
|
||||||
|
_score_hash_length = Serializable(32, TYPE_ULEB)
|
||||||
|
_score_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)
|
||||||
|
|
||||||
_score_300s = Serializable(0, TYPE_USHORT)
|
_score_300s = Serializable(0, TYPE_USHORT)
|
||||||
_score_100s = Serializable(0, TYPE_USHORT)
|
_score_100s = Serializable(0, TYPE_USHORT)
|
||||||
_score_50s = Serializable(0, TYPE_USHORT)
|
_score_50s = Serializable(0, TYPE_USHORT)
|
||||||
_score_gekis = Serializable(0, TYPE_USHORT)
|
_score_gekis = Serializable(0, TYPE_USHORT)
|
||||||
_score_katus = Serializable(0, TYPE_USHORT)
|
_score_katus = Serializable(0, TYPE_USHORT)
|
||||||
_score_miss = Serializable(0, TYPE_USHORT)
|
_score_miss = Serializable(0, TYPE_USHORT)
|
||||||
_score = Serializable(0, TYPE_INT)
|
_score = Serializable(0, TYPE_INT)
|
||||||
_combo = Serializable(0, TYPE_USHORT)
|
_combo = Serializable(0, TYPE_USHORT)
|
||||||
_perfect = Serializable(True, TYPE_BOOL)
|
_perfect = Serializable(True, TYPE_BOOL)
|
||||||
|
|
||||||
_mods = Serializable(osuMods.NOMOD, TYPE_INT)
|
_mods = Serializable(osuMods.NOMOD, TYPE_INT)
|
||||||
|
|
||||||
_lifebar_graph = Serializable(b"0|1,", TYPE_STRING)
|
_lifebar_graph_length = Serializable(4, TYPE_ULEB)
|
||||||
_timestamp = Serializable(0, TYPE_ULLONG)
|
_lifebar_graph = Serializable(b"0|1,", TYPE_STRING, length = _lifebar_graph_length, prefix = b"\x0b")
|
||||||
|
_timestamp = Serializable(0, TYPE_ULLONG)
|
||||||
|
|
||||||
_replay_data = Serializable(b"", TYPE_BYTESTREAM, post = append_and_compress)
|
#_replay_data_length = Serializable(0, TYPE_UINT)
|
||||||
|
_replay_data = Serializable(b"", TYPE_STRING, pre = append_and_compress)
|
||||||
|
|
||||||
_online_score_id= Serializable(0, TYPE_ULLONG)
|
_online_score_id = Serializable(0, TYPE_ULLONG)
|
||||||
|
|
||||||
# Marker (Only used as attribute section marker for auto-serializing)
|
# Marker (Only used as attribute section marker for auto-serializing)
|
||||||
_end_of_attributes = None
|
_end_of_attributes = None
|
||||||
|
@ -176,7 +181,16 @@ class Replay:
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_lengths(self):
|
||||||
|
self._beatmap_hash_length.value = len(self._beatmap_hash.value)
|
||||||
|
self._player_name_length.value = len(self._player_name.value)
|
||||||
|
self._score_hash_length.value = len(self._score_hash.value)
|
||||||
|
self._lifebar_graph_length.value = len(self._lifebar_graph.value)
|
||||||
|
|
||||||
|
#self._replay_data_length.value = len(self._replay_data.get_value())
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
self.update_lengths()
|
||||||
self.update_perfect()
|
self.update_perfect()
|
||||||
self.update_score_hash()
|
self.update_score_hash()
|
||||||
|
|
||||||
|
@ -246,3 +260,9 @@ class Replay:
|
||||||
|
|
||||||
with open(filename, "wb") as f:
|
with open(filename, "wb") as f:
|
||||||
f.write( serializer.flush() )
|
f.write( serializer.flush() )
|
||||||
|
|
||||||
|
def load(self, filename):
|
||||||
|
attribs = [ a for a in self.__dir__() if not a.startswith("__") ]
|
||||||
|
for attrib in attribs:
|
||||||
|
if attrib == "_end_of_attributes":
|
||||||
|
break
|
15
test.py
15
test.py
|
@ -3,17 +3,6 @@ from osuRepy.replay import Replay # Replay instance
|
||||||
|
|
||||||
from osuRepy.helpers import osuButtons, osuMods # Enum helpers
|
from osuRepy.helpers import osuButtons, osuMods # Enum helpers
|
||||||
|
|
||||||
replay = Replay(beatmap_hash = "9e66c0a0eadced7d07f06b3968a74cc0") # Create replay instance
|
replay = Replay() # Create replay instance
|
||||||
|
|
||||||
replay.write( ReplayFrame(2, 256, 192, osuButtons.M1) ) # Add replay frame (time, x, y, buttons)
|
replay.load("replay.osr")
|
||||||
|
|
||||||
replay.set_score(score = 29284624,
|
|
||||||
s300 = 416,
|
|
||||||
s50 = 2,
|
|
||||||
miss = 1,
|
|
||||||
combo = 358) # Set score info
|
|
||||||
|
|
||||||
replay.set_mods(osuMods.HIDDEN | osuMods.DOUBLETIME) # Enable HDDT mods
|
|
||||||
replay.set_timestamp(636880715111611126, False)
|
|
||||||
|
|
||||||
replay.save("myReplay.osr") # Export replay to file
|
|
Loading…
Reference in New Issue
Block a user