e.g. BGI 1.64. Like BGI (old) but with a different operation encoding. Used in Nursery Rhyme.
1 #!/usr/bin/env python
2
3 from sys import argv, exit, stdout
4 from mmap import mmap, ACCESS_COPY, ACCESS_READ
5 from struct import unpack, iter_unpack
6
7 def print(s, end="\n"):
8 stdout.buffer.write((str(s) + end).encode("utf-8"))
9
10 def uint(f):
11 return unpack("<I", f.read(4))[0]
12 def ushort(f):
13 return unpack("<H", f.read(2))[0]
14 def byte(f):
15 return unpack("<B", f.read(1))[0]
16 def char(f):
17 return unpack("<c", f.read(1))[0]
18 def string(f):
19 mystr = bytes()
20 c = char(f)
21 while c[0] != 0:
22 mystr += c
23 c = char(f)
24 return mystr
25
26 def short(x):
27 return "0x%04X" % x
28 def long(x):
29 return "0x%08X" % x
30
31 if len(argv) < 2:
32 print("No input given")
33 exit()
34
35 for fn in argv[1:]:
36 with open(fn, "rb") as f:
37 lowest_string_addr = -1
38 nullonly = False
39 string_addrs = []
40 while 1:
41 addr = f.tell()
42 if lowest_string_addr >= 0 and addr+1 >= lowest_string_addr:
43 #print(f"entered string table at {lowest_string_addr:08X} in {fn}")
44 break
45 try:
46 op = ushort(f)
47 except:
48 break
49 #print(f"\nop_{op:04X}")
50 if op in [0x0000]: # means we're in padding, not a NOP
51 nullonly = True
52 pass
53 elif nullonly:
54 #print(f"non-null command at {f.tell()-2:08X} after seeing null command")
55 #print(f"lowest_string_addr is {lowest_string_addr:08X}")
56 break
57 elif op in [0xFEFE]:
58 pass
59 elif op in [
60 0x0011,
61 0x001D,
62 0x0020,
63 0x0021,
64 0x0082,
65 0x0083, # ?
66 0x00C9,
67 0x00B8,
68 ]:
69 pass
70 elif op in [
71 0x0040,
72 0x0041,
73 ]:
74 num = uint(f)
75 num = uint(f)
76 x = string(f)
77 #print(f"{x.decode('shift-jis')}")
78 num = uint(f)
79 num = uint(f)
80 elif op in [
81 0x0042,
82 0x0043,
83 0x0044,
84 0x0045,
85 ]:
86 num = uint(f)
87 num = uint(f)
88 x = string(f)
89 #print(f"{x.decode('shift-jis')}")
90 num = uint(f)
91 elif op in [
92 0x0028,
93 ]:
94 x = string(f)
95 #print(f"{x.decode('shift-jis')}")
96 num = uint(f)
97 #print(f"{num}")
98 elif op in [
99 0x002B,
100 ]:
101 x = string(f)
102 #print(f"{x.decode('shift-jis')}")
103 num = uint(f)
104 elif op in [
105 0x0050,
106 0x0053,
107 ]:
108 x = string(f)
109 #print(f"{x.decode('shift-jis')}")
110 num = uint(f) # not var
111 elif op in [
112 0x0080,
113 ]:
114 x = string(f)
115 #print(f"{x.decode('shift-jis')}")
116 num = uint(f)
117 elif op in [
118 0x0046,
119 0x0047,
120 0x0070,
121 0x0074,
122 ]:
123 num = uint(f) # not var?
124 x = string(f)
125 #print(f"{x.decode('shift-jis')}")
126 num = uint(f) # not var?
127 elif op in [
128 0x0014,
129 0x0085,
130 ]:
131 x = string(f)
132 #print(f"{x.decode('shift-jis')}")
133 elif op in [
134 0x00C8, # command text?
135 ]:
136 x = string(f)
137 #print(f"{x.decode('shift-jis')}")
138 elif op in [
139 0x00C0, # jump to another script (and don't come back?)
140 0x00C1, # jump to another script (and come back?)
141 ]:
142 x = string(f)
143 #print(f"{x.decode('shift-jis')}")
144 elif op in [
145 0x00C2, # often comes after 0x00C0
146 ]:
147 pass
148 elif op in [
149 0x0010,
150 ]:
151 num = uint(f)
152 #print(f"{num}")
153 if num != 0: exit()
154 num = uint(f)
155 num = uint(f)
156 #print(f"{num}") # content text address
157 if lowest_string_addr == -1 or num < lowest_string_addr:
158 lowest_string_addr = num
159 string_addrs += [num]
160 elif op in [
161 0x006D,
162 ]:
163 num = uint(f)
164 #print(f"{num}")
165 num = uint(f)
166 #print(f"{num}")
167 if num != 256: exit()
168 num = uint(f)
169 #print(f"{num}")
170 elif op in [
171 0x0072,
172 0x0076,
173 ]:
174 num = uint(f)
175 #print(f"{num}")
176 num = uint(f)
177 #print(f"{num}")
178 num = uint(f)
179 #print(f"{num}")
180 elif op in [
181 0x0048,
182 ]:
183 num = uint(f)
184 #print(f"{num}")
185 num = uint(f)
186 #print(f"{num}")
187 elif op in [
188 0x0049,
189 ]:
190 num = uint(f)
191 #print(f"{num}")
192 num = uint(f)
193 #print(f"{num}")
194 elif op in [
195 0x0035,
196 0x003D,
197 ]:
198 num = uint(f)
199 #print(f"{num}")
200 num = uint(f)
201 #print(f"{num}")
202 elif op in [
203 0x00A3, # some kind of test/jump?
204 0x00A4, # some kind of test/jump?
205 0x00A5, # some kind of test/jump?
206 0x00A7, # some kind of test/jump?
207 0x00A8, # some kind of test/jump?
208 ]:
209 num = ushort(f)
210 #print(f"{num}")
211 num = ushort(f)
212 #print(f"{num}")
213 num = uint(f)
214 #print(f"{num}")
215 num = uint(f)
216 #print(f"{num}")
217 elif op in [
218 0x00A1, # some kind of test/jump?
219 0x00A2, # some kind of test/jump?
220 ]:
221 num = uint(f)
222 #print(f"{num}")
223 num = uint(f)
224 #print(f"{num}")
225 elif op in [
226 0x00AE, # unconditional?
227 ]:
228 num = uint(f)
229 elif op in [
230 0x002A,
231 0x0052,
232 0x0067,
233 0x0068,
234 0x0069,
235 0x006A,
236 0x006C,
237 0x006F,
238 0x0071,
239 0x0075,
240 0x008C,
241 0x008D,
242 0x008E,
243 0x0090,
244 0x0092,
245 0x00A0,
246 0x00BA,
247 0x00C4,
248 ]:
249 num = uint(f)
250 #print(f"{num}")
251 elif op in [
252 0x0066,
253 ]:
254 num = uint(f)
255 #print(f"{num}")
256 num = uint(f)
257 #print(f"{num}")
258 elif op in [
259 0x0029,
260 0x0051,
261 ]:
262 x = string(f)
263 #print(f"{x.decode('shift-jis')}")
264 x = string(f)
265 #print(f"{x.decode('shift-jis')}")
266 num = uint(f)
267 #print(f"{num}")
268 elif op in [
269 0x0012, # furigana mapper?
270 ]:
271 x = string(f)
272 #print(f"{x.decode('shift-jis')}")
273 x = string(f)
274 #print(f"{x.decode('shift-jis')}")
275 elif op in [
276 0x0013, # reuse furigana mapping?
277 ]:
278 x = string(f)
279 #print(f"{x.decode('shift-jis')}")
280 elif op in [
281 0x00D4,
282 0x00D8,
283 0x00DA,
284 0x00DB,
285 0x00DC,
286 ]:
287 uint(f)
288 elif op in [
289 0x0098,
290 0x0099,
291 ]:
292 ushort(f)
293 ushort(f)
294 ushort(f)
295 ushort(f)
296 elif op in [
297 0x00B9,
298 ]:
299 ushort(f)
300 ushort(f)
301 elif op in [
302 0x00B0,
303 ]:
304 num = uint(f)
305 #print(f"{num}")
306 for i in range(num):
307 x = string(f)
308 #print(f"{x.decode('shift-jis')}")
309 elif op in [
310 0x00A9,
311 ]:
312 num = uint(f)
313 #print(f"{num}")
314 for i in range(num):
315 uint(f)
316 elif op in [
317 0x0034, # effect related
318 ]:
319 uint(f)
320 x = string(f)
321 #print(f"{x.decode('shift-jis')}")
322 for i in range(5):
323 uint(f)
324 elif op in [
325 0x0036, # ?
326 0x016E, # ???
327 ]:
328 for i in range(6):
329 uint(f)
330 elif op in [
331 0x013A,
332 0x0139,
333 ]:
334 uint(f)
335 elif op in [
336 0x0138,
337 ]:
338 uint(f)
339 uint(f)
340 elif op in [
341 0x0137, # some kind of cut effect
342 ]:
343 uint(f)
344 x = string(f)
345 #print(f"{x.decode('shift-jis')}")
346 uint(f)
347 uint(f)
348 uint(f)
349 elif op in [
350 0x002c, # background related (pan and zoom?)
351 ]:
352
353 x = string(f)
354 #print(f"{x.decode('shift-jis')}")
355
356 for i in range(8):
357 uint(f)
358 elif op in [
359 0x0018,
360 0x001C,
361 0x002D, # background related (pan and zoom?)
362 ]:
363
364 for i in range(5):
365 uint(f)
366 elif op in [
367 0x001A,
368 0x001E,
369 ]:
370
371 uint(f)
372 uint(f)
373 uint(f)
374 elif op in [
375 0x001B,
376 ]:
377
378 x = string(f)
379
380 for i in range(7):
381 uint(f)
382 elif op in [
383 0x0088, # play movie or something
384 ]:
385
386 x = string(f)
387 #print(f"{x.decode('shift-jis')}")
388 else:
389 print(f"unknown op {op:04X} at {f.tell()-2:08X} in {fn}")
390 exit(0)
391 if len(string_addrs) > 0:
392 if True: # the right way to do it
393 for addr in string_addrs:
394 f.seek(addr)
395 x = string(f)
396 print(f"{x.decode('shift-jis')}")
397 else: # the wrong way, but validates that we captured all the text
398 f.seek(lowest_string_addr)
399 while True:
400 try:
401 x = string(f)
402 print(f"{x.decode('shift-jis')}")
403 except:
404 break
405 print("\n\n")