# -*- coding: utf-8 -*- # Thanks to Herbert Oppmann (herby) for all your work! from .chksum import ChkSum from .rgnbin import RgnBin from struct import unpack import configparser RGN_SIG = b"KpGr" # RGN structure might be: RGN > BIN or RGN > RGN > BIN # RGN = outside hull # BIN = firmware + hwid + checksum class ParseException(Exception): pass class Rgn: def __init__(self, filename: str=None): self.filename = filename self.struct = [] if filename is not None: self.load() def load(self): if self.filename is None: return False last_tlv6 = None last_tlv7 = None with open(self.filename, "rb") as f: sig = f.read(4) if sig != RGN_SIG: raise ParseException("Signature mismatch ({}, should be {})!".format(repr(sig), repr(RGN_SIG))) self.version = unpack("<H", f.read(2))[0] print("Version: {}".format(self.version)) while True: cur_offset = f.tell() header = f.read(5) if len(header) == 0: print("End of file reached.") break (length, type_id) = unpack("<Lc", header) print("Found record type: {} with {} Bytes length.".format(type_id, length)) rec = RgnRecord.factory(type_id, length, offset=cur_offset) payload = f.read(length) rec.set_payload(payload) self.add_rec(rec) f.close() def add_rec(self, new_rec): self.struct.append(new_rec) def print_struct(self): """ Prints the structure of the parsed GCD file """ pass def print_struct_full(self): """ Prints the structure of the parsed GCD file """ pass def validate(self, print_stats: bool=False): """ Checks and verifies all checksums in the GCD. """ # RGN has no checksum, but embedded BIN has def dump_to_files(self, output_basename: str): pass @staticmethod def from_recipe(recipe_file: str): pass def save(self, filename): pass class RgnRecord(): def __init__(self, type_id, expected_length, payload=None, offset=None): self.type_id = type_id self.length = expected_length self.is_binary = False self.payload = payload self.offset = offset self.is_parsed = False def set_payload(self, new_payload): self.payload = new_payload @staticmethod def factory(type_id, length: int = None, offset: int = None): if type_id == b"D": new_rec = RgnRecordD(type_id, length) elif type_id == b"A": new_rec = RgnRecordA(type_id, length) elif type_id == b"R": new_rec = RgnRecordR(type_id, length) new_rec.is_binary = True else: raise ParseException("Unknown record type: {} at offset 0x{:0x}".format(type_id, offset)) new_rec.offset = offset return new_rec class RgnRecordD(RgnRecord): """ Data record (2 Bytes) - ushort - Version """ def parse(self): self.is_parsed = True class RgnRecordA(RgnRecord): """ Application record - ushort - Application version - string - Builder - string - BuildDate - string - BuildTime """ def parse(self): self.is_parsed = True class RgnRecordR(RgnRecord): """ Region record - ushort - Region ID - uint - Delay in ms - uint - Region size (is record length - 10) - byte[Region size] - Contents """ def parse(self): self.is_parsed = True