252 lines
5.6 KiB
Python
252 lines
5.6 KiB
Python
# This is a sample Python script.
|
|
import enum
|
|
# Press Umschalt+F10 to execute it or replace it with your code.
|
|
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
|
|
|
|
import os
|
|
import re
|
|
import struct
|
|
from argparse import ArgumentParser
|
|
from io import BytesIO
|
|
from typing import BinaryIO
|
|
import hashlib
|
|
import json
|
|
|
|
from pygccxml.declarations import unsigned_int_t
|
|
|
|
|
|
class AImage(object):
|
|
def __init__(self, folder: str, output_dir: str = "out"):
|
|
self.out_path = os.path.join(folder, output_dir)
|
|
os.makedirs(self.out_path, exist_ok=True)
|
|
|
|
def parse(self):
|
|
pass
|
|
|
|
def version(self):
|
|
pass
|
|
|
|
def is_valid(self):
|
|
return False
|
|
|
|
@staticmethod
|
|
def _check(data: bytes, target: dict):
|
|
is_valid = True
|
|
is_valid &= AImage._check_len(data, target['LEN'])
|
|
is_valid &= AImage._check_fingerprint(data, target['DIGEST'])
|
|
is_valid &= AImage._check_version(data, 0x1B430, bytearray.fromhex(target['VERSION']))
|
|
|
|
print("is_valid =", is_valid)
|
|
|
|
return is_valid
|
|
|
|
@staticmethod
|
|
def _check_len(data: bytes, target: int):
|
|
return len(data) == target
|
|
|
|
@staticmethod
|
|
def _check_fingerprint(data: bytes, target: str):
|
|
h = hashlib.sha256()
|
|
h.update(data)
|
|
actual = h.digest()
|
|
print(actual)
|
|
return actual == target
|
|
|
|
@staticmethod
|
|
def _check_version(data: bytes, offset : int, target: bytearray):
|
|
actual = data[offset:offset+len(target)]
|
|
print(actual)
|
|
return actual == target
|
|
|
|
@staticmethod
|
|
def combine(filename_hi: str, filename_lo: str):
|
|
data_hi = b''
|
|
data_lo = b''
|
|
with open(filename_hi, "rb") as fp:
|
|
data_hi = fp.read()
|
|
with open(filename_lo, "rb") as fp:
|
|
data_lo = fp.read()
|
|
|
|
data = b''
|
|
for hi, lo in zip(data_hi, data_lo):
|
|
z = int.from_bytes([hi, lo], byteorder='big')
|
|
b = struct.pack('>H', z)
|
|
data += b
|
|
|
|
return data, data_hi, data_lo
|
|
|
|
def _save(self, filename: str, data):
|
|
with open(os.path.join(self.out_path, filename), 'wb') as fp:
|
|
fp.write(data)
|
|
|
|
def _json_save(self, filename: str, data):
|
|
with open(os.path.join(self.out_path, filename), 'w') as fp:
|
|
json.dump(data, fp, indent=4)
|
|
|
|
def get_waves(self):
|
|
pass
|
|
|
|
def get_wave_tables(self):
|
|
pass
|
|
|
|
class WaldorfMicroWave1V20(AImage):
|
|
pass
|
|
|
|
|
|
class WaldorfMicroWave1V25(AImage):
|
|
pass
|
|
|
|
class WaldorfMicroWave2V00(AImage):
|
|
class ImagePart(enum.Enum):
|
|
Hi = 0
|
|
Lo = 1
|
|
|
|
target = {
|
|
"LEN" : 131072,
|
|
"DIGEST" : b'\xe3G\n~\xfb\xd0P\xf4~\x8d\xf3\x12p9\x88X\x95e\xc2\xc5e\xdev\xe6\r\xaba\x8b\xadDY\x90',
|
|
"VERSION" : "322E3030300000004D6F6E204D617220372031323A30363A3132204D4554203139393400303230303934303337000000"
|
|
}
|
|
|
|
wave_data_descr = [
|
|
{
|
|
'offset': 0xAA18,
|
|
'len': 0x1B00
|
|
},
|
|
{
|
|
'offset': 0xAA18,
|
|
'len': 0x3000
|
|
}
|
|
]
|
|
|
|
wave_tables_descr = [
|
|
{
|
|
'offset': 0x9A18,
|
|
'len': 0x0E00
|
|
},
|
|
{
|
|
'offset': 0x9A18,
|
|
'len': 0x0600
|
|
}
|
|
]
|
|
|
|
def __init__(self, folder: str, out_dir="."):
|
|
AImage.__init__(self, folder, out_dir)
|
|
filename_hi = None
|
|
filename_lo = None
|
|
for filename in os.listdir(folder):
|
|
filepath = os.path.join(folder, filename)
|
|
if re.match('.*_H.bin', filename):
|
|
filename_hi = filepath
|
|
if re.match('.*_L.bin', filename):
|
|
filename_lo = filepath
|
|
|
|
if filename_hi is None:
|
|
return
|
|
|
|
if filename_lo is None:
|
|
return
|
|
|
|
data, data_hi, data_lo = self.combine(filename_hi, filename_lo)
|
|
|
|
self._is_valid = self._check(data, self.target)
|
|
if self._is_valid:
|
|
self._save("combined_test_be.bin", data)
|
|
|
|
wave_data = self.wave_extract(self.wave_data_descr, data_hi, data_lo)
|
|
self._save("wave_data.bin", wave_data)
|
|
|
|
wave_table = self.wave_table_extract(self.wave_tables_descr, data_hi, data_lo)
|
|
self._json_save("wave_table.json", wave_table)
|
|
|
|
|
|
@staticmethod
|
|
def wave_extract(descr, data_hi, data_lo):
|
|
data = b''
|
|
for des, rom_data in zip(descr, [data_hi, data_lo]):
|
|
offset = des['offset']
|
|
length = des['len']
|
|
data += rom_data[offset:offset+length]
|
|
return data
|
|
|
|
@staticmethod
|
|
def wave_table_extract(descr, data_hi, data_lo):
|
|
|
|
data = []
|
|
table_count = 0
|
|
pos = 0
|
|
for des, rom_data in zip(descr, [data_hi, data_lo]):
|
|
offset = des['offset']
|
|
length = des['len']
|
|
|
|
for addr in range(offset, offset + length, 2):
|
|
index = struct.unpack('>h', rom_data[addr:addr+2])[0]
|
|
if index > 0:
|
|
if pos == 0:
|
|
print(f"----------------------------")
|
|
print(f"- table #{table_count:03d}")
|
|
print(f"----------------------------")
|
|
table_count += 1
|
|
entry = {"index": index, "pos": pos}
|
|
print(entry)
|
|
data.append(entry)
|
|
|
|
if pos < 63:
|
|
pos += 1
|
|
else:
|
|
pos = 0
|
|
return data
|
|
|
|
def is_valid(self):
|
|
return self._is_valid
|
|
|
|
|
|
class PpgEvu1v00(AImage):
|
|
pass
|
|
|
|
class PpgEvu2v00(AImage):
|
|
pass
|
|
|
|
class WaldorfMicrowaveXT(AImage):
|
|
def __init__(self, folder: str, out_dir="."):
|
|
AImage.__init__(self, folder, out_dir)
|
|
filename_hi = None
|
|
filename_lo = None
|
|
for filename in os.listdir(folder):
|
|
filepath = os.path.join(folder, filename)
|
|
if re.match('upper_.*.bin', filename):
|
|
filename_hi = filepath
|
|
if re.match('lower_.*.bin', filename):
|
|
filename_lo = filepath
|
|
|
|
if filename_hi is None:
|
|
return
|
|
|
|
if filename_lo is None:
|
|
return
|
|
|
|
data, data_hi, data_lo = self.combine(filename_lo, filename_hi)
|
|
self._save("combined_be.bin", data)
|
|
|
|
|
|
def detect(args):
|
|
image = WaldorfMicroWave2V00(args.folder, args.output_dir)
|
|
image = WaldorfMicrowaveXT(args.folder, args.output_dir)
|
|
pass
|
|
|
|
def main(args):
|
|
image_type = detect(args)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = ArgumentParser()
|
|
parser.add_argument("folder", help="Folder with firmware binary image")
|
|
parser.add_argument("--output_dir", help="Location of output data", default='out')
|
|
|
|
args = parser.parse_args()
|
|
|
|
main(args)
|
|
|
|
|
|
|
|
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
|