diff --git a/gcddump.py b/gcddump.py new file mode 100644 index 0000000..b1a24c0 --- /dev/null +++ b/gcddump.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Dumps the structure of the given GCD file to file. +""" + +from grmn import Gcd +import sys + +if len(sys.argv) < 3: + print("Syntax: {} GCDFILE DUMPFILE (extension .rcp will be added)".format(sys.argv[0])) + sys.exit(1) + +FILE = sys.argv[1] +OUTFILE = sys.argv[2] + +print("Opening {}".format(FILE)) +gcd = Gcd(FILE) +print("Dumping to {}.rcp".format(OUTFILE)) +gcd.dump(OUTFILE) diff --git a/grmn/gcd.py b/grmn/gcd.py index 898d216..3b5ea15 100644 --- a/grmn/gcd.py +++ b/grmn/gcd.py @@ -8,6 +8,7 @@ GCD_SIG = b"G\x41RM\x49Nd\00" DEFAULT_COPYRIGHT = b"Copyright 1996-2017 by G\x61rm\x69n Ltd. or its subsidiaries." DEFAULT_FIRST_PADDING = 21 DEFAULT_ALIGN = 0x1000 # second padding block pads until 0x1000 +MAX_BLOCK_LENGTH = 0xff00 # binary blocks max len # Typical structure: # first 0x1000 Bytes: GCD_SIG > 0x0001 > 0x0002 > 0x0003 > 0x0005 > 0x0001 > 0x0002 @@ -27,6 +28,7 @@ class Gcd: if self.filename is None: return False last_tlv6 = None + last_tlv7 = None with open(self.filename, "rb") as f: sig = f.read(8) if sig != GCD_SIG: @@ -46,6 +48,9 @@ class Gcd: last_tlv6 = tlv elif tlv.type_id == 0x0007: tlv.set_tlv6(last_tlv6) + last_tlv7 = tlv + elif tlv.is_binary: + tlv.set_tlv7(last_tlv7) f.close() def add_tlv(self, new_tlv: TLV): @@ -98,3 +103,48 @@ class Gcd: else: print("☒ ONE OR MORE CHECKSUMS INVALID!") return all_ok + + def write_dump_block(self, f, name): + f.write("\n[BLOCK_{}]\n".format(name)) + + def write_dump_param(self, f, key, value, comment=None): + if comment is not None: + f.write("# {}\n".format(comment)) + f.write("{} = {}\n".format(key, value)) + + def dump(self, output_basename: str): + output_file = "{}.rcp".format(output_basename) + ctr = 0 + last_filename = None + with open(output_file, "wt") as f: + f.write("[GCD_DUMP]\n") + f.write("dump_by = grmn-gcd\n") + f.write("dump_ver = 1\n") + f.write("original_filename = {}\n\n".format(self.filename)) + f.write("# [BLOCK_nn] headers are just for parsing/grouping purposes.\n") + f.write("# Lengths are informational only, they will be calculated upon reassembly.\n") + for tlv in self.struct: + if tlv.is_binary: + outfile = "{}_{:04x}.bin".format(output_basename, tlv.type_id) + if outfile != last_filename: + self.write_dump_block(f, str(ctr)) + self.write_dump_param(f, "from_file", outfile) + for item in tlv.dump(): + self.write_dump_param(f, item[0], item[1], item[2]) + last_filename = outfile + with open(outfile, "wb") as of: + of.write(tlv.value) + else: + with open(outfile, "ab") as of: + of.write(tlv.value) + elif tlv.type_id == 0xffff: + # EOF marker + pass + else: + tlvinfo = tlv.dump() + if len(tlvinfo) > 0: + self.write_dump_block(f, str(ctr)) + for item in tlvinfo: + self.write_dump_param(f, item[0], item[1], item[2]) + ctr += 1 + f.close() diff --git a/grmn/tlv.py b/grmn/tlv.py index f2662ee..84eaa48 100644 --- a/grmn/tlv.py +++ b/grmn/tlv.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from . import devices +from binascii import hexlify from struct import pack, unpack TLV_TYPES = { @@ -22,6 +23,7 @@ TLV_TYPES = { class TLV: def __init__(self, type_id: int, expected_length: int, value=None, offset: int=None): self.type_id = type_id + self.is_binary = False self.offset = offset self.comment = TLV_TYPES.get(type_id, "Type {:04x} / {:d}".format(type_id, type_id)) self.length = expected_length @@ -33,10 +35,19 @@ class TLV: @staticmethod def factory(header: bytes, offset: int = None): (type_id, length) = unpack("