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}