-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathfirmware.py
132 lines (102 loc) · 3.85 KB
/
firmware.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import itertools
import json
import os
import struct
import zipfile
def zero_padding_bytes(size):
padding_zeroes = itertools.repeat(0, size)
return struct.pack("{}B".format(size), *padding_zeroes)
class FirmwareImage:
class Type:
UNKNOWN = -1
MAIN = 0
OLED = 1
MATRIX = 2
KEYBOARD = 3
WAVETABLES = 4
def __init__(self, file_name, image_num, data):
self.file_name = file_name
self.image_num = image_num
self.data = data
self.magic = struct.unpack("B", data[1:2])[0]
if file_name.startswith("nanowave_main"):
self.type = FirmwareImage.Type.MAIN
elif file_name.startswith("nanowave_iochip_oled"):
self.type = FirmwareImage.Type.OLED
elif file_name.startswith("nanowave_iochip_matrix"):
self.type = FirmwareImage.Type.MATRIX
elif file_name.startswith("nanowave_iochip_kbd"):
self.type = FirmwareImage.Type.KEYBOARD
elif file_name.startswith("nanowave_wavetables"):
self.type = FirmwareImage.Type.WAVETABLES
else:
self.type = FirmwareImage.Type.UNKNOWN
assert(self.type != FirmwareImage.Type.UNKNOWN)
# Verify that the first byte matches the image_num.
assert(struct.unpack("B", data[:1])[0] == image_num)
# Verify that the second byte (magic) and last byte (magic #2) match.
assert(struct.unpack("B", data[1:2])[0] ==
struct.unpack("B", data[-1:])[0])
# Verify that the padding at the start and end of the file, excluding magic
# bytes and image num, is all 0's.
# TODO: Determine actual end padding amount.
assert(data[2:64] == zero_padding_bytes(62))
# TODO: This fails with MicroFreak_Firmware_Update_3_0_6_1069. Why?
# assert(data[-10:-1] == zero_padding_bytes(9))
def log(self):
print("FirmwareImage:", self.image_num, ":", self.file_name,
"- magic =", self.magic, "- data size =", len(self.data))
class Firmware:
def __init__(self, images=dict(), version_number="", date=""):
self.images = images
self.version_number = version_number
self.date = date
def log(self):
print("Firmware:", self.version_number, "-", self.date)
for type, image in self.images.items():
image.log()
def get_image(self, type):
return self.images[type]
def put_image(self, image):
self.images[image.type] = image
def write_to_mff(self, path):
with zipfile.ZipFile(path, "w") as zf:
zf.writestr("info.json", self.build_json())
for type, image in self.images.items():
zf.writestr(image.file_name, image.data)
def build_json(self):
info = {}
info["version_number"] = self.version_number
info["date"] = self.date
info["images"] = []
for type, image in self.images.items():
image_info = {}
image_info["image_num"] = image.image_num
image_info["file_name"] = image.file_name
info["images"].append(image_info)
return json.dumps(info)
@classmethod
def load_from_mff(cls, path):
with zipfile.ZipFile(path, "r") as zf:
zipped_names = set(zf.namelist())
image_names = set(
filter(lambda s: os.path.splitext(s)[1] == ".bin",zipped_names))
zipped_names = zipped_names - image_names
info_name = "info.json"
assert(info_name in zipped_names)
zipped_names = zipped_names - set([info_name])
if len(zipped_names) > 0:
print("Unknown files in firmware:", zipped_names)
info = json.loads(zf.read(info_name))
images = {}
for image_entry in info["images"]:
image_num = image_entry["image_num"]
file_name = image_entry["file_name"]
assert(file_name in image_names)
data = zf.read(file_name)
image = FirmwareImage(file_name, image_num, data)
images[image.type] = image
return Firmware(images=images, version_number=info["version_number"],
date=info["date"])