summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoritsme <itsme@xs4all.nl>2021-07-07 10:25:29 +0200
committeritsme <itsme@xs4all.nl>2021-07-07 10:25:29 +0200
commit6ab29a7ec498d3893dd602b881ae4d354a784b10 (patch)
tree0f2947c84fa3c8eeca145cf752e188c57930044a
parent1604864c258cbe455bff6b46c8622cc58d14c1e9 (diff)
several updates:
* added 'readrec' method to Datafile * case insensitive filename matching * added 'destruct' option to decode top level stru information.
-rw-r--r--crodump.py133
1 files changed, 110 insertions, 23 deletions
diff --git a/crodump.py b/crodump.py
index 7a9d9f3..f50f45b 100644
--- a/crodump.py
+++ b/crodump.py
@@ -4,6 +4,8 @@ import struct
4from binascii import b2a_hex 4from binascii import b2a_hex
5from hexdump import hexdump, asasc, tohex, unhex 5from hexdump import hexdump, asasc, tohex, unhex
6from koddecoder import kodecode 6from koddecoder import kodecode
7from readers import ByteReader
8
7""" 9"""
8python3 crodump.py crodump chechnya_proverki_ul_2012 10python3 crodump.py crodump chechnya_proverki_ul_2012
9python3 crodump.py kodump -s 6 -o 0x4cc9 -e 0x5d95 chechnya_proverki_ul_2012/CroStru.dat 11python3 crodump.py kodump -s 6 -o 0x4cc9 -e 0x5d95 chechnya_proverki_ul_2012/CroStru.dat
@@ -28,10 +30,12 @@ def enumunreferenced(ranges, filesize):
28 30
29class Datafile: 31class Datafile:
30 """ Represent a single .dat with it's .tad index file """ 32 """ Represent a single .dat with it's .tad index file """
31 def __init__(self, dat, tad): 33 def __init__(self, dat, tad, need_decode=False):
32 self.dat = dat 34 self.dat = dat
33 self.tad = tad 35 self.tad = tad
34 36
37 self.need_decode = need_decode
38
35 self.readtad() 39 self.readtad()
36 40
37 self.dat.seek(0, io.SEEK_END) 41 self.dat.seek(0, io.SEEK_END)
@@ -48,9 +52,40 @@ class Datafile:
48 self.dat.seek(ofs) 52 self.dat.seek(ofs)
49 return self.dat.read(size) 53 return self.dat.read(size)
50 54
55 def readrec(self, idx):
56 ofs, ln, chk = self.tabidx[idx-1]
57 if ln==0xFFFFFFFF:
58 # deleted record
59 return
60
61 flags = ln>>24
62
63 ln &= 0xFFFFFFF
64 dat = self.readdata(ofs, ln)
65
66 if not dat:
67 # empty record
68 encdat = dat
69 elif self.need_decode and not flags:
70 extofs, extlen = struct.unpack("<LL", dat[:8])
71 encdat = dat[8:]
72 while len(encdat)<extlen:
73 dat = self.readdata(extofs, 0x200)
74 extofs, = struct.unpack("<L", dat[:4])
75 encdat += dat[4:]
76 encdat = encdat[:extlen]
77 else:
78 encdat = dat
79
80 if not self.need_decode:
81 return encdat
82 else:
83 return kodecode(idx, encdat)
84
85
51 def dump(self, args, nodecode=False): 86 def dump(self, args, nodecode=False):
52 print("tadhdr: %08x %08x" % tuple(self.tadhdr)) 87 print("tadhdr: %08x %08x" % tuple(self.tadhdr))
53 ranges = [] 88 ranges = [] # keep track of used bytes in the .dat file.
54 for i, (ofs, ln, chk) in enumerate(self.tadidx): 89 for i, (ofs, ln, chk) in enumerate(self.tadidx):
55 if ln==0xFFFFFFFF: 90 if ln==0xFFFFFFFF:
56 print("%5d: %08x %08x %08x" % (i, ofs, ln, chk)) 91 print("%5d: %08x %08x %08x" % (i, ofs, ln, chk))
@@ -60,11 +95,14 @@ class Datafile:
60 ln &= 0xFFFFFFF 95 ln &= 0xFFFFFFF
61 dat = self.readdata(ofs, ln) 96 dat = self.readdata(ofs, ln)
62 ranges.append((ofs, ofs+ln, "item #%d" % i)) 97 ranges.append((ofs, ofs+ln, "item #%d" % i))
63 plain = b''
64 decflag = ' ' 98 decflag = ' '
65 infostr = "" 99 infostr = ""
66 tail = b'' 100 tail = b''
67 if not nodecode and not flags: 101
102 if not dat:
103 # empty record
104 encdat = dat
105 elif not nodecode and not flags:
68 extofs, extlen = struct.unpack("<LL", dat[:8]) 106 extofs, extlen = struct.unpack("<LL", dat[:8])
69 infostr = "%08x;%08x" % (extofs, extlen) 107 infostr = "%08x;%08x" % (extofs, extlen)
70 encdat = dat[8:] 108 encdat = dat[8:]
@@ -101,19 +139,48 @@ class Database:
101 def __init__(self, dbdir): 139 def __init__(self, dbdir):
102 self.dbdir = dbdir 140 self.dbdir = dbdir
103 141
104 self.stru = self.getfile("Stru") 142 self.stru = self.getfile("Stru", need_decode=True)
105 self.index = self.getfile("Index") 143 self.index = self.getfile("Index")
106 self.bank = self.getfile("Bank") 144 self.bank = self.getfile("Bank")
107 self.sys = self.getfile("Sys") 145 self.sys = self.getfile("Sys", need_decode=True)
146 # BankTemp, Int
108 147
109 def getfile(self, name): 148
149 def getfile(self, name, need_decode=False):
110 try: 150 try:
111 return Datafile(open(self.getname(name, "dat"), "rb"), open(self.getname(name, "tad"), "rb")) 151 datname = self.getname(name, "dat")
152 tadname = self.getname(name, "tad")
153 if datname and tadname:
154 return Datafile(open(datname, "rb"), open(tadname, "rb"), need_decode)
112 except IOError: 155 except IOError:
113 return 156 return
114 157
115 def getname(self, name, ext): 158 def getname(self, name, ext):
116 return os.path.join(self.dbdir, "Cro%s.%s" % (name, ext)) 159 """
160 get a case-insensitive filename match for 'name.ext'.
161 Returns None when no matching file was not found.
162 """
163 basename = "Cro%s.%s" % (name, ext)
164 for fn in os.scandir(self.dbdir):
165 if basename.lower() == fn.name.lower():
166 return os.path.join(self.dbdir, fn.name)
167
168 def dump(self, args):
169 if self.stru:
170 print("stru")
171 self.stru.dump(args)
172 if args.struonly:
173 return
174 if self.index:
175 print("index")
176 self.index.dump(args)
177 if self.bank:
178 print("bank")
179 self.bank.dump(args)
180 if self.sys:
181 print("sys")
182 self.sys.dump(args)
183
117 184
118def incdata(data, s): 185def incdata(data, s):
119 """ 186 """
@@ -179,22 +246,38 @@ def cro_dump(args):
179 """ handle 'crodump' subcommand """ 246 """ handle 'crodump' subcommand """
180 db = Database(args.dbdir) 247 db = Database(args.dbdir)
181 248
182 if db.stru: 249 db.dump(args)
183 print("stru")
184 db.stru.dump(args)
185 if args.struonly:
186 return
187 if db.index:
188 print("index")
189 db.index.dump(args, nodecode=True)
190 if db.bank:
191 print("bank")
192 db.bank.dump(args, nodecode=True)
193 if db.sys:
194 print("sys")
195 db.sys.dump(args)
196 250
197 251
252def destruct(args):
253 """
254 decode the index#1 structure information record
255 Takes hex input from stdin.
256 """
257 import sys
258 data = sys.stdin.buffer.read()
259 data = unhex(data)
260
261 d = dict()
262
263 rd = ByteReader(data)
264
265 o = 0
266 startbyte = rd.readbyte()
267 while not rd.eof():
268 keylen = rd.readbyte()
269 keyname = rd.readbytes(keylen).decode('ascii')
270 if keyname in d:
271 print("duplicate key: %s" % keyname)
272
273 index_or_length = rd.readdword()
274 if index_or_length >> 31:
275 d[keyname] = rd.readbytes(index_or_length & 0x7FFFFFFF)
276 print("%-20s - %s" % (keyname, toout(args, d[keyname])))
277 else:
278 d[keyname] = index_or_length
279 print("%-20s -> %s" % (keyname, d[keyname]))
280
198def main(): 281def main():
199 import argparse 282 import argparse
200 parser = argparse.ArgumentParser(description='CRO hexdumper') 283 parser = argparse.ArgumentParser(description='CRO hexdumper')
@@ -222,6 +305,10 @@ def main():
222 cro.add_argument('dbdir', type=str) 305 cro.add_argument('dbdir', type=str)
223 cro.set_defaults(handler=cro_dump) 306 cro.set_defaults(handler=cro_dump)
224 307
308 des = subparsers.add_parser('destruct', help='Stru dumper')
309 des.add_argument('--ascdump', '-a', action='store_true')
310 des.set_defaults(handler=destruct)
311
225 args = parser.parse_args() 312 args = parser.parse_args()
226 313
227 if args.handler: 314 if args.handler: