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

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

CatSystem2 (last edited 2018-05-13 02:17:11 by weh)