diff --git a/docs/re/data/isl-stage02-dialogue.txt b/docs/re/data/isl-stage02-dialogue.txt new file mode 100644 index 0000000..b572289 --- /dev/null +++ b/docs/re/data/isl-stage02-dialogue.txt @@ -0,0 +1,220 @@ +# Stage02.ssb — dialogue, in script order + +Generated by `tools/re-capture/isl_dialogue.py`. Each line is one +`request_script_message` (built-in 64) and the caption it plays. + +213 message calls, 213 with text. + +004EFC MSG_VOICE_D_252 Rhino 3 has been shot down! +004FA4 MSG_VOICE_D_254 I've lost contact with Rhino 3! +00504C MSG_VOICE_D_256 Katana, come in! Katanaaaa! +0050F4 MSG_VOICE_D_257 Katana! Damn it! +0053C8 MSG_ACRO_034 This is an emergency announcement from the bridge! The ship is operational! All personnel evacuate immediately! +005470 MSG_ACRO_055 Looks like this is it! All personnel, abandon ship! +005518 MSG_ACRO_086 Captain! The ACROPOLIS can't go any further! +0055C0 MSG_VOICE_D_258 The ACROPOLIS has been crippled. +005668 MSG_VOICE_D_260 Katana, the ACROPOLIS has been destroyed! +005710 MSG_VOICE_D_261 Damn, they got the ACROPOLIS! た! +005AE4 MSG_VOICE_A_162 Katana, are you sure you're a rookie? You're too good! +005F80 MSG_VOICE_D_100 This foolishness can't last forever. We're still in good shape! +0061D0 MSG_VOICE_D_259 This is the TCAF ACROPOLIS. We are unable to continue combat! +0064C0 MSG_VOICE_D_180 We're okay now. Thanks for the backup! +006588 MSG_VOICE_D_183 The ship is in the clear now. +0069A4 MSG_VOICE_D_383 One fleet of the enemy targets has been destroyed! +007530 MSG_VOICE_D_285 Targets sighted! They're coming right for us! +0075D8 MSG_VOICE_A_120 Listen, aim for the attackers. Do not let the attackers near the base or the ACROPOLIS. +007678 MSG_VOICE_A_139 The Terran ships are blocking our path! +0077E8 MSG_VOICE_A_141 Enemy fleet sighted. Let's take care of two birds with one stone. All ships prepare to attack! +007898 MSG_VOICE_A_153 Katana, find the enemy attackers and coordinate your attacks with Ellen. +007948 MSG_VOICE_A_126 I repeat, a group of enemy aircraft is approaching! They're heading for Schlos Base! +0079F8 MSG_VOICE_A_124 Radar shows a group of enemy attackers on a direct course for the ACROPOLIS! +007A98 MSG_VOICE_A_152 Listen, if the ACROPOLIS gets in trouble, we have to bail it out fast! +007B48 MSG_VOICE_A_128 Enemy reinforcements! All units, intercept! +007BE8 MSG_VOICE_A_155 You're not getting to Schlos! +007C98 MSG_VOICE_A_123 Another group of attackers has appeared! All ships move to intercept them at once! +007D38 MSG_VOICE_A_169 No matter how many I shoot down, there's always more! +007DE8 MSG_VOICE_A_127 Captain, another group of enemy units is approaching! They could be fighters! +007E88 MSG_VOICE_A_163 This place is swarming with ADAN. When did they get so many ships? +007F38 MSG_VOICE_A_164 This is just like a real war. +007FE8 MSG_VOICE_A_174 Uppity backwater colonists! +008088 MSG_VOICE_A_175 Don't let any of them escape! Fire hard and heavy! +008138 MSG_VOICE_A_166 Don't get pulled into battle with the main fleet. +0081E8 MSG_VOICE_A_003 Finish them off in 3 minutes! +008298 MSG_VOICE_A_004 2 minutes left! Hurry! +008348 MSG_VOICE_A_005 1 minute left! No time to slack off! +0083F8 MSG_VOICE_A_006 Only 30 seconds left! +0085C8 MSG_VOICE_A_168 Damn it! Cut me some slack here! +008744 MSG_VOICE_A_125 A group of enemy aircraft is approaching! +0088C0 MSG_VOICE_A_156 There's too many! We can't stop them all! +008960 MSG_VOICE_A_142 It's time to take back everything you Terran scum stole from us! +008B30 MSG_VOICE_A_171 We've got to keep the enemy away from the ACROPOLIS! Cover me! +008CCC MSG_VOICE_D_286 Attackers approaching! +008D6C MSG_VOICE_A_157 Make sure you don't get hit! If even one of us dies here, we all go down! +008E0C MSG_VOICE_A_158 Who would've thought the ADAN fleet was this big. +008F10 MSG_VOICE_A_144 What are those orange ships? They don't look like normal ARROWHEADS! +009080 MSG_VOICE_D_340 All right! Good work! +009120 MSG_VOICE_A_159 Hey, Gene, do you have your shields up? +009224 MSG_VOICE_A_173 Hey, what squadron are those orange fighters from? I've never seen ships like that before. +009360 MSG_VOICE_D_160 The enemies have been destroyed. We're in the clear, Captain! +009400 MSG_VOICE_A_160 Even with shields, this Delta Saber isn't all that different from my old ARROWHEAD. I've already got it all figured out! +0094A0 MSG_VOICE_A_151 ACROPOLIS to all flights. If you run low on ammunition, return and resupply as necessary. +0095A4 MSG_VOICE_A_136 How could this happen? We're the only ones left! +0096CC MSG_VOICE_A_138 More enemies have entered Sector 37! +0097D0 MSG_VOICE_A_135 Damn, this is bad! The way things are going now, Brandon's lucky he died first! +0099D4 MSG_VOICE_D_000 This is the TCAF CHARON 027. Requesting backup immediately. We're surrounded by enemy fighters! +009B70 MSG_VOICE_D_001 All right, we managed to hang in there. Hurry with those emergency repairs! +009CE0 MSG_VOICE_A_167 I can't shake him! +009EE0 MSG_VOICE_D_155 Captain, the enemy is closing in on our ship! +009FE4 MSG_VOICE_D_157 This is the ACROPOLIS! We're taking concentrated fire! Requesting immediate backup! +00A084 MSG_VOICE_D_159 Katana, it looks like the ACROPOLIS is in trouble! We have to help them! +00A374 MSG_VOICE_D_287 Katana, those attackers are our targets! +00A51C MSG_VOICE_D_288 There's an attacker over there! +00BD2C MSG_VOICE_D_350 Katana, we're pretty banged up. Let's head back to the ACROPOLIS for repairs. +00EA64 MSG_VOICE_D_290 This is bad! Protect the ACROPOLIS! Otherwise, we won't have anywhere to return to! +00EB18 MSG_VOICE_D_291 The ACROPOLIS is taking a beating! Katana, help them! +00F91C MSG_VOICE_D_354 When you get close enough to resupply, information will appear on your indicator. +0108BC MSG_VOICE_D_292 Katana, what are you doing? We have to help the ACROPOLIS! Hurry! +010B4C MSG_VOICE_D_295 We're going to help them, right? It's our duty! +010E6C MSG_VOICE_D_296 Rhino 4 to CHARON 027. We're here to assist you! +01104C MSG_VOICE_D_298 We'll let another unit handle the CHARON. We wouldn't make it there in time anyway. +01126C MSG_VOICE_D_341 Katana, well done! +0137E0 MSG_VOICE_D_349 Hey, Katana. Why aren't you following your directives? +014320 MSG_VOICE_D_348 You aren't listening to me at all, are you Katana? Fine, I'll keep my advice to myself then. +0143E8 MSG_VOICE_D_347 Katana, do you think you should act on your own so much? Sometimes it's best to just follow orders. +0144B0 MSG_VOICE_D_346 Hey Katana. Are you going to ignore your orders? Don't come crying to me if the Leader gets mad. +0145CC MSG_VOICE_D_293 The CHARON 027 is under attack by enemy fighters. They are requesting aid from any nearby units. +0147EC MSG_VOICE_D_294 It looks like the CHARON is very far away. What should we do? +019358 MSG_VOICE_D_252 Rhino 3 has been shot down! +019400 MSG_VOICE_D_254 I've lost contact with Rhino 3! +0194A8 MSG_VOICE_D_256 Katana, come in! Katanaaaa! +019550 MSG_VOICE_D_257 Katana! Damn it! +0197F8 MSG_VOICE_A_182 But how? Is this the end for me? +0198A4 MSG_VOICE_A_183 Admiral Vieira's ship has been shot down! +01A824 MSG_VOICE_B_079 They've broken through! Inform Headquarters! +01AA20 MSG_VOICE_D_089 Magnificent! We broke through without taking a single hit! +01AAE0 MSG_TCAF_480 Yes! Watch and weep, ADAN! +01F2C4 MSG_VOICE_A_176 Rhino Leader, I've sent a tanker around to your location. +01F364 MSG_VOICE_A_177 Raymond, we'll take care of the ACROPOLIS. +01F51C MSG_VOICE_A_201 Katana, I'm worried about the ACROPOLIS. I wonder how things are going at Schlos Base. +01F5CC MSG_VOICE_A_200 Right now all we can do is protect the flagship. +01F67C MSG_VOICE_A_203 You just got drafted, didn't you? Well, that's the reality of war. I feel sorry for you, kid. +01F72C MSG_VOICE_A_184 Object approaching Schlos Base at high speed! +01F904 MSG_VOICE_A_179 That could be the TCAF's flagship! Do whatever it takes to bring it down! +01F9A4 MSG_VOICE_A_181 The enemy is aiming for our ship! Where is our support? +01FA64 MSG_VOICE_D_299 Katana, destroy the ship's engine to stop them in their tracks! Don't let them near the flagship! +01FC3C MSG_VOICE_A_180 Cannons ready! Don't let the enemy flagship escape! +020490 MSG_VOICE_A_190 When attacking an enemy ship, you can't just go in firing blindly. You've got to disable the ship's functionality. +020530 MSG_VOICE_A_191 Destroy the enemy's shield projector or weapons first for an easier victory. Keep that in mind, Katana! +0205D0 MSG_VOICE_A_192 If you destroy the shield projectors, we can use our beam weapons more effectively. +020670 MSG_VOICE_A_194 Aim for the enemy's weapons to weaken their offense. +020710 MSG_VOICE_A_195 Destroy the enemy's engine so they can't move. +0207B0 MSG_VOICE_A_196 Another effective technique is to hit the bridge so you can disrupt its command system. +020964 MSG_VOICE_A_197 Rhinos, don't let the CALIBAN out of your sight. +020B3C MSG_VOICE_A_185 Enemies approaching from directly ahead! +020CF8 MSG_VOICE_A_187 Don't let them near this ship! Now all your training will pay off! +020D98 MSG_VOICE_A_199 Hey, Katana, isn't that Admiral a jerk? I hate him! +020F18 MSG_VOICE_A_202 Stay alert! Any enemies here are likely to attack us all at once! +0210F0 MSG_VOICE_D_162 Admiral Vieira, enemies are approaching our ship! +0212AC MSG_VOICE_D_002 This is the AMALTHEA. We're surrounded by the enemy. Requesting immediate backup! +0213E4 MSG_VOICE_D_304 We're receiving a request for aid from the cruiser AMALTHEA. Any units in the area, please assist them. +021608 MSG_VOICE_D_003 Thank you for the support! We should be okay on our own now! +0216A8 MSG_VOICE_D_004 All fighters scramble immediately! Now it's our turn to help our comrades! +0218E0 MSG_VOICE_A_188 What are you doing? Dispose of them right now! +021A08 MSG_VOICE_A_189 What are you waiting for? Are those Delta Sabers of yours junk? +021B0C MSG_VOICE_D_175 Damn, somebody get these guys off of us! At this rate we'll be sunk in no time! +021BAC MSG_VOICE_D_176 Katana, one of our ships is in trouble. Should we go help them? +021CAC MSG_VOICE_D_175 Damn, somebody get these guys off of us! At this rate we'll be sunk in no time! +021D4C MSG_VOICE_D_176 Katana, one of our ships is in trouble. Should we go help them? +021E10 MSG_VOICE_D_166 That took you long enough! Now get rid of the rest! +021EF8 MSG_VOICE_D_167 Admiral Vieira's ship is safe! +022034 MSG_VOICE_A_198 Don't stray too far from the CALIBAN. Protecting it is our first priority. +022878 MSG_VOICE_D_351 Katana, we're in pretty bad shape. We should head to the tanker soon for repairs. +022918 MSG_VOICE_D_357 Katana, can you see it? The tanker is over there. +023180 MSG_VOICE_D_305 The AMALTHEA is very far away! Can we make it in time? +023948 MSG_VOICE_D_300 This is some pretty heavy fire. Katana, Ellen, destroy that ship's weapons. We can't let the flagship take any more hits! +023A64 MSG_VOICE_D_354 When you get close enough to resupply, information will appear on your indicator. +023DE0 MSG_VOICE_D_306 Assist the AMALTHEA? Roger! +0241D0 MSG_VOICE_D_307 Yeah, there's nothing we can do. If we leave the flagship there's no telling what he'll say. +0249D4 MSG_VOICE_D_303 This guy is pretty tough. Katana, can you take out the enemy's shield projector? +0298A0 MSG_VOICE_D_007 Captain, enemy forces are closing in! +029940 MSG_VOICE_D_006 The Terran forces are in disarray! Hit their carrier! +029C48 MSG_VOICE_A_206 An enemy missile has been fired. There's only one! +02A368 MSG_VOICE_A_212 More enemy cruise missiles have jumped out! How many does that make? +02A408 MSG_VOICE_A_210 We've picked up more incoming objects. 2 missiles! +02A4A8 MSG_VOICE_A_231 It's up to our squadron to stop the missiles! +02B47C MSG_VOICE_A_208 Three more enemy cruise missiles are approaching! +02B684 MSG_VOICE_D_252 Rhino 3 has been shot down! +02B72C MSG_VOICE_D_254 I've lost contact with Rhino 3! +02B7D4 MSG_VOICE_D_256 Katana, come in! Katanaaaa! +02B87C MSG_VOICE_D_257 Katana! Damn it! +02BB50 MSG_ACRO_034 This is an emergency announcement from the bridge! The ship is operational! All personnel evacuate immediately! +02BBF8 MSG_ACRO_055 Looks like this is it! All personnel, abandon ship! +02BCA0 MSG_ACRO_086 Captain! The ACROPOLIS can't go any further! +02BD48 MSG_VOICE_D_258 The ACROPOLIS has been crippled. +02BDF0 MSG_VOICE_D_260 Katana, the ACROPOLIS has been destroyed! +02BE98 MSG_VOICE_D_261 Damn, they got the ACROPOLIS! た! +02C150 MSG_VOICE_A_222 Captain, we've lost contact with Schlos Base! +02CC00 MSG_VOICE_D_008 The enemy forces have been destroyed! +02CD08 MSG_VOICE_D_012 Thanks to you all, we kept damages to the base to a minimum. You have my gratitude! +02CDB0 MSG_VOICE_D_345 I'm glad that went well. +02D140 MSG_VOICE_D_345 I'm glad that went well. +02D3F0 MSG_VOICE_A_216 A cruise missile has hit the base! +02D574 MSG_VOICE_A_216 A cruise missile has hit the base! +02D6F8 MSG_VOICE_A_216 A cruise missile has hit the base! +02D87C MSG_VOICE_A_216 A cruise missile has hit the base! +02DA00 MSG_VOICE_A_216 A cruise missile has hit the base! +02DB90 MSG_VOICE_A_216 A cruise missile has hit the base! +02DD14 MSG_VOICE_A_216 A cruise missile has hit the base! +02DE98 MSG_VOICE_A_216 A cruise missile has hit the base! +02E3F8 MSG_VOICE_A_207 Two more enemy missiles have been fired! +02E4B8 MSG_VOICE_D_308 Target the marks! Understood! missile in front of you! +02E560 MSG_VOICE_A_205 TCAF Schlos Base to ACROPOLIS. Are you able to intercept an enemy cruise missile? +02E6D0 MSG_VOICE_A_244 As soon as we recover a signal, we can send for help from the neighboring systems. Until then, hang in there! +02E770 MSG_VOICE_A_245 You ADAN dogs! Get back to the colonies where you belong! +02E820 MSG_VOICE_A_253 They're putting up more of a fight than expected. Get rid of those pesky fighters first! +02E8C0 MSG_VOICE_A_238 Don't let the missiles get near the base! +02E970 MSG_VOICE_A_246 This isn't an exercise! This is real combat! +02EA20 MSG_VOICE_A_247 We mustn't allow them to attack Lebendorf! +02EAD0 MSG_VOICE_A_228 Has there been contact from any nearby fleets? +02EB80 MSG_VOICE_A_248 Ah, and to think I was almost up for leave! Why did this have to happen now? +02EC30 MSG_VOICE_A_249 Do whatever it takes to protect Schlos Base! +02EE08 MSG_VOICE_A_256 That carrier is in the way! Shoot it down! +02EF88 MSG_VOICE_A_255 Set a new course! Drive off the TCAF fighters! +02F028 MSG_VOICE_A_257 Shoot down the TCAF that are harrying the missiles! +02F1F8 MSG_VOICE_A_239 Whoa, what sort of missiles are these? They're huge! +02F298 MSG_VOICE_A_240 ADAN's really outdone themselves. Let's show 'em we can handle whatever they got! +02F39C MSG_VOICE_D_008 The enemy forces have been destroyed! +02F43C MSG_VOICE_D_009 Don't let the fighters have all the glory. Regroup! +02F540 MSG_VOICE_D_158 Get rid of the enemies around the ACROPOLIS! +02F774 MSG_VOICE_A_215 All right! All the cruise missiles on this side are down! Raymond, what's the situation over there? +02F814 MSG_VOICE_A_241 How many are left? +02F904 MSG_VOICE_A_224 That's the last missile! Take it down! +02F9AC MSG_VOICE_A_223 Only one missile left to go! +02FAEC MSG_VOICE_A_251 A missile has been shot down! +02FB8C MSG_VOICE_A_236 Aim for the tips of the missiles! +02FCB8 MSG_VOICE_A_237 Hey, did the Admiral really just run away? +02FDE4 MSG_VOICE_A_251 A missile has been shot down! +02FF10 MSG_VOICE_A_252 Captain, quite a few of our cruise missiles have been shot down by the enemy! +03003C MSG_VOICE_A_242 Hey, those guys from the ACROPOLIS are pretty good! +0300DC MSG_VOICE_A_243 Look at the way those prototypes move! Those guys from the Genius Program are the real deal after all. +030208 MSG_VOICE_A_251 A missile has been shot down! +030858 MSG_VOICE_A_213 Commodore, more enemy missiles are approaching! +03091C MSG_VOICE_A_221 All right! Look at those fireworks! +0309BC MSG_VOICE_A_217 Pilots, the base can't take any more! We're counting on you! +030A80 MSG_VOICE_A_227 Do whatever it takes to stop those missiles! +030B68 MSG_VOICE_A_219 I don't care what you do, just don't let any more missiles get by! Direct all weapons systems towards stopping them! +030C08 MSG_VOICE_A_225 Fires are in Blocks D7 and D8! Fire control squads, respond! +030CA8 MSG_VOICE_A_229 Give me a report on the damage to the base! +030DB4 MSG_VOICE_A_218 An enemy missile has hit! Damage is severe! +030E54 MSG_VOICE_A_220 Their base has been hit! They're on their last legs! +030EF4 MSG_VOICE_A_230 Prepare for the worst! Evacuate all non-combat personnel. +030FB8 MSG_VOICE_A_226 Air pressure is dropping in Block F11! All personnel, evacuate the area! +031DD4 MSG_VOICE_D_351 Katana, we're in pretty bad shape. We should head to the tanker soon for repairs. +031E74 MSG_VOICE_D_357 Katana, can you see it? The tanker is over there. +032E84 MSG_VOICE_D_354 When you get close enough to resupply, information will appear on your indicator. +03313C MSG_VOICE_D_319 We're going to help the ACROPOLIS, right? Roger! +033418 MSG_VOICE_D_344 You did it, Katana! +0340F0 MSG_VOICE_D_312 Katana, what are you doing? We're supposed to attack the one with the marker! +0341B8 MSG_VOICE_D_311 Wrong one, Katana! You have to aim for the one with the marker! +034280 MSG_VOICE_D_309 Damn, this missile can take a beating! +0345E4 MSG_VOICE_D_316 Katana, that missile is getting close to the base. We have to stop it! diff --git a/docs/re/structures/isl-message-dialogue-link.md b/docs/re/structures/isl-message-dialogue-link.md new file mode 100644 index 0000000..1578ab3 --- /dev/null +++ b/docs/re/structures/isl-message-dialogue-link.md @@ -0,0 +1,70 @@ +# ISL `request_script_message` → caption text + +**✅ Settled 2026-08-26.** Every mission-script message call resolves to the +dialogue line it plays. The 28 stage scripts are readable as dialogue. + +## The link + +Built-in **64** (`request_script_message`) takes one operand in slot 0: a +two-word symbol reference into **symbol table 1**. The symbol's name is a +message id such as `MSG_VOICE_D_257`. The text lives in the language pack's +IXUD blocks (`dat/GP_MAIN_GAME_.pak`) as a **named field** whose key is +`__`, read via the record/field table documented in +[idxd-container.md](idxd-container.md). + + MSG_VOICE_D_257 → field key `MSG_VOICE_D_257_000_00` → "Katana! Damn it!" + +## Evidence + +Measured over all 28 `StageNN.ssb`: + +| | | +|---|---| +| built-in 64 call sites | **2683** | +| slot 0 not staged | 0 | +| operand not in symbol table 1 | 0 | +| operand symbol type | **6** at all 2683 sites | +| distinct message names | 1338 | +| names with caption text at `_000_00` | **2683 / 2683 (100 %)** | + +There is no residue: no call site takes a different operand shape, no name is +of a type other than 6, and no name is missing from the caption table. That +totality is the result — a partial rate would have meant the operand encoding +was only sometimes what I thought it was. + +Name families and their share of call sites: + + MSG_VOICE 2407 MSG_ADAN 118 MSG_ACRO 82 + MSG_ADPL 38 MSG_TCAF 35 MSG_RHIN 3 + +`MSG_VOICE_*` is crew/wingman dialogue; the four-letter families are +ship-side announcements (`ACRO` = ACROPOLIS, `ADAN`/`ADPL` = ADAN, +`TCAF`, `RHIN` = Rhino flight). + +## Why this was not obvious earlier + +The caption table only became readable on 2026-08-25, when `build_caption_text` +was switched from scraping adjacent string-pool tokens to reading the IXUD +**field** table — which took the extractable line count from 537 to 8800 +(8800 of 8800 distinct keys). Before that the great majority of these 1338 +names had no text to resolve *to*, and the 100 % figure was unreachable. + +## What this does not settle + +* **Which recording plays.** The caption is the *text*; the voice bank binding + is a separate and still-unresolved question — see + [voice-bank-leading-region.md](voice-bank-leading-region.md) and the + known case of a generic line playing against a specific subtitle. +* **Multi-line captions.** `isl_dialogue.py` concatenates pages `_000_00` + through `_000_03`. Whether the game wraps by page or by line, and whether + any id uses a page other than `000`, is unmeasured. +* **The other five languages.** Only the English pack was swept. +* **Ordering.** The tool prints call sites in file order, which is *script* + order, not necessarily *play* order — phases and branches are not resolved. + +## Tool + + tools/re-capture/isl_dialogue.py [GP_MAIN_GAME_E.pak] + +Sample output committed at [../data/isl-stage02-dialogue.txt](../data/isl-stage02-dialogue.txt) +(Stage 02, 213 message calls, 213 with text). diff --git a/tools/re-capture/isl_dialogue.py b/tools/re-capture/isl_dialogue.py new file mode 100644 index 0000000..4e58e49 --- /dev/null +++ b/tools/re-capture/isl_dialogue.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +"""Render a mission script's dialogue: every `request_script_message` in order, +with the caption text it plays. + + isl_dialogue.py [GP_MAIN_GAME_E.pak] + +Built-in 64 takes a symbol-table-1 (type 6) message name; the caption text lives +in the pack's IXUD blocks under `__`. All 2683 call sites +across the 28 stages resolve, so this is a total mapping rather than a sample. +""" +import collections +import struct +import sys + +import isl + +NO_NAME = 0xFFFFFFFF + + +def be32(b, o): + return struct.unpack_from('>I', b, o)[0] + + +def wstr(b, pool, off): + base = pool + 2 * off + e = base + while b[e:e + 2] != b'\0\0': + e += 2 + return ''.join(chr(c) for c in struct.unpack('>%dH' % ((e - base) // 2), b[base:e])) + + +def captions(pak_entries): + """`MSG_*` key -> text, read from the IXUD record/field table.""" + out = {} + for b in pak_entries.values(): + if b[:4] != b'IXUD': + continue + n = be32(b, 4) + o = 8 + 16 * n + m = be32(b, o) + o += 4 + fields = [struct.unpack_from('>III', b, o + 12 * i) for i in range(m)] + o += 12 * m + pool = o + 4 + for (_k, no, vo) in fields: + if no == NO_NAME: + continue + try: + key, val = wstr(b, pool, no), wstr(b, pool, vo) + except Exception: + continue + if key.startswith('MSG_') and val.strip(): + out.setdefault(key, val) + return out + + +def walk(b, base, nxt, sink): + staged, pending, off = {}, None, base + while off + 4 <= nxt: + w = be32(b, off) + op, ln = w & 0xFF, (w >> 8) & 0xFF + k1, k0 = (w >> 24) & 0xFF, (w >> 16) & 0xFF + if ln < 4 or ln % 4: + return + ws = [be32(b, off + i) for i in range(4, max(ln, 4), 4) if off + i + 4 <= len(b)] + if op in (0, 1) and len(ws) >= 2: + if k0 == 2 and k1 == 1: + pending = ws[1] + elif k0 == 3 and k1 == 2 and pending is not None: + staged[ws[0]] = pending + elif k0 == 3 and k1 == 1: + staged[ws[0]] = ws[1] + if op == 19 and ws: + sink(off, ws[0], dict(staged)) + staged = {} + off += ln + + +def main(): + ssb = sys.argv[1] + pak = sys.argv[2] if len(sys.argv) > 2 else \ + '/work/sylph_extract/dat/GP_MAIN_GAME_E.pak' + sys.path.insert(0, sys.path[0]) + from unitgroup import read_entry # noqa: F401 (kept for parity) + import zlib + import os + + def pak_entries(path): + d = open(path, 'rb').read() + assert d[:4] == b'IPFB' + segs, base, i = b'', path[:-4], 0 + while os.path.exists('%s.p%02d' % (base, i)): + segs += open('%s.p%02d' % (base, i), 'rb').read() + i += 1 + out = {} + for k in range(be32(d, 4)): + h, off, cs = struct.unpack_from('>III', d, 0x10 + 12 * k) + raw = segs[off:off + cs] + if raw[:2] == b'Z1': + raw = zlib.decompress(raw[10:]) + out[h] = raw + return out + + cap = captions(pak_entries(pak)) + b = isl.load(ssb) + s1 = isl.symbols(b, 1) + end = be32(b, 0x0C) + lines = [] + + def sink(off, bid, st): + if bid != 64: + return + v = st.get(0) + if not isinstance(v, int) or v not in s1: + return + name = s1[v][1] + text = [cap[k] for k in + ('%s_000_%02d' % (name, i) for i in range(4)) if k in cap] + lines.append((off, name, ' '.join(text))) + + bases = isl.phase_bases(b) + [end] + for i in range(len(bases) - 1): + walk(b, bases[i], bases[i + 1], sink) + + name = ssb.replace('\\', '/').split('/')[-1] + print('# %s — dialogue, in script order' % name) + print() + print('Generated by `tools/re-capture/isl_dialogue.py`. Each line is one') + print('`request_script_message` (built-in 64) and the caption it plays.') + print() + print('%d message calls, %d with text.' % (len(lines), sum(1 for _, _, t in lines if t))) + print() + seen = set() + for off, nm, text in lines: + if off in seen: + continue + seen.add(off) + print('%06X %-22s %s' % (off, nm, text)) + + +if __name__ == '__main__': + main()