diff options
author | itsme <itsme@xs4all.nl> | 2021-07-12 23:00:24 +0200 |
---|---|---|
committer | itsme <itsme@xs4all.nl> | 2021-07-12 23:06:55 +0200 |
commit | accc195b894c45f1a9a837188d38bf9df1ab0fd5 (patch) | |
tree | 7e10395b56d469e806d3fae6f4d9050f4d2cf5e5 | |
parent | 1ff6d6d7a20ca924998374ee54cede1f86450d52 (diff) |
now handling long compressed records.
-rw-r--r-- | crodump.py | 55 |
1 files changed, 45 insertions, 10 deletions
@@ -87,6 +87,9 @@ class Datafile: | |||
87 | return self.dat.read(size) | 87 | return self.dat.read(size) |
88 | 88 | ||
89 | def readrec(self, idx): | 89 | def readrec(self, idx): |
90 | """ | ||
91 | extract and decode a single record. | ||
92 | """ | ||
90 | ofs, ln, chk = self.tadidx[idx-1] | 93 | ofs, ln, chk = self.tadidx[idx-1] |
91 | if ln==0xFFFFFFFF: | 94 | if ln==0xFFFFFFFF: |
92 | # deleted record | 95 | # deleted record |
@@ -120,6 +123,9 @@ class Datafile: | |||
120 | 123 | ||
121 | 124 | ||
122 | def dump(self, args): | 125 | def dump(self, args): |
126 | """ | ||
127 | dump decodes all references data, and optionally will print out all unused bytes in the .dat file. | ||
128 | """ | ||
123 | print("hdr: %-6s dat: %04x %s enc:%04x bs:%04x, tad: %08x %08x" % (self.name, self.hdrunk, self.version, self.encoding, self.blocksize, self.nrdeleted, self.firstdeleted)) | 129 | print("hdr: %-6s dat: %04x %s enc:%04x bs:%04x, tad: %08x %08x" % (self.name, self.hdrunk, self.version, self.encoding, self.blocksize, self.nrdeleted, self.firstdeleted)) |
124 | ranges = [] # keep track of used bytes in the .dat file. | 130 | ranges = [] # keep track of used bytes in the .dat file. |
125 | for i, (ofs, ln, chk) in enumerate(self.tadidx): | 131 | for i, (ofs, ln, chk) in enumerate(self.tadidx): |
@@ -183,20 +189,31 @@ class Datafile: | |||
183 | print("%08x-%08x: %s" % (o, o+l, toout(args, dat))) | 189 | print("%08x-%08x: %s" % (o, o+l, toout(args, dat))) |
184 | 190 | ||
185 | def iscompressed(self, data): | 191 | def iscompressed(self, data): |
192 | """ | ||
193 | Note that the compression header uses big-endian numbers. | ||
194 | """ | ||
186 | if len(data)<11: | 195 | if len(data)<11: |
187 | return | 196 | return |
188 | size, flag = struct.unpack_from(">HH", data, 0) | ||
189 | if size+5 != len(data): | ||
190 | return | ||
191 | if flag!=0x800: | ||
192 | return | ||
193 | if data[-3:] != b"\x00\x00\x02": | 197 | if data[-3:] != b"\x00\x00\x02": |
194 | return | 198 | return |
199 | o = 0 | ||
200 | while o < len(data)-3: | ||
201 | size, flag = struct.unpack_from(">HH", data, o) | ||
202 | if flag!=0x800 and flag!=0x008: | ||
203 | return | ||
204 | o += size + 2 | ||
195 | return True | 205 | return True |
196 | 206 | ||
197 | def decompress(self, data): | 207 | def decompress(self, data): |
198 | C = zlib.decompressobj(-15) | 208 | result = b"" |
199 | return C.decompress(data[8:-3]) | 209 | o = 0 |
210 | while o < len(data)-3: | ||
211 | size, flag, crc = struct.unpack_from(">HHL", data, o) | ||
212 | C = zlib.decompressobj(-15) | ||
213 | result += C.decompress(data[o+8:o+8+size]) | ||
214 | o += size + 2 | ||
215 | return result | ||
216 | |||
200 | 217 | ||
201 | def dump_bank_definition(args, bankdict): | 218 | def dump_bank_definition(args, bankdict): |
202 | """ | 219 | """ |
@@ -222,9 +239,9 @@ def decode_field(data): | |||
222 | unk4 = rd.readdword() # Always 0x00000009 or 0x0001000d | 239 | unk4 = rd.readdword() # Always 0x00000009 or 0x0001000d |
223 | remain = rd.readbytes() | 240 | remain = rd.readbytes() |
224 | 241 | ||
225 | print("Type: %d (%02d/%02d) %04x,(%d-%d),%04x - '%s' -- %s" % (typ, idx1, idx2, unk1, unk2, unk3, unk4, name, tohex(remain))) | 242 | print("Type: %2d (%2d/%2d) %04x,(%d-%4d),%04x - '%s' -- %s" % (typ, idx1, idx2, unk1, unk2, unk3, unk4, name, tohex(remain))) |
226 | else: | 243 | else: |
227 | print("Type: %d %2d %d,%d - '%s'" % (typ, idx1, unk1, unk2, name)) | 244 | print("Type: %2d %2d %d,%d - '%s'" % (typ, idx1, unk1, unk2, name)) |
228 | 245 | ||
229 | 246 | ||
230 | def destruct_base_definition(args, data): | 247 | def destruct_base_definition(args, data): |
@@ -239,7 +256,10 @@ def destruct_base_definition(args, data): | |||
239 | unkname = rd.readname() | 256 | unkname = rd.readname() |
240 | unk7 = rd.readdword() | 257 | unk7 = rd.readdword() |
241 | nrfields = rd.readdword() | 258 | nrfields = rd.readdword() |
259 | if args.verbose: | ||
260 | print("table: %s" % tohex(data[:rd.o])) | ||
242 | print("%d,%d,%d,%d,%d %d,%d '%s' '%s'" % (*unk123, *unk45, unk7, nrfields, tablename, unkname)) | 261 | print("%d,%d,%d,%d,%d %d,%d '%s' '%s'" % (*unk123, *unk45, unk7, nrfields, tablename, unkname)) |
262 | |||
243 | fields = [] | 263 | fields = [] |
244 | for _ in range(nrfields): | 264 | for _ in range(nrfields): |
245 | l = rd.readword() | 265 | l = rd.readword() |
@@ -363,12 +383,20 @@ class Database: | |||
363 | if not self.bank: | 383 | if not self.bank: |
364 | print("No CroBank.dat found") | 384 | print("No CroBank.dat found") |
365 | return | 385 | return |
386 | if args.skipencrypted and self.bank.encoding==3: | ||
387 | print("Skipping encrypted CroBank") | ||
388 | return | ||
366 | nerr = 0 | 389 | nerr = 0 |
367 | xref = defaultdict(int) | 390 | xref = defaultdict(int) |
368 | for i in range(args.maxrecs): | 391 | for i in range(args.maxrecs): |
369 | try: | 392 | try: |
370 | data = self.bank.readrec(i) | 393 | data = self.bank.readrec(i) |
371 | if not args.stats: | 394 | if args.find1d: |
395 | if data and (data.find(b"\x1d")>0 or data.find(b"\x1b")>0): | ||
396 | print("%d -> %s" % (i, b2a_hex(data))) | ||
397 | break | ||
398 | |||
399 | elif not args.stats: | ||
372 | if data is None: | 400 | if data is None: |
373 | print("%5d: <deleted>" % i) | 401 | print("%5d: <deleted>" % i) |
374 | else: | 402 | else: |
@@ -393,6 +421,11 @@ class Database: | |||
393 | for k, v in xref.items(): | 421 | for k, v in xref.items(): |
394 | print("%5d * %s" % (v, k)) | 422 | print("%5d * %s" % (v, k)) |
395 | 423 | ||
424 | def readrec(self, sysnum): | ||
425 | data = self.bank.readrec(sysnum) | ||
426 | tabnum, = struct.unpack_from("<B", data, 0) | ||
427 | fields = data[1:].split(b"\x1e") | ||
428 | |||
396 | def incdata(data, s): | 429 | def incdata(data, s): |
397 | """ | 430 | """ |
398 | add 's' to each byte. | 431 | add 's' to each byte. |
@@ -543,6 +576,8 @@ def main(): | |||
543 | p.add_argument('--verbose', '-v', action='store_true') | 576 | p.add_argument('--verbose', '-v', action='store_true') |
544 | p.add_argument('--ascdump', '-a', action='store_true') | 577 | p.add_argument('--ascdump', '-a', action='store_true') |
545 | p.add_argument('--maxrecs', '-n', type=str, help="max nr or recots to output") | 578 | p.add_argument('--maxrecs', '-n', type=str, help="max nr or recots to output") |
579 | p.add_argument('--find1d', action='store_true') | ||
580 | p.add_argument('--inclencrypted', action='store_false', dest='skipencrypted', default='true', help='include encrypted records in the output') | ||
546 | p.add_argument('--stats', action='store_true', help='calc table stats from the first byte of each record') | 581 | p.add_argument('--stats', action='store_true', help='calc table stats from the first byte of each record') |
547 | p.add_argument('dbdir', type=str) | 582 | p.add_argument('dbdir', type=str) |
548 | p.set_defaults(handler=bank_dump) | 583 | p.set_defaults(handler=bank_dump) |