diff --git a/osuRepy/replay.py b/osuRepy/replay.py index f57d835..d0b6760 100644 --- a/osuRepy/replay.py +++ b/osuRepy/replay.py @@ -7,11 +7,26 @@ from .helpers import osuMods from .helpers import osuRanks from .helpers import typeSerializer +from io import BufferedReader from os.path import isfile from hashlib import md5 as _md5 -def md5(str): - return _md5(str.encode("ascii")).hexdigest().encode() +def md5_str(data): + if type(data) is str: + data = data.encode("ascii") + return _md5(data).hexdigest().encode() + +def md5_file(file): + if type(file) is not BufferedReader: + raise Exception("File is not a BufferedReader") + + if file.mode != "rb": + raise Exception("File is not in ReadBytes mode") + + hash = _md5() + for chunk in iter(lambda: file.read(4096), b""): + hash.update(chunk) + return hash.hexdigest() class Replay: mode = osuModes.STANDARD # Byte @@ -31,7 +46,7 @@ class Replay: perfect = True # Byte mods = osuMods.NOMOD # Int - + lifebar_graph = b"0|1," # Byte[] timestamp = 0 # Long @@ -50,58 +65,64 @@ class Replay: for k, v in kwargs.items(): if k in allowed_kwargs.keys(): allowed_kwargs[k](v) # Run callback func - + # Set validators ----------------------------------------------------------- def set_mode(self, mode_id): if mode_id > 3 or mode_id < 0: raise Exception("Invalid mode") self.mode_id = mode_id - + 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 = osu_version - + def set_beatmap_hash(self, md5_hash): 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 = md5_hash - + + def set_beatmap(self, filepath): + if not isfile(filepath): + raise Exception("Beatmap file not found") + with open(filepath, "rb") as f: + self.beatmap_hash = md5_file(f) + def set_player_name(self, player_name): self.player_name = 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 = md5_hash.encode() - + def set_mods(self, mods): if mods < 0 or mods > (1 << 30) - 1: raise Exception("Mods are out of range") self.mods = mods - + # -------------------------------------------------------------------------- # Update variables --------------------------------------------------------- def update_perfect(self): m = [self.score_100s, self.score_50s, self.score_katus, self.score_miss] self.perfect = sum(m) == 0 - + def update_score_hash(self): self.set_score_hash( - md5("%d%s%s%s%d%d" % ( + md5_str("%d%s%s%s%d%d" % ( self.combo, "osu", self.player_name, self.beatmap_hash, self.score, self.get_rank() )) ) - + def update(self): self.update_perfect() self.update_score_hash() - + # -------------------------------------------------------------------------- # Get / Helpers ------------------------------------------------------------ @@ -131,7 +152,7 @@ class Replay: if r300 > .6: return osuRanks.C return osuRanks.D - + # -------------------------------------------------------------------------- # Write replay data -------------------------------------------------------- @@ -140,9 +161,9 @@ class Replay: for _frame in frame: # (frames) self.write(_frame) return - + self.replay_data += bytes(frame) - + # -------------------------------------------------------------------------- # IO replay data -----------------------------------------------------------