summaryrefslogtreecommitdiff
path: root/crodump.py
diff options
context:
space:
mode:
Diffstat (limited to 'crodump.py')
-rw-r--r--crodump.py161
1 files changed, 104 insertions, 57 deletions
diff --git a/crodump.py b/crodump.py
index af4dc36..cc0a7f2 100644
--- a/crodump.py
+++ b/crodump.py
@@ -217,33 +217,42 @@ class Datafile:
217 return result 217 return result
218 218
219 219
220def dump_bank_definition(args, bankdict): 220def dump_db_definition(args, dbdict):
221 """ 221 """
222 decode the 'bank' / database definition 222 decode the 'bank' / database definition
223 """ 223 """
224 for k, v in bankdict.items(): 224 for k, v in dbdict.items():
225 if re.search(b'[^\x0d\x0a\x09\x20-\x7e\xc0-\xff]', v): 225 if re.search(b'[^\x0d\x0a\x09\x20-\x7e\xc0-\xff]', v):
226 print("%-20s - %s" % (k, toout(args, v))) 226 print("%-20s - %s" % (k, toout(args, v)))
227 else: 227 else:
228 print("%-20s - \"%s\"" % (k, strescape(v))) 228 print("%-20s - \"%s\"" % (k, strescape(v)))
229 229
230class FieldDefinition:
231 def __init__(self, data):
232 self.decode(data)
230 233
231def decode_field(data): 234 def decode(self, data):
232 rd = ByteReader(data) 235 self.defdata = data
233 typ = rd.readword() 236
234 idx1 = rd.readdword() 237 rd = ByteReader(data)
235 name = rd.readname() 238 self.typ = rd.readword()
236 unk1 = rd.readdword() 239 self.idx1 = rd.readdword()
237 unk2 = rd.readbyte() # Always 1 240 self.name = rd.readname()
238 if typ: 241 self.flags = rd.readdword()
239 idx2 = rd.readdword() 242 self.minval = rd.readbyte() # Always 1
240 unk3 = rd.readdword() # max value or length 243 if self.typ:
241 unk4 = rd.readdword() # Always 0x00000009 or 0x0001000d 244 self.idx2 = rd.readdword()
242 remain = rd.readbytes() 245 self.maxval = rd.readdword() # max value or length
243 246 self.unk4 = rd.readdword() # Always 0x00000009 or 0x0001000d
244 print("Type: %2d (%2d/%2d) %04x,(%d-%4d),%04x - %-40s -- %s" % (typ, idx1, idx2, unk1, unk2, unk3, unk4, "'%s'" % name, tohex(remain))) 247 else:
245 else: 248 self.idx2 = self.maxval = self.unk4 = None
246 print("Type: %2d %2d %d,%d - '%s'" % (typ, idx1, unk1, unk2, name)) 249 self.remaining = rd.readbytes()
250
251 def __str__(self):
252 if self.typ:
253 return "Type: %2d (%2d/%2d) %04x,(%d-%4d),%04x - %-40s -- %s" % (self.typ, self.idx1, self.idx2, self.flags, self.minval, self.maxval, self.unk4, "'%s'" % self.name, tohex(self.remaining))
254 else:
255 return "Type: %2d %2d %d,%d - '%s'" % (self.typ, self.idx1, self.flags, self.minval, self.name)
247 256
248""" 257"""
249 2 Base000 - 000001 050001 000000000000000546696c657302464c01000000010000001b000000000000000fd1e8f1f2e5ecedfbe920edeeece5f0010000000000000000010000000000000000 258 2 Base000 - 000001 050001 000000000000000546696c657302464c01000000010000001b000000000000000fd1e8f1f2e5ecedfbe920edeeece5f0010000000000000000010000000000000000
@@ -268,47 +277,60 @@ def decode_field(data):
268 2 Base000 - 00000300090102000000000000000000000005d4e0e9ebfb02464c01000000010000001b000000000000000fd1e8f1f2e5ecedfbe920edeeece5f001000000000000000000000000060000000000000000ffffffffffffffffffffffffffffffffffffffff1700000003ffffffffffffffffffffffff06000000010000000000 277 2 Base000 - 00000300090102000000000000000000000005d4e0e9ebfb02464c01000000010000001b000000000000000fd1e8f1f2e5ecedfbe920edeeece5f001000000000000000000000000060000000000000000ffffffffffffffffffffffffffffffffffffffff1700000003ffffffffffffffffffffffff06000000010000000000
269""" 278"""
270 279
271def destruct_base_definition(args, data): 280class TableDefinition:
272 """ 281 def __init__(self, data):
273 decode the 'base' / table definition 282 self.decode(data)
274 """ 283
275 rd = ByteReader(data) 284 def decode(self, data):
285 """
286 decode the 'base' / table definition
287 """
288 rd = ByteReader(data)
289
290 self.unk1 = rd.readword()
291 self.version = rd.readbyte()
292 if self.version > 1:
293 _ = rd.readbyte() # always 0 anyway
294 self.unk2 = rd.readbyte() # if this is not 5 (but 9), there's another 4 bytes inserted, this could be a length-byte.
295 self.unk3 = rd.readbyte()
296
297 if self.unk2 > 5: # seen only 5 and 9 for now with 9 implying an extra dword
298 _ = rd.readdword()
276 299
277 unk1 = rd.readword() 300 self.tableid = rd.readdword()
278 version = rd.readbyte() 301 self.unk5 = rd.readdword()
279 if version > 1:
280 _ = rd.readbyte() # always 0 anyway
281 unk2 = rd.readbyte() # if this is not 5 (but 9), there's another 4 bytes inserted
282 unk3 = rd.readbyte()
283 302
284 if unk2 > 5: # seen only 5 and 9 for now with 9 implying an extra dword 303 self.tablename = rd.readname()
285 _ = rd.readdword() 304 self.abbrev = rd.readname()
305 self.unk7 = rd.readdword()
306 nrfields = rd.readdword()
286 307
287 unk4 = rd.readdword() 308 self.headerdata = data[:rd.o]
288 unk5 = rd.readdword()
289 309
290 tablename = rd.readname() 310 self.fields = []
291 abbrev = rd.readname() 311 for _ in range(nrfields):
292 unk7 = rd.readdword() 312 l = rd.readword()
293 nrfields = rd.readdword() 313 fielddef = rd.readbytes(l)
314 self.fields.append(FieldDefinition(fielddef))
294 315
295 if args.verbose: 316 self.remainingdata = rd.readbytes()
296 print("table: %s" % tohex(data[:rd.o]))
297 print("%d,%d,%d,%d,%d,%d %d,%d '%s' '%s'" % (unk1, version, unk2, unk3, unk4, unk5, unk7, nrfields, tablename, abbrev))
298 317
299 fields = [] 318 def dump(self, args):
300 for _ in range(nrfields): 319 if args.verbose:
301 l = rd.readword() 320 print("table: %s" % tohex(self.headerdata))
302 fielddef = rd.readbytes(l) 321 print("%d,%d,%d,%d,%d,%d %d,%d '%s' '%s'" % ( self.unk1, self.version, self.unk2, self.unk3, self.tableid, self.unk5, self.unk7, len(self.fields), self.tablename, self.abbrev))
322
323 for field in self.fields:
324 if args.verbose:
325 print("field: @%04x: %04x - %s" % (field.byteoffset, len(field.defdata), tohex(field.defdata)))
326 print(str(field))
303 if args.verbose: 327 if args.verbose:
304 print("field: @%04x: %04x - %s" % (rd.o, l, tohex(fielddef))) 328 print("remaining: %s" % tohex(self.remainingdata))
305 fields.append(decode_field(fielddef))
306 remaining = rd.readbytes()
307 329
308 print("rem: %s" % tohex(remaining))
309 330
310def destruct_sys3_def(rd): 331def destruct_sys3_def(rd):
311 pass 332 pass
333
312def destruct_sys4_def(rd): 334def destruct_sys4_def(rd):
313 n = rd.readdword() 335 n = rd.readdword()
314 for _ in range(n): 336 for _ in range(n):
@@ -335,7 +357,7 @@ def destruct_sys_definition(args, data):
335 357
336 358
337class Database: 359class Database:
338 """ represent the entire database, consisting of stru, index and bank files """ 360 """ represent the entire database, consisting of Stru, Index and Bank files """
339 def __init__(self, dbdir): 361 def __init__(self, dbdir):
340 self.dbdir = dbdir 362 self.dbdir = dbdir
341 363
@@ -379,9 +401,9 @@ class Database:
379 if not self.stru: 401 if not self.stru:
380 print("missing CroStru file") 402 print("missing CroStru file")
381 return 403 return
382 self.dumptabledefs(args) 404 self.dump_db_table_defs(args)
383 405
384 def decode_bank_definition(self, data): 406 def decode_db_definition(self, data):
385 """ 407 """
386 decode the 'bank' / database definition 408 decode the 'bank' / database definition
387 """ 409 """
@@ -403,7 +425,7 @@ class Database:
403 d[keyname] = refdata[1:] 425 d[keyname] = refdata[1:]
404 return d 426 return d
405 427
406 def dumptabledefs(self, args): 428 def dump_db_table_defs(self, args):
407 """ 429 """
408 decode the table defs from recid #1, which always has table-id #3 430 decode the table defs from recid #1, which always has table-id #3
409 Note that I don't know if it is better to refer to this by recid, or by table-id. 431 Note that I don't know if it is better to refer to this by recid, or by table-id.
@@ -414,13 +436,37 @@ class Database:
414 dbinfo = self.stru.readrec(1) 436 dbinfo = self.stru.readrec(1)
415 if dbinfo[:1] != b"\x03": 437 if dbinfo[:1] != b"\x03":
416 print("WARN: expected dbinfo to start with 0x03") 438 print("WARN: expected dbinfo to start with 0x03")
417 dbdef = self.decode_bank_definition(dbinfo[1:]) 439 dbdef = self.decode_db_definition(dbinfo[1:])
418 dump_bank_definition(args, dbdef) 440 dump_db_definition(args, dbdef)
419 441
420 for k, v in dbdef.items(): 442 for k, v in dbdef.items():
421 if k.startswith("Base") and k[4:].isnumeric(): 443 if k.startswith("Base") and k[4:].isnumeric():
422 print("== %s ==" % k) 444 print("== %s ==" % k)
423 tbdef = destruct_base_definition(args, v) 445 tbdef = TableDefinition(v)
446 tbdef.dump(args)
447
448 def enumerate_tables(self):
449 dbinfo = self.stru.readrec(1)
450 if dbinfo[:1] != b"\x03":
451 print("WARN: expected dbinfo to start with 0x03")
452 dbdef = self.decode_db_definition(dbinfo[1:])
453
454 for k, v in dbdef.items():
455 if k.startswith("Base") and k[4:].isnumeric():
456 yield TableDefinition(v)
457
458 def enumerate_records(self, table):
459 """
460 usage:
461 for tab in db.enumerate_tables():
462 for rec in db.enumerate_records(tab):
463 print(sqlformatter(tab, rec))
464 """
465 for i in range(1, args.maxrecs+1):
466 data = db.readrec(i)
467 if data and struct.unpack_from("<B", data, 0) == table.tableid:
468 yield data[1:].split(b"\x1e")
469
424 470
425 def recdump(self, args): 471 def recdump(self, args):
426 if args.index: 472 if args.index:
@@ -594,9 +640,10 @@ def destruct(args):
594 data = unhex(data) 640 data = unhex(data)
595 641
596 if args.type==1: 642 if args.type==1:
597 destruct_bank_definition(args, data) 643 destruct_db_definition(args, data)
598 elif args.type==2: 644 elif args.type==2:
599 destruct_base_definition(args, data) 645 tbdef = TableDefinition(data)
646 tbdef.dump(args)
600 elif args.type==3: 647 elif args.type==3:
601 destruct_sys_definition(args, data) 648 destruct_sys_definition(args, data)
602 649