Invoke on something like dump/*.cst. Needs regex postprocessing, as is common.

   1 import os, sys, zlib
   2 from struct import unpack
   3 
   4 for fname in sys.argv[1:]:
   5     with open(fname, "rb") as f:
   6         fdata = f.read()
   7         
   8         header = fdata[:8].decode("utf-8")
   9         if header != "CatScene":
  10             continue
  11         
  12         compressed_len = unpack("<I", bytes(fdata[0x08:0x0C]))[0]
  13         decompressed_len = unpack("<I", bytes(fdata[0x0C:0x10]))[0]
  14         
  15         decompressed = zlib.decompress(bytes(fdata[0x10:compressed_len+0x10]))
  16         fdata = decompressed
  17         
  18         flen          = unpack("<I", bytes(fdata[0x00:0x04]))[0]
  19         ranges        = unpack("<I", bytes(fdata[0x04:0x08]))[0]
  20         ranges_bytes  = unpack("<I", bytes(fdata[0x08:0x0C]))[0]
  21         headerlen     = unpack("<I", bytes(fdata[0x0C:0x10]))[0]
  22         
  23         # header structure is something like
  24         # uint32 filelen
  25         # uint32 num_ranges
  26         # uint32 len_ranges_bytes
  27         # uint32 len_header_bytes (excluding these first four words)
  28         
  29         # struct[num_ranges] {uint32, uint32} // where first is length of range, second is index of range
  30         # uint32[entries] // indexed into by ranges
  31         # with this part (the two arrays together) being len_header_bytes bytes long
  32         
  33         # we don't actually need any of that for just script ripping
  34         
  35         code = fdata[headerlen+0x10:]
  36         
  37         i = 0
  38         while len(code[i:]) > 0:
  39             # guess
  40             oplen = code[i]
  41             if oplen != 1:
  42                 print("oplen error")
  43                 exit()
  44             i += 1
  45             
  46             op = code[i]
  47             i += 1
  48             if op in [0x20, 0x21, 0x30]:
  49                 string = []
  50                 char = code[i]
  51                 while char != 0:
  52                     string += [char]
  53                     i += 1
  54                     char = code[i]
  55                 i += 1
  56                 string = bytes(string).decode("cp932")
  57                 
  58                 if op == 0x20:
  59                     print(string.replace("\n", "").replace("\\n",""), end="")
  60             elif op in [0x02]: # linefeed
  61                 print("")
  62                 i += 1
  63             else:
  64                 print(f"unknown op {op:02X} at {i+headerlen+0x10:08X} in {fname}")
  65                 exit()
  66         print("")