122 lines
3.0 KiB
Python
Executable File
122 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
LONG_KEY = (
|
|
0x6c, 0x1b, 0x99, 0x5f, 0xb9, 0xcd, 0x5f, 0x13,
|
|
0xcb, 0x04, 0x20, 0x0e, 0x5e, 0x1c, 0xa1, 0x0e,
|
|
)
|
|
OTHER_LONG_KEY = (
|
|
0x67, 0xa8, 0xd6, 0x26, 0xb6, 0xdd, 0x45, 0x1b,
|
|
0x32, 0x7e, 0x22, 0x13, 0x15, 0xc2, 0x94, 0x37,
|
|
)
|
|
|
|
|
|
class Byte:
|
|
def __init__(self, v: int):
|
|
self.v = v & 0xff
|
|
|
|
def __add__(self, v: int):
|
|
return Byte(self.v + v)
|
|
|
|
def __iadd__(self, v: "Byte"):
|
|
self.v = (self.v + v) & 0xff
|
|
return self
|
|
|
|
def __sub__(self, v: int):
|
|
return Byte(self.v - v)
|
|
|
|
def __isub__(self, v: "Byte"):
|
|
self.v = (self.v - v) & 0xff
|
|
return self
|
|
|
|
def __mod__(self, v: int):
|
|
return Byte(self.v % v)
|
|
|
|
def __imod__(self, v: int):
|
|
self.v %= v
|
|
return self
|
|
|
|
def __xor__(self, v: int):
|
|
return Byte(self.v ^ v)
|
|
|
|
def __ixor__(self, v: int):
|
|
self.v = (self.v ^ v) & 0xff
|
|
return self
|
|
|
|
def __and__(self, v: int):
|
|
return Byte(self.v & v)
|
|
|
|
def __iand__(self, v: int):
|
|
self.v = (self.v & v) & 0xff
|
|
return self
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Byte):
|
|
return self.v == other.v
|
|
elif isinstance(other, int):
|
|
return self.v == (other & 0xff)
|
|
raise ValueError(f"Object {other:r} of invalid type {type(other)}")
|
|
|
|
|
|
def decode_line(line: bytes, method: int) -> str:
|
|
line = line.rstrip(b"\r\n")
|
|
key = LONG_KEY
|
|
seed = len(line) % len(LONG_KEY)
|
|
dline = bytearray(len(line))
|
|
for i, c in enumerate(line):
|
|
b = Byte(c)
|
|
if dline[i - 2:i] == b'//':
|
|
key = OTHER_LONG_KEY
|
|
if method == 1:
|
|
if b == ord(b'\t'):
|
|
b = Byte(0x9f)
|
|
|
|
b -= 0x20
|
|
b ^= key[seed]
|
|
b &= 0x7f
|
|
b += 0x20
|
|
|
|
seed += 7
|
|
seed %= len(key)
|
|
|
|
if b == 0x9f:
|
|
b = Byte(ord(b'\t'))
|
|
else:
|
|
if b == ord(b'\t'):
|
|
b = Byte(0x80)
|
|
|
|
b -= 0x20
|
|
if (b & 0x80) == 0:
|
|
b ^= key[seed] & 0x7f
|
|
b += 0x20
|
|
|
|
seed += 7
|
|
seed %= len(key)
|
|
|
|
if b == 0x80:
|
|
b = Byte(ord(b'\t'))
|
|
dline[i] = b.v
|
|
return dline.decode(errors="replace")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(allow_abbrev=False, description="Decode a Carmageddon encoded file")
|
|
parser.add_argument("file", metavar="FILE", nargs="?", help="input file (default=stdin)")
|
|
parser.add_argument("--method", choices=[1, 2], type=int, default=2, help="decryption method to use")
|
|
args = parser.parse_args()
|
|
|
|
istream = open(args.file, "rb") if args.file else sys.stdin.buffer
|
|
|
|
for line in istream.readlines():
|
|
if line[0] == ord(b'@'):
|
|
dline = decode_line(line[1:], args.method)
|
|
print(dline)
|
|
else:
|
|
print(line.decode())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|