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 (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}