Add reading and decoding RGN structure.

This commit is contained in:
Markus Birth 2019-10-12 17:29:45 +02:00
parent 98263e6af2
commit 3545d2b12e
Signed by: mbirth
GPG Key ID: A9928D7A098C3A9A
2 changed files with 104 additions and 9 deletions

View File

@ -1,17 +1,30 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Thanks to Herbert Oppmann (herby) for all your work! # Thanks to Herbert Oppmann (herby) for all your work!
from .ansi import RESET, RED
from .chksum import ChkSum from .chksum import ChkSum
from .rgnbin import RgnBin from .rgnbin import RgnBin
from struct import unpack from struct import unpack
import configparser import configparser
RGN_SIG = b"KpGr" RGN_SIG = b"KpGr"
DEFAULT_BUILDER = "SQA"
# RGN structure might be: RGN > BIN or RGN > RGN > BIN # RGN structure might be: RGN > BIN or RGN > RGN > BIN
# RGN = outside hull # RGN = outside hull
# BIN = firmware + hwid + checksum # BIN = firmware + hwid + checksum
REGION_TYPES = {
0x000a: "dskimg.bin",
0x000c: "boot.bin",
0x000e: "fw_all.bin",
0x0010: "logo.bin",
0x004e: "ZIP file",
0x0055: "fw_all2.bin",
0x00f5: "GCD firmware update file",
0x00ff: "pk_text.zip",
}
class ParseException(Exception): class ParseException(Exception):
pass pass
@ -32,15 +45,14 @@ class Rgn:
if sig != RGN_SIG: if sig != RGN_SIG:
raise ParseException("Signature mismatch ({}, should be {})!".format(repr(sig), repr(RGN_SIG))) raise ParseException("Signature mismatch ({}, should be {})!".format(repr(sig), repr(RGN_SIG)))
self.version = unpack("<H", f.read(2))[0] self.version = unpack("<H", f.read(2))[0]
print("Version: {}".format(self.version))
while True: while True:
cur_offset = f.tell() cur_offset = f.tell()
header = f.read(5) header = f.read(5)
if len(header) == 0: if len(header) == 0:
print("End of file reached.") #print("End of file reached.")
break break
(length, type_id) = unpack("<Lc", header) (length, type_id) = unpack("<Lc", header)
print("Found record type: {} with {} Bytes length.".format(type_id, length)) #print("Found record type: {} with {} Bytes length.".format(type_id, length))
rec = RgnRecord.factory(type_id, length, offset=cur_offset) rec = RgnRecord.factory(type_id, length, offset=cur_offset)
payload = f.read(length) payload = f.read(length)
rec.set_payload(payload) rec.set_payload(payload)
@ -52,20 +64,24 @@ class Rgn:
def print_struct(self): def print_struct(self):
""" """
Prints the structure of the parsed GCD file Prints the structure of the parsed RGN file
""" """
pass print("RGN File Version: {}".format(self.version))
print("{} records.".format(len(self.struct)))
for i, rec in enumerate(self.struct):
print("#{:03d}: {}".format(i, rec))
def print_struct_full(self): def print_struct_full(self):
""" """
Prints the structure of the parsed GCD file Prints the structure of the parsed RGN file
""" """
pass self.print_struct()
def validate(self, print_stats: bool=False): def validate(self, print_stats: bool=False):
""" """
Checks and verifies all checksums in the GCD. Checks and verifies all checksums in the RGN.
""" """
return True
# RGN has no checksum, but embedded BIN has # RGN has no checksum, but embedded BIN has
def dump_to_files(self, output_basename: str): def dump_to_files(self, output_basename: str):
@ -104,15 +120,50 @@ class RgnRecord():
new_rec.offset = offset new_rec.offset = offset
return new_rec return new_rec
def __str__(self):
rec_type = "Unknown"
if self.type_id == b"D":
rec_type = "Data Version"
elif self.type_id == b"A":
rec_type = "Application Version"
elif self.type_id == b"R":
rec_type = "Region"
if self.length != 1:
plural = "s"
offset = ""
if self.offset:
offset = " at 0x{:x}".format(self.offset)
lenstr = ""
if self.length:
lenstr = ", {:d} Byte{}".format(self.length, plural)
return "RGN {} Record{}{}".format(rec_type, offset, lenstr)
class RgnRecordD(RgnRecord): class RgnRecordD(RgnRecord):
""" """
Data record (2 Bytes) Data record (2 Bytes)
- ushort - Version - ushort - Version
""" """
def __init__(self, type_id, expected_length, payload=None, offset=None):
super().__init__(type_id, expected_length, payload, offset)
self.version = None
def parse(self): def parse(self):
if self.is_parsed:
# already parsed
return
self.version = unpack("<H", self.payload)[0]
self.is_parsed = True self.is_parsed = True
def __str__(self):
txt = super().__str__()
if not self.is_parsed:
self.parse()
txt += "\n - Data Version: {}".format(self.version)
return txt
class RgnRecordA(RgnRecord): class RgnRecordA(RgnRecord):
""" """
Application record Application record
@ -122,9 +173,33 @@ class RgnRecordA(RgnRecord):
- string - BuildTime - string - BuildTime
""" """
def __init__(self, type_id, expected_length, payload=None, offset=None):
super().__init__(type_id, expected_length, payload, offset)
self.version = None
self.builder = None
self.build_date = None
self.build_time = None
def parse(self): def parse(self):
if self.is_parsed:
# already parsed
return
self.version = unpack("<H", self.payload[0:2])[0]
splits = self.payload[2:].split(b"\0", 2)
self.builder = splits[0].decode("utf-8")
self.build_date = splits[1].decode("utf-8")
self.build_time = splits[2].decode("utf-8")
self.is_parsed = True self.is_parsed = True
def __str__(self):
txt = super().__str__()
if not self.is_parsed:
self.parse()
txt += "\n - Application Version: {}".format(self.version)
txt += "\n - Builder: {}".format(self.builder)
txt += "\n - Build time: {} {}".format(self.build_date, self.build_time)
return txt
class RgnRecordR(RgnRecord): class RgnRecordR(RgnRecord):
""" """
Region record Region record
@ -134,5 +209,25 @@ class RgnRecordR(RgnRecord):
- byte[Region size] - Contents - byte[Region size] - Contents
""" """
def __init__(self, type_id, expected_length, payload=None, offset=None):
super().__init__(type_id, expected_length, payload, offset)
self.region_id = None
self.delay_ms = None
self.size = None
def parse(self): def parse(self):
if self.is_parsed:
# already parsed
return
(self.region_id, self.delay_ms, self.size) = unpack("<HLL", self.payload[0:10])
self.is_parsed = True self.is_parsed = True
def __str__(self):
txt = super().__str__()
if not self.is_parsed:
self.parse()
rgn_type = REGION_TYPES.get(self.region_id, RED + "Unknown" + RESET)
txt += "\n - Region ID: {:04x} ({})".format(self.region_id, rgn_type)
txt += "\n - Flash delay: {} ms".format(self.delay_ms)
txt += "\n - Binary size: {} Bytes".format(self.size)
return txt

View File

@ -14,5 +14,5 @@ print("Opening {}".format(FILE))
rgn = Rgn(FILE) rgn = Rgn(FILE)
#rgn.print_struct() rgn.print_struct()
#rgn.validate(True) #rgn.validate(True)