[LINUX] Python script for cat/dat file extraction

The place to discuss scripting and game modifications for X³: Terran Conflict and X³: Albion Prelude.

Moderators: Moderators for English X Forum, Scripting / Modding Moderators

akruppa
Posts: 31
Joined: Fri, 25. Oct 13, 13:57
x3ap

[LINUX] Python script for cat/dat file extraction

Post by akruppa »

I got bored with not having access to the most basic scripter tools under Linux. Here is a very quick and very dirty Python 3.2 hack to extract files from .cat/.dat file pairs. Say

python3 path/to/01.cat

to get a file listing, and

python3 path/to/01.cat foo/bar/filename

to extract that file from the dat to "foo/bar/filename". An existing file is overwritten.

Almost completely untested, use at your own risk.

Code: Select all

import sys
import os.path

class cat(object):
    def __init__(self, catfilename):
        with open(catfilename, "rb") as inputfile:
            data = inputfile.read()
        self.datfilename, *lines = self.cat_decrypt(data).splitlines()
        self.entries = {}
        nextpos = 0
        for line in lines:
            name, length = line.rsplit(" ", 1)
            length = int(length)
            self.entries[name] = (nextpos, length)
            nextpos += length
        try:
            self.datfile = open(os.path.dirname(catfilename) + '/' + self.datfilename, "rb")
        except FileNotFoundError:
            self.datfile = open(os.path.dirname(catfilename) + '/' + 
                    os.path.basename(catfilename).split(".")[0] + ".dat", "rb")

    @staticmethod
    def cat_magic():
        magic = 0xDA
        while True:
            magic = (magic + 1) % 256
            yield magic

    @staticmethod
    def cat_decrypt(data):
        magic = cat.cat_magic()
        return "".join([chr(b ^ next(magic)) for b in data])

    def list(self):
        return tuple(self.entries.keys())

    def read_dat(self, pos, length):
        self.datfile.seek(pos)
        return bytes([b ^ 0x33 for b in self.datfile.read(length)])

    def read_file(self, filename):
        if not filename in self.entries:
            return None
        return self.read_dat(*self.entries[filename])

    def copyfile(self, filename):
        data = self.read_file(filename)
        if not data:
            sys.stderr.write("Could not file %s\n" % filename)
            return
        os.makedirs(os.path.dirname(filename), exist_ok=True)
        with open(filename, "wb") as outputfile:
            outputfile.write(data)

if len(sys.argv) == 1:
  print("%s: <cat file name> [file to extract]")
  sys.exit()

c = cat(sys.argv[1])
if len(sys.argv) == 2:
  print("\n".join(c.list()))
elif len(sys.argv) == 3:
  c.copyfile(sys.argv[2])
User avatar
vukica
Posts: 1744
Joined: Sun, 10. Aug 08, 18:05
x4

Post by vukica »

python <3
Split say NEED MORE FIREPOWER!!

Return to “X³: Terran Conflict / Albion Prelude - Scripts and Modding”