Compare commits
5 Commits
serializer
...
master
Author | SHA1 | Date | |
---|---|---|---|
ad7c608df6 | |||
8b7ebbe7e0 | |||
65436df5d0 | |||
088674041a | |||
abbc9bb2f0 |
|
@ -27,7 +27,7 @@ replay.save("myReplay.osr") # Export replay to file
|
|||
# Enums (from helpers)
|
||||
### osuButtons
|
||||
|Name|Value|
|
||||
|-|-|
|
||||
|--|--|
|
||||
|NONE|0|
|
||||
|LEFTMOUSE|1|
|
||||
|RIGHTMOUSE|2|
|
||||
|
@ -41,7 +41,7 @@ replay.save("myReplay.osr") # Export replay to file
|
|||
|
||||
### osuModes
|
||||
|Name|Value|
|
||||
|-|-|
|
||||
|--|--|
|
||||
|STANDARD|0|
|
||||
|TAIKO|1|
|
||||
|CATCH_THE_BEAT|2|
|
||||
|
@ -49,7 +49,7 @@ replay.save("myReplay.osr") # Export replay to file
|
|||
|
||||
### osuMods
|
||||
|Name|Value|
|
||||
|-|-|
|
||||
|--|--|
|
||||
|NOMOD|0|
|
||||
|NOFAIL|1|
|
||||
|EASY|2|
|
||||
|
@ -84,7 +84,7 @@ replay.save("myReplay.osr") # Export replay to file
|
|||
|
||||
### osuRanks
|
||||
|Name|Value|
|
||||
|-|-|
|
||||
|--|--|
|
||||
|SSH|0|
|
||||
|SH|1|
|
||||
|SS|2|
|
||||
|
|
|
@ -3,6 +3,8 @@ import string
|
|||
#import lzma
|
||||
import pylzma # Required cause pythons standard library lzma has missing support
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from .frame import ReplayFrame
|
||||
|
||||
from .helpers import osuModes
|
||||
|
@ -34,7 +36,7 @@ def md5_file(file):
|
|||
hash = _md5()
|
||||
for chunk in iter(lambda: file.read(4096), b""):
|
||||
hash.update(chunk)
|
||||
return hash.hexdigest()
|
||||
return hash.hexdigest().encode()
|
||||
|
||||
def append_and_compress(data):
|
||||
return lzma_compress(data + b"-12345|0|0|1337,")
|
||||
|
@ -45,33 +47,32 @@ def lzma_compress(data):
|
|||
return struct.pack("<I", len(comp)) + comp # For some odd ass reason this doesnt use ULEB
|
||||
|
||||
class Replay:
|
||||
_mode = Serializable(osuModes.STANDARD, TYPE_BYTE)
|
||||
_osu_version = Serializable(20181216, TYPE_INT)
|
||||
_beatmap_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)
|
||||
_player_name = Serializable(b"osu!", TYPE_STRING)
|
||||
_score_hash = Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)
|
||||
structure = OrderedDict([
|
||||
("mode", Serializable(osuModes.STANDARD, TYPE_BYTE)),
|
||||
("osu_version", Serializable(20181216, TYPE_INT)),
|
||||
("beatmap_hash",Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)),
|
||||
("player_name", Serializable(b"osu!", TYPE_STRING)),
|
||||
("score_hash", Serializable(b"d41d8cd98f00b204e9800998ecf8427e", TYPE_STRING)),
|
||||
|
||||
_score_300s = Serializable(0, TYPE_USHORT)
|
||||
_score_100s = Serializable(0, TYPE_USHORT)
|
||||
_score_50s = Serializable(0, TYPE_USHORT)
|
||||
_score_gekis = Serializable(0, TYPE_USHORT)
|
||||
_score_katus = Serializable(0, TYPE_USHORT)
|
||||
_score_miss = Serializable(0, TYPE_USHORT)
|
||||
_score = Serializable(0, TYPE_INT)
|
||||
_combo = Serializable(0, TYPE_USHORT)
|
||||
_perfect = Serializable(True, TYPE_BOOL)
|
||||
("score_300s", Serializable(0, TYPE_USHORT)),
|
||||
("score_100s", Serializable(0, TYPE_USHORT)),
|
||||
("score_50s", Serializable(0, TYPE_USHORT)),
|
||||
("score_gekis", Serializable(0, TYPE_USHORT)),
|
||||
("score_katus", Serializable(0, TYPE_USHORT)),
|
||||
("score_miss", Serializable(0, TYPE_USHORT)),
|
||||
("score", Serializable(0, TYPE_INT)),
|
||||
("combo", Serializable(0, TYPE_USHORT)),
|
||||
("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)
|
||||
_timestamp = Serializable(0, TYPE_ULLONG)
|
||||
("lifebar_graph", Serializable(b"0|1,", TYPE_STRING)),
|
||||
("timestamp", Serializable(0, TYPE_ULLONG)),
|
||||
|
||||
_replay_data = Serializable(b"", TYPE_BYTESTREAM, post = append_and_compress)
|
||||
("replay_data", Serializable(b"", TYPE_BYTESTREAM, post = append_and_compress)),
|
||||
|
||||
_online_score_id= Serializable(0, TYPE_ULLONG)
|
||||
|
||||
# Marker (Only used as attribute section marker for auto-serializing)
|
||||
_end_of_attributes = None
|
||||
("online_score_id", Serializable(0, TYPE_ULLONG))
|
||||
])
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
allowed_kwargs = {
|
||||
|
@ -92,12 +93,12 @@ class Replay:
|
|||
def set_mode(self, mode):
|
||||
if mode > 3 or mode < 0:
|
||||
raise Exception("Invalid mode")
|
||||
self._mode.value = mode
|
||||
self.structure["mode"].value = mode
|
||||
|
||||
def set_osu_version(self, osu_version):
|
||||
if type(osu_version) is not int:
|
||||
raise Exception("osu! version must be an int")
|
||||
self._osu_version.value = osu_version
|
||||
self.structure["osu_version"].value = osu_version
|
||||
|
||||
def set_beatmap_hash(self, md5_hash):
|
||||
if type(md5_hash) is bytes:
|
||||
|
@ -105,49 +106,49 @@ class Replay:
|
|||
|
||||
if len(md5_hash) != 32 or len([x for x in md5_hash if x in string.hexdigits]) != 32:
|
||||
raise Exception("Invalid beatmap hash")
|
||||
self._beatmap_hash.value = md5_hash.encode()
|
||||
self.structure["beatmap_hash"].value = md5_hash.encode()
|
||||
|
||||
def set_beatmap_file(self, filepath):
|
||||
if not isfile(filepath):
|
||||
raise Exception("Beatmap file not found")
|
||||
with open(filepath, "rb") as f:
|
||||
self._beatmap_hash.value = md5_file(f)
|
||||
self.structure["beatmap_hash"].value = md5_file(f)
|
||||
|
||||
def set_player_name(self, player_name):
|
||||
if type(player_name) is str:
|
||||
player_name = player_name.encode()
|
||||
self._player_name.value = player_name
|
||||
self.structure["player_name"].value = player_name
|
||||
|
||||
def set_score_hash(self, md5_hash):
|
||||
if type(md5_hash) is bytes:
|
||||
md5_hash = md5_hash.decode()
|
||||
if len(md5_hash) != 32 or len([x for x in md5_hash if x in string.hexdigits]) != 32:
|
||||
raise Exception("Invalid replay hash")
|
||||
self._score_hash.value = md5_hash.encode()
|
||||
self.structure["score_hash"].value = md5_hash.encode()
|
||||
|
||||
def set_score(self, score = None, combo = None, s300 = None, s100 = None, s50 = None, sgekis = None, skatus = None, miss = None):
|
||||
if type(score) is int: self._score.value = score
|
||||
if type(combo) is int: self._combo.value = combo
|
||||
if type(s300) is int: self._score_300s.value = s300
|
||||
if type(s100) is int: self._score_100s.value = s100
|
||||
if type(s50) is int: self._score_50s.value = s50
|
||||
if type(sgekis) is int: self._score_gekis.value = sgekis
|
||||
if type(skatus) is int: self._score_katus.value = skatus
|
||||
if type(miss) is int: self._score_miss.value = miss
|
||||
if type(score) is int: self.structure["score"].value = score
|
||||
if type(combo) is int: self.structure["combo"].value = combo
|
||||
if type(s300) is int: self.structure["score_300s"].value = s300
|
||||
if type(s100) is int: self.structure["score_100s"].value = s100
|
||||
if type(s50) is int: self.structure["score_50s"].value = s50
|
||||
if type(sgekis) is int: self.structure["score_gekis"].value = sgekis
|
||||
if type(skatus) is int: self.structure["score_katus"].value = skatus
|
||||
if type(miss) is int: self.structure["score_miss"].value = miss
|
||||
|
||||
def set_mods(self, mods):
|
||||
if mods < 0 or mods > (1 << 30) - 1:
|
||||
raise Exception("Mods are out of range")
|
||||
self._mods.value = mods
|
||||
self.structure["mods"].value = mods
|
||||
|
||||
def set_lifebar_graph(self, graph):
|
||||
t_graph = type(graph)
|
||||
if t_graph is list:
|
||||
self._lifebar_graph.value = b"".join(graph)
|
||||
self.structure["lifebar_graph"].value = b"".join(graph)
|
||||
elif t_graph is str:
|
||||
self._lifebar_graph.value = graph.encode()
|
||||
self.structure["lifebar_graph"].value = graph.encode()
|
||||
elif t_graph is bytes:
|
||||
self._lifebar_graph.value = graph
|
||||
self.structure["lifebar_graph"].value = graph
|
||||
else:
|
||||
raise Exception("Invalid lifebar data")
|
||||
|
||||
|
@ -156,23 +157,23 @@ class Replay:
|
|||
timestamp += 62135599380000 # offset
|
||||
timestamp *= 10 ** 4
|
||||
|
||||
self._timestamp.value = timestamp
|
||||
self.structure["timestamp"].value = timestamp
|
||||
|
||||
def set_online_score_id(self, online_score_id):
|
||||
self._online_score_id.value = online_score_id
|
||||
self.structure["online_score_id"].value = online_score_id
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Update variables ---------------------------------------------------------
|
||||
|
||||
def update_perfect(self):
|
||||
m = [self._score_100s.value, self._score_50s.value, self._score_katus.value, self._score_miss.value]
|
||||
self._perfect.value = sum(m) == 0
|
||||
m = [self.structure["score_100s"].value, self.structure["score_50s"].value, self.structure["score_katus"].value, self.structure["score_miss"].value]
|
||||
self.structure["perfect"].value = sum(m) == 0
|
||||
|
||||
def update_score_hash(self):
|
||||
self.set_score_hash(
|
||||
md5_str(b"%d%b%b%b%d%b" % (
|
||||
self._combo.value, b"osu", self._player_name.value,
|
||||
self._beatmap_hash.value, self._score.value, self.get_rank()
|
||||
self.structure["combo"].value, b"osu", self.structure["player_name"].value,
|
||||
self.structure["beatmap_hash"].value, self.structure["score"].value, self.get_rank()
|
||||
))
|
||||
)
|
||||
|
||||
|
@ -184,10 +185,10 @@ class Replay:
|
|||
# Get / Helpers ------------------------------------------------------------
|
||||
|
||||
def get_hits(self):
|
||||
return sum([self._score_300s.value, self._score_100s.value, self._score_50s.value])
|
||||
return sum([self.structure["score_300s"].value, self.structure["score_100s"].value, self.structure["score_50s"].value])
|
||||
|
||||
def get_possible_hits(self):
|
||||
return self.get_hits() + self._score_miss.value
|
||||
return self.get_hits() + self.structure["score_miss"].value
|
||||
|
||||
def get_rank(self):
|
||||
hits = self.get_possible_hits()
|
||||
|
@ -195,17 +196,17 @@ class Replay:
|
|||
print("Can not calculate rank without any score data (Defaulting to Fail)")
|
||||
return osuRanks.F
|
||||
|
||||
r300 = self._score_300s.value / hits
|
||||
r50 = self._score_50s.value / hits
|
||||
h = osuMods.any_enabled(self._mods.value, osuMods.HIDDEN | osuMods.FLASHLIGHT)
|
||||
r300 = self.structure["score_300s"].value / hits
|
||||
r50 = self.structure["score_50s"].value / hits
|
||||
h = osuMods.any_enabled(self.structure["mods"].value, osuMods.HIDDEN | osuMods.FLASHLIGHT)
|
||||
|
||||
if r300 == 1:
|
||||
return osuRanks.SSH if h else osuRanks.SS
|
||||
if r300 > .9 and r50 <= .01 and self._score_miss.value == 0:
|
||||
if r300 > .9 and r50 <= .01 and self.structure["score_miss"].value == 0:
|
||||
return osuRanks.SH if h else osuRanks.S
|
||||
if r300 > .8 and self._score_miss.value == 0 or r300 > .9:
|
||||
if r300 > .8 and self.structure["score_miss"].value == 0 or r300 > .9:
|
||||
return osuRanks.A
|
||||
if r300 > .7 and self._score_miss.value == 0 or r300 > .8:
|
||||
if r300 > .7 and self.structure["score_miss"].value == 0 or r300 > .8:
|
||||
return osuRanks.B
|
||||
if r300 > .6:
|
||||
return osuRanks.C
|
||||
|
@ -221,11 +222,11 @@ class Replay:
|
|||
self.write(_frame)
|
||||
return
|
||||
if t_frame is ReplayFrame:
|
||||
self._replay_data.value += bytes(frame)
|
||||
self.structure["replay_data"].value += bytes(frame)
|
||||
elif t_frame is bytes:
|
||||
self._replay_data.value += frame
|
||||
self.structure["replay_data"].value += frame
|
||||
elif t_frame is str:
|
||||
self._replay_data.value += frame.encode()
|
||||
self.structure["replay_data"].value += frame.encode()
|
||||
else:
|
||||
raise Exception("Invalid frame data")
|
||||
|
||||
|
@ -237,12 +238,7 @@ class Replay:
|
|||
|
||||
serializer = Serializer()
|
||||
|
||||
attribs = [ a for a in self.__dir__() if not a.startswith("__") ]
|
||||
for attrib in attribs:
|
||||
if attrib == "_end_of_attributes":
|
||||
break
|
||||
|
||||
serializer.add( self.__getattribute__(attrib) ) # Add value to serializer
|
||||
[ serializer.add( struc ) for struc in self.structure.values() ] # Add values to serializer
|
||||
|
||||
with open(filename, "wb") as f:
|
||||
f.write( serializer.flush() )
|
||||
|
|
Loading…
Reference in New Issue
Block a user