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): |
