Extract Scene.pck with https://github.com/xmoeproject/SiglusExtract/tree/master/binary (requires game data - executable, Gameexe.dat, etc)

Dump content text with:

   1 #!python
   2 import sys, struct
   3 
   4 fname = ""
   5 
   6 #suppress_print = True
   7 suppress_print = False
   8 
   9 def r4(f):
  10     return struct.unpack("<I", f.read(4))[0]
  11 
  12 def r2(f):
  13     return struct.unpack("<H", f.read(2))[0]
  14 
  15 def r1(f):
  16     return struct.unpack("<B", f.read(1))[0]
  17 
  18 def print(s, end="\n"):
  19     if suppress_print: return
  20     sys.stdout.buffer.write((str(s) + str(end)).encode('utf-8'))
  21 
  22 def dump_text(f, n):
  23     was = f.tell()
  24     
  25     key = (table[n][2] * 0x7087)%0x10000
  26     array = []
  27     f.seek(table[n][0]*2 + table[n][3])
  28     for i in range(table[n][1]):
  29         r = r2(f)
  30         r = r ^ key
  31         array += [((r&0x00FF)>> 0)&0xFF]
  32         array += [((r&0xFF00)>> 8)&0xFF]
  33     array = bytes(array).decode(encoding="utf-16-le", errors="ignore")
  34     #print(array)
  35     f.seek(was)
  36     
  37     return array
  38 
  39 
  40 for arg in sys.argv[1:]:
  41     table = []
  42     code = []
  43     if arg.endswith(".ss"):
  44         #print(f"dumping {arg}")
  45         fname = arg
  46         with open(arg, "rb") as f:
  47             f.seek(4)
  48             start = r4(f)
  49             end = start+r4(f)
  50             tablepos = r4(f)
  51             tablelen = r4(f)
  52             textoffset = r4(f)
  53             f.seek(tablepos)
  54             for i in range(tablelen):
  55                 offset = r4(f)
  56                 table += [[offset, r4(f), i, textoffset]]
  57             f.seek(start)
  58             while f.tell() < end:
  59                 code += [r1(f)]
  60             for i in range(len(code)):
  61                 if tuple(code[i:i+5]) == (0x02, 0x14, 0, 0, 0) and code[i+9] == 0x31:
  62                     #print(" ".join([f"{x:02X}" for x in code[i:i+10]]))
  63                     which = struct.unpack("<I",bytes(code[i+5:i+9]))[0]
  64                     #print(f"{which:08X}")
  65                     array = dump_text(f, which)
  66                     print(array)

Yes, I tried doing it the right way by parsing the bytecode, but there's an operation (0x30) with stateful variable-length-ness, and nothing I did would fix it. I got it working for one game (hatsuyuki sakura), but it failed on another (rewrite+). At that point I gave up and did the above. 0x02 is the command for pushing stuff onto the stack, 14 00 00 00 means it's a string, and 0x31 displays the text. I have reason to suspect that 0x31 doesn't actually induce a linebreak, so this should be considered flawed, but without real engine documentation it's the best I can do. If you want the bytecode parser that worked for hatsuyuki sakura, @ me on twitter.