diff --git a/docs/re/data/cutscene-message-table.txt b/docs/re/data/cutscene-message-table.txt new file mode 100644 index 00000000..eb53dab5 --- /dev/null +++ b/docs/re/data/cutscene-message-table.txt @@ -0,0 +1,320 @@ +# Cutscene message table — dat/GP_MAIN_GAME_E.pak, IDXD objects +# +# One line per page. A page is one subtitle box; successive pages are +# successive utterances, which is why the speaker changes mid-message. +# Columns: id, page, speaker, portrait, on-screen seconds, audio cue, +# then the caption text. + +MSG_DEMO_000 p0 CRICHTON CRICHTON_02 2s DEMO_000 ACROPOLIS to Rhino Leader. Permission granted to return fire. + p1 CRICHTON CRICHTON_07 2.3s DEMO_001 We're sending Bird Flight to back you up. + p2 CRICHTON CRICHTON_02 2.2s DEMO_002 Raymond, take care of the cadets. + p3 RAYMOND RAYMOND_01 2s DEMO_003 Don't worry. I'll make sure we get out of this. + p4 RAYMOND RAYMOND_06 5.2s DEMO_004 Ellen, Katana, engage the enemy together! +MSG_DEMO_005 p0 ELLEN ELLEN_04 1.1s DEMO_005 Rhino 3, roger that! + p1 KATANA KATANA_04 1.8s DEMO_006 Rhino 2, roger that! +MSG_DEMO_007 p0 RAYMOND RAYMOND_01 2.9s DEMO_007 Okay, it looks like we can escape now. + p1 KATANA KATANA_06 3.2s DEMO_008 Rhino Leader, a jump-out signal has appeared on the radar! +MSG_DEMO_009 p0 ELLEN ELLEN_05 1.6s DEMO_009 No way! There's more of them? + p1 RAYMOND RAYMOND_03 3.6s DEMO_010 Rhino Leader to ACROPOLIS! There's a whole fleet of them now! + p2 CRICHTON CRICHTON_07 1.4s DEMO_011 What? Is there any way you can get out of there? + p3 RAYMOND RAYMOND_07 2.8s DEMO_012 If we get rid of those fighters, we can cut our way out of here. +MSG_DEMO_013 p0 SANDRA SANDRA_06 2.8s DEMO_013 This is Bird Leader! Do you copy, Raymond? +MSG_DEMO_014 p0 RAYMOND RAYMOND_06 3.6s DEMO_014 We did it! Okay, all pilots follow my lead! +MSG_DEMO_015 p0 RAYMOND RAYMOND_01 4.8s DEMO_015 Rhino Leader to ACROPOLIS. We made it through and we're coming home. + p1 CRICHTON CRICHTON_01 3.1s DEMO_016 Roger. It's good to see you're all safe. +MSG_DEMO_017 p0 KATANA KATANA_07 2.1s DEMO_017 Yeah, but Brandon ... + p1 ANTONIUS ANTONIUS_03 5.4s DEMO_018 Damn. There's only seven of us. + p2 ELLEN ELLEN_11 3.6s DEMO_019 Brandon ... How could this happen? + p3 KATANA KATANA_08 2.1s DEMO_020 Ellen ... +MSG_DEMO_021 p0 RAYMOND RAYMOND_02 2.5s DEMO_021 It's over … for now. +MSG_DEMO_022 p0 RAYMOND RAYMOND_04 4.2s DEMO_022 This is Rhino Leader. Enemy destroyed. I think Katana here deserves a medal. + p1 CRICHTON CRICHTON_03 3.6s DEMO_023 Understood. I'll have some hot coffee for you when you get back. +MSG_DEMO_024 p0 RAYMOND RAYMOND_04 4s DEMO_024 Katana, I didn't know you could fly like that. + p1 KATANA KATANA_07 2.3s DEMO_025 Yeah, but Brandon ... + p2 RAYMOND RAYMOND_01 2.3s DEMO_026 It wasn't your fault. + p3 RAYMOND RAYMOND_02 2.1s DEMO_027 We're going home, Katana. + p4 KATANA KATANA_07 2s DEMO_028 Right. +MSG_DEMO_029 p0 VIEIRA VIEIRA_02 4.2s DEMO_029 TCAF CALIBAN to Schlos Base. Haven't you been able to get a signal yet? + p1 HIGGINS HIGGINS_02 4.3s DEMO_030 Admiral Vieira, we are under attack right now! Please give up on contacting headquarters! +MSG_DEMO_031 p0 RAYMOND RAYMOND_01 2.4s DEMO_031 Katana, Ellen, stick together and you'll be okay. + p1 RAYMOND RAYMOND_05 2.9s DEMO_032 Gene, you're behind me. Now, don't screw up! + p2 GENE GENE_01 1.5s DEMO_033 Yes sir. + p3 RAYMOND RAYMOND_06 3.7s DEMO_034 Don't be afraid! Show them what our Delta Sabers can do! +MSG_DEMO_035 p0 VIEIRA VIEIRA_02 4.1s DEMO_035 TCAF CALIBAN to ACROPOLIS. + p1 VIEIRA VIEIRA_01 3.8s DEMO_036 We are withdrawing temporarily to reorganize the front. I need you to escort us. + p2 CRICHTON CRICHTON_04 2.3s DEMO_037 Admiral, what do you mean? We're currently engaged in ... + p3 VIEIRA VIEIRA_02 4.6s DEMO_038 No backtalk! My ship must be protected at all costs! + p4 CRICHTON CRICHTON_06 1.3s DEMO_039 Understood. +MSG_DEMO_040 p0 CRICHTON CRICHTON_02 2.5s DEMO_040 ACROPOLIS to Rhino Leader. I need you to provide an escort for the flagship CALIBAN. + p1 NATALIE NATALIE_01 1.8s DEMO_041 I've sent the coordinates of Admiral Vieira's ship. + p2 RAYMOND RAYMOND_01 2s DEMO_042 Rhino Leader, roger that. +MSG_DEMO_043 p0 TCAFBASEOP TCAFBASEOP_02 2.7s DEMO_043 Incoming interstellar cruise missile! + p1 HIGGINS HIGGINS_02 1.3s DEMO_044 +MSG_DEMO_045 p0 HIGGINS HIGGINS_01 2.5s DEMO_045 At this rate the base won't last long! Your orders, sir? + p1 VIEIRA VIEIRA_02 3.6s DEMO_046 We've sustained severe damage as well, Higgins! I'm turning over command to you! + p2 HIGGINS HIGGINS_02 2.5s DEMO_047 Admiral? + p3 VIEIRA VIEIRA_01 3s DEMO_048 I .. uh … I have to bring word of the invasion to our other fleets! + p4 VIEIRA VIEIRA_02 4s DEMO_049 Set a course for Hargenteen in the Ingres System. Commence jump! +MSG_DEMO_050 p0 KATANA KATANA_04 1s DEMO_050 The Admiral's ship ... + p1 ELLEN ELLEN_05 1.5s DEMO_051 Did he just run away? +MSG_DEMO_052 p0 IAN IAN_03 3s DEMO_052 More cruise missiles have jumped out! Their target is Schlos Base! + p1 CRICHTON CRICHTON_04 2s DEMO_053 All Flights! Shoot down those missiles! + p2 RAYMOND RAYMOND_06 1.2s DEMO_054 Roger that! +MSG_DEMO_055 p0 CRICHTON CRICHTON_04 3.4s DEMO_055 This is the TCAF ACROPOLIS. Schlos Base, can you read me? +MSG_DEMO_056 p0 HIGGINS HIGGINS_02 3.3s DEMO_056 This is Schlos Base. We're still here, barely. + p1 HIGGINS HIGGINS_01 3.8s DEMO_057 But it appears the ADAN forces are retreating. Good work everyone. +MSG_DEMO_058 p0 ELLEN ELLEN_02 2s DEMO_058 We did it, Katana! +MSG_DEMO_059 p0 KATANA KATANA_08 2.1s DEMO_059 We tried to destroy all the missiles, but ... + p1 RAYMOND RAYMOND_04 2.8s DEMO_060 No, you did fine. It's those fools' fault for turning chicken. + p2 GENE GENE_01 2s DEMO_061 I bet we're going to get medals for this! +MSG_DEMO_062 p0 CRICHTON CRICHTON_02 2.4s DEMO_062 Let's help the others. Pilots, return to the carrier. + p1 RAYMOND RAYMOND_05 1.5s DEMO_063 Roger. +MSG_DEMO_064 p0 CRICHTON CRICHTON_07 3.3s DEMO_064 This is the ACROPOLIS! Come in, Headquarters! We are suffering heavy losses! +MSG_DEMO_065 p0 HIGGINS HIGGINS_01 3.7s DEMO_065 This is acting Admiral Higgins. Our fleet is also engaged in combat. We can't spare anyone to assist you. + p1 HIGGINS HIGGINS_02 2.2s DEMO_066 You're going to have to complete your mission with the units you have. +MSG_DEMO_067 p0 CRICHTON CRICHTON_05 1.6s DEMO_067A Understood. +MSG_DEMO_069 p0 CRICHTON CRICHTON_02 2.9s DEMO_068 Our first priority is the safety of the transit platforms. Pilots, get rid of the nearby enemies. +MSG_DEMO_070 p0 IAN IAN_02 2.7s DEMO_069 The enemy squadron has been destroyed! The area is now secure. + p1 CRICHTON CRICHTON_01 1.2s DEMO_070 Good. +MSG_DEMO_072 p0 CRICHTON CRICHTON_02 4.7s DEMO_071 ACROPOLIS to the cargoships. You can now take off. Evacuate as soon as you are ready. + p1 SHIPLADY1 SHIPLADY1_02 3.7s DEMO_072 Roger that! We three are going first. Keep us covered! + p2 CRICHTON CRICHTON_02 2.6s DEMO_073 Rhino Flight, Bird Flight, escort the cargoships. +MSG_DEMO_075 p0 SHIPLADY1 SHIPLADY1_01 4.5s DEMO_074 This is LADY 1. We are preparing to exit the atmosphere. Take care of yourselves! +MSG_DEMO_076 p0 NATALIE NATALIE_02 2.3s DEMO_075 All cargoships successfully evacuated. + p1 CRICHTON CRICHTON_07 1.2s DEMO_076 Who's next? + p2 NATALIE NATALIE_02 1.5s DEMO_077 The next group of four ships is the last. + p3 CRICHTON CRICHTON_02 2s DEMO_078 Good, then we're going along with them. + p4 CRICHTON CRICHTON_07 1.6s DEMO_079 Natalie, call back the squadron to escort us. + p5 NATALIE NATALIE_01 0.8s DEMO_080 Roger. +MSG_DEMO_082 p0 IAN IAN_03 3.3s DEMO_081 Captain! A new group of enemies is approaching at high speed! It's the same black squadron! +MSG_DEMO_083 p0 MARGRAS2 MARGRAS2_05 2s DEMO_082 Night Ravens, attack! +MSG_DEMO_084 p0 MARGRAS2 MARGRAS2_01 2.9s DEMO_083 That's enough. All units, retreat. +MSG_DEMO_085 p0 TASKENT TASKENT_03 4.5s DEMO_084 Damn it! I'll make them pay the next time I see them! + p1 MARGRAS2 MARGRAS2_02 2.8s DEMO_085 Yeah, I didn't expect resistance like that, either. +MSG_DEMO_087 p0 RAYMOND RAYMOND_01 1.8s DEMO_086 Who knew ADAN had pilots that skilled? + p1 KATANA KATANA_03 3.3s DEMO_087 I was surprised, too. Their leader was especially tough. + p2 ELLEN ELLEN_09 2.7s DEMO_088 They can try, but it won't get them anywhere! + p3 RAYMOND RAYMOND_02 2.7s DEMO_089 Personally, I hope they don't come back. + p4 RAYMOND RAYMOND_04 4.2s DEMO_090 We're lucky we had you, Katana. Your technique just keeps getting better! + p5 KATANA KATANA_02 1.5s DEMO_091 Thank you, sir. +MSG_DEMO_093 p0 NATALIE NATALIE_02 2.9s DEMO_092 Beginning atmosphere exit sequence. All units return to the carrier. + p1 RAYMOND RAYMOND_01 2.6s DEMO_093 Roger that. Say goodbye to Lebendorf. +MSG_DEMO_095 p0 KATANA KATANA_04 2.2s DEMO_094 Rhino Leader, they're here! +MSG_DEMO_096 p0 ELLEN ELLEN_03 2.4s DEMO_095 They haven't noticed us yet. + p1 RAYMOND RAYMOND_01 3.3s DEMO_096 All units, start your engines. Let's do this! + p2 RAYMOND RAYMOND_06 4.2s DEMO_097 Whether the rest of the fleet escapes or not depends on us. Everybody stay focused! +MSG_DEMO_099 p0 IAN IAN_04 3.5s DEMO_098 Captain! Enemies approaching from 2 o'clock! It's a mixed group of ships and fighters! +MSG_DEMO_100 p0 IAN IAN_03 2.3s DEMO_099 They're fast! It's that black squadron! +MSG_DEMO_101 p0 ELLEN ELLEN_04 2.5s DEMO_100 The black squadron ... Katana ... it couldn't be. + p1 KATANA KATANA_05 2.2s DEMO_101 Could it really be Margras? +MSG_DEMO_103 p0 RAYMOND RAYMOND_06 3s DEMO_102 Don't let any of them get near the ACROPOLIS! + p1 RAYMOND RAYMOND_01 2.4s DEMO_103 And Katana, don't go chasing after any more enemies! + p2 GENE GENE_01 1.9s DEMO_104 Yeah, you heard Rhino Leader. + p3 ELLEN ELLEN_04 1.8s DEMO_105 But Katana had a reason for going after that one! + p4 KATANA KATANA_08 2.3s DEMO_106 It's okay, Ellen. + p5 KATANA KATANA_03 1.7s DEMO_107 I'll be more careful from now on, Leader. + p6 RAYMOND RAYMOND_05 1.8s DEMO_108 All right. Now get out there and do your best! + p7 KATANA KATANA_02 1.3s DEMO_109 Yes, sir! +MSG_DEMO_111 p0 IAN IAN_03 1.7s DEMO_110 Two more enemy ships have jumped out! They're close! + p1 CRICHTON CRICHTON_05 1.4s DEMO_111 +MSG_DEMO_113 p0 NATALIE NATALIE_04 1s DEMO_112 Deck 3 has been hit! + p1 IAN IAN_04 1.2s DEMO_113 Captain, at this rate we're goners! + p2 CRICHTON CRICHTON_02 3.3s DEMO_114 Rhino Flight, attack the destroyers around the ACROPOLIS! Bird Flight, check the destroyers farther out! + p3 CRICHTON CRICHTON_06 1.6s DEMO_115 Russell, how much longer will those engine repairs take? + p4 RUSSELL RUSSELL_02 0.8s DEMO_116 At least ten minutes. + p5 CRICHTON CRICHTON_02 1.4s DEMO_117 Do it in five! +MSG_DEMO_119 p0 CRICHTON CRICHTON_04 2s DEMO_118 We mustn't give in! Show some spirit! +MSG_DEMO_120 p0 RUSSELL RUSSELL_02 3.2s DEMO_119 Captain, the engine is back online! We can go any time! + p1 IAN IAN_02 2.5s DEMO_120 Enemy reinforcements! They're headed this way! + p2 CRICHTON CRICHTON_02 2.5s DEMO_121 Let's cut our way through them and jump. Pilots, return to the carrier! + p3 RAYMOND RAYMOND_06 2.2s DEMO_122 Roger that. Katana, let's go! +MSG_DEMO_124 p0 ELLEN ELLEN_06 2.9s DEMO_123 This is just too much! Why do they keep following us wherever we go? + p1 GENE GENE_03 1.9s DEMO_124 I thought we were finally gonna be able to relax once we reached Hargenteen. + p2 KATANA KATANA_01 1.4s DEMO_125 There's nothing we can do about it, so let's just get it over with. + p3 RAYMOND RAYMOND_05 1.4s DEMO_126 That's right. This is no time to slack off. + p4 RAYMOND RAYMOND_01 3.5s DEMO_127 We'll be making contact with the enemy soon, so save your complaints for them! +MSG_DEMO_129 p0 ADANB ADANB_01 2.8s DEMO_128 All ships, commence attack on the TCAF squadron. +MSG_DEMO_130 p0 ELLEN ELLEN_09 2.7s DEMO_129 Reinforcements? I thought we'd seen the last of them. + p1 RAYMOND RAYMOND_03 3.6s DEMO_130 Well, you were wrong. Everybody be careful! + p2 KATANA KATANA_03 1.1s DEMO_131 Roger that! +MSG_DEMO_133 p0 TCAFMIRANDA TCAFMIRANDA_01 4.5s DEMO_132 This is the TCAF MIRANDA. Glad to see you're still with us, ACROPOLIS. + p1 TCAFMIRANDA TCAFMIRANDA_02 3.2s DEMO_133 We're about to begin our ambush, so we'll need all the help we can get from you and your squadron. +MSG_DEMO_135 p0 IAN IAN_03 2.7s DEMO_134 Something is jumping out in front of us! It's big! +MSG_DEMO_136 p0 ADANETERNAL ADANETERNAL_02 3.5s DEMO_135 Ready … Aim … Fire! +MSG_DEMO_137 p0 CRICHTON CRICHTON_07 3.1s DEMO_136 This is bad. Pilots, take out that battleship! + p1 ELLEN ELLEN_08 3s DEMO_137 You want me to stop them? Easy for you to say! +MSG_DEMO_139 p0 IAN IAN_01 1.5s DEMO_138 Enemy battleship destroyed! + p1 CRICHTON CRICHTON_01 2.4s DEMO_139 Looks like we've driven them off for now. +MSG_DEMO_141 p0 KATANA KATANA_10 1.8s DEMO_140 Rhino Leader! Are you all right? + p1 RAYMOND RAYMOND_07 2s DEMO_141 I'm fine. +MSG_DEMO_143 p0 VIEIRA VIEIRA_03 4s DEMO_142 It's time to show the enemy what the 17th Independent Squadron is made of! Give it your all! +MSG_DEMO_144 p0 ANTONIUS ANTONIUS_01 2.9s DEMO_143 Like I need him to tell me that! They should leave everything up to us White Griffons! + p1 SANDRA SANDRA_06 1.7s DEMO_144 All right everybody, time to do this! +MSG_DEMO_146 p0 ELLEN ELLEN_01 1.2s DEMO_145 Everyone's really pumped up! + p1 KATANA KATANA_02 1.6s DEMO_146 Yeah, let's do it! + p2 YOJI YOJI_03 2.3s DEMO_147 It's an honor to serve under your command, Rhino Leader. I'm lookin' forward to it. +MSG_DEMO_149 p0 RAYMOND RAYMOND_11 3.3s DEMO_148 ACROPOLIS to White Griffon Squadron. Your first priority ... get rid of the enemy's Flight units. + p1 RAYMOND RAYMOND_14 2.8s DEMO_149 Katana, go in there full-force. I know you can handle it. +MSG_DEMO_151 p0 KATANA KATANA_03 1.2s DEMO_150 Yes sir! +MSG_DEMO_152 p0 TCAFCALIBANOP TCAFCALIBANOP_01 1.9s DEMO_151 Sir, the enemy forces are driven off and our course is secured! + p1 VIEIRA VIEIRA_02 4.2s DEMO_152 Good. We're going to charge the enemy! All ships, full speed ahead! + p2 VIEIRA VIEIRA_01 2.2s DEMO_153 ACROPOLIS, make an opening for us! + p3 CRICHTON CRICHTON_06 1.4s DEMO_154 Are you telling us to take the lead again? + p4 VIEIRA VIEIRA_02 2.3s DEMO_155 Do I need to repeat myself? + p5 CRICHTON CRICHTON_02 2.1s DEMO_156 Understood. Commencing charge. +MSG_DEMO_158 p0 RAYMOND RAYMOND_14 3.5s DEMO_157 We're all through here, Katana. Good job. + p1 KATANA KATANA_02 1s DEMO_158 Thank you, sir. + p2 VIEIRA VIEIRA_03 3.5s DEMO_159 We are heading into enemy territory! All ships, finish preparations and commence jump! +MSG_DEMO_161 p0 VIEIRA VIEIRA_02 2.8s DEMO_160 What? What happened? + p1 TCAFCALIBANOP TCAFCALIBANOP_02 3s DEMO_161 It was a mine! There's a mine field around the enemy base! + p2 VIEIRA VIEIRA_02 1.2s DEMO_162 What? +MSG_DEMO_164 p0 CRICHTON CRICHTON_07 1.3s DEMO_163 We've got to do something to help the others. + p1 RAYMOND RAYMOND_16 2s DEMO_164 Katana, destroy the mines around the fleet! +MSG_DEMO_166 p0 ADANOPB ADANOPB_02 1.7s DEMO_165 They've destroyed the mines! + p1 ADANBASE ADANBASE_01 2.5s DEMO_166 Send out the defense force to intercept them. +MSG_DEMO_168 p0 VIEIRA VIEIRA_03 3.3s DEMO_167 Well done! All my pilots are aces! + p1 VIEIRA VIEIRA_02 3s DEMO_168 We're attacking the enemy base. All ships, follow my lead! +MSG_DEMO_170 p0 TCAFCALIBANOP TCAFCALIBANOP_01 1.6s DEMO_169 The enemy base has been destroyed! + p1 VIEIRA VIEIRA_03 4.6s DEMO_170 Good. Now maybe those ADAN scum know how foolish they were to challenge us. + p2 VIEIRA VIEIRA_01 2.5s DEMO_171 Call in the fighters and set a course for the next target! +MSG_DEMO_173 p0 VIEIRA VIEIRA_03 3.3s DEMO_172 Heh, is this all they have? We'll scatter them like ants! + p1 VIEIRA VIEIRA_01 2s DEMO_173 Destroyers and fighters, attack! +MSG_DEMO_175 p0 ELLEN ELLEN_08 2.8s DEMO_174 Hey, Katana, could this be the same Acheron ... ? + p1 KATANA KATANA_04 3.1s DEMO_175 Yeah, this should be Margras' home planet. + p2 ELLEN ELLEN_04 2.5s DEMO_176 But I thought he said it was a green planet? +MSG_DEMO_178 p0 KATANA KATANA_08 1.8s DEMO_177 What happened here? +MSG_DEMO_179 p0 TCAFCALIBANOP TCAFCALIBANOP_01 2.7s DEMO_178 The enemy force is moving away from the targeted facility. They could be trying to escape. + p1 VIEIRA VIEIRA_01 2.2s DEMO_179 So they're running in fear. Chase them down! +MSG_DEMO_181 p0 CRICHTON CRICHTON_06 1.8s DEMO_180 Admiral Vieira, what about investigating the other wreckage? + p1 VIEIRA VIEIRA_03 4.2s DEMO_181 What? Well, the ACROPOLIS should be able to handle that on its own. I leave it up to you. + p2 VIEIRA VIEIRA_01 1.6s DEMO_182 But I want the White Griffons sent after the enemy. + p3 CRICHTON CRICHTON_07 2.2s DEMO_183 But if we do that then we won't have enough cover. + p4 VIEIRA VIEIRA_03 2.6s DEMO_184 There aren't any enemies in those other ruins. + p5 CRICHTON CRICHTON_02 2.3s DEMO_185 Understood. I'll send Rhino Flight over your way. +MSG_DEMO_187 p0 CRICHTON CRICHTON_02 1.7s DEMO_186 Bird Flight stays with the ACROPOLIS. +MSG_DEMO_188 p0 ADANPLD ADANPLD_02 2.6s DEMO_187 Those idiots have fallen right into our trap. Attack! +MSG_DEMO_189 p0 VIEIRA VIEIRA_02 3.1s DEMO_188 Warships? Where were they hiding those? + p1 VIEIRA VIEIRA_01 2.3s DEMO_189 Retreat! Retreat! + p2 VIEIRA VIEIRA_02 2.6s DEMO_190A White Griffon Squadron! Protect my ship! + p3 ELLEN ELLEN_05 2s DEMO_190B The black squadron? Katana! + p4 KATANA KATANA_06 2s DEMO_190C What are they doing here? +MSG_DEMO_192 p0 TCAFCALIBANOP TCAFCALIBANOP_02 2.1s DEMO_191 Enemy reinforcements are heading this way! + p1 VIEIRA VIEIRA_02 2.5s DEMO_192 More of them? We've got no choice. Commence jump! +MSG_DEMO_194 p0 TASKENT TASKENT_02 2.5s DEMO_193 Damn, the big fish got away. +MSG_DEMO_195 p0 TASKENT TASKENT_04 2.5s DEMO_194 Oh well, I'll just go and hit that carrier over there! + p1 RAYMOND RAYMOND_13 2.8s DEMO_195 Katana, return to the ACROPOLIS now! Sandra and the others are in trouble! + p2 ELLEN ELLEN_09 1.5s DEMO_196 We've got troubles of our own! + p3 KATANA KATANA_11 1.3s DEMO_197 Ellen, you and the others go! I'll handle things here! +MSG_DEMO_199 p0 TASKENT TASKENT_04 1s DEMO_198 You little punk! +MSG_DEMO_200 p0 TASKENT TASKENT_03 1s DEMO_199 +MSG_DEMO_201 p0 KATANA KATANA_09 3.7s DEMO_200 Aaaaah! Margras! +MSG_DEMO_202 p0 MARGRAS MARGRAS_06 2.6s DEMO_201 This isn't good! +MSG_DEMO_203 p0 KATANA KATANA_09 1.6s DEMO_202 Wait, Margras! +MSG_DEMO_204 p0 RUSSELL RUSSELL_01 2.8s DEMO_203 Engineering report: there's no problem with the atmospheric composition, so we should be able to use it as fuel. + p1 CRICHTON CRICHTON_03 2.1s DEMO_204 Understood. Looks like we're going to make it back to Hargenteen after all. +MSG_DEMO_206 p0 IAN IAN_02 2.4s DEMO_205 Incoming enemy fighters! They're heading straight for the ACROPOLIS! + p1 CRICHTON CRICHTON_06 2s DEMO_206 Why can't they just leave us alone? + p2 CRICHTON CRICHTON_02 3.1s DEMO_207 Bird Flight, protect ACROPOLIS. Rhino Flight, move to intercept the enemy. + p3 CRICHTON CRICHTON_07 1.8s DEMO_208 We'll continue resupplying as we take evasive action. + p4 KATANA KATANA_03 1.8s DEMO_209 Roger that! Moving to intercept. + p5 SANDRA SANDRA_04 1.7s DEMO_210 We're counting on you, Rhino Leader! +MSG_DEMO_212 p0 ADANA ADANA_02 2.4s DEMO_211 There they are! Attack! +MSG_DEMO_213 p0 CRICHTON CRICHTON_05 2.3s DEMO_212 + p1 CRICHTON CRICHTON_07 2s DEMO_213 White Griffons, attack the enemy fleet! +MSG_DEMO_215 p0 CRICHTON CRICHTON_02 3s DEMO_214 Now! All engines full power! We're leaving! + p1 RUSSELL RUSSELL_01 2.5s DEMO_215 Understood! Hold on tight! +MSG_DEMO_217 p0 RUSSELL RUSSELL_02 2.3s DEMO_216A Here we go! +MSG_DEMO_218 p0 HIGGINS HIGGINS_01 3.2s DEMO_219 TCAF VINSON to ACROPOLIS. + p1 HIGGINS HIGGINS_03 4.2s DEMO_220 It's been a while since Lebendorf. I'm proud to be fighting alongside you again. + p2 CRICHTON CRICHTON_03 2.2s DEMO_221 The feeling is mutual, Commodore Higgins. Let's give it everything we've got! +MSG_DEMO_221 p0 TCAFOPA TCAFOPA_02 3s DEMO_217 The vanguard of the enemy fleet is speeding up. They're charging! +MSG_DEMO_222 p0 TCAFOLYMPUS TCAFOLYMPUS_02 2s DEMO_218 All ships, prepare for combat! +MSG_DEMO_223 p0 CRICHTON CRICHTON_04 1.9s DEMO_222 Why have they spread out? + p1 IAN IAN_02 2s DEMO_223 Something is jumping out in front of us. Mass reading is ... + p2 IAN IAN_03 3s DEMO_224 Impossible! It has a mass reading of almost thirty trillion tons! +MSG_DEMO_226 p0 CRICHTON CRICHTON_01 3.1s DEMO_225 The enemy's main force has been destroyed. We've gained a firm advantage in the battle. + p1 NATALIE NATALIE_03 1.9s DEMO_226 Way to go, White Griffons! + p2 IAN IAN_02 2.1s DEMO_227 Something is jumping out in front of us. Mass reading is ... + p3 IAN IAN_03 3s DEMO_228 Impossible! It has a mass reading of almost thirty trillion tons! +MSG_DEMO_230 p0 SANDRA SANDRA_06 2.2s DEMO_245 Damn it. All of them lost, in an instant! + p1 ELLEN ELLEN_04 3.2s DEMO_246 Huh, what the … what is that thing? +MSG_DEMO_232 p0 CRICHTON CRICHTON_04 2.2s DEMO_247 Back into formation! We have to help the fleet and get out ourselves! +MSG_DEMO_233 p0 KATANA KATANA_10 1.8s DEMO_248 That's right. Don't give up! +MSG_DEMO_234 p0 RUSSELL RUSSELL_01 2.2s DEMO_249 Captain, I think we should get moving, too. + p1 CRICHTON CRICHTON_02 2.8s DEMO_250 I agree. Call back the White Griffons! +MSG_DEMO_236 p0 IAN IAN_02 1.6s DEMO_255 We destroyed all the enemy forces in the area! + p1 CRICHTON CRICHTON_06 1.2s DEMO_256 Commodore Higgins, now's our chance! +MSG_DEMO_238 p0 HIGGINS HIGGINS_02 3.3s DEMO_257 Very well. Commence jump! Set a course for Earth! + p1 HIGGINS HIGGINS_03 4s DEMO_258 We never would have gotten out without the White Griffons. Once we get back, I'm giving you all medals. +MSG_DEMO_240 p0 IAN IAN_02 4s DEMO_259 It's definitely the Night Raven Squadron. However, we don't have any information regarding one of the fighters. It's most likely a prototype. +MSG_DEMO_241 p0 ELLEN ELLEN_04 1.2s DEMO_260 Katana ... + p1 KATANA KATANA_11 1.3s DEMO_261 I know. +MSG_DEMO_243 p0 MARGRAS MARGRAS_04 2.9s DEMO_262 Katana, can you hear me? Katana! + p1 KATANA KATANA_05 1.5s DEMO_263 Margras! + p2 MARGRAS MARGRAS_02 2.4s DEMO_264 So you're the one they sent after us ... Good. + p3 KATANA KATANA_04 0.3s DEMO_265 Wait, there's something I have to ask you. + p4 MARGRAS MARGRAS_05 2.5s DEMO_266 Fight me. Let's end this here! +MSG_DEMO_248 p0 ADANPLE ADANPLE_02 2.4s DEMO_267 Sir, we've lost contact with Raven 5! + p1 MARGRAS MARGRAS_01 2.6s DEMO_268 Katana, you've gotten better. +MSG_DEMO_250 p0 MARGRAS MARGRAS_06 1.5s DEMO_269 Stay back, all of you. +MSG_DEMO_251 p0 KATANA KATANA_06 1.5s DEMO_270 Ellen, below you! + p1 ELLEN ELLEN_10 1s DEMO_271 +MSG_DEMO_253 p0 ELLEN ELLEN_05 1.7s DEMO_272 Katana! Margras! +MSG_DEMO_254 p0 MARGRAS MARGRAS_05 2.3s DEMO_273 Come on, Katana! It's one-on-one now! +MSG_DEMO_255 p0 KATANA KATANA_03 2.2s DEMO_274 All right, have it your way. +MSG_DEMO_256 p0 MARGRAS MARGRAS_06 2.3s DEMO_275 Damn! Move! +MSG_DEMO_257 p0 MARGRAS MARGRAS_05 2.4s DEMO_276 Katana! Why aren't you firing? + p1 KATANA KATANA_04 3s DEMO_277 Because this fight is over. Just let it end. + p2 MARGRAS MARGRAS_04 1.7s DEMO_278A +MSG_DEMO_260 p0 MARGRAS MARGRAS_07 3s DEMO_278B +MSG_DEMO_261 p0 HIGGINS HIGGINS_01 5s DEMO_279 I'm going to need all your strength to protect Earth and put an end to this war! + p1 HIGGINS HIGGINS_02 4s DEMO_280 First we have to break through the enemy forces in front of us. Good luck everyone! +MSG_DEMO_263 p0 KATANA KATANA_03 3.4s DEMO_281 It's time, everybody. White Griffons, attack! + p1 MARGRAS3 MARGRAS3_05 1.7s DEMO_282 Night Ravens, follow me! +MSG_DEMO_265 p0 HIGGINS HIGGINS_02 3.2s DEMO_283 Nicely done! Now let's head for the Prometheus Driver! +MSG_DEMO_266 p0 SANDRA SANDRA_02 3.4s DEMO_284 More of them! Guess they aren't gonna let us waltz right up to their front door! +MSG_DEMO_267 p0 ELLEN ELLEN_05 2.5s DEMO_285 What is this? There are so many of them! + p1 MARGRAS3 MARGRAS3_04 1.6s DEMO_286 Phantoms! + p2 HIGGINS HIGGINS_02 3.6s DEMO_287 All ships, clear out the enemy and support the fighters! We need as many of them as possible to reach the Driver! +MSG_DEMO_270 p0 CRICHTON CRICHTON_02 1.6s DEMO_288 Now, all units charge! + p1 KATANA KATANA_09 2s DEMO_289 Roger that! + p2 SANDRA SANDRA_05 3.1s DEMO_290 Damn, there's no end to them! + p3 ELLEN ELLEN_03 2.1s DEMO_291 Katana, you go on! We'll take care of things here! + p4 KATANA KATANA_03 1.7s DEMO_292 All right! Take care of yourself, Ellen! +MSG_DEMO_275 p0 MARGRAS3 MARGRAS3_01 1.5s DEMO_293 Katana! +MSG_DEMO_276 p0 KATANA KATANA_04 1.6s DEMO_294 Are we the only ones that made it through? + p1 MARGRAS3 MARGRAS3_02 1.9s DEMO_295 Don't worry. The two of us is all it'll take. + p2 KATANA KATANA_01 1.5s DEMO_296 You got that right. Let's go! +MSG_DEMO_279 p0 NATALIE NATALIE_02 3.9s DEMO_297 Rhino Leader, continue your current course. The firing mechanism is directly ahead. + p1 NATALIE NATALIE_04 2.6s DEMO_298 Hurry, we don't have much more time. The energy reading from inside the Driver is increasing. +MSG_DEMO_281 p0 KATANA KATANA_08 3s DEMO_299 ACROPOLIS? ACROPOLIS, do you read me? +MSG_DEMO_282 p0 KATANA KATANA_11 2.7s DEMO_300 Get out of my way! +MSG_DEMO_283 p0 KATANA KATANA_04 1.1s DEMO_301 This must be the firing mechanism! +MSG_DEMO_284 p0 NATALIE NATALIE_04 1.7s DEMO_302 Rhino Leader! Rhino Leader, come in! + p1 KATANA KATANA_03 1.3s DEMO_303 This is Rhino Leader! + p2 NATALIE NATALIE_03 2.8s DEMO_304 Oh, thank goodness you're safe! + p3 CRICHTON CRICHTON_02 2.5s DEMO_305 They should almost be finished charging. + p4 CRICHTON CRICHTON_07 1.5s DEMO_306 Destroy the firing mechanism now! + p5 KATANA KATANA_04 1.3s DEMO_307 Understood! +MSG_DEMO_290 p0 KATANA KATANA_11 1.8s DEMO_308 +MSG_DEMO_291 p0 KATANA KATANA_06 1.7s DEMO_309 Watch this, Margras. +MSG_DEMO_292 p0 KATANA KATANA_04 1s DEMO_310 What the? +MSG_DEMO_400 p0 NATALIE NATALIE_03 2s DEMO_216B We did it! +MSG_DEMO_401 p0 CRICHTON CRICHTON_01 3s DEMO_216C White Griffons, return to the ACROPOLIS! We're heading home to Hargenteen! +MSG_DEMO_500 p0 KATANA KATANA_07 2s +MSG_DEMO_600 p0 NATALIE NATALIE_01 1.9s Rhino 3 has landed. Commencing resupply. +MSG_DEMO_600 p0 NATALIE NATALIE_01 1.9s Rhino 3 has landed. Commencing resupply. +MSG_DEMO_600 p0 NATALIE NATALIE_01 1.9s Rhino 3 has landed. Commencing resupply. +MSG_DEMO_601 p0 NATALIE NATALIE_01 2.2s Rhino Leader has landed. Commencing resupply. +MSG_DEMO_601 p0 NATALIE NATALIE_01 2.2s Rhino Leader has landed. Commencing resupply. +MSG_DEMO_601 p0 NATALIE NATALIE_01 2.2s Rhino Leader has landed. Commencing resupply. +MSG_DEMO_602 p0 NATALIE NATALIE_02 1.7s Resupply complete. You are cleared for take-off! +MSG_DEMO_602 p0 NATALIE NATALIE_02 1.7s Resupply complete. You are cleared for take-off! +MSG_DEMO_602 p0 NATALIE NATALIE_02 1.7s Resupply complete. You are cleared for take-off! +MSG_DEMO_602 p0 NATALIE NATALIE_02 1.7s Resupply complete. You are cleared for take-off! +MSG_DEMO_603 p0 TCAFSUPPLY TCAFSUPPLY_01 1.6s All right, beginning resupply. +MSG_DEMO_603 p0 TCAFSUPPLY TCAFSUPPLY_01 1.6s All right, beginning resupply. +MSG_DEMO_603 p0 TCAFSUPPLY TCAFSUPPLY_01 1.6s All right, beginning resupply. +MSG_DEMO_603 p0 TCAFSUPPLY TCAFSUPPLY_01 1.6s All right, beginning resupply. +MSG_DEMO_604 p0 TCAFSUPPLY TCAFSUPPLY_03 1.6s Resupply complete. Switching to take-off sequence. +MSG_DEMO_604 p0 TCAFSUPPLY TCAFSUPPLY_03 1.6s Resupply complete. Switching to take-off sequence. diff --git a/docs/re/structures/cutscene-message-table.md b/docs/re/structures/cutscene-message-table.md new file mode 100644 index 00000000..cd56568d --- /dev/null +++ b/docs/re/structures/cutscene-message-table.md @@ -0,0 +1,89 @@ +# The cutscene message table — speaker, portrait, timing and audio cue + +**✅ Settled 2026-08-26.** The `MSG_DEMO_*` family is not called from any stage +script; it is driven by its own IDXD tables in the language packs. Those tables +carry, per line of cutscene dialogue: **who says it, which portrait is shown, +how long it stays on screen, and which audio cue plays**. + +This answers the question left open by +[isl-message-dialogue-link.md](isl-message-dialogue-link.md) — what drives +cutscene dialogue, given that built-in 64 never mentions it. + +## Where it lives + +32 IDXD objects per language pack (`dat/GP_MAIN_GAME_.pak`). Each holds a +`Generic` record with a `Count`, plus some number of `Message_NNN` records — +**149 in total** across the English pack, covering **138 distinct ids**. + +## Record layout + +A `Message_NNN` record has two named fields, `ID` and `PageCount`, and then +**9 positional fields per page**: + +| slot | content | example | +|---|---|---| +| 0 | speaker | `CharacterNATALIE` | +| 1 | portrait | `FaceNATALIE_01` | +| 2 | constant `None` at all 149 records | `None` | +| 3 | on-screen seconds, 1.00 – 5.00 (mean 2.48) | `1.9` | +| 4 | audio cue, or empty | `DEMO_017` | +| 5–8 | the four caption keys of that page | `MSG_DEMO_600_000_00` … `_03` | + +The field count is exactly **9 · PageCount + 2**, and that identity holds for +every one of the 7 distinct PageCount values present (1→11, 2→20, 3→29, 4→38, +5→47, 6→56, 8→74). That is what pins the per-page grouping. + +## Evidence + +* **1 252 of 1 252** caption-key slots equal `__` exactly, with + **zero** mismatches. The grouping is not a guess about which slot is which. +* **78** records span more than one page — which independently equals the 78 + multi-page `MSG_DEMO` ids counted from the *caption* side, by a different + method. Two measurements, same number. +* In **68 of those 78** the speaker changes between pages, confirming from the + data that successive pages are successive utterances rather than one long + speech. +* The id sets close exactly: **138 ids in the caption table, 138 in the message + table, none on either side without the other.** +* 28 distinct speakers, 98 distinct portraits. + +## The audio cue (slot 4) + +296 of 313 pages carry a cue, and there are **296 distinct values** — one per +page, never reused. It joins to the `SOUNDS` table described in +[sound-cue-table.md](sound-cue-table.md): + + DEMO_017 -> sound id 8017 + +The numbering rule is `DEMO_nnn` → `8000 + nnn`, which holds for **286 of 286** +plain-numeric cues. The 9 remaining cues carry a letter suffix +(`DEMO_067A`, `DEMO_190A`–`C`, `DEMO_216A`–`C`, `DEMO_278A`–`B`) and are +assigned 8400–8408 in order. There is no unexplained residue. + +Within a message, **77 of the 78** multi-page records number their cues +consecutively; one does not. + +**A guess I had and dropped:** slot 4 looked at first like a movie reference, +since the values resemble the `DEMO_nnn` naming of a clip. It is not — there +are 97 `.wmv` files on the disc and their names look nothing like this +(`ADV.wmv`, `RT01A.wmv`), and the values are unique *per page*, which no movie +reference would be. + +## What this does not settle + +* **17 pages have no cue** (slot 4 empty). Silent, or voiced by another route, + is unmeasured. +* **Slot 2 is `None` at all 149 records**, so what it would hold otherwise is + unknown — a constant with no observed variation carries no information. +* **5 ids have more than one record** (`MSG_DEMO_600`–`604`), accounting for + the 11 extra records over 138 ids. Whether these are context variants or + duplicates was not investigated. +* **Nothing here was run.** This is a static read of the tables; the playback + order, and whether slot 3 is really the on-screen duration rather than an + audio length or a delay, has not been checked against the running game. + +## Artifact + +[`../data/cutscene-message-table.txt`](../data/cutscene-message-table.txt) — +all 313 pages with speaker, portrait, seconds, cue and text. +Regenerate with `tools/re-capture/sound_cues.py demo`. diff --git a/docs/re/structures/sound-cue-table.md b/docs/re/structures/sound-cue-table.md new file mode 100644 index 00000000..35b11a10 --- /dev/null +++ b/docs/re/structures/sound-cue-table.md @@ -0,0 +1,94 @@ +# The sound-cue table, and how dialogue binds to a recording + +**✅ Settled 2026-08-26.** `dat/tables.pak` carries the game's cue index. It +closes the join from a message id to the `.slb` bank that voices it — the +missing link between the script/caption side and the audio side. + +## The table + +One IDXD object in `dat/tables.pak` (373 680 bytes, 5 records, 10 937 fields) +holds: + +| record | shape | content | +|---|---|---| +| `SETTINGS` | 3 fields | `PATH` = `game:\dat\sound.pak+`, `PARAM` = `Pj_Silph.xgs`, and a positional `BANK_SE` | +| `BANK_SE` | 1 positional | `Static.slb` | +| `FILES` | 5 135 positional | bank paths — `jpn\Voice\VOICE_TCAF_592.slb`, `BGM_001.slb`, … | +| `STAGES` | **0 fields** | present but empty | +| `SOUNDS` | 5 798 **named** | cue name → numeric sound id | + +`SOUNDS` is the only record on the disc found so far that is entirely named +fields, and the names are the join key. + +Cue families and their id ranges — the id space is partitioned by family, with +no overlap: + + VOICE n=5044 1500 .. 7331 + SE n= 322 1 .. 901 + DEMO n= 295 8000 .. 8408 + BR n= 101 8500 .. 8600 + BGM n= 32 1001 .. 1109 + JNGL n= 3 1201 .. 1203 + +5 797 of the 5 798 values are numeric and **all 5 797 are distinct** — the ids +are unique, so the id is not how banks get shared. One cue's value is +non-numeric. + +The id is **not** an index into `FILES`: ids run to 8 600 and `FILES` has only +5 135 entries. `FILES` is reached by *name*, not by id. + +## The join: a message id to a recording + + MSG_VOICE_D_257 (built-in 64's operand, see isl-message-dialogue-link.md) + -> cue VOICE_D_257 `MSG_` becomes `VOICE_` + -> id 6945 SOUNDS + -> bank jpn\etc\VOICE_D_257.slb FILES, matched on stem + +The prefix rule has one wrinkle, and getting it wrong is easy: + +* **`MSG_` is replaced by `VOICE_`** — it is not simply stripped. +* A name that already carries `VOICE_` keeps the one it has rather than + doubling it: `MSG_VOICE_D_257` → `VOICE_D_257`, but `MSG_TCAF_592` → + `VOICE_TCAF_592` (not `TCAF_592`). + +**I got this wrong first.** Stripping `MSG_` resolved 1 250 of 1 338 script +message names and left 88 unresolved, and the 88 were exactly the four-letter +ship families (`ADAN` 32, `ADPL` 27, `TCAF` 12, `ACRO` 3, `RHIN` 2) — which I +was ready to write up as "text-only announcements with no recording". Seeing +`jpn\Voice\VOICE_TCAF_592.slb` in `FILES` refuted that: the recordings exist, +my rule was wrong. Under the corrected rule the count is **1 326 of 1 338**. + +`SOUNDS` and `FILES` agree exactly — the same 12 names are absent from both, +and **every one of the 5 135 bank files has a cue** (zero orphans). 663 cues +have no bank file; those are `SE`/`BGM`-family cues living in other banks. + +## The 12 script lines with no recording + + MSG_VOICE_A_273 MSG_VOICE_D_183 + MSG_VOICE_D_600 … MSG_VOICE_D_608 MSG_VOICE_D_611 + +All twelve are in the `VOICE` family, have caption text, are called from the +stage scripts, and have neither a cue nor a bank. Ten of them form the near +contiguous block `D_600`–`D_611`. Whether they are subtitle-only by design or +a late cut is **not settled** — the shape (a contiguous high block) suggests +lines added after the recording sessions, but that is a guess, not a +measurement. + +## What this does *not* settle + +* **The known wrong-recording case is not explained by this table.** + `VOICE_D_452` — the generic "Resupply complete" line documented in + [voice-bank-leading-region.md](../voice-bank-leading-region.md) — has its own + unique id (7140) and its own bank. Since every id is distinct, bank sharing + is **not** happening at the cue level, so that symptom still has to be + explained inside the bank/`sound.pak` layer. +* **`STAGES` is empty**, and what would have populated it is unknown. +* **`SOUNDS`' one non-numeric value** was not chased down. +* Only the English pack and the `jpn\` bank paths were examined. Every path in + `FILES` begins with `jpn\`, which is consistent with the already-recorded + finding that the voice set is Japanese, but the other language packs' + tables were not diffed against this one. + +## Tool + + tools/re-capture/sound_cues.py summary | resolve | demo | unbound diff --git a/tools/re-capture/sound_cues.py b/tools/re-capture/sound_cues.py new file mode 100644 index 00000000..172d7067 --- /dev/null +++ b/tools/re-capture/sound_cues.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +"""The sound-cue table in `dat/tables.pak`, and what a message id binds to. + + sound_cues.py summary # table shape and cue families + sound_cues.py resolve MSG_VOICE_D_257 # message id -> cue -> id + bank file + sound_cues.py demo # the cutscene message table + sound_cues.py unbound # script message ids with no recording + +One IDXD object in `dat/tables.pak` carries five records: `SETTINGS`, +`BANK_SE`, `FILES` (5 135 `.slb` bank paths), `STAGES` (empty) and `SOUNDS` +(5 798 named fields, cue name -> numeric sound id). Cue names are the join key: +a script message `MSG_` plays the cue `VOICE_`, and the bank file is +`jpn\\Voice\\VOICE_.slb`. See docs/re/structures/sound-cue-table.md. +""" +import collections +import os +import re +import struct +import sys +import zlib + +TABLES = '/work/sylph_extract/dat/tables.pak' + + +def be32(b, o): + return struct.unpack_from('>I', b, o)[0] + + +def cstr(b, pool, off): + base = pool + off + return b[base:b.index(b'\0', base)].decode('latin1') + + +def pak_entries(path): + d = open(path, 'rb').read() + 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': + try: + raw = zlib.decompress(raw[10:]) + except Exception: + pass + out[h] = raw + return out + + +def records(b): + """IDXD -> {record name: [(field name or None, value)]}.""" + n = be32(b, 4) + o = 8 + 16 * n + recs = [struct.unpack_from('>IIII', b, 8 + 16 * i) for i in range(n)] + m = be32(b, o) + o += 4 + fl = [struct.unpack_from('>III', b, o + 12 * i) for i in range(m)] + o += 12 * m + pool = o + 4 + out = {} + for (_nh, no, fb, fe) in recs: + name = cstr(b, pool, no) if no != 0xFFFFFFFF else None + out[name] = [(cstr(b, pool, fno) if fno != 0xFFFFFFFF else None, + cstr(b, pool, fvo)) for (_k, fno, fvo) in fl[fb:fe]] + return out + + +def sound_table(): + obj = [v for v in pak_entries(TABLES).values() + if v[:4] == b'IDXD' and b'DEMO_017' in v][0] + r = records(obj) + sounds = {k: v for k, v in r['SOUNDS']} + files = [v for _, v in r['FILES']] + return sounds, files, r + + +def cue_for(message_id): + """`MSG_VOICE_D_257` -> `VOICE_D_257`; `MSG_TCAF_592` -> `VOICE_TCAF_592`. + + The `MSG_` prefix becomes `VOICE_`, except that a name already carrying + `VOICE_` keeps the one it has rather than doubling it. + """ + x = message_id[4:] if message_id.startswith('MSG_') else message_id + return x if x.startswith('VOICE_') else 'VOICE_' + x + + +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(lang='E'): + """`MSG_*__` -> text, from the pack's IXUD field tables.""" + pak = '/work/sylph_extract/dat/GP_MAIN_GAME_%s.pak' % lang + out = {} + for b in pak_entries(pak).values(): + if b[:4] != b'IXUD': + continue + n = be32(b, 4) + o = 8 + 16 * n + m = be32(b, o) + o += 4 + fl = [struct.unpack_from('>III', b, o + 12 * i) for i in range(m)] + o += 12 * m + pool = o + 4 + for (_k, no, vo) in fl: + if no == 0xFFFFFFFF: + 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 demo_messages(lang='E'): + """The cutscene message table: id -> per-page (speaker, face, dur, cue).""" + pak = '/work/sylph_extract/dat/GP_MAIN_GAME_%s.pak' % lang + out = {} + for b in pak_entries(pak).values(): + if b[:4] != b'IDXD' or b'MSG_DEMO' not in b: + continue + for name, fs in records(b).items(): + if not name or not name.startswith('Message_'): + continue + named = {k: v for k, v in fs if k} + pos = [v for k, v in fs if k is None] + pc = int(named['PageCount']) + out.setdefault(named['ID'], []).append( + [tuple(pos[9 * p:9 * p + 9]) for p in range(pc)]) + return out + + +def main(): + what = sys.argv[1] if len(sys.argv) > 1 else 'summary' + sounds, files, recs = sound_table() + stems = {os.path.basename(f.replace('\\', '/'))[:-4] + for f in files if f.lower().endswith('.slb')} + + if what == 'resolve': + mid = sys.argv[2] + c = cue_for(mid) + print('message %s' % mid) + print(' cue %s' % c) + print(' sound id %s' % sounds.get(c, '')) + hit = [f for f in files + if os.path.basename(f.replace('\\', '/'))[:-4] == c] + print(' bank file %s' % (hit[0] if hit else '')) + return + + if what == 'demo': + cap = captions() + print('# Cutscene message table — dat/GP_MAIN_GAME_E.pak, IDXD objects') + print('#') + print('# One line per page. A page is one subtitle box; successive pages are') + print('# successive utterances, which is why the speaker changes mid-message.') + print('# Columns: id, page, speaker, portrait, on-screen seconds, audio cue,') + print('# then the caption text.') + print() + for mid, variants in sorted(demo_messages().items()): + for pages in variants: + for i, g in enumerate(pages): + text = ' '.join(cap.get('%s_%03d_%02d' % (mid, i, l), '') + for l in range(4)).strip() + print('%-16s p%d %-14s %-18s %5ss %-11s %s' + % (mid if i == 0 else '', i, + g[0].replace('Character', ''), + g[1].replace('Face', ''), g[3], g[4], text)) + return + + if what == 'unbound': + for c in sorted(set(sounds) - stems): + print('%-24s id=%s (cue with no bank file)' % (c, sounds[c])) + return + + print('records : %s' % ', '.join(str(k) for k in recs)) + print('SOUNDS cues : %d' % len(sounds)) + print('FILES bank paths : %d (distinct stems %d)' % (len(files), len(stems))) + print('cues with no file : %d' % len(set(sounds) - stems)) + print('files with no cue : %d' % len(stems - set(sounds))) + fam = collections.Counter(re.match(r'[A-Za-z]+', k).group(0) for k in sounds) + print('\ncue families and their sound-id ranges:') + rng = collections.defaultdict(list) + for k, v in sounds.items(): + if v.lstrip('-').isdigit(): + rng[re.match(r'[A-Za-z]+', k).group(0)].append(int(v)) + for p, _ in fam.most_common(): + v = rng.get(p, []) + if v: + print(' %-8s n=%-5d %6d .. %6d' % (p, len(v), min(v), max(v))) + + +if __name__ == '__main__': + main()