Files
wave_extract/main.py
T
2025-08-11 13:17:31 +02:00

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/