diff --git a/README.md b/README.md index a1fa374..c83fa72 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,29 @@ # pygbx + A Python library to parse Gbx files, used in [TMTrackNN](https://github.com/donadigo/TMTrackNN) and other projects of mine that involve extracting useful data from Gbx files. # Examples + (For more documentation, refer to the docstrings in the source files.) ## Read metadata about a Challenge map: + ```python from pygbx import Gbx, GbxType -g = Gbx('A01-Race.Challenge.Gbx') -challenge = g.get_class_by_id(GbxType.CHALLENGE) -if not challenge: - quit() +with Gbx('A01-Race.Challenge.Gbx') as g: + challenge = g.get_class_by_id(GbxType.CHALLENGE) + if not challenge: + quit() -print(f'Map Name: {challenge.map_name}') -print(f'Map Author: {challenge.map_author}') -print(f'Environment: {challenge.environment}') + print(f'Map Name: {challenge.map_name}') + print(f'Map Author: {challenge.map_author}') + print(f'Environment: {challenge.environment}') ``` ### Output: + ``` Map Name: A01-Race Map Author: Nadeo @@ -27,20 +31,22 @@ Environment: Stadium ``` ## Enumerate blocks present in a Challenge map: + ```python from pygbx import Gbx, GbxType -g = Gbx('A-0.Challenge.Gbx') -challenges = g.get_classes_by_ids([GbxType.CHALLENGE, GbxType.CHALLENGE_OLD]) -if not challenges: - quit() +with Gbx('A-0.Challenge.Gbx') as g: + challenges = g.get_classes_by_ids([GbxType.CHALLENGE, GbxType.CHALLENGE_OLD]) + if not challenges: + quit() -challenge = challenges[0] -for block in challenge.blocks: - print(block) + challenge = challenges[0] + for block in challenge.blocks: + print(block) ``` ### Output: + ``` Name: StadiumRoadMainStartLine Rotation: 0 @@ -61,21 +67,23 @@ Flags: 0b1000000000000 ``` ## Read metadata about a ghost in a Replay: + ```python from pygbx import Gbx, GbxType -g = Gbx('A04_5_77.Replay.Gbx') -ghost = g.get_class_by_id(GbxType.CTN_GHOST) -if not ghost: - quit() +with Gbx('A04_5_77.Replay.Gbx') as g: + ghost = g.get_class_by_id(GbxType.CTN_GHOST) + if not ghost: + quit() -print(f'Race time: {ghost.race_time}') -print(f'Num respawns: {ghost.num_respawns}') -print(f'Game version: {ghost.game_version}') -print(f'Map UID: {ghost.uid}') + print(f'Race time: {ghost.race_time}') + print(f'Num respawns: {ghost.num_respawns}') + print(f'Game version: {ghost.game_version}') + print(f'Map UID: {ghost.uid}') ``` ### Output: + ``` Race time: 5770 Num respawns: 0 diff --git a/pygbx/gbx.py b/pygbx/gbx.py index f9ae431..16e1fc1 100644 --- a/pygbx/gbx.py +++ b/pygbx/gbx.py @@ -79,9 +79,15 @@ def __init__(self, obj): else: self.root_parser = ByteReader(obj) - self.magic = self.root_parser.read(3, '3s') - if self.magic.decode('utf-8') != 'GBX': + magic = self.root_parser.read(3, '3s') + try: + magic = magic.decode('utf-8') + except: + raise GbxLoadError(f'obj is not a valid Gbx data: can not decode unicode') + + if magic != 'GBX': raise GbxLoadError(f'obj is not a valid Gbx data: magic string is incorrect') + self.version = self.root_parser.read(2, 'H') self.classes = {} self.root_classes = {} @@ -125,7 +131,7 @@ def __init__(self, obj): self.root_parser.skip(4) self.root_parser.push_info() - self.positions['data_size'] = self.root_parser.pop_info() + self.positions["data_size"] = self.root_parser.pop_info() data_size = self.root_parser.read_uint32() compressed_data_size = self.root_parser.read_uint32() @@ -135,6 +141,17 @@ def __init__(self, obj): bp = ByteReader(self.data) self._read_node(self.class_id, -1, bp) + def __enter__(self): + return self + + def __exit__(self, exception_type, exception_value, exception_traceback): + self.close() + + """Closes the current file connection.""" + def close(self): + if self.f: + self.f.close() + def __read_sub_folder(self): num_sub_folders = self.root_parser.read_uint32() for folder in range(num_sub_folders):