diff options
-rw-r--r-- | crodump.py | 74 | ||||
-rw-r--r-- | hexdump.py | 9 |
2 files changed, 72 insertions, 11 deletions
@@ -1,13 +1,23 @@ | |||
1 | import os.path | 1 | import os.path |
2 | import io | ||
2 | import struct | 3 | import struct |
3 | from binascii import b2a_hex | 4 | from binascii import b2a_hex |
4 | from hexdump import hexdump, asasc, tohex | 5 | from hexdump import hexdump, asasc, tohex, unhex |
5 | from koddecoder import kodecode | 6 | from koddecoder import kodecode |
6 | """ | 7 | """ |
7 | python3 crodump.py crodump chechnya_proverki_ul_2012 | 8 | python3 crodump.py crodump chechnya_proverki_ul_2012 |
8 | python3 crodump.py kodump -s 6 -o 0x4cc9 -e 0x5d95 chechnya_proverki_ul_2012/CroStru.dat | 9 | python3 crodump.py kodump -s 6 -o 0x4cc9 -e 0x5d95 chechnya_proverki_ul_2012/CroStru.dat |
9 | """ | 10 | """ |
10 | 11 | ||
12 | def enumunreferenced(ranges, filesize): | ||
13 | o = 0 | ||
14 | for start, end, desc in sorted(ranges): | ||
15 | if start > o: | ||
16 | yield o, start-o | ||
17 | o = end | ||
18 | if o<filesize: | ||
19 | yield o, filesize-o | ||
20 | |||
11 | class Datafile: | 21 | class Datafile: |
12 | def __init__(self, dat, tad): | 22 | def __init__(self, dat, tad): |
13 | self.dat = dat | 23 | self.dat = dat |
@@ -15,6 +25,9 @@ class Datafile: | |||
15 | 25 | ||
16 | self.readtad() | 26 | self.readtad() |
17 | 27 | ||
28 | self.dat.seek(0, io.SEEK_END) | ||
29 | self.datsize = self.dat.tell() | ||
30 | |||
18 | def readtad(self): | 31 | def readtad(self): |
19 | self.tad.seek(0) | 32 | self.tad.seek(0) |
20 | hdrdata = self.tad.read(2*4) | 33 | hdrdata = self.tad.read(2*4) |
@@ -28,21 +41,37 @@ class Datafile: | |||
28 | 41 | ||
29 | def dump(self, args, dokodecode=False, plainbytes=0): | 42 | def dump(self, args, dokodecode=False, plainbytes=0): |
30 | print("tadhdr: %08x %08x" % tuple(self.tadhdr)) | 43 | print("tadhdr: %08x %08x" % tuple(self.tadhdr)) |
44 | ranges = [] | ||
31 | for i, (ofs, ln, chk) in enumerate(self.tadidx): | 45 | for i, (ofs, ln, chk) in enumerate(self.tadidx): |
32 | if ln==0xFFFFFFFF: | 46 | if ln==0xFFFFFFFF: |
33 | print("%5d: %08x %08x %08x" % (i, ofs, ln, chk)) | 47 | print("%5d: %08x %08x %08x" % (i, ofs, ln, chk)) |
34 | continue | 48 | continue |
35 | flags = ln>>24 | 49 | flags = ln>>24 |
50 | |||
36 | ln &= 0xFFFFFFF | 51 | ln &= 0xFFFFFFF |
37 | dat = self.readdata(ofs, ln) | 52 | dat = self.readdata(ofs, ln) |
38 | plain = b'' | 53 | plain = b'' |
54 | decrypted = ' ' | ||
39 | if dokodecode and not args.nokod: | 55 | if dokodecode and not args.nokod: |
40 | plain = dat[:plainbytes] | 56 | pb = plainbytes if flags else 8 |
41 | dat = kodecode(i+1, dat[plainbytes:]) | 57 | plain = dat[:pb] |
58 | dat = kodecode(i+1, dat[pb:]) | ||
59 | decrypted = '*' if flags else '+' | ||
42 | if args.ascdump: | 60 | if args.ascdump: |
43 | print("%5d: %08x-%08x: (%02x:%08x) %s %s" % (i, ofs, ofs+ln, flags, chk, tohex(plain), asasc(dat))) | 61 | print("%5d: %08x-%08x: (%02x:%08x) %s %s%s" % (i, ofs, ofs+ln, flags, chk, tohex(plain), decrypted, asasc(dat))) |
44 | else: | 62 | else: |
45 | print("%5d: %08x-%08x: (%02x:%08x) %s %s" % (i, ofs, ofs+ln, flags, chk, tohex(plain), tohex(dat))) | 63 | print("%5d: %08x-%08x: (%02x:%08x) %s %s%s" % (i, ofs, ofs+ln, flags, chk, tohex(plain), decrypted, tohex(dat))) |
64 | ranges.append((ofs, ofs+ln, "item #%d" % i)) | ||
65 | |||
66 | if args.verbose: | ||
67 | # output parts not referenced in the .tad file. | ||
68 | for o, l in enumunreferenced(ranges, self.datsize): | ||
69 | dat = self.readdata(o, l) | ||
70 | if args.ascdump: | ||
71 | print("%08x-%08x: %s" % (o, o+l, asasc(dat))) | ||
72 | else: | ||
73 | print("%08x-%08x: %s" % (o, o+l, tohex(dat))) | ||
74 | |||
46 | 75 | ||
47 | class Database: | 76 | class Database: |
48 | def __init__(self, dbdir): | 77 | def __init__(self, dbdir): |
@@ -62,6 +91,12 @@ class Database: | |||
62 | def getname(self, name, ext): | 91 | def getname(self, name, ext): |
63 | return os.path.join(self.dbdir, "Cro%s.%s" % (name, ext)) | 92 | return os.path.join(self.dbdir, "Cro%s.%s" % (name, ext)) |
64 | 93 | ||
94 | def incdata(data, s): | ||
95 | """ | ||
96 | add 's' to each byte. | ||
97 | This is useful for finding the correct shift from an incorrectly shifted chunk. | ||
98 | """ | ||
99 | return b"".join(struct.pack("<B", (_+s)&0xFF) for _ in data) | ||
65 | 100 | ||
66 | def decode_kod(args, data): | 101 | def decode_kod(args, data): |
67 | """ | 102 | """ |
@@ -75,6 +110,17 @@ def decode_kod(args, data): | |||
75 | args.shift = int(args.shift, 0) | 110 | args.shift = int(args.shift, 0) |
76 | enc = kodecode(args.shift, data) | 111 | enc = kodecode(args.shift, data) |
77 | hexdump(args.offset, enc) | 112 | hexdump(args.offset, enc) |
113 | elif args.increment: | ||
114 | # explicitly specified shift. | ||
115 | for s in range(256): | ||
116 | enc = incdata(data, s) | ||
117 | if args.ascdump: | ||
118 | print("%02x: %s" % (s, asasc(enc))) | ||
119 | else: | ||
120 | print("%02x: %s" % (s, tohex(enc))) | ||
121 | |||
122 | |||
123 | |||
78 | else: | 124 | else: |
79 | # output with all possible 'shift' values. | 125 | # output with all possible 'shift' values. |
80 | for s in range(256): | 126 | for s in range(256): |
@@ -106,6 +152,8 @@ def kod_hexdump(args): | |||
106 | # no filename -> read from stdin. | 152 | # no filename -> read from stdin. |
107 | import sys | 153 | import sys |
108 | data = sys.stdin.buffer.read() | 154 | data = sys.stdin.buffer.read() |
155 | if args.unhex: | ||
156 | data = unhex(data) | ||
109 | decode_kod(args, data) | 157 | decode_kod(args, data) |
110 | 158 | ||
111 | 159 | ||
@@ -115,6 +163,8 @@ def cro_dump(args): | |||
115 | if db.stru: | 163 | if db.stru: |
116 | print("stru") | 164 | print("stru") |
117 | db.stru.dump(args, dokodecode=True) | 165 | db.stru.dump(args, dokodecode=True) |
166 | if args.struonly: | ||
167 | return | ||
118 | if db.index: | 168 | if db.index: |
119 | print("index") | 169 | print("index") |
120 | db.index.dump(args) | 170 | db.index.dump(args) |
@@ -132,20 +182,24 @@ def main(): | |||
132 | subparsers = parser.add_subparsers() | 182 | subparsers = parser.add_subparsers() |
133 | parser.set_defaults(handler=None) | 183 | parser.set_defaults(handler=None) |
134 | 184 | ||
135 | ko = subparsers.add_parser('kodump', help='KOD dumper') | 185 | ko = subparsers.add_parser('kodump', help='KOD/hex dumper') |
136 | ko.add_argument('--offset', '-o', type=str, default="0") | 186 | ko.add_argument('--offset', '-o', type=str, default="0") |
137 | ko.add_argument('--length', '-l', type=str) | 187 | ko.add_argument('--length', '-l', type=str) |
138 | ko.add_argument('--endofs', '-e', type=str) | 188 | ko.add_argument('--endofs', '-e', type=str) |
139 | ko.add_argument('--shift', '-s', type=str) | 189 | ko.add_argument('--unhex', '-x', action='store_true', help="assume the input contains hex data") |
140 | ko.add_argument('--ascdump', '-a', action='store_true') | 190 | ko.add_argument('--shift', '-s', type=str, help="KOD decode with the specified shift") |
141 | ko.add_argument('--nokod', '-n', action='store_true') | 191 | ko.add_argument('--increment', '-i', action='store_true', help="assume data is already KOD decoded, but with wrong shift -> dump alternatives.") |
142 | ko.add_argument('filename', type=str, nargs='?') | 192 | ko.add_argument('--ascdump', '-a', action='store_true', help="CP1251 asc dump of the data") |
193 | ko.add_argument('--nokod', '-n', action='store_true', help="don't KOD decode") | ||
194 | ko.add_argument('filename', type=str, nargs='?', help="dump either stdin, or the specified file") | ||
143 | ko.set_defaults(handler=kod_hexdump) | 195 | ko.set_defaults(handler=kod_hexdump) |
144 | 196 | ||
145 | cro = subparsers.add_parser('crodump', help='CROdumper') | 197 | cro = subparsers.add_parser('crodump', help='CROdumper') |
198 | cro.add_argument('--verbose', '-v', action='store_true') | ||
146 | cro.add_argument('--kodecode', '-k', action='store_true') | 199 | cro.add_argument('--kodecode', '-k', action='store_true') |
147 | cro.add_argument('--ascdump', '-a', action='store_true') | 200 | cro.add_argument('--ascdump', '-a', action='store_true') |
148 | cro.add_argument('--nokod', '-n', action='store_true') | 201 | cro.add_argument('--nokod', '-n', action='store_true') |
202 | cro.add_argument('--struonly', action='store_true') | ||
149 | cro.add_argument('dbdir', type=str) | 203 | cro.add_argument('dbdir', type=str) |
150 | cro.set_defaults(handler=cro_dump) | 204 | cro.set_defaults(handler=cro_dump) |
151 | 205 | ||
@@ -1,9 +1,16 @@ | |||
1 | import struct | 1 | import struct |
2 | from binascii import b2a_hex | 2 | from binascii import b2a_hex, a2b_hex |
3 | """ | 3 | """ |
4 | Simple hexdump, 16 bytes per line with offset. | 4 | Simple hexdump, 16 bytes per line with offset. |
5 | """ | 5 | """ |
6 | 6 | ||
7 | def unhex(data): | ||
8 | if type(data)==bytes: | ||
9 | data = data.decode('ascii') | ||
10 | data = data.replace(' ', '') | ||
11 | data = data.strip() | ||
12 | return a2b_hex(data) | ||
13 | |||
7 | def ashex(line): | 14 | def ashex(line): |
8 | return " ".join("%02x" % _ for _ in line) | 15 | return " ".join("%02x" % _ for _ in line) |
9 | def aschr(b): | 16 | def aschr(b): |