Skip to content

Instantly share code, notes, and snippets.

@xyzz
Created January 9, 2014 11:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xyzz/e027d4f1505c6f4dd7d8 to your computer and use it in GitHub Desktop.
Save xyzz/e027d4f1505c6f4dd7d8 to your computer and use it in GitHub Desktop.
import json
import sys
import binascii
from struct import unpack, pack
lines = []
first = None
def gets(data, start):
global first
if not first:
first = start
x = b""
cur = start
while (cur - start) % 2 != 0 or data[cur:cur + 2] != b"\x00\x00":
x += pack("B", data[cur])
cur += 1
s = x.decode("utf-16le")
return s
def dump(data, start):
lines.append(gets(data, start))
def check(data, pos, what):
for x in what:
if data[pos:pos + len(x)] == x:
return True
return False
def hexify(data):
return binascii.hexlify(data).decode("ascii")
skip = {
b"\x0c\x0f": 15,
b"\x18\x06": 6,
}
def to_int(data):
return int.from_bytes(data, byteorder="little")
def decode(input_file, output_file):
# print("working with ", input_name)
data = open(input_file, "rb").read()
size = unpack("<I", data[6:10])[0]
commands = []
commands.append([
"header",
data[2]
])
cur = 0x20
counter = 0
while counter < size and cur < len(data):
banner = data[cur:cur + 2]
# 09 08 AA AA BB BB BB BB
# ^ ^ string address
# | number, in order
if banner == b"\x09\x08":
commands.append([
"msg1",
gets(data, to_int(data[cur + 4:cur + 8]))
])
cur += 8
counter += 1
# 0B 0F AA AA XX XX XX XX XX XX BB BB BB BB XX
# ^ ^ unknown ^ ^ unknown
# | number, in order | string address
elif banner == b"\x0b\x0f":
commands.append([
"msg2",
hexify(data[cur + 4:cur + 10]),
int(data[cur + 14]),
gets(data, to_int(data[cur + 10:cur + 14]))
])
cur += 15
counter += 1
# 13 03 AA
# ^ number of choices
# for each choice:
# XX XX XX XX XX XX BB BB BB BB
# ^ unknown ^ choice string address
elif banner == b"\x13\x03":
cnt = data[cur + 2]
cur += 3
choices = []
for x in range(cnt):
choices.append([
hexify(data[cur:cur + 6]),
gets(data, to_int(data[cur + 6:cur + 10]))
])
cur += 10
commands.append([
"choice",
choices
])
elif banner in skip:
commands.append([
"raw",
hexify(data[cur:cur + skip[banner]])
])
cur += skip[banner]
else:
offset = 0
# i'm stupid so let's have a stupid heuristic here
while cur < len(data) and not check(data, cur, [b"\x09\x08" + pack("<H", counter), b"\x0b\x0f" + pack("<H", counter), b"\x13\x03"]):
cur += 1
offset += 1
data_skip = data[cur - offset:cur]
commands.append([
"raw",
hexify(data_skip)
])
if cur < first:
commands.append([
"raw",
hexify(data[cur:first])
])
if counter != size:
print("WARN: counter != size")
fout = open(output_file, "w")
for command in commands:
fout.write(json.dumps(command, ensure_ascii=False))
fout.write("\n")
fout.close()
# decrease = 0
# while data[-1 - decrease] == 0:
# decrease += 1
# total = len(" ".join(lines).encode("utf-16le"))
# original = len(data) - first - decrease + 1
# print("total is: ", total)
# print("original is: ", original)
#if total == original:
# print("all fine", total)
#if total > original:
# print("WARNING: total > original")
#if original - total > 10:
# print("WARNING: original - total = ", original - total)"""
def encode(input_file, output_file):
commands = []
fin = open(input_file, "r")
for line in fin.readlines():
commands.append(json.loads(line))
fin.close()
data = bytearray()
count = 0
strings = []
magic_header_constant = 0
for command in commands:
if command[0] == "raw":
data += binascii.unhexlify(command[1].encode("ascii"))
elif command[0] == "msg1":
t = b"\x09\x08" + count.to_bytes(2, byteorder="little") + b"\x00\x00\x00\x00"
strings.append([command[1], len(data) + 4])
data += t
count += 1
elif command[0] == "msg2":
t = b"\x0b\x0f" + count.to_bytes(2, byteorder="little") + binascii.unhexlify(command[1].encode("ascii")) + b"\x00\x00\x00\x00" + command[2].to_bytes(1, byteorder="little")
strings.append([command[3], len(data) + 10])
data += t
count += 1
elif command[0] == "choice":
t = b"\x13\x03" + len(command[1]).to_bytes(1, byteorder="little")
for choice in command[1]:
t += binascii.unhexlify(choice[0].encode("ascii")) + b"\x00\x00\x00\x00"
strings.append([choice[1], len(data) + len(t) - 4])
data += t
elif command[0] == "header":
magic_header_constant = command[1]
else:
assert(False == "unknown command")
# generate the header
header = b"\x18\x06" + magic_header_constant.to_bytes(4, byteorder="little") + count.to_bytes(4, byteorder="little") + b"\x00" * 22
data = bytearray(header + data)
# process strings
for string, put_to in strings:
offset = len(data)
data += string.encode("utf-16le") + b"\x00\x00"
# print(string.encode("utf-16le"))
put_to += len(header)
assert(data[put_to:put_to + 4] == b'\x00\x00\x00\x00')
data[put_to:put_to + 4] = offset.to_bytes(4, byteorder="little")
if len(data) % 32 != 0:
data += b"\x00" * (32 - len(data) % 32)
fout = open(output_file, "wb")
fout.write(data)
fout.close()
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: script.py decode|encode INPUT OUTPUT")
print(" - decode decodes .tbl to text")
print(" - encode compiles text to .tbl")
sys.exit(0)
command = sys.argv[1]
if command == "decode":
decode(sys.argv[2], sys.argv[3])
elif command == "encode":
encode(sys.argv[2], sys.argv[3])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment