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.