rpfm_lib/files/portrait_settings/mod.rs
1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2024 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//! This is a module to read/write binary Portrait Settings files.
12//!
13//! Portrait settings are files containing information about the small portrait each unit uses,
14//! tipically at the bottom left of the screen (may vary from game to game) or in the Character Screen in campaign.
15//!
16//! TODO: add format info.
17
18use getset::*;
19use serde_derive::{Serialize, Deserialize};
20
21use crate::error::{Result, RLibError};
22use crate::binary::{ReadBytes, WriteBytes};
23use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
24use crate::utils::check_size_mismatch;
25
26/// Extension used by PortraitSettings.
27pub const EXTENSION: &str = ".bin";
28
29mod versions;
30
31#[cfg(test)] mod portrait_settings_test;
32
33//---------------------------------------------------------------------------//
34// Enum & Structs
35//---------------------------------------------------------------------------//
36
37/// This represents an entire PortraitSettings decoded in memory.
38#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
39#[getset(get = "pub", get_mut = "pub", set = "pub")]
40pub struct PortraitSettings {
41
42 /// Version of the PortraitSettings.
43 version: u32,
44
45 /// Entries on the PortraitSettings.
46 entries: Vec<Entry>,
47}
48
49/// This represents a generic Portrait Settings Entry.
50#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
51#[getset(get = "pub", get_mut = "pub", set = "pub")]
52pub struct Entry {
53
54 /// Id of the entry. Points to an art set key.
55 id: String,
56
57 /// Settings for the head camera.
58 ///
59 /// This is the porthole camera you see in campaign, at the bottom left.
60 camera_settings_head: CameraSetting,
61
62 /// Settings for the body camera. Optional.
63 ///
64 /// This is the camera used for displaying the full body of the character in their details window in campaign.
65 /// This is only needed for characters. Regular units do not have access to the characters window, so it's not needed for them.
66 camera_settings_body: Option<CameraSetting>,
67
68 /// Variants? Need more info about this.
69 variants: Vec<Variant>
70}
71
72/// This represents a Camera setting of a Portrait.
73///
74/// Note that the camera has an auto-level feature, so the camera may autorotate to compensate vertical rotation (pitch)
75/// greater than 90/-90 degrees.
76#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
77#[getset(get = "pub", get_mut = "pub", set = "pub")]
78pub struct CameraSetting {
79
80 /// Distance from the character to the camera.
81 z: f32,
82
83 /// Vertical displacement of the camera.
84 y: f32,
85
86 /// Rotation angle of the camera, sideways. In degrees.
87 yaw: f32,
88
89 /// Rotation angle of the camera, vertically. In degrees.
90 pitch: f32,
91
92 /// Only in V1.
93 distance: f32,
94
95 /// Only in V1.
96 theta: f32,
97
98 /// Only in V1.
99 phi: f32,
100
101 /// Field of View.
102 fov: f32,
103
104 /// Skeleton node that the camera will use as default focus point.
105 ///
106 /// Optional. If provided, all displacementes/rotations are relative to this point.
107 skeleton_node: String,
108}
109
110/// This represents a generic variant of a Portrait.
111#[derive(PartialEq, Eq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
112#[getset(get = "pub", get_mut = "pub", set = "pub")]
113pub struct Variant {
114
115 /// Variant Filename. Points to the column of the same name in the Variants table.
116 filename: String,
117
118 /// Path of the diffuse image of the Variant.
119 file_diffuse: String,
120
121 /// No idea. Optional.
122 file_mask_1: String,
123
124 /// No idea. Optional.
125 file_mask_2: String,
126
127 /// No idea. Optional.
128 file_mask_3: String,
129
130 /// Only in v1.
131 season: String,
132
133 /// Only in v1.
134 level: i32,
135
136 /// Only in v1.
137 age: i32,
138
139 /// Only in v1.
140 politician: bool,
141
142 /// Only in v1.
143 faction_leader: bool,
144}
145
146//---------------------------------------------------------------------------//
147// Implementation of PortraitSettings
148//---------------------------------------------------------------------------//
149
150impl Decodeable for PortraitSettings {
151
152 fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
153 let version = data.read_u32()?;
154
155 let mut settings = Self::default();
156 settings.version = version;
157
158 match version {
159 1 => settings.read_v1(data)?,
160 4 => settings.read_v4(data)?,
161 _ => Err(RLibError::DecodingPortraitSettingUnsupportedVersion(version as usize))?,
162 }
163
164 // Trigger an error if there's left data on the source.
165 check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
166
167 Ok(settings)
168 }
169}
170
171impl Encodeable for PortraitSettings {
172
173 fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
174 buffer.write_u32(self.version)?;
175
176 match self.version {
177 1 => self.write_v1(buffer)?,
178 4 => self.write_v4(buffer)?,
179 _ => unimplemented!()
180 }
181
182 Ok(())
183 }
184}
185
186
187impl PortraitSettings {
188
189 pub fn from_json(data: &str) -> Result<Self> {
190 serde_json::from_str(data).map_err(From::from)
191 }
192
193 pub fn to_json(&self) -> Result<String> {
194 serde_json::to_string_pretty(&self).map_err(From::from)
195 }
196}
197
198impl Default for Variant {
199 fn default() -> Self {
200 Self {
201 filename: Default::default(),
202 file_diffuse: Default::default(),
203 file_mask_1: Default::default(),
204 file_mask_2: Default::default(),
205 file_mask_3: Default::default(),
206 season: "none".to_owned(),
207 level: Default::default(),
208 age: Default::default(),
209 politician: Default::default(),
210 faction_leader: Default::default(),
211 }
212 }
213}