Skip to main content

rpfm_lib/files/tile_database/
assembly_kit.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//! Building a tile database from its sources in the Assembly Kit, as BOB's "Process tile database" does.
12//!
13//! The sources are `raw_data/terrain/tiles/<battle|campaign>/_tile_database/_settings.xml` and one XML per tile in its `TILES` folder.
14//! The result is byte for byte what BOB writes from the same sources.
15
16use std::collections::{BTreeMap, HashSet};
17
18use crate::error::{Result, RLibError};
19use crate::xml::{XmlElement, parse_elements};
20
21use super::*;
22use super::texture::Texture;
23use super::tile_link::TileLink;
24use super::tile_link_target::TileLinkTarget;
25use super::tile_variation::TileVariation;
26
27/// Version BOB writes the render parameters with, whatever the XML says.
28const RENDER_PARAMS_VERSION: u16 = 6;
29
30/// Tile set the battle database always ends with.
31const USER_TILE_SET: &str = "user";
32
33/// The elements of an XML document, to read attributes and children from.
34struct Document {
35    elements: Vec<XmlElement>,
36    root: usize,
37}
38
39//---------------------------------------------------------------------------//
40//                     Implementation of TileDatabase
41//---------------------------------------------------------------------------//
42
43impl TileDatabase {
44
45    /// Builds a tile database from its sources in the Assembly Kit.
46    ///
47    /// Tile sets that tiles use but `_settings.xml` doesn't define are added after the defined ones, with empty values,
48    /// and so is the `user` set if requested: the battle database always has it, the campaign one doesn't.
49    ///
50    /// # Arguments
51    ///
52    /// * `settings` - The contents of `_settings.xml`.
53    /// * `tiles` - The contents of each tile's XML.
54    /// * `order` - Locations of the tiles, in the order they go in the database. If None, larger tiles go first, then by location.
55    /// * `add_user_tile_set` - If the `user` tile set is added.
56    ///
57    /// # Returns
58    ///
59    /// The tile database.
60    ///
61    /// # Errors
62    ///
63    /// Returns [`RLibError::DecodingTileDatabaseXml`] if any source is malformed, or a tile is missing from `order`.
64    pub fn from_assembly_kit(settings: &str, tiles: &[&str], order: Option<&[String]>, add_user_tile_set: bool) -> Result<Self> {
65        let settings = Document::parse(settings)?;
66        let root = settings.root();
67        let mut database = Self::default();
68        database.set_serialise_version(1);
69        database.set_render_params(settings.render_params(settings.child(root, "RENDER_PARAMS")?)?);
70        database.set_conversion_params(settings.conversion_params(settings.child(root, "CONVERSION_PARAMS")?)?);
71
72        let climates = settings.children(settings.child(root, "CLIMATES")?);
73        database.set_climates(climates.into_iter().map(|climate| settings.climate(climate)).collect::<Result<_>>()?);
74
75        let tiles = tiles.iter().map(|tile| Document::parse(tile)).collect::<Result<Vec<_>>>()?;
76
77        let mut tile_sets = settings.children(settings.child(root, "TILE_SETS")?).into_iter()
78            .map(|tile_set| settings.tile_set(tile_set))
79            .collect::<Result<Vec<_>>>()?;
80
81        let mut known = tile_sets.iter().map(|tile_set| tile_set.name().to_owned()).collect::<HashSet<_>>();
82        let mut undefined = vec![];
83        for tile in &tiles {
84            let name = tile.attribute(tile.root(), "tile_set")?;
85            if known.insert(name.to_owned()) {
86                undefined.push(name.to_owned());
87            }
88        }
89
90        if add_user_tile_set && known.insert(USER_TILE_SET.to_owned()) {
91            undefined.push(USER_TILE_SET.to_owned());
92        }
93
94        for name in undefined {
95            let mut tile_set = TileSet::default();
96            tile_set.set_serialise_version(1);
97            tile_set.set_name(name);
98            tile_sets.push(tile_set);
99        }
100
101        database.set_tile_sets(tile_sets);
102
103        let mut tiles = tiles.iter().map(|tile| tile.tile()).collect::<Result<Vec<_>>>()?;
104        match order {
105            Some(order) => {
106                let rank = order.iter().enumerate().map(|(index, location)| (location.as_str(), index)).collect::<BTreeMap<_, _>>();
107                if let Some(tile) = tiles.iter().find(|tile| !rank.contains_key(tile.location().as_str())) {
108                    return Err(xml_error(format!("tile missing from the order list: {}", tile.location())));
109                }
110
111                tiles.sort_by_key(|tile| rank[tile.location().as_str()]);
112            }
113            None => tiles.sort_by(|a, b| (b.width() * b.height()).cmp(&(a.width() * a.height())).then_with(|| a.location().cmp(b.location()))),
114        }
115
116        database.set_tiles(tiles);
117        Ok(database)
118    }
119}
120
121impl Document {
122
123    fn parse(xml: &str) -> Result<Self> {
124        let (elements, roots) = parse_elements(xml).map_err(xml_error)?;
125        let root = *roots.first().ok_or_else(|| xml_error("empty document".to_owned()))?;
126
127        Ok(Self { elements, root })
128    }
129
130    fn root(&self) -> usize {
131        self.root
132    }
133
134    fn attribute(&self, element: usize, name: &str) -> Result<&str> {
135        let element = &self.elements[element];
136        element.attributes.iter()
137            .find(|(attribute, _)| attribute == name)
138            .map(|(_, value)| value.as_str())
139            .ok_or_else(|| xml_error(format!("missing attribute '{name}' on <{}>", element.name)))
140    }
141
142    fn f32(&self, element: usize, name: &str) -> Result<f32> {
143        let value = self.attribute(element, name)?;
144        value.parse().map_err(|_| xml_error(format!("'{value}' of '{name}' is not a number")))
145    }
146
147    fn u32(&self, element: usize, name: &str) -> Result<u32> {
148        let value = self.attribute(element, name)?;
149        value.parse().map_err(|_| xml_error(format!("'{value}' of '{name}' is not an unsigned integer")))
150    }
151
152    fn i32(&self, element: usize, name: &str) -> Result<i32> {
153        let value = self.attribute(element, name)?;
154        value.parse().map_err(|_| xml_error(format!("'{value}' of '{name}' is not an integer")))
155    }
156
157    fn u16(&self, element: usize, name: &str) -> Result<u16> {
158        let value = self.attribute(element, name)?;
159        value.parse().map_err(|_| xml_error(format!("'{value}' of '{name}' is not a version")))
160    }
161
162    fn bool(&self, element: usize, name: &str) -> Result<bool> {
163        Ok(self.attribute(element, name)? == "true")
164    }
165
166    fn string(&self, element: usize, name: &str) -> Result<String> {
167        self.attribute(element, name).map(|value| value.to_owned())
168    }
169
170    fn child(&self, element: usize, name: &str) -> Result<usize> {
171        self.elements[element].children.iter().copied()
172            .find(|&child| self.elements[child].name == name)
173            .ok_or_else(|| xml_error(format!("missing <{name}> in <{}>", self.elements[element].name)))
174    }
175
176    fn children(&self, element: usize) -> Vec<usize> {
177        self.elements[element].children.clone()
178    }
179
180    fn render_params(&self, element: usize) -> Result<RenderParams> {
181        let mut params = RenderParams::default();
182        params.set_serialise_version(RENDER_PARAMS_VERSION);
183        params.set_lf_height(self.f32(element, "lf_height")?);
184        params.set_hf_height(self.f32(element, "hf_height")?);
185        params.set_blend_pixel_scale(self.f32(element, "blend_pixel_scale")?);
186        params.set_unit_scale(self.f32(element, "unit_scale")?);
187        params.set_mid_distance_detail_scale(self.f32(element, "mid_distance_detail_scale")?);
188        params.set_mid_distance_detail_strength(self.f32(element, "mid_distance_detail_strength")?);
189        params.set_mid_distance_normal_strength(self.f32(element, "mid_distance_normal_strength")?);
190        params.set_mid_distance_detail_near(self.f32(element, "mid_distance_detail_near")?);
191        params.set_mid_distance_detail_far(self.f32(element, "mid_distance_detail_far")?);
192        params.set_mid_distance_detail_slope_low(self.f32(element, "mid_distance_detail_slope_low")?);
193        params.set_mid_distance_detail_slope_high(self.f32(element, "mid_distance_detail_slope_high")?);
194        params.set_vertical_offset(self.f32(element, "vertical_offset")?);
195        params.set_normal_lf_scale(self.f32(element, "normal_lf_scale")?);
196        params.set_normal_tile_scale(self.f32(element, "normal_tile_scale")?);
197        params.set_normal_terrain_scale(self.f32(element, "normal_terrain_scale")?);
198        params.set_blend_contrast(self.f32(element, "blend_contrast")?);
199        params.set_layer_exempt_0(self.string(element, "layer_exempt_0")?);
200        params.set_layer_exempt_1(self.string(element, "layer_exempt_1")?);
201        params.set_layer_exempt_2(self.string(element, "layer_exempt_2")?);
202        params.set_layer_exempt_3(self.string(element, "layer_exempt_3")?);
203        params.set_campaign_sea_transparency_scale(self.f32(element, "campaign_sea_transparency_scale")?);
204        params.set_campaign_sea_uv_scale(self.f32(element, "campaign_sea_uv_scale")?);
205        params.set_campaign_lake_transparency_scale(self.f32(element, "campaign_lake_transparency_scale")?);
206        params.set_near_distance_detail_distance(self.f32(element, "near_distance_detail_distance")?);
207        params.set_near_distance_detail_scale(self.f32(element, "near_distance_detail_scale")?);
208        params.set_near_distance_detail_strength(self.f32(element, "near_distance_detail_strength")?);
209        params.set_mid_distance_detail_near_lq(self.f32(element, "mid_distance_detail_near_lq")?);
210        Ok(params)
211    }
212
213    fn conversion_params(&self, element: usize) -> Result<ConversionParams> {
214        let mut params = ConversionParams::default();
215        params.set_serialise_version(self.u16(element, "serialise_version")?);
216        params.set_triangle_density(self.i32(element, "triangle_density")?);
217        params.set_max_lf_heights_per_pixel(self.i32(element, "max_lf_heights_per_pixel")?);
218        params.set_triangle_decimation_size_factors0(self.f32(element, "triangle_decimation_size_factors0")?);
219        params.set_triangle_decimation_size_factors1(self.f32(element, "triangle_decimation_size_factors1")?);
220        params.set_triangle_decimation_size_factors2(self.f32(element, "triangle_decimation_size_factors2")?);
221        params.set_triangle_decimation_size_factors3(self.f32(element, "triangle_decimation_size_factors3")?);
222        params.set_triangle_decimation_size_factors4(self.f32(element, "triangle_decimation_size_factors4")?);
223        params.set_triangle_decimation_size_factors5(self.f32(element, "triangle_decimation_size_factors5")?);
224        params.set_triangle_decimation_angle_factors0(self.f32(element, "triangle_decimation_angle_factors0")?);
225        params.set_triangle_decimation_angle_factors1(self.f32(element, "triangle_decimation_angle_factors1")?);
226        params.set_triangle_decimation_angle_factors2(self.f32(element, "triangle_decimation_angle_factors2")?);
227        params.set_triangle_decimation_angle_factors3(self.f32(element, "triangle_decimation_angle_factors3")?);
228        params.set_triangle_decimation_angle_factors4(self.f32(element, "triangle_decimation_angle_factors4")?);
229        params.set_triangle_decimation_angle_factors5(self.f32(element, "triangle_decimation_angle_factors5")?);
230        Ok(params)
231    }
232
233    fn climate(&self, element: usize) -> Result<Climate> {
234        let mut climate = Climate::default();
235        climate.set_serialise_version(self.u16(element, "serialise_version")?);
236        climate.set_name(self.string(element, "name")?);
237        climate.set_texture_set(self.string(element, "texture_set")?);
238        climate.set_red(self.f32(element, "red")?);
239        climate.set_green(self.f32(element, "green")?);
240        climate.set_blue(self.f32(element, "blue")?);
241        climate.set_textures(self.children(self.child(element, "TEXTURES")?).into_iter().map(|texture| self.texture(texture)).collect::<Result<_>>()?);
242        climate.set_grass_alpha_add(self.f32(element, "grass_alpha_add")?);
243        climate.set_grass_alpha_mul(self.f32(element, "grass_alpha_mul")?);
244        climate.set_destruction_climate(self.string(element, "destruction_climate")?);
245        Ok(climate)
246    }
247
248    fn texture(&self, element: usize) -> Result<Texture> {
249        let mut texture = Texture::default();
250        texture.set_serialise_version(self.u16(element, "serialise_version")?);
251        texture.set_name(self.string(element, "name")?);
252        texture.set_mid_distance_strength(self.f32(element, "mid_distance_strength")?);
253        texture.set_lerp_reflectivity0(self.f32(element, "lerp_reflectivity0")?);
254        texture.set_lerp_smoothness0(self.f32(element, "lerp_smoothness0")?);
255        texture.set_lerp_reflectivity1(self.f32(element, "lerp_reflectivity1")?);
256        texture.set_lerp_smoothness1(self.f32(element, "lerp_smoothness1")?);
257        texture.set_lerp_reflectivity2(self.f32(element, "lerp_reflectivity2")?);
258        texture.set_lerp_smoothness2(self.f32(element, "lerp_smoothness2")?);
259        texture.set_lerp_reflectivity3(self.f32(element, "lerp_reflectivity3")?);
260        texture.set_lerp_smoothness3(self.f32(element, "lerp_smoothness3")?);
261        texture.set_blend_pixel_scale(self.f32(element, "blend_pixel_scale")?);
262        texture.set_outfield_blend_pixel_scale(self.f32(element, "outfield_blend_pixel_scale")?);
263        Ok(texture)
264    }
265
266    fn tile_set(&self, element: usize) -> Result<TileSet> {
267        let mut tile_set = TileSet::default();
268        tile_set.set_serialise_version(self.u16(element, "serialise_version")?);
269        tile_set.set_name(self.string(element, "name")?);
270        tile_set.set_linking_tile(self.string(element, "linking_tile")?);
271        tile_set.set_shared_geometry(self.string(element, "shared_geometry")?);
272        tile_set.set_also_place_tile_set(self.string(element, "also_place_tile_set")?);
273        tile_set.set_link_as_set(self.string(element, "link_as_set")?);
274        tile_set.set_red(self.f32(element, "red")?);
275        tile_set.set_green(self.f32(element, "green")?);
276        tile_set.set_blue(self.f32(element, "blue")?);
277        Ok(tile_set)
278    }
279
280    fn tile(&self) -> Result<Tile> {
281        let element = self.root;
282        let mut tile = Tile::default();
283        tile.set_serialise_version(self.u16(element, "serialise_version")?);
284        tile.set_location(self.string(element, "location")?);
285        tile.set_tile_set(self.string(element, "tile_set")?);
286        tile.set_mask(self.string(element, "mask")?);
287        tile.set_width(self.u32(element, "width")?);
288        tile.set_height(self.u32(element, "height")?);
289        tile.set_red(self.f32(element, "red")?);
290        tile.set_green(self.f32(element, "green")?);
291        tile.set_blue(self.f32(element, "blue")?);
292        tile.set_requires_infield_lodding(self.bool(element, "requires_infield_lodding")?);
293        tile.set_random_rotatable(self.bool(element, "random_rotatable")?);
294        tile.set_custom_alpha_blend_texture(self.string(element, "custom_alpha_blend_texture")?);
295        tile.set_scalable(self.bool(element, "scalable")?);
296        tile.set_encampable(self.bool(element, "encampable")?);
297        tile.set_custom_blend_tile(self.string(element, "custom_blend_tile")?);
298
299        let texture_group = self.child(element, "TEXTURE_GROUP")?;
300        tile.set_texture_red(self.string(texture_group, "red")?);
301        tile.set_texture_green(self.string(texture_group, "green")?);
302        tile.set_texture_blue(self.string(texture_group, "blue")?);
303        tile.set_texture_alpha(self.string(texture_group, "alpha")?);
304
305        tile.set_variations(self.children(self.child(element, "VARIATIONS")?).into_iter().map(|variation| self.tile_variation(variation)).collect::<Result<_>>()?);
306        tile.set_link_targets(self.children(self.child(element, "TILE_LINK_TARGETS")?).into_iter().map(|target| self.tile_link_target(target)).collect::<Result<_>>()?);
307        tile.set_links(self.children(self.child(element, "TILE_LINKS")?).into_iter().map(|link| self.tile_link(link)).collect::<Result<_>>()?);
308        Ok(tile)
309    }
310
311    fn tile_variation(&self, element: usize) -> Result<TileVariation> {
312        let mut variation = TileVariation::default();
313        variation.set_serialise_version(self.u16(element, "serialise_version")?);
314        variation.set_location(self.string(element, "location")?);
315        variation.set_min_height(self.f32(element, "min_height")?);
316        variation.set_scale(self.f32(element, "scale")?);
317        variation.set_normal_strength(self.f32(element, "normal_strength")?);
318        variation.set_overlap_border_size(self.f32(element, "overlap_border_size")?);
319        variation.set_raw_data_tri_density(self.u32(element, "raw_data_tri_density")?);
320        variation.set_blend_common(self.string(element, "blend_common")?);
321        variation.set_index_common(self.string(element, "index_common")?);
322        variation.set_normal_common(self.string(element, "normal_common")?);
323        variation.set_red(self.f32(element, "red")?);
324        variation.set_green(self.f32(element, "green")?);
325        variation.set_blue(self.f32(element, "blue")?);
326        variation.set_barbarian(self.bool(element, "barbarian")?);
327        Ok(variation)
328    }
329
330    fn tile_link_target(&self, element: usize) -> Result<TileLinkTarget> {
331        let mut target = TileLinkTarget::default();
332        target.set_serialise_version(self.u16(element, "serialise_version")?);
333        target.set_tile_set(self.string(element, "tile_set")?);
334        target.set_x(self.i32(element, "x")?);
335        target.set_y(self.i32(element, "y")?);
336        Ok(target)
337    }
338
339    fn tile_link(&self, element: usize) -> Result<TileLink> {
340        let mut link = TileLink::default();
341        link.set_serialise_version(self.u16(element, "serialise_version")?);
342        link.set_link_set(self.string(element, "link_set")?);
343        link.set_x(self.i32(element, "x")?);
344        link.set_y(self.i32(element, "y")?);
345        link.set_base_x(self.i32(element, "base_x")?);
346        link.set_base_y(self.i32(element, "base_y")?);
347        link.set_is_entry(self.bool(element, "is_entry")?);
348        link.set_blend_size(self.u32(element, "blend_size")?);
349        link.set_no_offline_blend(self.bool(element, "no_offline_blend")?);
350        link.set_test(self.string(element, "test")?);
351        Ok(link)
352    }
353}
354
355/// Error of a source that can't be read.
356fn xml_error(reason: String) -> RLibError {
357    RLibError::DecodingTileDatabaseXml(reason)
358}