Skip to main content

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