rpfm_lib/files/cs2_parsed/mod.rs
1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! CS2 Parsed file format support.
12//!
13//! CS2 Parsed files (`.cs2.parsed`) define gameplay logic and interaction data for 3D
14//! building models in Total War games. These files contain information about unit
15//! placement, pathfinding, collision, defense positions, and various building-specific
16//! behaviors.
17//!
18//! # File Format
19//!
20//! CS2 Parsed files are binary files containing:
21//! - Version header
22//! - UI flag position for minimap display
23//! - Building pieces with destruction states
24//! - Gameplay logic: platforms, pipes, gates, EF lines, etc.
25//! - Legacy collision data (moved to separate `.cs2.collision` files in newer games)
26//! - Projectile emitters (moved to map data in newer games)
27//!
28//! # Supported Versions
29//!
30//! - **Version 0**: Legacy format (Empire/Napoleon)
31//! - **Version 8**: Legacy format
32//! - **Version 9**: Legacy format
33//! - **Version 10**: Legacy format
34//! - **Version 11**: Legacy format
35//! - **Version 12**: Legacy format
36//! - **Version 13**: Legacy format
37//! - **Version 18**: Older format (Three Kingdoms)
38//! - **Version 20**: Older format (Troy/Warhammer II)
39//! - **Version 21**: Current format (Warhammer III)
40//!
41//! # Key Components
42//!
43//! ## Platforms
44//! Define walkable surfaces where units can stand and fight.
45//!
46//! ## Pipes
47//! Define paths for unit movement between platforms (stairs, ladders, doors, etc.).
48//!
49//! ## EF Lines (Entity Formation Lines)
50//! Define positions where units form up for specific actions (firing lines, boarding
51//! positions, officer spawn points, etc.).
52//!
53//! ## Gates
54//! Define entrance/exit collision for wall gates.
55//!
56//! ## Docking Lines
57//! Define where siege equipment can attach to walls.
58//!
59//! # Usage
60//!
61//! ```rust,ignore
62//! use rpfm_lib::files::cs2_parsed::Cs2Parsed;
63//! use rpfm_lib::files::Decodeable;
64//!
65//! // Decode from binary data
66//! let parsed = Cs2Parsed::decode(&mut data, &None)?;
67//!
68//! // Access building pieces
69//! for piece in parsed.pieces() {
70//! println!("Piece: {}", piece.name());
71//!
72//! // Access destructs (damage states)
73//! for destruct in piece.destructs() {
74//! println!(" Destruct: {} ({} platforms, {} pipes)",
75//! destruct.name(),
76//! destruct.platforms().len(),
77//! destruct.pipes().len()
78//! );
79//! }
80//! }
81//! ```
82//!
83//! # File Location
84//!
85//! These files are typically found at:
86//! - `rigidmodels/buildings/*/*.cs2.parsed`
87
88use getset::*;
89use serde_derive::{Serialize, Deserialize};
90
91use crate::error::{Result, RLibError};
92use crate::binary::{ReadBytes, WriteBytes};
93use crate::files::{cs2_collision::Collision3d, DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
94use crate::files::bmd::common::*;
95use crate::games::GameInfo;
96use crate::utils::check_size_mismatch;
97
98/// File extension for CS2 Parsed files.
99pub const EXTENSION: &str = ".cs2.parsed";
100
101#[cfg(test)] mod cs2_parsed_test;
102
103mod versions;
104
105//---------------------------------------------------------------------------//
106// Enum & Structs
107//---------------------------------------------------------------------------//
108
109/// Represents a CS2 Parsed file decoded in memory.
110///
111/// Contains all gameplay logic data for a building model, including unit placement,
112/// pathfinding, collision, and various building-specific behaviors.
113///
114/// # Fields
115///
116/// * `version` - File format version (0, 8-13, 18, 20, or 21)
117/// * `ui_flag` - Flag position shown on minimap
118/// * `bounding_box` - Overall bounds (not present in v20 onwards)
119/// * `int_1` - Unknown field
120/// * `pieces` - List of building pieces with destruction states
121///
122/// # Examples
123///
124/// ```rust,ignore
125/// let parsed = Cs2Parsed::decode(&mut data, &None)?;
126/// println!("Version: {}", parsed.version());
127/// println!("Pieces: {}", parsed.pieces().len());
128/// ```
129#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
130pub struct Cs2Parsed {
131 /// File format version number.
132 version: u32,
133
134 /// Flag position for minimap display.
135 ui_flag: UiFlag,
136
137 /// Overall bounding box (not present in v20 onwards).
138 bounding_box: Cube,
139
140 /// Unknown field.
141 int_1: i32,
142
143 /// List of building pieces.
144 pieces: Vec<Piece>,
145}
146
147/// A piece of a building model with multiple destruction states.
148///
149/// Buildings are composed of multiple pieces, each with one or more destruction
150/// states (destructs). As a building takes damage, it transitions between these states.
151///
152/// # Fields
153///
154/// * `name` - Name identifier for this piece
155/// * `node_name` - Scene node name for attachment
156/// * `node_transform` - Transformation matrix for positioning
157/// * `int_3` - Unknown field
158/// * `int_4` - Unknown field (only in v21)
159/// * `destructs` - List of destruction states for this piece
160/// * `f_6` - Unknown field
161///
162/// # Examples
163///
164/// ```rust,ignore
165/// for piece in parsed.pieces() {
166/// println!("Piece: {} ({} destructs)", piece.name(), piece.destructs().len());
167/// }
168/// ```
169#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
170pub struct Piece {
171 /// Name identifier for this piece.
172 name: String,
173
174 /// Scene node name for attachment.
175 node_name: String,
176
177 /// Transformation matrix for positioning.
178 node_transform: Transform4x4,
179
180 /// Unknown field.
181 int_3: i32,
182
183 /// Unknown field (only in v21).
184 int_4: i32,
185
186 /// List of destruction states for this piece.
187 destructs: Vec<Destruct>,
188
189 /// Unknown field.
190 f_6: f32,
191}
192
193/// A destruction state of a building piece.
194///
195/// Each piece can have multiple destructs representing different damage states.
196/// Destructs contain all the gameplay logic for that damage state: platforms,
197/// pipes, gates, EF lines, collision, etc.
198///
199/// # Fields
200///
201/// * `name` - Name identifier for this destruction state
202/// * `index` - Index of this destruct
203/// * `collision_3d` - Collision mesh (legacy, moved to `.cs2.collision` in newer games)
204/// * `collision_outlines` - 2D collision outlines
205/// * `windows` - Number of window positions
206/// * `doors` - Number of door positions
207/// * `gates` - Gate collision data for wall gates
208/// * `pipes` - Unit movement paths (stairs, ladders, doors, etc.)
209/// * `orange_thingies` - Unknown (possibly no-go zones)
210/// * `platforms` - Walkable surfaces for units
211/// * `uk_2` - Unknown field
212/// * `bounding_box` - Bounding box for this destruct
213/// * `cannon_emitters` - Projectile emitter count for cannons (legacy)
214/// * `arrow_emitters` - Projectile emitters for arrows (legacy)
215/// * `docking_points` - Number of docking points
216/// * `soft_collisions` - Soft collision data
217/// * `uk_7` - Unknown field (v18 and later)
218/// * `vfx_emitters` - VFX the destruct always plays (v13 and before)
219/// * `file_refs` - Attached model references (e.g., torches)
220/// * `ef_lines` - Entity Formation lines for unit positioning
221/// * `docking_lines` - Lines where siege equipment can attach
222/// * `f_1` - Unknown field
223/// * `action_vfx` - VFX for actions
224/// * `action_vfx_attachments` - VFX attachment points
225/// * `bin_data` - Unknown binary data (correlates with VFX count)
226/// * `bin_data_2` - Unknown binary data (present in some Three Kingdoms gates)
227///
228/// # Examples
229///
230/// ```rust,ignore
231/// for destruct in piece.destructs() {
232/// println!("Destruct '{}': {} platforms, {} pipes",
233/// destruct.name(),
234/// destruct.platforms().len(),
235/// destruct.pipes().len()
236/// );
237/// }
238/// ```
239#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
240pub struct Destruct {
241 /// Name identifier for this destruction state.
242 name: String,
243
244 /// Index of this destruct.
245 index: u32,
246
247 /// Collision mesh (legacy, moved to separate `.cs2.collision` files in newer games).
248 collision_3d: Collision3d,
249
250 /// 2D collision outlines.
251 collision_outlines: Vec<CollisionOutline>,
252
253 /// Number of window positions.
254 windows: i32,
255
256 /// Number of door positions.
257 doors: i32,
258
259 /// Gate collision data for wall gates.
260 gates: Vec<Gate>,
261
262 /// Unit movement paths between platforms.
263 pipes: Vec<Pipe>,
264
265 /// Unknown (possibly no-go zones).
266 orange_thingies: Vec<Vec<OrangeThingy>>,
267
268 /// Walkable platform surfaces.
269 platforms: Vec<Platform>,
270
271 /// Unknown field.
272 uk_2: i32,
273
274 /// Bounding box for this destruct.
275 bounding_box: Cube,
276
277 /// Projectile emitter count for cannons (legacy, moved to map in newer games).
278 cannon_emitters: i32,
279
280 /// Projectile emitters for arrows (legacy, moved to map in newer games).
281 arrow_emitters: Vec<ProjectileEmitter>,
282
283 /// Number of docking points.
284 docking_points: i32,
285
286 /// Soft collision data.
287 soft_collisions: Vec<SoftCollisions>,
288
289 /// Unknown field (v18 and later; not checked whether it is the count of `vfx_emitters` there too).
290 uk_7: i32,
291
292 /// VFX the destruct always plays (v13 and before). In Attila, the name of the effect is the key without its first three and its last
293 /// `_` tokens: `piece01_destruct01_emitter_action_vfx_torch_fire_small_01` plays `action_vfx_torch_fire_small`. No file shipped with
294 /// the game has any, but they play in game.
295 vfx_emitters: Vec<Vfx>,
296
297 /// Attached model references (e.g., torches).
298 file_refs: Vec<FileRef>,
299
300 /// Entity Formation lines for unit positioning.
301 ef_lines: Vec<EFLine>,
302
303 /// Lines where siege equipment can attach.
304 docking_lines: Vec<DockingLine>,
305
306 /// Unknown field.
307 f_1: f32,
308
309 /// VFX for actions.
310 action_vfx: Vec<Vfx>,
311
312 /// VFX attachment points.
313 action_vfx_attachments: Vec<Vfx>,
314
315 /// Unknown binary data (correlates with VFX count).
316 bin_data: Vec<Vec<i16>>,
317
318 /// Unknown binary data (present in some Three Kingdoms gates).
319 bin_data_2: Vec<Vec<i16>>,
320}
321
322/// UI flag position shown on the minimap.
323///
324/// Defines where the building's flag icon appears on the tactical map.
325///
326/// # Fields
327///
328/// * `name` - Name identifier for the flag
329/// * `transform` - Transformation matrix for flag position and orientation
330#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
331pub struct UiFlag {
332 /// Name identifier for the flag.
333 name: String,
334
335 /// Transformation matrix for flag position.
336 transform: Transform4x4,
337}
338
339/// Gate collision data for wall gates.
340///
341/// Defines the collision geometry for gates where units enter and exit cities.
342/// Contains two collision meshes, possibly for open and closed states.
343///
344/// # Fields
345///
346/// * `collision_1` - First collision mesh
347/// * `collision_2` - Second collision mesh
348/// * `uk_1` - Unknown field
349/// * `uk_2` - Unknown field
350#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
351pub struct Gate {
352 /// First collision mesh.
353 collision_1: Collision3d,
354
355 /// Second collision mesh.
356 collision_2: Collision3d,
357
358 /// Unknown field.
359 uk_1: u32,
360
361 /// Unknown field.
362 uk_2: u32,
363}
364
365/// A 3D collision outline.
366///
367/// Defines a named 3D outline used for collision detection.
368///
369/// # Fields
370///
371/// * `name` - Name identifier for this collision outline
372/// * `vertices` - 3D polyline defining the outline
373/// * `uk_1` - Unknown field
374#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
375pub struct CollisionOutline {
376 /// Name identifier for this collision outline.
377 name: String,
378
379 /// 3D polyline vertices.
380 vertices: Outline3d,
381
382 /// Unknown field.
383 uk_1: u32,
384}
385
386/// Soft collision data.
387///
388/// Defines soft collision zones with transforms and positions.
389///
390/// # Fields
391///
392/// * `name` - Name identifier
393/// * `transform` - Transformation matrix
394/// * `uk_1` - Unknown field
395/// * `point_1` - 2D point position
396#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
397pub struct SoftCollisions {
398 /// Name identifier.
399 name: String,
400
401 /// Transformation matrix.
402 transform: Transform4x4,
403
404 /// Unknown field.
405 uk_1: i16,
406
407 /// 2D point position.
408 point_1: Point2d,
409}
410
411/// Projectile emitter position for building-based ranged attacks.
412///
413/// Defines where projectiles (arrows/cannonballs) are fired from on a building.
414/// Legacy feature used in Thrones of Britannia and older games. Newer games
415/// moved this logic to the map itself.
416///
417/// # Fields
418///
419/// * `name` - Name identifier for this emitter
420/// * `transform` - Transformation matrix for emitter position and orientation
421#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
422pub struct ProjectileEmitter {
423 /// Name identifier for this emitter.
424 name: String,
425
426 /// Transformation matrix for position and orientation.
427 transform: Transform4x4,
428}
429
430/// Reference to an attached building model.
431///
432/// Used to attach additional models to buildings (e.g., torches on Attila's walls,
433/// decorative elements).
434///
435/// # Fields
436///
437/// * `key` - Path to the model file to attach
438/// * `name` - Name identifier for this attachment
439/// * `transform` - Transformation matrix for attachment position
440/// * `uk_1` - Unknown field (possibly unique ID within the file)
441#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
442pub struct FileRef {
443 /// Path to the model file to attach.
444 key: String,
445
446 /// Name identifier for this attachment.
447 name: String,
448
449 /// Transformation matrix for attachment position.
450 transform: Transform4x4,
451
452 /// Unknown field (possibly unique ID within the file).
453 uk_1: i16,
454}
455
456/// Unknown vertex data (possibly no-go zones).
457///
458/// # Fields
459///
460/// * `vertex` - 2D vertex position
461/// * `vertex_type` - Type identifier for this vertex
462#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
463pub struct OrangeThingy {
464 /// 2D vertex position.
465 vertex: Point2d,
466
467 /// Type identifier for this vertex.
468 vertex_type: u32,
469}
470
471/// A walkable platform surface where units can stand and fight.
472///
473/// Platforms define the areas where units can be placed within a building.
474/// Different flags control how the pathfinder treats the platform.
475///
476/// # Fields
477///
478/// * `normal` - Surface normal vector
479/// * `vertices` - 3D outline defining the platform boundary
480/// * `flag_1` - Unknown behavior flag
481/// * `flag_2` - Treats platform as ground if true (units can walk freely)
482/// * `flag_3` - Unknown flag (set in siege tower ramp platforms)
483///
484/// # Examples
485///
486/// ```rust,ignore
487/// for platform in destruct.platforms() {
488/// if *platform.flag_2() {
489/// println!("Platform '{}' is treated as ground", /* no name field */);
490/// }
491/// }
492/// ```
493#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
494pub struct Platform {
495 /// Surface normal vector.
496 normal: Point3d,
497
498 /// 3D polyline defining platform boundary.
499 vertices: Outline3d,
500
501 /// Unknown behavior flag.
502 flag_1: bool,
503
504 /// Treats platform as ground if true (units can walk freely).
505 flag_2: bool,
506
507 /// Unknown flag (set in siege tower ramp platforms).
508 flag_3: bool,
509}
510
511/// A path for unit movement between platforms.
512///
513/// Pipes define how units move between different platform levels (stairs, ladders,
514/// doors, teleports, etc.). On ships, pipe vertices must align with deck vertices.
515///
516/// # Fields
517///
518/// * `name` - Name identifier for this pipe
519/// * `line` - 3D polyline defining the movement path
520/// * `line_type` - Type of pipe (stairs, ladder, door, etc.)
521///
522/// # Examples
523///
524/// ```rust,ignore
525/// for pipe in destruct.pipes() {
526/// println!("Pipe '{}': {:?}", pipe.name(), pipe.line_type());
527/// }
528/// ```
529#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
530pub struct Pipe {
531 /// Name identifier for this pipe.
532 name: String,
533
534 /// 3D polyline defining the movement path.
535 line: Outline3d,
536
537 /// Type of pipe (stairs, ladder, door, etc.).
538 line_type: PipeType,
539}
540
541/// Entity Formation line for unit positioning and actions.
542///
543/// EF Lines define where units should form up for specific actions like firing,
544/// boarding, defending, or spawning. Different types serve different purposes.
545/// See `EFLineType` for available types and their uses.
546///
547/// # Fields
548///
549/// * `name` - Name identifier for this line
550/// * `action` - Type of action/formation for this line
551/// * `start` - Starting point of the line
552/// * `end` - Ending point of the line
553/// * `direction` - Direction vector for unit orientation
554/// * `parent_index` - Parent index for hierarchical relationships
555///
556/// # Examples
557///
558/// ```rust,ignore
559/// for ef_line in destruct.ef_lines() {
560/// println!("EF Line '{}': {:?}", ef_line.name(), ef_line.action());
561/// }
562/// ```
563#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
564pub struct EFLine {
565 /// Name identifier for this line.
566 name: String,
567
568 /// Type of action/formation for this line.
569 action: EFLineType,
570
571 /// Starting point of the line.
572 start: Point3d,
573
574 /// Ending point of the line.
575 end: Point3d,
576
577 /// Direction vector for unit orientation.
578 direction: Point3d,
579
580 /// Parent index for hierarchical relationships.
581 parent_index: u32,
582}
583
584/// Line where siege equipment can attach to walls.
585///
586/// Docking lines are required on walls for siege towers and ladders to attach.
587/// Without these, siege equipment cannot dock to the wall.
588///
589/// # Fields
590///
591/// * `key` - Key identifier for this docking line
592/// * `start` - Starting point of the line
593/// * `end` - Ending point of the line
594/// * `direction` - Direction vector for docking orientation
595#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
596pub struct DockingLine {
597 /// Key identifier for this docking line.
598 key: String,
599
600 /// Starting point of the line.
601 start: Point2d,
602
603 /// Ending point of the line.
604 end: Point2d,
605
606 /// Direction vector for docking orientation.
607 direction: Point2d,
608}
609
610/// Visual effects attachment point.
611///
612/// Defines where VFX (particle effects, animations, etc.) are attached to the building.
613///
614/// # Fields
615///
616/// * `key` - Path to the VFX resource
617/// * `matrix_1` - Transformation matrix for VFX position and orientation
618#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
619pub struct Vfx {
620 /// Path to the VFX resource.
621 key: String,
622
623 /// Transformation matrix for VFX position and orientation.
624 matrix_1: Transform4x4,
625}
626
627/// Type of pipe for unit movement.
628///
629/// Defines the movement behavior for a pipe. Different types handle different
630/// movement scenarios like stairs, ladders, doors, teleportation, etc.
631///
632/// # Variants by Use Case
633///
634/// ## Naval Movement (Ships)
635/// - [`ShipStaircase`](Self::ShipStaircase) - Movement through ship staircases
636/// - [`ShipWalk`](Self::ShipWalk) - Walking movement on ships (unconfirmed)
637/// - [`ShipLadder`](Self::ShipLadder) - Climbing ship ladders
638///
639/// ## Wall Climbing
640/// - [`SiegeLadder1`](Self::SiegeLadder1) - Wall ladders in Empire/Napoleon
641/// - [`SiegeLadder2`](Self::SiegeLadder2) - Wall ladders in Warhammer games
642/// - [`Rope`](Self::Rope) - Rope climbing in Three Kingdoms
643///
644/// ## Siege Equipment
645/// - [`LadderLeft`](Self::LadderLeft) - Left ladder in siege towers
646/// - [`LadderRight`](Self::LadderRight) - Right ladder in siege towers
647/// - [`JumpRamp`](Self::JumpRamp) - Jumping from siege tower ramps
648///
649/// ## Doors and Entry
650/// - [`DoorNoTeleport`](Self::DoorNoTeleport) - Door threshold for garrisonable buildings
651/// - [`WallDoorTeleport`](Self::WallDoorTeleport) - Teleport between ends (Warhammer walls)
652/// - [`GroundTeleport`](Self::GroundTeleport) - Barricade teleportation (Warhammer III)
653///
654/// ## Other
655/// - [`Stairs`](Self::Stairs) - Interior wall stairs (Three Kingdoms)
656/// - [`Jump`](Self::Jump) - Jumping onto walls
657/// - [`UnknownSambucaPipe`](Self::UnknownSambucaPipe) - Sambuca-related (Thrones of Britannia)
658#[derive(Default, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
659#[repr(i32)]
660enum PipeType {
661 /// Ship staircase movement.
662 #[default]
663 ShipStaircase = 1,
664 /// Ship walking movement (unconfirmed).
665 ShipWalk = 2,
666 /// Ship ladder climbing.
667 ShipLadder = 3,
668 /// Wall climbing ladders (Empire/Napoleon).
669 SiegeLadder1 = 8,
670 /// Interior wall stairs (Three Kingdoms).
671 Stairs = 9,
672 /// Rope wall climbing (Three Kingdoms).
673 Rope = 10,
674 /// Sambuca pipe (Thrones of Britannia).
675 UnknownSambucaPipe = 11,
676 /// Door entry threshold for garrisonable buildings.
677 DoorNoTeleport = 13,
678 /// Jumping onto walls (PT_JUMP).
679 Jump = 14,
680 /// Teleportation between pipe ends (Warhammer walls, PT_WALL_DOOR).
681 WallDoorTeleport = 30,
682 /// Jumping from siege tower ramps to walls (PT_JUMP_RAMP).
683 JumpRamp = 32,
684 /// Left ladder in siege towers.
685 LadderLeft = 33,
686 /// Right ladder in siege towers.
687 LadderRight = 34,
688 /// Wall climbing ladders (Warhammer).
689 SiegeLadder2 = 35,
690 /// Barricade teleportation (Warhammer III).
691 GroundTeleport = 38,
692}
693
694/// Type of Entity Formation line.
695///
696/// Defines the purpose of an EF line - where units should position themselves
697/// for specific actions like firing, boarding, defending, or spawning.
698///
699/// # Variants by Use Case
700///
701/// ## Wall Defense
702/// - [`LowWall`](Self::LowWall) - Mid-size walls, first row in Warhammer
703/// - [`HighWall`](Self::HighWall) - Full-size walls
704/// - [`Overflow`](Self::Overflow) - Rows behind the second, waiting positions
705/// - [`CrenelLeftOuter`](Self::CrenelLeftOuter) - Crenellation positions (likely)
706/// - [`CrenelLeftInner`](Self::CrenelLeftInner) - Crenellation positions (likely)
707/// - [`CrenelRightInner`](Self::CrenelRightInner) - Crenellation positions (likely)
708/// - [`CrenelRightOuter`](Self::CrenelRightOuter) - Crenellation positions (likely)
709///
710/// ## Building Defense
711/// - [`Window`](Self::Window) - Window firing positions
712/// - [`EntranceDefense`](Self::EntranceDefense) - Melee defense at building entrances
713///
714/// ## Naval - Spawn Points
715/// - [`Marines`](Self::Marines) - Marine spawn locations and gun placements
716/// - [`Seamen`](Self::Seamen) - Seamen spawn locations
717/// - [`Captain`](Self::Captain) - Captain spawn point
718/// - [`Officer1`](Self::Officer1) - First officer spawn point
719/// - [`Officer2`](Self::Officer2) - Second officer spawn point
720/// - [`Officer3`](Self::Officer3) - Third officer spawn point
721///
722/// ## Naval - Combat Positions
723/// - [`NavalFiringPositionStand`](Self::NavalFiringPositionStand) - Standing firing positions
724/// - [`NavalFiringPositionCrouch`](Self::NavalFiringPositionCrouch) - Crouching firing positions
725/// - [`NavalFiringPositionStand360`](Self::NavalFiringPositionStand360) - 360° standing fire
726/// - [`NavalPerimeterPosition`](Self::NavalPerimeterPosition) - Perimeter positions (unknown)
727///
728/// ## Naval - Boarding and Disembark
729/// - [`Boarding`](Self::Boarding) - Boarding rope launch/entry points (required for boarding)
730/// - [`DisembarkLeft`](Self::DisembarkLeft) - Left-side disembarkation point
731/// - [`DisembarkRight`](Self::DisembarkRight) - Right-side disembarkation point
732///
733/// ## Equipment and Other
734/// - [`EnginePlacement`](Self::EnginePlacement) - Engine placement (unknown)
735/// - [`SecondaryEnginePlacement`](Self::SecondaryEnginePlacement) - Secondary engine (unknown)
736/// - [`GunnersOverflow`](Self::GunnersOverflow) - Gunner overflow (unknown)
737/// - [`Tree`](Self::Tree) - Unknown purpose
738///
739/// ## Invalid/Reserved
740/// - [`NumPurposes`](Self::NumPurposes) - Probably invalid
741/// - [`InvalidPurposes`](Self::InvalidPurposes) - Probably invalid
742#[derive(Default, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)]
743#[repr(i32)]
744enum EFLineType {
745 /// Mid-size walls, first row in Warhammer walls.
746 #[default]
747 LowWall = 0,
748 /// Full-size walls.
749 HighWall = 1,
750 /// Window firing positions in garrisonable buildings.
751 Window = 2,
752 /// Rows behind the second one, waiting positions.
753 Overflow = 3,
754 /// Naval: Marine spawn locations and gun placements.
755 Marines = 4,
756 /// Naval: Seamen spawn locations.
757 Seamen = 5,
758 /// Naval: Gunner overflow (unknown).
759 GunnersOverflow = 6,
760 /// Naval: Captain spawn point.
761 Captain = 7,
762 /// Naval: First officer spawn point.
763 Officer1 = 8,
764 /// Naval: Boarding rope launch/entry points (required for boarding).
765 Boarding = 9,
766 /// Naval: Standing firing positions.
767 NavalFiringPositionStand = 10,
768 /// Naval: Crouching firing positions.
769 NavalFiringPositionCrouch = 11,
770 /// Naval: 360° standing firing positions.
771 NavalFiringPositionStand360 = 12,
772 /// Naval: Perimeter positions (unknown).
773 NavalPerimeterPosition = 13,
774 /// Unknown purpose.
775 Tree = 14,
776 /// Melee defense at building entrances.
777 EntranceDefense = 15,
778 /// Naval: Second officer spawn point.
779 Officer2 = 16,
780 /// Naval: Third officer spawn point.
781 Officer3 = 17,
782 /// Crenellation positions (likely).
783 CrenelLeftOuter = 18,
784 /// Crenellation positions (likely).
785 CrenelLeftInner = 19,
786 /// Crenellation positions (likely).
787 CrenelRightInner = 20,
788 /// Crenellation positions (likely).
789 CrenelRightOuter = 21,
790 /// Engine placement (unknown).
791 EnginePlacement = 22,
792 /// Secondary engine placement (unknown).
793 SecondaryEnginePlacement = 23,
794 /// Naval: Left-side disembarkation point.
795 DisembarkLeft = 24,
796 /// Naval: Right-side disembarkation point.
797 DisembarkRight = 25,
798 /// Probably invalid.
799 NumPurposes = 26,
800 /// Probably invalid.
801 InvalidPurposes = 27,
802}
803
804//---------------------------------------------------------------------------//
805// Implementation of Cs2Parsed
806//---------------------------------------------------------------------------//
807
808impl Decodeable for Cs2Parsed {
809
810 fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
811 let mut decoded = Self::default();
812 decoded.version = data.read_u32()?;
813
814 match decoded.version {
815 21 => decoded.read_v21(data)?,
816 20 => decoded.read_v20(data)?,
817 18 => decoded.read_v18(data)?,
818 13 => decoded.read_v13(data)?,
819 12 => decoded.read_v12(data)?,
820 11 => decoded.read_v11(data)?,
821 10 => decoded.read_v10(data)?,
822 9 => decoded.read_v9(data)?,
823 8 => decoded.read_v8(data)?,
824 0 => decoded.read_v0(data)?,
825 _ => return Err(RLibError::DecodingUnsupportedVersion(decoded.version as usize)),
826 }
827
828 // Trigger an error if there's left data on the source.
829 check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
830
831 Ok(decoded)
832 }
833}
834
835impl Encodeable for Cs2Parsed {
836
837 fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
838 buffer.write_u32(self.version)?;
839
840 match self.version {
841 21 => self.write_v21(buffer)?,
842 20 => self.write_v20(buffer)?,
843 18 => self.write_v18(buffer)?,
844 13 => self.write_v13(buffer)?,
845 12 => self.write_v12(buffer)?,
846 11 => self.write_v11(buffer)?,
847 10 => self.write_v10(buffer)?,
848 9 => self.write_v9(buffer)?,
849 8 => self.write_v8(buffer)?,
850 0 => self.write_v0(buffer)?,
851 _ => unimplemented!()
852 }
853
854 Ok(())
855 }
856}
857
858impl TryFrom<i32> for EFLineType {
859 type Error = RLibError;
860
861 fn try_from(value: i32) -> Result<Self, Self::Error> {
862 match value {
863 _ if value == Self::LowWall as i32 => Ok(Self::LowWall),
864 _ if value == Self::HighWall as i32 => Ok(Self::HighWall),
865 _ if value == Self::Window as i32 => Ok(Self::Window),
866 _ if value == Self::Overflow as i32 => Ok(Self::Overflow),
867 _ if value == Self::Marines as i32 => Ok(Self::Marines),
868 _ if value == Self::Seamen as i32 => Ok(Self::Seamen),
869 _ if value == Self::GunnersOverflow as i32 => Ok(Self::GunnersOverflow),
870 _ if value == Self::Captain as i32 => Ok(Self::Captain),
871 _ if value == Self::Officer1 as i32 => Ok(Self::Officer1),
872 _ if value == Self::Boarding as i32 => Ok(Self::Boarding),
873 _ if value == Self::NavalFiringPositionStand as i32 => Ok(Self::NavalFiringPositionStand),
874 _ if value == Self::NavalFiringPositionCrouch as i32 => Ok(Self::NavalFiringPositionCrouch),
875 _ if value == Self::NavalFiringPositionStand360 as i32 => Ok(Self::NavalFiringPositionStand360),
876 _ if value == Self::NavalPerimeterPosition as i32 => Ok(Self::NavalPerimeterPosition),
877 _ if value == Self::Tree as i32 => Ok(Self::Tree),
878 _ if value == Self::EntranceDefense as i32 => Ok(Self::EntranceDefense),
879 _ if value == Self::Officer2 as i32 => Ok(Self::Officer2),
880 _ if value == Self::Officer3 as i32 => Ok(Self::Officer3),
881 _ if value == Self::CrenelLeftOuter as i32 => Ok(Self::CrenelLeftOuter),
882 _ if value == Self::CrenelLeftInner as i32 => Ok(Self::CrenelLeftInner),
883 _ if value == Self::CrenelRightInner as i32 => Ok(Self::CrenelRightInner),
884 _ if value == Self::CrenelRightOuter as i32 => Ok(Self::CrenelRightOuter),
885 _ if value == Self::EnginePlacement as i32 => Ok(Self::EnginePlacement),
886 _ if value == Self::SecondaryEnginePlacement as i32 => Ok(Self::SecondaryEnginePlacement),
887 _ if value == Self::DisembarkLeft as i32 => Ok(Self::DisembarkLeft),
888 _ if value == Self::DisembarkRight as i32 => Ok(Self::DisembarkRight),
889 _ if value == Self::NumPurposes as i32 => Ok(Self::NumPurposes),
890 _ if value == Self::InvalidPurposes as i32 => Ok(Self::InvalidPurposes),
891 _ => Err(RLibError::UnknownEFLineType(value.to_string())),
892 }
893 }
894}
895
896impl From<EFLineType> for i32 {
897 fn from(value: EFLineType) -> Self {
898 value as i32
899 }
900}
901
902impl TryFrom<i32> for PipeType {
903 type Error = RLibError;
904
905 fn try_from(value: i32) -> Result<Self, Self::Error> {
906 match value {
907 _ if value == Self::ShipStaircase as i32 => Ok(Self::ShipStaircase),
908 _ if value == Self::ShipWalk as i32 => Ok(Self::ShipWalk),
909 _ if value == Self::ShipLadder as i32 => Ok(Self::ShipLadder),
910 _ if value == Self::SiegeLadder1 as i32 => Ok(Self::SiegeLadder1),
911 _ if value == Self::Stairs as i32 => Ok(Self::Stairs),
912 _ if value == Self::Rope as i32 => Ok(Self::Rope),
913 _ if value == Self::UnknownSambucaPipe as i32 => Ok(Self::UnknownSambucaPipe),
914 _ if value == Self::DoorNoTeleport as i32 => Ok(Self::DoorNoTeleport),
915 _ if value == Self::Jump as i32 => Ok(Self::Jump),
916 _ if value == Self::WallDoorTeleport as i32 => Ok(Self::WallDoorTeleport),
917 _ if value == Self::JumpRamp as i32 => Ok(Self::JumpRamp),
918 _ if value == Self::LadderLeft as i32 => Ok(Self::LadderLeft),
919 _ if value == Self::LadderRight as i32 => Ok(Self::LadderRight),
920 _ if value == Self::SiegeLadder2 as i32 => Ok(Self::SiegeLadder2),
921 _ if value == Self::GroundTeleport as i32 => Ok(Self::GroundTeleport),
922 _ => Err(RLibError::UnknownPipeType(value.to_string())),
923 }
924 }
925}
926
927impl From<PipeType> for i32 {
928 fn from(value: PipeType) -> Self {
929 value as i32
930 }
931}
932
933impl Cs2Parsed {
934
935 /// Migrates this CS2 Parsed file to be compatible with a specific game.
936 ///
937 /// Converts the file version to the maximum version supported by the target game.
938 /// Games generally support all previous format versions, so migration only occurs
939 /// if the current file version is newer than the game's maximum supported version.
940 ///
941 /// # Arguments
942 ///
943 /// * `game` - Target game information containing maximum supported version
944 ///
945 /// # Returns
946 ///
947 /// * `Ok(())` - Migration successful or not needed
948 /// * `Err(RLibError::GameDoesntSupportCs2Migration)` - Game doesn't support CS2 files
949 ///
950 /// # Examples
951 ///
952 /// ```rust,ignore
953 /// let mut parsed = Cs2Parsed::decode(&mut data, &None)?;
954 /// parsed.migrate_game(&game_info)?;
955 /// // File is now compatible with the target game
956 /// ```
957 pub fn migrate_game(&mut self, game: &GameInfo) -> Result<()> {
958
959 if *game.max_cs2_parsed_version() == 0 {
960 return Err(RLibError::GameDoesntSupportCs2Migration)
961 }
962
963 // Games (at least until thrones) seem to support all previous formats.
964 // So we only perform a migration if the format of the file is newer than the latest supported one by the game.
965 if self.version > *game.max_cs2_parsed_version() {
966 self.version = *game.max_cs2_parsed_version();
967 }
968
969 Ok(())
970 }
971}