-
Notifications
You must be signed in to change notification settings - Fork 5
/
madspack.py
160 lines (112 loc) · 3.26 KB
/
madspack.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
""" Copyright 2015-2017 sta256+mpskit at gmail.com
This file is part of mpskit.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See LICENSE file for more details.
"""
from io import BytesIO
from common import *
from fab import read_fab, write_fab
"""
MADSPACK format
size repeat content
---------------------------------------------
12 magic MADSPACK 2.0
2 ykhm 0x1A
2 count (max 16)
10 16 section_header
count section
section_header
size repeat content
---------------------------------------------
2 flags -- bit 0 set => fab compression
4 size -- uncompressed size
4 csize -- compressed size
"""
verbose = 0
def load_madspack(madspack_name):
parts = []
i = 0
while i < 16:
n = "{}.s{:02}.part".format(madspack_name, i)
if not os.path.exists(n):
if verbose:
print('load madspack: file not found:', n)
break
parts.append(open(n, 'rb'))
i += 1
if i == 0:
print('ERROR: no parts to load')
sys.exit(2)
return parts
def save_madspack(madspack_name, parts):
for i,part in enumerate(parts):
part.seek(0)
n = "{}.s{:02}.part".format(madspack_name, i)
open(n, 'wb').write(part.read())
part.seek(0)
output(n)
def read_madspack(madspack_name):
"""
f -- input stream
"""
f = open2(madspack_name, 'rb')
magic = f.read(12).decode('ascii')
if magic != 'MADSPACK 2.0':
fail("invalid madspack version; expected=MADSPACK 2.0; got={}; file={}", magic, madspack_name)
f.seek(14)
_count = read_uint16(f) # num of parts
header = io.BytesIO(f.read(0xA0)) # max 16 parts
header.seek(0)
parts = []
for i in range(_count):
flag = read_uint16(header)
size = read_uint32(header)
compressed_size = read_uint32(header)
if (flag & 1) == 0:
assert compressed_size == size
## no compression on this entry
data = BytesIO(f.read(size))
elif (flag & 1) == 1:
if compressed_size == size:
warning("madspack decoder: flag indicate fab compression but csize equals usize in: {}", madspack_name)
## fab compressed
data = read_fab(f, size)
else:
raise Error("madspack unknown mode = {}".format(mode))
parts.append(data)
return parts
def write_madspack(madspack_name, parts):
"""
f -- output stream
"""
f = open2(madspack_name, 'wb')
write_ascii(f, 'MADSPACK 2.0')
write_uint16(f, 0x1A)
assert f.tell() == 14
count = len(parts)
write_uint16(f, count) # num of parts
header_pos = f.tell()
parts_pos = header_pos + 0xA0
for i in range(count):
# ------------------------
f.seek(parts_pos)
# without fab
parts[i].seek(0)
f.write(parts[i].read())
size = f.tell() - parts_pos
parts_pos = f.tell()
# ------------------------
f.seek(header_pos)
# hash? mode?
write_uint16(f, 0) # mode no_fab = 0, fab = 1
# size
write_uint32(f, size)
# compressed_size
write_uint32(f, size)
header_pos = f.tell()
output(madspack_name)