From 4998bdaf65da2d3370bf7321a0965623bbcca7ea Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 10 Jan 2026 16:27:44 -0700 Subject: [PATCH] Savegame: kaitai format (#1713) * Add savegame.ksy * Updates * Updates --- LEGO1/lego/legoomni/src/race/legorace.cpp | 10 +- docs/README.md | 8 + docs/samples/G0.GS | Bin 0 -> 4567 bytes docs/samples/G1.GS | Bin 0 -> 17084 bytes docs/samples/G2.GS | Bin 0 -> 17156 bytes docs/savegame.ksy | 1014 +++++++++++++++++++++ 6 files changed, 1027 insertions(+), 5 deletions(-) create mode 100755 docs/samples/G0.GS create mode 100755 docs/samples/G1.GS create mode 100755 docs/samples/G2.GS create mode 100644 docs/savegame.ksy diff --git a/LEGO1/lego/legoomni/src/race/legorace.cpp b/LEGO1/lego/legoomni/src/race/legorace.cpp index ea56a692..5f869095 100644 --- a/LEGO1/lego/legoomni/src/race/legorace.cpp +++ b/LEGO1/lego/legoomni/src/race/legorace.cpp @@ -116,19 +116,19 @@ void LegoRace::Enable(MxBool p_enable) // FUNCTION: LEGO1 0x10015f30 RaceState::RaceState() { - m_entries[0].m_id = 1; + m_entries[0].m_id = LegoActor::c_pepper; m_entries[0].m_lastScore = 0; m_entries[0].m_score = 0; - m_entries[1].m_id = 2; + m_entries[1].m_id = LegoActor::c_mama; m_entries[1].m_lastScore = 0; m_entries[1].m_score = 0; - m_entries[2].m_id = 3; + m_entries[2].m_id = LegoActor::c_papa; m_entries[2].m_lastScore = 0; m_entries[2].m_score = 0; - m_entries[3].m_id = 4; + m_entries[3].m_id = LegoActor::c_nick; m_entries[3].m_lastScore = 0; m_entries[3].m_score = 0; - m_entries[4].m_id = 5; + m_entries[4].m_id = LegoActor::c_laura; m_entries[4].m_lastScore = 0; m_entries[4].m_score = 0; m_state = RaceState::e_carrace; diff --git a/docs/README.md b/docs/README.md index 297f7ce3..0ea419d7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ Kaitai Struct allows you to define binary formats in a YAML-based `.ksy` file, w | File | Extension | Description | |------|-----------|-------------| +| [`savegame.ksy`](/docs/savegame.ksy) | `.GS` | Main game save data (game state, progress, customizations) | | [`players.ksy`](/docs/players.ksy) | `.gsi` | Player profile save data (usernames) | | [`history.ksy`](/docs/history.ksy) | `.gsi` | Score history and high scores | @@ -29,6 +30,9 @@ See the [Kaitai Struct Visualizer installation instructions](https://github.com/ The [Kaitai Struct Visualizer](https://github.com/kaitai-io/kaitai_struct_visualizer) (`ksv`) provides an interactive terminal UI for exploring binary files. ```bash +# View a save game file +ksv samples/G0.GS savegame.ksy + # View a Players.gsi file ksv samples/Players.gsi players.ksy @@ -41,6 +45,9 @@ ksv samples/History.gsi history.ksy `ksdump` outputs the parsed structure as JSON or YAML for scripting and inspection. ```bash +# Dump a save game to JSON +ksdump --format json samples/G0.GS savegame.ksy + # Dump Players.gsi to JSON ksdump --format json samples/Players.gsi players.ksy @@ -51,5 +58,6 @@ ksdump --format yaml samples/History.gsi history.ksy ## Sample Files The [`samples/`](/docs/samples/) directory contains example save files for testing: +- `G0.GS`, `G1.GS`, `G2.GS` - Sample main game save files (slots 0, 1, 2) - `Players.gsi` - Sample player profile data - `History.gsi` - Sample score history data diff --git a/docs/samples/G0.GS b/docs/samples/G0.GS new file mode 100755 index 0000000000000000000000000000000000000000..205f5b8d59d1edf943da26364d2c000e13caccee GIT binary patch literal 4567 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 M`-+_2XP$!p0TInpWdHyG literal 0 HcmV?d00001 diff --git a/docs/samples/G2.GS b/docs/samples/G2.GS new file mode 100755 index 0000000000000000000000000000000000000000..2d941effd16d2719250e08a404ffc5836ce8e51d GIT binary patch literal 17156 zcmeI4-D@LN6u|GzB$+1L(vPi(zHI&g?e-%n`qJ3AU0JES>$VCCQsyhkq|;2jGc`>g z+FBo6Q5MCA1%(A)^g)DmDV3^AwP1bF7oQh=5_}LuT%}7_h-W65%$=L0O`&Bg-U~zS zIrrRi&;6Y$V13B`-^s z=4oX^Qf6yfW!BUBwyJLB8?||pz*<$mz6YbA+2tX2HPTc&r>jjOn=@_V`SoM4n%aW4 znXSEGdmjAAY)NmjT1hXeUc8>IDraS)vK+I9lq(OOaygbY!!S!O-Yfy+$sqD9X+*Bd z`C{XCRoSrRhAk8SAB48nkrsnGPzqUL3!2VLUTA zF$K4}*TRAj4ts+05DF3XLZUx8iClUc*IBqA?K>KP&jBWf(XqUMsa8N)Q(SsGkUyk*rEfmJFPz2 zje_y?Fmf*=_c3w=BYO8J%E;n@GVrTM105WcMTUO_2lFpN0Cc`s`&bD4QM^PPb(+7T zAPRyg)@8_Nu%ci?wEsvn912nSVDlFRgD~Y^6vRM1Kt2OQ8w-LTlo*p ztydflSK8>KP(9{wrJcUBO}BynHv0ZE66`4^C%7Nv>BoFzda52;`)nh3?d11F+q6+X z?bKL{TB6y~_am*R`$EP0VTbqcIX{T1w}5ZAzpmM+u&)z*{u$IbseGA^x99K4OdN*_qJ|j_A1@u zk=3`q{eECM{ci8wPhP)%_x7)!-@oOUe){|y>m6v~+%tp2GpV6FAAXbh>e|i*+8Evb zZFwoJjqa|$IWjf&W8Ygz#~MCUzV*suUuV$d?#7ol2DV@RZY6!^lD%F^KYsg#*WPoj z`~JC_a-jXZHG=-w3O!ZYe(8%}#-{U+CO?X4>CXE*ZUooIet@8luEmj(cYfa($m!Wt zaGQXuAFjQ1U zPJk0|2{`W*;E$^Yf2Y6;f)hAC0*moj&+&QZo;d+dfD_;ZH~~(86W|0m0ZxDucrXNN z?-U4sr|@9-^Hn(kPJk2O1ULasfD_;ZH~~)Jze6DZ@FT)1gqwhi|9>|?;O&1+U{(A8 z_K)GTNKv12Pm7!e`BgX(clhQUY&~cqJI>19s;%_i_+dYf+TUTvg@)#z`1;q09Qxl16mNy@h;p+zc5$9lbH>zr{NHs@rJWqh{xq#-L`E<{#x#z{tqJIH>BANvN literal 0 HcmV?d00001 diff --git a/docs/savegame.ksy b/docs/savegame.ksy new file mode 100644 index 00000000..f08d5c22 --- /dev/null +++ b/docs/savegame.ksy @@ -0,0 +1,1014 @@ +meta: + id: savegame + title: Main Save Game File + application: LEGO Island + file-extension: gs + license: CC0-1.0 + endian: le +doc: | + Main save game file format for LEGO Island (1997). Stores complete game + progress including customization, mission scores, and world state. + + The file is located at `/G.GS` where slot is 0-9 and + save_path is typically the game's installation directory. + + File structure: + 1. Header (version, player ID, act, actor) + 2. Variables section (vehicle colors, background color, light position) + 3. Character manager data (66 actors) + 4. Plant manager data (81 plants) + 5. Building manager data (16 buildings) + 6. Game states (mission progress, scores, etc.) + 7. Previous area (for Act 2/3 saves) + +seq: + - id: version + type: s4 + doc: | + File format version. Must be 0x1000c (65548) for valid saves. + The game rejects files with mismatched versions. + - id: player_id + type: s2 + doc: Current player's unique ID from the player profile. + - id: current_act + type: u2 + enum: act + doc: Current game act (0 = Act 1, 1 = Act 2, 2 = Act 3). + - id: actor_id + type: u1 + enum: actor + doc: Currently selected playable character. + - id: variables + type: variable + repeat: until + repeat-until: _.is_end_marker + doc: | + Vehicle customization colors and game settings. Contains 43 color + variables, background color, light position, and ends with + "END_OF_VARIABLES" marker. + - id: characters + type: character_manager + doc: Character manager data for all 66 actors in the game. + - id: plants + type: plant_entry + repeat: expr + repeat-expr: 81 + doc: Plant manager data for all 81 plants in the game world. + - id: buildings + type: building_entry + repeat: expr + repeat-expr: 16 + doc: Building manager data for all 16 buildings in the game world. + - id: building_next_variant + type: u1 + doc: Next building variant to use (cycles through variants). + - id: state_count + type: s2 + doc: Number of serialized game states that follow. + - id: states + type: game_state + repeat: expr + repeat-expr: state_count + doc: Serialized game state objects (mission progress, scores, etc.). + - id: previous_area + type: s2 + doc: | + Previous area ID for Act 2/3 saves. Set to -1 (undefined) for Act 1. + Used to restore the player's location when loading a save. + +types: + variable: + doc: | + A named variable with a string value. Used for vehicle colors and + game settings. The "END_OF_VARIABLES" marker has no value. + seq: + - id: name_length + type: u1 + doc: Length of variable name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: Variable name (e.g., "c_dbbkfny0", "backgroundcolor"). + - id: value_length + type: u1 + if: not is_end_marker + doc: Length of variable value in bytes. + - id: value + type: str + size: value_length + encoding: ASCII + if: not is_end_marker + doc: | + Variable value. For colors this is a color name like "lego red". + For backgroundcolor this is "set R G B". + For lightposition this is a number "1" or "2". + instances: + is_end_marker: + value: name == "END_OF_VARIABLES" + doc: True if this is the end-of-variables marker. + + character_manager: + doc: | + All 66 character entries in the game, in the order defined by g_actorInfoInit. + Each entry is 16 bytes, for a total of 1056 bytes. + seq: + - id: pepper + type: pepper_character_entry + doc: Pepper Roni + - id: mama + type: standard_character_entry + doc: Mama Brickolini + - id: papa + type: standard_character_entry + doc: Papa Brickolini + - id: nick + type: standard_character_entry + doc: Nick Brick + - id: laura + type: standard_character_entry + doc: Laura Brick + - id: infoman + type: infoman_character_entry + doc: Infomaniac + - id: brickstr + type: standard_character_entry + doc: Brickster + - id: studs + type: standard_character_entry + doc: Studs Linkin + - id: rhoda + type: standard_character_entry + doc: Rhoda Hogg + - id: valerie + type: standard_character_entry + doc: Valerie Stubbins + - id: snap + type: standard_character_entry + doc: Snap Lockitt + - id: pt + type: standard_character_entry + - id: mg + type: standard_character_entry + doc: Margaret Patricia "Maggie" Post + - id: bu + type: standard_character_entry + - id: ml + type: standard_character_entry + - id: nu + type: standard_character_entry + - id: na + type: standard_character_entry + doc: Nancy Nubbins + - id: cl + type: standard_character_entry + - id: en + type: standard_character_entry + - id: re + type: standard_character_entry + - id: ro + type: standard_character_entry + - id: d1 + type: standard_character_entry + - id: d2 + type: standard_character_entry + - id: d3 + type: standard_character_entry + - id: d4 + type: standard_character_entry + - id: l1 + type: standard_character_entry + - id: l2 + type: standard_character_entry + - id: l3 + type: standard_character_entry + - id: l4 + type: standard_character_entry + - id: l5 + type: standard_character_entry + - id: l6 + type: standard_character_entry + - id: b1 + type: standard_character_entry + - id: b2 + type: standard_character_entry + - id: b3 + type: standard_character_entry + - id: b4 + type: standard_character_entry + - id: cm + type: standard_character_entry + - id: gd + type: standard_character_entry + - id: rd + type: standard_character_entry + - id: pg + type: standard_character_entry + doc: Polly Gone + - id: bd + type: standard_character_entry + - id: sy + type: standard_character_entry + - id: gn + type: standard_character_entry + - id: df + type: standard_character_entry + - id: bs + type: standard_character_entry + - id: lt + type: standard_character_entry + - id: st + type: standard_character_entry + - id: bm + type: standard_character_entry + - id: jk + type: standard_character_entry + - id: ghost + type: ghost_character_entry + - id: ghost01 + type: ghost_character_entry + - id: ghost02 + type: ghost_character_entry + - id: ghost03 + type: ghost_character_entry + - id: ghost04 + type: ghost_character_entry + - id: ghost05 + type: ghost_character_entry + - id: hg + type: standard_character_entry + - id: pntgy + type: standard_character_entry + - id: pep + type: pepper_character_entry + - id: cop01 + type: standard_character_entry + - id: actor_01 + type: standard_character_entry + - id: actor_02 + type: standard_character_entry + - id: actor_03 + type: standard_character_entry + - id: actor_04 + type: standard_character_entry + - id: actor_05 + type: standard_character_entry + - id: btmncycl + type: standard_character_entry + - id: cboycycl + type: standard_character_entry + - id: boatman + type: standard_character_entry + + standard_character_entry: + doc: | + Character customization and state for actors using the standard hat parts + (g_hatPartIndices). Hat index 0-19 maps directly to hat_part enum. + Total size is 16 bytes. + seq: + - id: sound + type: s4 + doc: Sound/voice variant index. + - id: move + type: s4 + doc: Movement/animation variant index. + - id: mood + type: u1 + doc: Character mood state. + - id: hat_part_name_index + type: u1 + enum: standard_hat + doc: Index into standard hat parts (0-19 = standard hats). + - id: hat_name_index + type: u1 + enum: lego_color + doc: Hat color. + - id: infogron_name_index + type: u1 + enum: lego_color + doc: Torso (infogron) color. + - id: armlft_name_index + type: u1 + enum: lego_color + doc: Left arm color. + - id: armrt_name_index + type: u1 + enum: lego_color + doc: Right arm color. + - id: leglft_name_index + type: u1 + enum: lego_color + doc: Left leg color. + - id: legrt_name_index + type: u1 + enum: lego_color + doc: Right leg color. + + pepper_character_entry: + doc: | + Character customization and state for Pepper (uses g_pepperHatPartIndices). + Hat index 0=phat, 1-20 map to standard hats 0-19. + Total size is 16 bytes. + seq: + - id: sound + type: s4 + doc: Sound/voice variant index. + - id: move + type: s4 + doc: Movement/animation variant index. + - id: mood + type: u1 + doc: Character mood state. + - id: hat_part_name_index + type: u1 + enum: pepper_hat + doc: Index into Pepper's hat parts (0=phat, 1-20=standard hats 0-19). + - id: hat_name_index + type: u1 + enum: lego_color + doc: Hat color. + - id: infogron_name_index + type: u1 + enum: lego_color + doc: Torso (infogron) color. + - id: armlft_name_index + type: u1 + enum: lego_color + doc: Left arm color. + - id: armrt_name_index + type: u1 + enum: lego_color + doc: Right arm color. + - id: leglft_name_index + type: u1 + enum: lego_color + doc: Left leg color. + - id: legrt_name_index + type: u1 + enum: lego_color + doc: Right leg color. + + infoman_character_entry: + doc: | + Character customization and state for Infoman (uses g_infomanHatPartIndices). + Hat index 0=icap (only option). + Total size is 16 bytes. + seq: + - id: sound + type: s4 + doc: Sound/voice variant index. + - id: move + type: s4 + doc: Movement/animation variant index. + - id: mood + type: u1 + doc: Character mood state. + - id: hat_part_name_index + type: u1 + enum: infoman_hat_index + doc: Index into Infoman's hat parts (0=icap only). + - id: hat_name_index + type: u1 + enum: lego_color + doc: Hat color. + - id: infogron_name_index + type: u1 + enum: lego_color + doc: Torso (infogron) color. + - id: armlft_name_index + type: u1 + enum: lego_color + doc: Left arm color. + - id: armrt_name_index + type: u1 + enum: lego_color + doc: Right arm color. + - id: leglft_name_index + type: u1 + enum: lego_color + doc: Left leg color. + - id: legrt_name_index + type: u1 + enum: lego_color + doc: Right leg color. + + ghost_character_entry: + doc: | + Character customization and state for ghosts (uses g_ghostHatPartIndices). + Hat index 0=sheet (only option). + Total size is 16 bytes. + seq: + - id: sound + type: s4 + doc: Sound/voice variant index. + - id: move + type: s4 + doc: Movement/animation variant index. + - id: mood + type: u1 + doc: Character mood state. + - id: hat_part_name_index + type: u1 + enum: ghost_hat_index + doc: Index into ghost hat parts (0=sheet only). + - id: hat_name_index + type: u1 + enum: lego_color + doc: Hat color. + - id: infogron_name_index + type: u1 + enum: lego_color + doc: Torso (infogron) color. + - id: armlft_name_index + type: u1 + enum: lego_color + doc: Left arm color. + - id: armrt_name_index + type: u1 + enum: lego_color + doc: Right arm color. + - id: leglft_name_index + type: u1 + enum: lego_color + doc: Left leg color. + - id: legrt_name_index + type: u1 + enum: lego_color + doc: Right leg color. + + plant_entry: + doc: | + Plant state data for a single plant in the game world. + Total size is 12 bytes per plant. + seq: + - id: variant + type: u1 + enum: plant_variant + doc: Plant type (flower, tree, bush, palm). + - id: sound + type: u4 + doc: Sound effect index when interacting. + - id: move + type: u4 + doc: Movement/animation state. + - id: mood + type: u1 + doc: Plant mood/state value. + - id: color + type: u1 + enum: plant_color + doc: Plant color variant. + - id: counter + type: s1 + doc: | + Growth/interaction counter. Affects plant height. + This is used in Act 2/3. + + building_entry: + doc: | + Building state data for a single building in the game world. + Total size is 10 bytes per building. + seq: + - id: sound + type: u4 + doc: Sound effect index. + - id: move + type: u4 + doc: Movement/animation state. + - id: mood + type: u1 + doc: Building mood/state value. + - id: counter + type: s1 + doc: | + Interaction counter. Affects building height adjustment. + This is used in Act 2/3. + + game_state: + doc: | + A serialized game state object. The name determines the type and + format of the data that follows. + seq: + - id: name_length + type: s2 + doc: Length of state class name. + - id: name + type: str + size: name_length + encoding: ASCII + doc: | + State class name (e.g., "Act1State", "PizzeriaState"). + Determines the format of the following data. + - id: data + type: + switch-on: name + cases: + '"PizzeriaState"': pizzeria_state_data + '"PizzaMissionState"': pizza_mission_state_data + '"TowTrackMissionState"': score_mission_state_data + '"AmbulanceMissionState"': score_mission_state_data + '"HospitalState"': hospital_state_data + '"GasStationState"': gas_station_state_data + '"PoliceState"': police_state_data + '"JetskiRaceState"': race_state_data + '"CarRaceState"': race_state_data + '"LegoJetskiBuildState"': vehicle_build_state_data + '"LegoCopterBuildState"': vehicle_build_state_data + '"LegoDuneCarBuildState"': vehicle_build_state_data + '"LegoRaceCarBuildState"': vehicle_build_state_data + '"AnimState"': anim_state_data + '"Act1State"': act1_state_data + doc: State-specific data. Format depends on state class name. + + pizzeria_state_data: + doc: | + Pizzeria state tracking playlist indices for each actor. + Total size is 10 bytes (5 x S16). + seq: + - id: pepper_playlist_index + type: s2 + doc: Pepper's next playlist index. + - id: mama_playlist_index + type: s2 + doc: Mama's next playlist index. + - id: papa_playlist_index + type: s2 + doc: Papa's next playlist index. + - id: nick_playlist_index + type: s2 + doc: Nick's next playlist index. + - id: laura_playlist_index + type: s2 + doc: Laura's next playlist index. + + pizza_mission_state_data: + doc: | + Pizza delivery mission state for all 5 actors. + Total size is 40 bytes (5 missions x 4 x S16). + seq: + - id: pepper + type: pizza_mission_entry + doc: Pepper's pizza delivery mission data. + - id: mama + type: pizza_mission_entry + doc: Mama's pizza delivery mission data. + - id: papa + type: pizza_mission_entry + doc: Papa's pizza delivery mission data. + - id: nick + type: pizza_mission_entry + doc: Nick's pizza delivery mission data. + - id: laura + type: pizza_mission_entry + doc: Laura's pizza delivery mission data. + + pizza_mission_entry: + doc: Single actor's pizza mission data (8 bytes). + seq: + - id: unk0x06 + type: s2 + doc: Unknown field at offset 0x06. + - id: counter + type: s2 + doc: Mission attempt counter. + - id: score + type: s2 + enum: score_color + doc: Current/last mission score. + - id: hi_score + type: s2 + enum: score_color + doc: High score for this mission. + + score_mission_state_data: + doc: | + Mission state with scores and high scores for all 5 actors. + Used by TowTrackMissionState and AmbulanceMissionState. + Total size is 20 bytes (10 x S16). + seq: + - id: pepper_score + type: s2 + enum: score_color + doc: Pepper's current/last score. + - id: mama_score + type: s2 + enum: score_color + doc: Mama's current/last score. + - id: papa_score + type: s2 + enum: score_color + doc: Papa's current/last score. + - id: nick_score + type: s2 + enum: score_color + doc: Nick's current/last score. + - id: laura_score + type: s2 + enum: score_color + doc: Laura's current/last score. + - id: pepper_high_score + type: s2 + enum: score_color + doc: Pepper's high score. + - id: mama_high_score + type: s2 + enum: score_color + doc: Mama's high score. + - id: papa_high_score + type: s2 + enum: score_color + doc: Papa's high score. + - id: nick_high_score + type: s2 + enum: score_color + doc: Nick's high score. + - id: laura_high_score + type: s2 + enum: score_color + doc: Laura's high score. + + hospital_state_data: + doc: | + Hospital interaction state for all actors. + Total size is 12 bytes (6 x S16). + seq: + - id: state_actor + type: s2 + doc: Current actor state. + - id: state_pepper + type: s2 + doc: Pepper's hospital interaction state. + - id: state_mama + type: s2 + doc: Mama's hospital interaction state. + - id: state_papa + type: s2 + doc: Papa's hospital interaction state. + - id: state_nick + type: s2 + doc: Nick's hospital interaction state. + - id: state_laura + type: s2 + doc: Laura's hospital interaction state. + + gas_station_state_data: + doc: | + Gas station interaction state for all actors. + Total size is 10 bytes (5 x S16). + seq: + - id: pepper_action + type: s2 + doc: Pepper's gas station action state. + - id: mama_action + type: s2 + doc: Mama's gas station action state. + - id: papa_action + type: s2 + doc: Papa's gas station action state. + - id: nick_action + type: s2 + doc: Nick's gas station action state. + - id: laura_action + type: s2 + doc: Laura's gas station action state. + + police_state_data: + doc: | + Police station state. Stores the police script ID. + Total size is 4 bytes (1 x S32). + seq: + - id: police_script + type: s4 + doc: Current police script/animation ID. + + race_state_data: + doc: | + Race state with scores for all 5 actors. + Used by JetskiRaceState and CarRaceState. + Total size is 25 bytes (5 entries x 5 bytes). + seq: + - id: pepper + type: race_entry + doc: Pepper's race scores. + - id: mama + type: race_entry + doc: Mama's race scores. + - id: papa + type: race_entry + doc: Papa's race scores. + - id: nick + type: race_entry + doc: Nick's race scores. + - id: laura + type: race_entry + doc: Laura's race scores. + + race_entry: + doc: Single actor's race score entry (5 bytes). + seq: + - id: id + type: u1 + enum: actor + doc: Actor ID. + - id: last_score + type: s2 + enum: score_color + doc: Score from last race. + - id: high_score + type: s2 + enum: score_color + doc: Best score (high score). + + vehicle_build_state_data: + doc: | + Vehicle build state tracking build progress. + Used by LegoJetskiBuildState, LegoCopterBuildState, + LegoDuneCarBuildState, and LegoRaceCarBuildState. + Total size is 4 bytes (4 x U8). + seq: + - id: introduction_counter + type: u1 + doc: Number of times intro has been shown. + - id: finished_build + type: u1 + doc: Whether vehicle build was completed (0/1). + - id: played_exit_script + type: u1 + doc: Whether exit animation has played (0/1). + - id: placed_part_count + type: u1 + doc: Number of parts placed during current build. + + anim_state_data: + doc: | + Animation manager state. Contains extra character ID and + two variable-length arrays for tracking animation states. + seq: + - id: extra_character_id + type: u4 + doc: Extra character ID. + - id: anim_count + type: u4 + doc: Number of animation entries in first array. + - id: anim_indices + type: u2 + repeat: expr + repeat-expr: anim_count + doc: Animation index values. + - id: location_flags_count + type: u4 + doc: Number of location flag entries. + - id: location_flags + type: u1 + repeat: expr + repeat-expr: location_flags_count + doc: Location flags for animation positions. + + act1_state_data: + doc: | + Act 1 state containing named plane data and textures. + Always contains exactly 7 named planes (for each vehicle type), + followed by conditional textures based on which planes have names, + and two final fields. + seq: + - id: motocycle_plane + type: named_plane + doc: Motorcycle spawn plane. + - id: bike_plane + type: named_plane + doc: Bike spawn plane. + - id: skateboard_plane + type: named_plane + doc: Skateboard spawn plane. + - id: helicopter_plane + type: named_plane + doc: Helicopter spawn plane. + - id: jetski_plane + type: named_plane + doc: Jetski spawn plane. + - id: dunebuggy_plane + type: named_plane + doc: Dune buggy spawn plane. + - id: racecar_plane + type: named_plane + doc: Racecar spawn plane. + - id: helicopter_textures + type: act1_texture + repeat: expr + repeat-expr: 3 + if: helicopter_plane.name_length > 0 + doc: Helicopter textures (windshield, left jet, right jet). + - id: jetski_textures + type: act1_texture + repeat: expr + repeat-expr: 2 + if: jetski_plane.name_length > 0 + doc: Jetski textures (front, windshield). + - id: dunebuggy_texture + type: act1_texture + if: dunebuggy_plane.name_length > 0 + doc: Dune buggy front texture. + - id: racecar_textures + type: act1_texture + repeat: expr + repeat-expr: 3 + if: racecar_plane.name_length > 0 + doc: Racecar textures (front, back, tail). + - id: cpt_click_dialogue_next_index + type: s2 + doc: Next dialogue index for Captain Click. + - id: played_exit_explanation + type: u1 + doc: Whether exit explanation has been played (0/1). + + act1_texture: + doc: | + A texture used for customizable surfaces in Act 1. + Contains filename and LegoImage bitmap data. + seq: + - id: name_length + type: s2 + doc: Length of texture filename. + - id: name + type: str + size: name_length + encoding: ASCII + doc: Texture filename (e.g., "chwind.gif"). + - id: width + type: u4 + doc: Image width in pixels. + - id: height + type: u4 + doc: Image height in pixels. + - id: palette_count + type: u4 + doc: Number of palette entries. + - id: palette + type: palette_entry + repeat: expr + repeat-expr: palette_count + doc: Palette entries (RGB values). + - id: bitmap_data + size: width * height + doc: Raw pixel data (1 byte per pixel, indexed). + + palette_entry: + doc: A single RGB palette entry. + seq: + - id: red + type: u1 + doc: Red component (0-255). + - id: green + type: u1 + doc: Green component (0-255). + - id: blue + type: u1 + doc: Blue component (0-255). + + named_plane: + doc: | + A named plane used for actor positioning in Act 1. + Total size is variable: 2 + name_length + 36 bytes. + seq: + - id: name_length + type: s2 + doc: Length of plane name (S16 format like other strings). + - id: name + type: str + size: name_length + encoding: ASCII + doc: Plane name identifier (e.g., "INT43", "EDG02_51"). + - id: position + type: f4 + repeat: expr + repeat-expr: 3 + doc: Position vector (X, Y, Z). + - id: direction + type: f4 + repeat: expr + repeat-expr: 3 + doc: Direction/forward vector (X, Y, Z). + - id: up + type: f4 + repeat: expr + repeat-expr: 3 + doc: Up vector (X, Y, Z). + +enums: + act: + 0: act1 + 1: act2 + 2: act3 + + actor: + 0: none + 1: pepper + 2: mama + 3: papa + 4: nick + 5: laura + + plant_variant: + 0: flower + 1: tree + 2: bush + 3: palm + + plant_color: + 0: white + 1: black + 2: yellow + 3: red + 4: green + + lego_color: + 0: white + 1: black + 2: yellow + 3: red + 4: blue + 5: brown + 6: lt_grey + 7: green + + hat_part: + 0: baseball + 1: chef + 2: cap + 3: cophat + 4: helmet + 5: ponytail + 6: pageboy + 7: shrthair + 8: bald + 9: flower + 10: cboyhat + 11: cuphat + 12: cathat + 13: backbcap + 14: pizhat + 15: caprc + 16: capch + 17: capdb + 18: capjs + 19: capmd + 20: sheet + 21: phat + 22: icap + + standard_hat: + 0: baseball + 1: chef + 2: cap + 3: cophat + 4: helmet + 5: ponytail + 6: pageboy + 7: shrthair + 8: bald + 9: flower + 10: cboyhat + 11: cuphat + 12: cathat + 13: backbcap + 14: pizhat + 15: caprc + 16: capch + 17: capdb + 18: capjs + 19: capmd + + pepper_hat: + 0: phat + 1: baseball + 2: chef + 3: cap + 4: cophat + 5: helmet + 6: ponytail + 7: pageboy + 8: shrthair + 9: bald + 10: flower + 11: cboyhat + 12: cuphat + 13: cathat + 14: backbcap + 15: pizhat + 16: caprc + 17: capch + 18: capdb + 19: capjs + 20: capmd + + infoman_hat_index: + 0: icap + + ghost_hat_index: + 0: sheet + + score_color: + 0: grey + 1: yellow + 2: blue + 3: red