tiger_lib/
everything.rs

1//! Stores everything known about the game and mod being validated.
2//!
3//! References to [`Everything`] are passed down through nearly all of the validation logic, so
4//! that individual functions can access all the defined game items.
5
6use std::borrow::Cow;
7use std::fmt::Debug;
8use std::path::{Path, PathBuf};
9#[cfg(any(feature = "ck3", feature = "vic3"))]
10use std::sync::RwLock;
11
12use anyhow::Result;
13use rayon::{Scope, scope};
14use strum::IntoEnumIterator;
15use thiserror::Error;
16
17#[cfg(any(feature = "ck3", feature = "vic3"))]
18use crate::block::BV;
19use crate::block::Block;
20#[cfg(feature = "ck3")]
21use crate::ck3::data::{
22    characters::Characters,
23    climate::Climate,
24    doctrines::Doctrines,
25    gameconcepts::GameConcepts,
26    interaction_cats::CharacterInteractionCategories,
27    maa::MenAtArmsTypes,
28    prov_history::ProvinceHistories,
29    prov_terrain::{ProvinceProperties, ProvinceTerrains},
30    provinces::Ck3Provinces,
31    title_history::TitleHistories,
32    titles::Titles,
33    traits::Traits,
34    wars::Wars,
35};
36#[cfg(feature = "ck3")]
37use crate::ck3::tables::misc::*;
38use crate::config_load::{check_for_legacy_ignore, load_filter};
39use crate::context::ScopeContext;
40#[cfg(any(feature = "ck3", feature = "vic3"))]
41use crate::data::data_binding::DataBindings;
42use crate::data::{
43    assets::Assets,
44    defines::Defines,
45    gui::Gui,
46    localization::Localization,
47    on_actions::OnActions,
48    scripted_effects::{Effect, Effects},
49    scripted_triggers::{Trigger, Triggers},
50};
51#[cfg(feature = "jomini")]
52use crate::data::{
53    coa::Coas, events::Events, music::Musics, script_values::ScriptValues,
54    scripted_lists::ScriptedLists, scripted_modifiers::ScriptedModifiers,
55};
56use crate::db::{Db, DbKind};
57use crate::dds::DdsFiles;
58#[cfg(feature = "eu5")]
59use crate::eu5::data::provinces::Eu5Provinces;
60#[cfg(feature = "eu5")]
61use crate::eu5::tables::misc::*;
62use crate::fileset::{FileEntry, FileKind, FileStage, Fileset};
63use crate::game::Game;
64#[cfg(any(feature = "ck3", feature = "vic3"))]
65use crate::helpers::TigerHashSet;
66#[cfg(feature = "hoi4")]
67use crate::hoi4::data::{
68    events::Hoi4Events, gfx::Gfx, music::Hoi4Musics, provinces::Hoi4Provinces,
69};
70#[cfg(feature = "hoi4")]
71use crate::hoi4::tables::misc::*;
72#[cfg(feature = "imperator")]
73use crate::imperator::data::{decisions::Decisions, provinces::ImperatorProvinces};
74#[cfg(feature = "imperator")]
75use crate::imperator::tables::misc::*;
76use crate::item::{Item, ItemLoader};
77use crate::lowercase::Lowercase;
78use crate::macros::MACRO_MAP;
79use crate::parse::ParserMemory;
80#[cfg(feature = "vic3")]
81use crate::parse::json::parse_json_file;
82use crate::pdxfile::PdxFile;
83#[cfg(any(feature = "ck3", feature = "vic3"))]
84use crate::report::err;
85#[cfg(feature = "jomini")]
86use crate::report::warn;
87use crate::report::{ErrorKey, OutputStyle, Severity, report, set_output_style};
88use crate::rivers::Rivers;
89#[cfg(feature = "jomini")]
90use crate::scopes::Scopes;
91use crate::token::{Loc, Token};
92#[cfg(feature = "jomini")]
93use crate::variable_scopes::VariableScopes;
94use crate::variables::Variables;
95#[cfg(feature = "vic3")]
96use crate::vic3::data::{
97    buy_packages::BuyPackage, history::History, provinces::Vic3Provinces,
98    strategic_regions::StrategicRegion, terrain::TerrainMask,
99};
100#[cfg(feature = "vic3")]
101use crate::vic3::tables::misc::*;
102
103#[derive(Debug, Error)]
104#[allow(clippy::enum_variant_names)]
105pub enum FilesError {
106    #[error("Could not read game files at {path}")]
107    VanillaUnreadable { path: PathBuf, source: walkdir::Error },
108    #[error("Could not read mod files at {path}")]
109    ModUnreadable { path: PathBuf, source: walkdir::Error },
110    #[error("Could not read config file at {path}")]
111    ConfigUnreadable { path: PathBuf },
112}
113
114/// A record of everything known about the game and mod being validated.
115///
116/// References to [`Everything`] are passed down through nearly all of the validation logic, so
117/// that individual functions can access all the defined game items.
118///
119/// The validator has two main phases: parsing and validation.
120/// * During parsing, the script files are read, parsed, and loaded into the various databases.
121///   `Everything` is mutable during this period.
122/// * During validation, `Everything` is immutable and cross-checking between item types can be done safely.
123#[derive(Debug)]
124pub struct Everything {
125    /// Config from file
126    config: Block,
127
128    /// The global parser state, carrying information between files.
129    /// Currently only used by the pdxfile parser, to handle the `reader_export` directory,
130    /// which is specially processed before all other files.
131    pub parser: ParserMemory,
132
133    /// A cache of define values (from common/defines) that are missing and that have already been
134    /// warned about as missing. This is to avoid duplicate warnings.
135    #[cfg(any(feature = "ck3", feature = "vic3"))]
136    warned_defines: RwLock<TigerHashSet<String>>,
137
138    /// Tracks all the files (vanilla and mods) that are relevant to the current validation.
139    pub(crate) fileset: Fileset,
140
141    /// Tracks specifically the .dds files, and their formats and sizes.
142    pub(crate) dds: DdsFiles,
143
144    /// A general database of item types. Most items go here. The ones that need special handling
145    /// go in the separate databases listed below.
146    pub(crate) database: Db,
147
148    pub(crate) localization: Localization,
149
150    #[cfg(feature = "jomini")]
151    pub(crate) scripted_lists: ScriptedLists,
152
153    pub(crate) defines: Defines,
154
155    #[cfg(feature = "jomini")]
156    pub(crate) events: Events,
157    #[cfg(feature = "hoi4")]
158    pub(crate) events_hoi4: Hoi4Events,
159    #[cfg(feature = "imperator")]
160    pub(crate) decisions_imperator: Decisions,
161
162    #[cfg(feature = "jomini")]
163    pub(crate) scripted_modifiers: ScriptedModifiers,
164    pub(crate) on_actions: OnActions,
165
166    #[cfg(feature = "ck3")]
167    pub(crate) interaction_cats: CharacterInteractionCategories,
168
169    #[cfg(feature = "ck3")]
170    pub(crate) provinces_ck3: Ck3Provinces,
171    #[cfg(feature = "vic3")]
172    pub(crate) provinces_vic3: Vic3Provinces,
173    #[cfg(feature = "imperator")]
174    pub(crate) provinces_imperator: ImperatorProvinces,
175    #[cfg(feature = "eu5")]
176    pub(crate) provinces_eu5: Eu5Provinces,
177    #[cfg(feature = "hoi4")]
178    pub(crate) provinces_hoi4: Hoi4Provinces,
179
180    #[cfg(feature = "ck3")]
181    pub(crate) province_histories: ProvinceHistories,
182    #[cfg(feature = "ck3")]
183    pub(crate) province_properties: ProvinceProperties,
184    #[cfg(feature = "ck3")]
185    pub(crate) province_terrains: ProvinceTerrains,
186
187    #[cfg(feature = "ck3")]
188    pub(crate) gameconcepts: GameConcepts,
189
190    #[cfg(feature = "ck3")]
191    pub(crate) titles: Titles,
192
193    #[cfg(feature = "ck3")]
194    pub(crate) characters: Characters,
195
196    #[cfg(feature = "jomini")]
197    pub(crate) script_values: ScriptValues,
198
199    pub(crate) triggers: Triggers,
200    pub(crate) effects: Effects,
201
202    #[cfg(feature = "ck3")]
203    pub(crate) traits: Traits,
204
205    #[cfg(feature = "ck3")]
206    pub(crate) title_history: TitleHistories,
207
208    #[cfg(feature = "ck3")]
209    pub(crate) doctrines: Doctrines,
210
211    #[cfg(feature = "ck3")]
212    pub(crate) menatarmstypes: MenAtArmsTypes,
213
214    pub(crate) gui: Gui,
215    #[cfg(any(feature = "ck3", feature = "vic3"))]
216    pub(crate) data_bindings: DataBindings,
217
218    #[cfg(feature = "hoi4")]
219    pub(crate) gfx: Gfx,
220    pub(crate) assets: Assets,
221    #[cfg(feature = "hoi4")]
222    pub(crate) music_hoi4: Hoi4Musics,
223    #[cfg(feature = "jomini")]
224    pub(crate) music: Musics,
225
226    #[cfg(feature = "jomini")]
227    pub(crate) coas: Coas,
228
229    #[cfg(feature = "vic3")]
230    pub(crate) history: History,
231
232    #[cfg(feature = "ck3")]
233    pub(crate) wars: Wars,
234
235    pub(crate) variables: Variables,
236
237    #[cfg(feature = "jomini")]
238    pub(crate) global_scopes: VariableScopes,
239    #[cfg(feature = "jomini")]
240    pub(crate) global_list_scopes: VariableScopes,
241    #[cfg(feature = "jomini")]
242    pub(crate) variable_scopes: VariableScopes,
243    #[cfg(feature = "jomini")]
244    pub(crate) variable_list_scopes: VariableScopes,
245}
246
247macro_rules! load_all_generic {
248    ($s: ident, $t: ident) => {
249        $s.spawn(|_| $t.fileset.handle(&mut $t.dds, &$t.parser));
250        $s.spawn(|_| $t.fileset.handle(&mut $t.defines, &$t.parser));
251        $s.spawn(|_| $t.fileset.handle(&mut $t.triggers, &$t.parser));
252        $s.spawn(|_| $t.fileset.handle(&mut $t.effects, &$t.parser));
253        $s.spawn(|_| $t.fileset.handle(&mut $t.assets, &$t.parser));
254        $s.spawn(|_| $t.fileset.handle(&mut $t.gui, &$t.parser));
255        $s.spawn(|_| $t.fileset.handle(&mut $t.on_actions, &$t.parser));
256    };
257}
258
259#[cfg(feature = "ck3")]
260macro_rules! load_all_ck3 {
261    ($s: ident, $t: ident) => {
262        $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
263        $s.spawn(|_| $t.fileset.handle(&mut $t.interaction_cats, &$t.parser));
264        $s.spawn(|_| $t.fileset.handle(&mut $t.province_histories, &$t.parser));
265        $s.spawn(|_| $t.fileset.handle(&mut $t.province_properties, &$t.parser));
266        $s.spawn(|_| $t.fileset.handle(&mut $t.province_terrains, &$t.parser));
267        $s.spawn(|_| $t.fileset.handle(&mut $t.gameconcepts, &$t.parser));
268        $s.spawn(|_| $t.fileset.handle(&mut $t.titles, &$t.parser));
269        $s.spawn(|_| $t.fileset.handle(&mut $t.characters, &$t.parser));
270        $s.spawn(|_| $t.fileset.handle(&mut $t.traits, &$t.parser));
271        $s.spawn(|_| $t.fileset.handle(&mut $t.title_history, &$t.parser));
272        $s.spawn(|_| $t.fileset.handle(&mut $t.doctrines, &$t.parser));
273        $s.spawn(|_| $t.fileset.handle(&mut $t.menatarmstypes, &$t.parser));
274        $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
275        $s.spawn(|_| $t.fileset.handle(&mut $t.data_bindings, &$t.parser));
276        $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_ck3, &$t.parser));
277        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
278        $s.spawn(|_| $t.fileset.handle(&mut $t.wars, &$t.parser));
279        $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
280        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
281        $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
282        $s.spawn(|_| crate::ck3::data::buildings::Building::finalize(&mut $t.database));
283    };
284}
285
286#[cfg(feature = "vic3")]
287macro_rules! load_all_vic3 {
288    ($s: ident, $t: ident) => {
289        $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
290        $s.spawn(|_| $t.fileset.handle(&mut $t.history, &$t.parser));
291        $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_vic3, &$t.parser));
292        $s.spawn(|_| $t.fileset.handle(&mut $t.data_bindings, &$t.parser));
293        $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
294        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
295        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
296        $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
297        $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
298        $s.spawn(|_| {
299            Everything::load_json(
300                &$t.fileset,
301                &mut $t.database,
302                Item::TerrainMask,
303                TerrainMask::add_json,
304            );
305        });
306    };
307}
308
309#[cfg(feature = "imperator")]
310macro_rules! load_all_imperator {
311    ($s: ident, $t: ident) => {
312        $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
313        $s.spawn(|_| $t.fileset.handle(&mut $t.decisions_imperator, &$t.parser));
314        $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_imperator, &$t.parser));
315        $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
316        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
317        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
318        $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
319        $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
320    };
321}
322
323#[cfg(feature = "eu5")]
324macro_rules! load_all_eu5 {
325    ($s: ident, $t: ident) => {
326        $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
327        $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
328        $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_eu5, &$t.parser));
329        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
330        $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
331        $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
332        $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
333    };
334}
335
336#[cfg(feature = "hoi4")]
337macro_rules! load_all_hoi4 {
338    ($s: ident, $t: ident) => {
339        $s.spawn(|_| $t.fileset.handle(&mut $t.events_hoi4, &$t.parser));
340        $s.spawn(|_| $t.fileset.handle(&mut $t.gfx, &$t.parser));
341        $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_hoi4, &$t.parser));
342        $s.spawn(|_| $t.fileset.handle(&mut $t.music_hoi4, &$t.parser));
343    };
344}
345
346macro_rules! scan_all_generic {
347    ($s: ident) => {
348        $s.triggers.scan_variables(&mut $s.variables);
349        $s.effects.scan_variables(&mut $s.variables);
350        $s.on_actions.scan_variables(&mut $s.variables);
351    };
352}
353
354#[cfg(feature = "ck3")]
355macro_rules! scan_all_ck3 {
356    ($s: ident) => {
357        $s.events.scan_variables(&mut $s.variables);
358        $s.interaction_cats.scan_variables(&mut $s.variables);
359        $s.province_histories.scan_variables(&mut $s.variables);
360        $s.titles.scan_variables(&mut $s.variables);
361        $s.characters.scan_variables(&mut $s.variables);
362        $s.traits.scan_variables(&mut $s.variables);
363        $s.title_history.scan_variables(&mut $s.variables);
364        $s.doctrines.scan_variables(&mut $s.variables);
365        $s.menatarmstypes.scan_variables(&mut $s.variables);
366        $s.music.scan_variables(&mut $s.variables);
367        $s.scripted_lists.scan_variables(&mut $s.variables);
368        $s.coas.scan_variables(&mut $s.variables);
369        $s.scripted_modifiers.scan_variables(&mut $s.variables);
370        $s.script_values.scan_variables(&mut $s.variables);
371    };
372}
373
374#[cfg(feature = "vic3")]
375macro_rules! scan_all_vic3 {
376    ($s: ident) => {
377        $s.events.scan_variables(&mut $s.variables);
378        $s.history.scan_variables(&mut $s.variables);
379        $s.coas.scan_variables(&mut $s.variables);
380        $s.scripted_lists.scan_variables(&mut $s.variables);
381        $s.scripted_modifiers.scan_variables(&mut $s.variables);
382        $s.script_values.scan_variables(&mut $s.variables);
383        $s.music.scan_variables(&mut $s.variables);
384    };
385}
386
387#[cfg(feature = "imperator")]
388macro_rules! scan_all_imperator {
389    ($s: ident) => {
390        $s.events.scan_variables(&mut $s.variables);
391        $s.decisions_imperator.scan_variables(&mut $s.variables);
392        $s.coas.scan_variables(&mut $s.variables);
393        $s.scripted_lists.scan_variables(&mut $s.variables);
394        $s.scripted_modifiers.scan_variables(&mut $s.variables);
395        $s.script_values.scan_variables(&mut $s.variables);
396        $s.music.scan_variables(&mut $s.variables);
397    };
398}
399
400#[cfg(feature = "eu5")]
401macro_rules! scan_all_eu5 {
402    ($s: ident) => {
403        $s.events.scan_variables(&mut $s.variables);
404        $s.coas.scan_variables(&mut $s.variables);
405        $s.scripted_lists.scan_variables(&mut $s.variables);
406        $s.scripted_modifiers.scan_variables(&mut $s.variables);
407        $s.script_values.scan_variables(&mut $s.variables);
408        $s.music.scan_variables(&mut $s.variables);
409    };
410}
411#[cfg(feature = "hoi4")]
412macro_rules! scan_all_hoi4 {
413    ($s: ident) => {
414        $s.events_hoi4.scan_variables(&mut $s.variables);
415        $s.music_hoi4.scan_variables(&mut $s.variables);
416    };
417}
418
419impl Everything {
420    /// Create a new `Everything` instance, ready for validating a mod.
421    ///
422    /// `vanilla_dir` is the path to the base game files. If it's `None`, then no vanilla files
423    /// will be loaded. This will seriously affect validation, but it's ok if you just want to load
424    /// and examine the mod files.
425    ///
426    /// `mod_root` is the path to the mod files. The config file will also be looked for there.
427    ///
428    /// `replace_paths` is from the similarly named field in the `.mod` file.
429    pub fn new(
430        config_filepath: Option<&Path>,
431        vanilla_dir: Option<&Path>,
432        workshop_dir: Option<&Path>,
433        paradox_dir: Option<&Path>,
434        mod_root: &Path,
435        replace_paths: Vec<PathBuf>,
436    ) -> Result<Self> {
437        let mut fileset = Fileset::new(vanilla_dir, mod_root.to_path_buf(), replace_paths);
438
439        let config_file_name = match Game::game() {
440            #[cfg(feature = "ck3")]
441            Game::Ck3 => "ck3-tiger.conf",
442            #[cfg(feature = "vic3")]
443            Game::Vic3 => "vic3-tiger.conf",
444            #[cfg(feature = "imperator")]
445            Game::Imperator => "imperator-tiger.conf",
446            #[cfg(feature = "eu5")]
447            Game::Eu5 => "eu5-tiger.conf",
448            #[cfg(feature = "hoi4")]
449            Game::Hoi4 => "hoi4-tiger.conf",
450        };
451
452        let config_file = match config_filepath {
453            Some(path) => path.to_path_buf(),
454            None => mod_root.join(config_file_name),
455        };
456
457        let config = if config_file.is_file() {
458            Self::read_config(config_file_name, &config_file)
459                .ok_or(FilesError::ConfigUnreadable { path: config_file })?
460        } else {
461            Block::new(Loc::for_file(
462                config_file.clone(),
463                FileStage::NoStage,
464                FileKind::Mod,
465                config_file.clone(),
466            ))
467        };
468
469        fileset.config(config.clone(), workshop_dir, paradox_dir)?;
470
471        fileset.scan_all()?;
472        fileset.finalize();
473
474        #[cfg(feature = "jomini")]
475        let global_scopes = VariableScopes::new("global_var:");
476        #[cfg(feature = "jomini")]
477        let global_list_scopes = VariableScopes::new("global list ");
478        #[cfg(feature = "jomini")]
479        let variable_scopes = VariableScopes::new("var:");
480        #[cfg(feature = "jomini")]
481        let variable_list_scopes = VariableScopes::new("variable list ");
482
483        #[cfg(feature = "ck3")]
484        if Game::is_ck3() {
485            variable_list_scopes
486                .config_override("lover_object_of_importance", Scopes::Character | Scopes::Flag);
487            variable_list_scopes
488                .config_override("lover_object_of_importance_2", Scopes::Character | Scopes::Flag);
489            variable_scopes.config_override("random_location", Scopes::Province | Scopes::Value);
490            variable_scopes
491                .config_override("task_contract_object", Scopes::Character | Scopes::Artifact);
492        }
493
494        #[cfg(feature = "jomini")]
495        if Game::is_jomini() {
496            if let Some(block) = config.get_field_block("scope_override") {
497                for (key, token) in block.iter_assignments() {
498                    let mut scopes = Scopes::empty();
499                    if token.lowercase_is("all") {
500                        scopes = Scopes::all();
501                    } else {
502                        for part in token.split('|') {
503                            if let Some(scope) = Scopes::from_snake_case(part.as_str()) {
504                                scopes |= scope;
505                            } else {
506                                let msg = format!("unknown scope type `{part}`");
507                                warn(ErrorKey::Config).msg(msg).loc(part).push();
508                            }
509                        }
510                    }
511                    if let Some(name) = key.strip_prefix("var:") {
512                        variable_scopes.config_override(name.as_str(), scopes);
513                    } else if let Some(name) = key.strip_prefix("var_list:") {
514                        variable_list_scopes.config_override(name.as_str(), scopes);
515                    } else if let Some(name) = key.strip_prefix("global_var:") {
516                        global_scopes.config_override(name.as_str(), scopes);
517                    } else if let Some(name) = key.strip_prefix("global_list:") {
518                        global_list_scopes.config_override(name.as_str(), scopes);
519                    }
520                }
521            }
522        }
523
524        Ok(Everything {
525            parser: ParserMemory::default(),
526            fileset,
527            dds: DdsFiles::default(),
528            config,
529            #[cfg(any(feature = "ck3", feature = "vic3"))]
530            warned_defines: RwLock::new(TigerHashSet::default()),
531            database: Db::default(),
532            localization: Localization::default(),
533            #[cfg(feature = "jomini")]
534            scripted_lists: ScriptedLists::default(),
535            defines: Defines::default(),
536            #[cfg(feature = "jomini")]
537            events: Events::default(),
538            #[cfg(feature = "hoi4")]
539            events_hoi4: Hoi4Events::default(),
540            #[cfg(feature = "imperator")]
541            decisions_imperator: Decisions::default(),
542            #[cfg(feature = "jomini")]
543            scripted_modifiers: ScriptedModifiers::default(),
544            on_actions: OnActions::default(),
545            #[cfg(feature = "ck3")]
546            interaction_cats: CharacterInteractionCategories::default(),
547            #[cfg(feature = "ck3")]
548            provinces_ck3: Ck3Provinces::default(),
549            #[cfg(feature = "vic3")]
550            provinces_vic3: Vic3Provinces::default(),
551            #[cfg(feature = "imperator")]
552            provinces_imperator: ImperatorProvinces::default(),
553            #[cfg(feature = "eu5")]
554            provinces_eu5: Eu5Provinces::default(),
555            #[cfg(feature = "hoi4")]
556            provinces_hoi4: Hoi4Provinces::default(),
557            #[cfg(feature = "ck3")]
558            province_histories: ProvinceHistories::default(),
559            #[cfg(feature = "ck3")]
560            province_properties: ProvinceProperties::default(),
561            #[cfg(feature = "ck3")]
562            province_terrains: ProvinceTerrains::default(),
563            #[cfg(feature = "ck3")]
564            gameconcepts: GameConcepts::default(),
565            #[cfg(feature = "ck3")]
566            titles: Titles::default(),
567            #[cfg(feature = "ck3")]
568            characters: Characters::default(),
569            #[cfg(feature = "jomini")]
570            script_values: ScriptValues::default(),
571            triggers: Triggers::default(),
572            effects: Effects::default(),
573            #[cfg(feature = "ck3")]
574            traits: Traits::default(),
575            #[cfg(feature = "ck3")]
576            title_history: TitleHistories::default(),
577            #[cfg(feature = "ck3")]
578            doctrines: Doctrines::default(),
579            #[cfg(feature = "ck3")]
580            menatarmstypes: MenAtArmsTypes::default(),
581            gui: Gui::default(),
582            #[cfg(any(feature = "ck3", feature = "vic3"))]
583            data_bindings: DataBindings::default(),
584            #[cfg(feature = "hoi4")]
585            gfx: Gfx::default(),
586            assets: Assets::default(),
587            #[cfg(feature = "hoi4")]
588            music_hoi4: Hoi4Musics::default(),
589            #[cfg(feature = "jomini")]
590            music: Musics::default(),
591            #[cfg(feature = "jomini")]
592            coas: Coas::default(),
593            #[cfg(feature = "vic3")]
594            history: History::default(),
595            #[cfg(feature = "ck3")]
596            wars: Wars::default(),
597            variables: Variables::new(),
598            #[cfg(feature = "jomini")]
599            global_scopes,
600            #[cfg(feature = "jomini")]
601            global_list_scopes,
602            #[cfg(feature = "jomini")]
603            variable_scopes,
604            #[cfg(feature = "jomini")]
605            variable_list_scopes,
606        })
607    }
608
609    fn read_config(name: &str, path: &Path) -> Option<Block> {
610        let entry =
611            FileEntry::new(PathBuf::from(name), FileStage::NoStage, FileKind::Mod, path.to_owned());
612        PdxFile::read_optional_bom(&entry, &ParserMemory::default())
613    }
614
615    pub fn load_config_filtering_rules(&self) {
616        check_for_legacy_ignore(&self.config);
617        load_filter(&self.config);
618    }
619
620    /// Load the `OutputStyle` settings from the config.
621    /// Note that the settings from the config can still be overridden
622    /// by supplying the --no-color flag.
623    fn load_output_styles(&self, default_color: bool) -> OutputStyle {
624        // Treat a missing output_style block and an empty output_style block exactly the same.
625        let block = match self.config.get_field_block("output_style") {
626            Some(block) => Cow::Borrowed(block),
627            None => Cow::Owned(Block::new(self.config.loc)),
628        };
629        if !block.get_field_bool("enable").unwrap_or(default_color) {
630            return OutputStyle::no_color();
631        }
632        let mut style = OutputStyle::default();
633        for severity in Severity::iter() {
634            if let Some(error_block) =
635                block.get_field_block(format!("{severity}").to_ascii_lowercase().as_str())
636            {
637                if let Some(color) = error_block.get_field_value("color") {
638                    style.set(severity, color.as_str());
639                }
640            }
641        }
642        style
643    }
644
645    pub fn load_output_settings(&self, default_colors: bool) {
646        set_output_style(self.load_output_styles(default_colors));
647    }
648
649    #[cfg(feature = "vic3")]
650    fn load_json<F>(fileset: &Fileset, db: &mut Db, itype: Item, add_json: F)
651    where
652        F: Fn(&mut Db, Block) + Sync + Send,
653    {
654        for block in fileset.filter_map_under(&PathBuf::from(itype.path()), |entry| {
655            if entry.filename().to_string_lossy().ends_with(".json") {
656                parse_json_file(entry)
657            } else {
658                None
659            }
660        }) {
661            add_json(db, block);
662        }
663    }
664
665    #[cfg(feature = "ck3")]
666    fn load_reader_export(&mut self) {
667        let path = PathBuf::from("reader_export");
668        for entry in self.fileset.get_files_under(&path) {
669            if entry.filename().to_string_lossy().ends_with(".txt") {
670                PdxFile::reader_export(entry, &mut self.parser.pdxfile);
671            }
672        }
673    }
674
675    fn load_pdx_files(&mut self, loader: &ItemLoader) {
676        let path = PathBuf::from(loader.itype().path());
677        let recursive = loader.recursive();
678        let expect_count = path.components().count() + 1;
679        for mut block in self.fileset.filter_map_under(&path, |entry| {
680            // It's <= expect_count because some loader paths are files not directories
681            if (recursive || entry.path().components().count() <= expect_count)
682                && entry.filename().to_string_lossy().ends_with(loader.extension())
683            {
684                PdxFile::read_encoded(entry, loader.encoding(), &self.parser)
685            } else {
686                None
687            }
688        }) {
689            if loader.whole_file() {
690                let fname = block.loc.filename();
691                // unwrap is safe here because of the ends_with check above.
692                let key = fname.strip_suffix(loader.extension()).unwrap();
693                let key = Token::new(key, block.loc);
694                (loader.adder())(&mut self.database, key, block);
695            } else {
696                for (key, block) in block.drain_definitions_warn() {
697                    (loader.adder())(&mut self.database, key, block);
698                }
699            }
700        }
701    }
702
703    fn load_all_normal_pdx_files(&mut self) {
704        for loader in inventory::iter::<ItemLoader> {
705            if loader.for_game(Game::game()) {
706                self.load_pdx_files(loader);
707            }
708        }
709    }
710
711    pub fn load_all(&mut self) {
712        #[cfg(feature = "ck3")]
713        self.load_reader_export();
714        self.load_all_normal_pdx_files();
715
716        std::thread::scope(|s| {
717            s.spawn(|| self.fileset.handle(&mut self.localization, &self.parser));
718
719            scope(|s| {
720                load_all_generic!(s, self);
721                match Game::game() {
722                    #[cfg(feature = "ck3")]
723                    Game::Ck3 => {
724                        load_all_ck3!(s, self);
725                    }
726                    #[cfg(feature = "vic3")]
727                    Game::Vic3 => {
728                        load_all_vic3!(s, self);
729                    }
730                    #[cfg(feature = "imperator")]
731                    Game::Imperator => {
732                        load_all_imperator!(s, self);
733                    }
734                    #[cfg(feature = "eu5")]
735                    Game::Eu5 => {
736                        load_all_eu5!(s, self);
737                    }
738                    #[cfg(feature = "hoi4")]
739                    Game::Hoi4 => {
740                        load_all_hoi4!(s, self);
741                    }
742                }
743            });
744
745            self.database.add_subitems();
746            scan_all_generic!(self);
747            match Game::game() {
748                #[cfg(feature = "ck3")]
749                Game::Ck3 => {
750                    scan_all_ck3!(self);
751                }
752                #[cfg(feature = "vic3")]
753                Game::Vic3 => {
754                    scan_all_vic3!(self);
755                }
756                #[cfg(feature = "imperator")]
757                Game::Imperator => {
758                    scan_all_imperator!(self);
759                }
760                #[cfg(feature = "eu5")]
761                Game::Eu5 => {
762                    scan_all_eu5!(self);
763                }
764                #[cfg(feature = "hoi4")]
765                Game::Hoi4 => {
766                    scan_all_hoi4!(self);
767                }
768            }
769            self.database.scan_variables(&mut self.variables);
770        });
771    }
772
773    fn validate_all_generic<'a>(&'a self, s: &Scope<'a>) {
774        s.spawn(|_| self.fileset.validate(self));
775        s.spawn(|_| self.defines.validate(self));
776        s.spawn(|_| self.triggers.validate(self));
777        s.spawn(|_| self.effects.validate(self));
778        s.spawn(|_| self.assets.validate(self));
779        s.spawn(|_| self.gui.validate(self));
780        s.spawn(|_| self.on_actions.validate(self));
781        s.spawn(|_| self.dds.validate());
782    }
783
784    #[cfg(feature = "ck3")]
785    fn validate_all_ck3<'a>(&'a self, s: &Scope<'a>) {
786        s.spawn(|_| self.events.validate(self));
787        s.spawn(|_| self.interaction_cats.validate(self));
788        s.spawn(|_| self.province_histories.validate(self));
789        s.spawn(|_| self.province_properties.validate(self));
790        s.spawn(|_| self.province_terrains.validate(self));
791        s.spawn(|_| self.gameconcepts.validate(self));
792        s.spawn(|_| self.titles.validate(self));
793        s.spawn(|_| self.characters.validate(self));
794        s.spawn(|_| self.traits.validate(self));
795        s.spawn(|_| self.title_history.validate(self));
796        s.spawn(|_| self.doctrines.validate(self));
797        s.spawn(|_| self.menatarmstypes.validate(self));
798        s.spawn(|_| self.data_bindings.validate(self));
799        s.spawn(|_| self.provinces_ck3.validate(self));
800        s.spawn(|_| self.wars.validate(self));
801        s.spawn(|_| self.coas.validate(self));
802        s.spawn(|_| self.scripted_lists.validate(self));
803        s.spawn(|_| self.scripted_modifiers.validate(self));
804        s.spawn(|_| self.script_values.validate(self));
805        s.spawn(|_| self.music.validate(self));
806        s.spawn(|_| Climate::validate_all(&self.database, self));
807    }
808
809    #[cfg(feature = "vic3")]
810    fn validate_all_vic3<'a>(&'a self, s: &Scope<'a>) {
811        if std::env::var("TIGER_CHECK_MODIFS").is_ok() {
812            for line in std::io::stdin().lines().map_while(Result::ok) {
813                if !line.starts_with(' ') {
814                    if let Some(name) = line.strip_suffix(":") {
815                        eprintln!("checking modif {name}");
816                        let loc: Loc = FileEntry::new(
817                            "stdin".into(),
818                            FileStage::NoStage,
819                            FileKind::Vanilla,
820                            "stdin".into(),
821                        )
822                        .into();
823                        let name_token = Token::new(name, loc);
824                        crate::vic3::tables::modifs::lookup_engine_modif(
825                            &name_token,
826                            &Lowercase::new(name),
827                            self,
828                            Some(Severity::Error),
829                        );
830                    }
831                }
832            }
833        }
834        s.spawn(|_| self.events.validate(self));
835        s.spawn(|_| self.history.validate(self));
836        s.spawn(|_| self.provinces_vic3.validate(self));
837        s.spawn(|_| self.data_bindings.validate(self));
838        s.spawn(|_| self.coas.validate(self));
839        s.spawn(|_| self.scripted_lists.validate(self));
840        s.spawn(|_| self.scripted_modifiers.validate(self));
841        s.spawn(|_| self.script_values.validate(self));
842        s.spawn(|_| self.music.validate(self));
843        s.spawn(|_| StrategicRegion::crosscheck(self));
844        s.spawn(|_| BuyPackage::crosscheck(self));
845    }
846
847    #[cfg(feature = "imperator")]
848    fn validate_all_imperator<'a>(&'a self, s: &Scope<'a>) {
849        s.spawn(|_| self.events.validate(self));
850        s.spawn(|_| self.decisions_imperator.validate(self));
851        s.spawn(|_| self.provinces_imperator.validate(self));
852        s.spawn(|_| self.coas.validate(self));
853        s.spawn(|_| self.scripted_lists.validate(self));
854        s.spawn(|_| self.scripted_modifiers.validate(self));
855        s.spawn(|_| self.script_values.validate(self));
856        s.spawn(|_| self.music.validate(self));
857    }
858
859    #[cfg(feature = "eu5")]
860    fn validate_all_eu5<'a>(&'a self, s: &Scope<'a>) {
861        s.spawn(|_| self.events.validate(self));
862        s.spawn(|_| self.coas.validate(self));
863        s.spawn(|_| self.provinces_eu5.validate(self));
864        s.spawn(|_| self.scripted_lists.validate(self));
865        s.spawn(|_| self.scripted_modifiers.validate(self));
866        s.spawn(|_| self.script_values.validate(self));
867        s.spawn(|_| self.music.validate(self));
868    }
869
870    #[cfg(feature = "hoi4")]
871    fn validate_all_hoi4<'a>(&'a self, s: &Scope<'a>) {
872        s.spawn(|_| self.events_hoi4.validate(self));
873        s.spawn(|_| self.provinces_hoi4.validate(self));
874        s.spawn(|_| self.gfx.validate(self));
875        s.spawn(|_| self.music_hoi4.validate(self));
876    }
877
878    pub fn validate_all(&self) {
879        scope(|s| {
880            self.validate_all_generic(s);
881            match Game::game() {
882                #[cfg(feature = "ck3")]
883                Game::Ck3 => self.validate_all_ck3(s),
884                #[cfg(feature = "vic3")]
885                Game::Vic3 => self.validate_all_vic3(s),
886                #[cfg(feature = "imperator")]
887                Game::Imperator => self.validate_all_imperator(s),
888                #[cfg(feature = "eu5")]
889                Game::Eu5 => self.validate_all_eu5(s),
890                #[cfg(feature = "hoi4")]
891                Game::Hoi4 => self.validate_all_hoi4(s),
892            }
893            s.spawn(|_| self.database.validate(self));
894        });
895        self.localization.validate_pass2(self);
896    }
897
898    pub fn check_rivers(&mut self) {
899        let mut rivers = Rivers::default();
900        self.fileset.handle(&mut rivers, &self.parser);
901        rivers.validate(self);
902    }
903
904    #[cfg(feature = "ck3")]
905    pub fn check_pod(&mut self) {
906        self.province_histories.check_pod_faiths(self, &self.titles);
907        self.characters.check_pod_flags(self);
908        self.localization.check_pod_loca(self);
909    }
910
911    pub fn check_unused(&mut self) {
912        self.localization.check_unused(self);
913        self.fileset.check_unused_dds(self);
914    }
915
916    #[allow(dead_code)]
917    pub(crate) fn item_has_property(&self, itype: Item, key: &str, property: &str) -> bool {
918        self.database.has_property(itype, key, property, self)
919    }
920
921    #[cfg(feature = "ck3")] // vic3 happens not to use
922    pub(crate) fn item_lc_has_property(
923        &self,
924        itype: Item,
925        key: &Lowercase,
926        property: &str,
927    ) -> bool {
928        self.database.lc_has_property(itype, key, property, self)
929    }
930
931    #[cfg(feature = "ck3")]
932    fn item_exists_ck3(&self, itype: Item, key: &str) -> bool {
933        match itype {
934            Item::ActivityState => ACTIVITY_STATES.contains(&key),
935            Item::ArtifactHistory => ARTIFACT_HISTORY.contains(&key),
936            Item::ArtifactRarity => ARTIFACT_RARITIES.contains(&&*key.to_ascii_lowercase()),
937            Item::Character => self.characters.exists(key),
938            Item::CharacterInteractionCategory => self.interaction_cats.exists(key),
939            Item::Coa => self.coas.exists(key),
940            Item::CoaTemplate => self.coas.template_exists(key),
941            Item::DangerType => DANGER_TYPES.contains(&key),
942            Item::DlcFeature => DLC_FEATURES_CK3.contains(&key),
943            Item::Doctrine => self.doctrines.exists(key),
944            Item::DoctrineBooleanParameter => self.doctrines.boolean_parameter_exists(key),
945            Item::DoctrineCategory => self.doctrines.category_exists(key),
946            Item::DoctrineParameter => self.doctrines.parameter_exists(key),
947            Item::Event => self.events.exists(key),
948            Item::EventNamespace => self.events.namespace_exists(key),
949            Item::GameConcept => self.gameconcepts.exists(key),
950            Item::GeneAttribute => self.assets.attribute_exists(key),
951            Item::GeneticConstraint => self.traits.constraint_exists(key),
952            Item::MenAtArms => self.menatarmstypes.exists(key),
953            Item::MenAtArmsBase => self.menatarmstypes.base_exists(key),
954            Item::Music => self.music.exists(key),
955            Item::PrisonType => PRISON_TYPES.contains(&key),
956            Item::Province => self.provinces_ck3.exists(key),
957            Item::RewardItem => REWARD_ITEMS.contains(&key),
958            Item::ScriptedList => self.scripted_lists.exists(key),
959            Item::ScriptedModifier => self.scripted_modifiers.exists(key),
960            Item::ScriptValue => self.script_values.exists(key),
961            Item::Sexuality => SEXUALITIES.contains(&key),
962            Item::Skill => SKILLS.contains(&key),
963            Item::Sound => self.valid_sound(key),
964            Item::Title => self.titles.exists(key),
965            Item::TitleHistory => self.title_history.exists(key),
966            Item::Trait => self.traits.exists(key),
967            Item::TraitFlag => self.traits.flag_exists(key),
968            Item::TraitTrack => self.traits.track_exists(key),
969            Item::TraitCategory => TRAIT_CATEGORIES.contains(&key),
970            _ => self.database.exists(itype, key),
971        }
972    }
973
974    #[cfg(feature = "vic3")]
975    fn item_exists_vic3(&self, itype: Item, key: &str) -> bool {
976        match itype {
977            Item::Approval => APPROVALS.contains(&key),
978            Item::Attitude => ATTITUDES.contains(&&*key.to_lowercase()),
979            Item::CharacterRole => CHARACTER_ROLES.contains(&key),
980            Item::Coa => self.coas.exists(key),
981            Item::CoaTemplate => self.coas.template_exists(key),
982            Item::CountryTier => COUNTRY_TIERS.contains(&key),
983            Item::DlcFeature => DLC_FEATURES_VIC3.contains(&key),
984            Item::Event => self.events.exists(key),
985            Item::EventCategory => EVENT_CATEGORIES.contains(&key),
986            Item::EventNamespace => self.events.namespace_exists(key),
987            Item::GeneAttribute => self.assets.attribute_exists(key),
988            Item::InfamyThreshold => INFAMY_THRESHOLDS.contains(&key),
989            Item::Level => LEVELS.contains(&key),
990            Item::Music => self.music.exists(key),
991            Item::RelationsThreshold => RELATIONS.contains(&key),
992            Item::ScriptedList => self.scripted_lists.exists(key),
993            Item::ScriptedModifier => self.scripted_modifiers.exists(key),
994            Item::ScriptValue => self.script_values.exists(key),
995            Item::SecretGoal => SECRET_GOALS.contains(&key),
996            Item::Sound => self.valid_sound(key),
997            Item::Strata => STRATA.contains(&key),
998            Item::TerrainKey => TERRAIN_KEYS.contains(&key),
999            Item::TransferOfPower => TRANSFER_OF_POWER.contains(&key),
1000            _ => self.database.exists(itype, key),
1001        }
1002    }
1003
1004    #[cfg(feature = "imperator")]
1005    fn item_exists_imperator(&self, itype: Item, key: &str) -> bool {
1006        match itype {
1007            Item::Coa => self.coas.exists(key),
1008            Item::CoaTemplate => self.coas.template_exists(key),
1009            Item::DlcName => DLC_NAME_IMPERATOR.contains(&key),
1010            Item::Decision => self.decisions_imperator.exists(key),
1011            Item::Event => self.events.exists(key),
1012            Item::EventNamespace => self.events.namespace_exists(key),
1013            Item::GeneAttribute => self.assets.attribute_exists(key),
1014            Item::Music => self.music.exists(key),
1015            Item::Province => self.provinces_imperator.exists(key),
1016            Item::ScriptedList => self.scripted_lists.exists(key),
1017            Item::ScriptedModifier => self.scripted_modifiers.exists(key),
1018            Item::ScriptValue => self.script_values.exists(key),
1019            Item::Sound => self.valid_sound(key),
1020            _ => self.database.exists(itype, key),
1021        }
1022    }
1023
1024    #[cfg(feature = "eu5")]
1025    fn item_exists_eu5(&self, itype: Item, key: &str) -> bool {
1026        match itype {
1027            Item::Coa => self.coas.exists(key),
1028            Item::CoaTemplate => self.coas.template_exists(key),
1029            Item::DlcFeature => DLC_FEATURES_EU5.contains(&key),
1030            Item::Event => self.events.exists(key),
1031            Item::EventNamespace => self.events.namespace_exists(key),
1032            Item::GeneAttribute => self.assets.attribute_exists(key),
1033            Item::Music => self.music.exists(key),
1034            Item::ScriptedList => self.scripted_lists.exists(key),
1035            Item::ScriptedModifier => self.scripted_modifiers.exists(key),
1036            Item::ScriptValue => self.script_values.exists(key),
1037            Item::Sound => self.valid_sound(key),
1038            Item::Currency => CURRENCIES.contains(&key),
1039            Item::CharacterTraitCategory => CHARACTER_TRAIT_CATEGORY.contains(&key),
1040            _ => self.database.exists(itype, key),
1041        }
1042    }
1043
1044    #[cfg(feature = "hoi4")]
1045    fn item_exists_hoi4(&self, itype: Item, key: &str) -> bool {
1046        match itype {
1047            Item::AiStrategyType => AI_STRATEGY_TYPES.contains(&key),
1048            Item::Event => self.events_hoi4.exists(key),
1049            Item::EventNamespace => self.events_hoi4.namespace_exists(key),
1050            Item::Music => self.music_hoi4.exists(key),
1051            Item::MusicAsset => self.assets.music_exists(key),
1052            Item::Pdxmesh => self.gfx.mesh_exists(key),
1053            Item::Province => self.provinces_hoi4.exists(key),
1054            Item::Sprite => self.gfx.sprite_exists(key),
1055            _ => self.database.exists(itype, key),
1056        }
1057    }
1058
1059    pub(crate) fn item_exists(&self, itype: Item, key: &str) -> bool {
1060        match itype {
1061            Item::Asset => self.assets.asset_exists(key),
1062            Item::BlendShape => self.assets.blend_shape_exists(key),
1063            Item::Define => self.defines.exists(key),
1064            Item::Entity => self.assets.entity_exists(key),
1065            Item::Entry => self.fileset.entry_exists(key),
1066            Item::File => self.fileset.exists(key),
1067            Item::GuiLayer => self.gui.layer_exists(key),
1068            Item::GuiTemplate => self.gui.template_exists(key),
1069            Item::GuiType => self.gui.type_exists(&Lowercase::new(key)),
1070            Item::Localization => self.localization.exists(key),
1071            Item::OnAction => self.on_actions.exists(key),
1072            #[cfg(feature = "jomini")]
1073            Item::Pdxmesh => self.assets.mesh_exists(key),
1074            Item::ScriptedEffect => self.effects.exists(key),
1075            Item::ScriptedTrigger => self.triggers.exists(key),
1076            Item::TextFormat => self.gui.textformat_exists(key),
1077            Item::TextIcon => self.gui.texticon_exists(key),
1078            Item::TextureFile => self.assets.texture_exists(key),
1079            Item::WidgetName => self.gui.name_exists(key),
1080            Item::Directory | Item::Shortcut => true, // TODO
1081            _ => match Game::game() {
1082                #[cfg(feature = "ck3")]
1083                Game::Ck3 => self.item_exists_ck3(itype, key),
1084                #[cfg(feature = "vic3")]
1085                Game::Vic3 => self.item_exists_vic3(itype, key),
1086                #[cfg(feature = "imperator")]
1087                Game::Imperator => self.item_exists_imperator(itype, key),
1088                #[cfg(feature = "eu5")]
1089                Game::Eu5 => self.item_exists_eu5(itype, key),
1090                #[cfg(feature = "hoi4")]
1091                Game::Hoi4 => self.item_exists_hoi4(itype, key),
1092            },
1093        }
1094    }
1095
1096    /// Return true iff the item `key` is found with a case insensitive match.
1097    /// This function is **incomplete**. It only contains the item types for which case insensitive
1098    /// matches are needed; this is currently the ones used in `src/ck3/tables/modif.rs`.
1099    #[cfg(feature = "ck3")]
1100    fn item_exists_lc_ck3(&self, itype: Item, key: &Lowercase) -> bool {
1101        match itype {
1102            Item::MenAtArmsBase => self.menatarmstypes.base_exists_lc(key),
1103            Item::Trait => self.traits.exists_lc(key),
1104            Item::TraitTrack => self.traits.track_exists_lc(key),
1105            _ => self.database.exists_lc(itype, key),
1106        }
1107    }
1108
1109    /// Return true iff the item `key` is found with a case insensitive match.
1110    /// This function is **incomplete**. It only contains the item types for which case insensitive
1111    /// matches are needed; this is currently the ones used in `src/vic3/tables/modif.rs`.
1112    #[cfg(feature = "vic3")]
1113    fn item_exists_lc_vic3(&self, itype: Item, key: &Lowercase) -> bool {
1114        match itype {
1115            Item::TerrainKey => TERRAIN_KEYS.contains(&key.as_str()),
1116            _ => self.database.exists_lc(itype, key),
1117        }
1118    }
1119
1120    /// Return true iff the item `key` is found with a case insensitive match.
1121    /// This function is **incomplete**. It only contains the item types for which case insensitive
1122    /// matches are needed; this is currently the ones used in `src/imperator/tables/modif.rs`.
1123    #[cfg(feature = "imperator")]
1124    fn item_exists_lc_imperator(&self, itype: Item, key: &Lowercase) -> bool {
1125        #[allow(clippy::match_single_binding)]
1126        match itype {
1127            _ => self.database.exists_lc(itype, key),
1128        }
1129    }
1130
1131    /// Return true iff the item `key` is found with a case insensitive match.
1132    /// This function is **incomplete**. It only contains the item types for which case insensitive
1133    /// matches are needed.
1134    #[cfg(feature = "eu5")]
1135    fn item_exists_lc_eu5(&self, itype: Item, key: &Lowercase) -> bool {
1136        self.database.exists_lc(itype, key)
1137    }
1138
1139    /// Return true iff the item `key` is found with a case insensitive match.
1140    /// This function is **incomplete**. It only contains the item types for which case insensitive
1141    /// matches are needed.
1142    #[cfg(feature = "hoi4")]
1143    fn item_exists_lc_hoi4(&self, itype: Item, key: &Lowercase) -> bool {
1144        #[allow(clippy::match_single_binding)]
1145        match itype {
1146            Item::EventNamespace => self.events_hoi4.namespace_exists_lc(key),
1147            _ => self.database.exists_lc(itype, key),
1148        }
1149    }
1150
1151    /// Return true iff the item `key` is found with a case insensitive match.
1152    /// This function is **incomplete**. It only contains the item types for which case insensitive
1153    /// matches are needed; this is currently the ones used in modif lookups.
1154    pub(crate) fn item_exists_lc(&self, itype: Item, key: &Lowercase) -> bool {
1155        #[allow(clippy::match_single_binding)]
1156        match itype {
1157            _ => match Game::game() {
1158                #[cfg(feature = "ck3")]
1159                Game::Ck3 => self.item_exists_lc_ck3(itype, key),
1160                #[cfg(feature = "vic3")]
1161                Game::Vic3 => self.item_exists_lc_vic3(itype, key),
1162                #[cfg(feature = "imperator")]
1163                Game::Imperator => self.item_exists_lc_imperator(itype, key),
1164                #[cfg(feature = "eu5")]
1165                Game::Eu5 => self.item_exists_lc_eu5(itype, key),
1166                #[cfg(feature = "hoi4")]
1167                Game::Hoi4 => self.item_exists_lc_hoi4(itype, key),
1168            },
1169        }
1170    }
1171
1172    pub(crate) fn mark_used(&self, itype: Item, key: &str) {
1173        match itype {
1174            Item::File => self.fileset.mark_used(key),
1175            Item::Localization => {
1176                self.localization.mark_used_return_exists(key);
1177            }
1178            _ => (),
1179        }
1180    }
1181
1182    pub(crate) fn verify_exists(&self, itype: Item, token: &Token) {
1183        self.verify_exists_implied(itype, token.as_str(), token);
1184    }
1185
1186    pub(crate) fn verify_exists_max_sev(&self, itype: Item, token: &Token, max_sev: Severity) {
1187        self.verify_exists_implied_max_sev(itype, token.as_str(), token, max_sev);
1188    }
1189
1190    pub(crate) fn verify_exists_implied_max_sev(
1191        &self,
1192        itype: Item,
1193        key: &str,
1194        token: &Token,
1195        max_sev: Severity,
1196    ) {
1197        match itype {
1198            Item::Entry => self.fileset.verify_entry_exists(key, token, max_sev),
1199            Item::File => self.fileset.verify_exists_implied(key, token, max_sev),
1200            Item::Localization => self.localization.verify_exists_implied(key, token, max_sev),
1201            Item::Music => match Game::game() {
1202                #[cfg(feature = "ck3")]
1203                Game::Ck3 => self.music.verify_exists_implied(key, token, max_sev),
1204                #[cfg(feature = "vic3")]
1205                Game::Vic3 => self.music.verify_exists_implied(key, token, max_sev),
1206                #[cfg(feature = "imperator")]
1207                Game::Imperator => self.music.verify_exists_implied(key, token, max_sev),
1208                #[cfg(feature = "eu5")]
1209                Game::Eu5 => self.music.verify_exists_implied(key, token, max_sev),
1210                #[cfg(feature = "hoi4")]
1211                Game::Hoi4 => self.music_hoi4.verify_exists_implied(key, token, max_sev),
1212            },
1213            Item::Province => match Game::game() {
1214                #[cfg(feature = "ck3")]
1215                Game::Ck3 => self.provinces_ck3.verify_exists_implied(key, token, max_sev),
1216                #[cfg(feature = "vic3")]
1217                Game::Vic3 => self.provinces_vic3.verify_exists_implied(key, token, max_sev),
1218                #[cfg(feature = "imperator")]
1219                Game::Imperator => {
1220                    self.provinces_imperator.verify_exists_implied(key, token, max_sev);
1221                }
1222                #[cfg(feature = "eu5")]
1223                Game::Eu5 => {
1224                    self.provinces_eu5.verify_exists_implied(key, token, max_sev);
1225                }
1226                #[cfg(feature = "hoi4")]
1227                #[cfg(feature = "hoi4")]
1228                Game::Hoi4 => {
1229                    self.provinces_hoi4.verify_exists_implied(key, token, max_sev);
1230                }
1231            },
1232            Item::TextureFile => {
1233                if let Some(entry) = self.assets.get_texture(key) {
1234                    // TODO: avoid allocating a string here
1235                    self.fileset.mark_used(&entry.path().to_string_lossy());
1236                } else {
1237                    let msg = format!("no texture file {key} anywhere under {}", itype.path());
1238                    report(ErrorKey::MissingFile, itype.severity().at_most(max_sev))
1239                        .conf(itype.confidence())
1240                        .msg(msg)
1241                        .loc(token)
1242                        .push();
1243                }
1244            }
1245            _ => {
1246                if !self.item_exists(itype, key) {
1247                    let path = itype.path();
1248                    let msg = if path.is_empty() {
1249                        format!("unknown {itype} {key}")
1250                    } else {
1251                        format!("{itype} {key} not defined in {path}")
1252                    };
1253                    report(ErrorKey::MissingItem, itype.severity().at_most(max_sev))
1254                        .conf(itype.confidence())
1255                        .msg(msg)
1256                        .loc(token)
1257                        .push();
1258                }
1259            }
1260        }
1261    }
1262
1263    #[allow(dead_code)]
1264    pub(crate) fn verify_exists_implied_max_sev_lc(
1265        &self,
1266        itype: Item,
1267        key: &Lowercase,
1268        token: &Token,
1269        max_sev: Severity,
1270    ) {
1271        if !self.item_exists_lc(itype, key) {
1272            let path = itype.path();
1273            let msg = if path.is_empty() {
1274                format!("unknown {itype} {key}")
1275            } else {
1276                format!("{itype} {key} not defined in {path}")
1277            };
1278            report(ErrorKey::MissingItem, itype.severity().at_most(max_sev))
1279                .conf(itype.confidence())
1280                .msg(msg)
1281                .loc(token)
1282                .push();
1283        }
1284    }
1285
1286    pub(crate) fn verify_exists_implied(&self, itype: Item, key: &str, token: &Token) {
1287        self.verify_exists_implied_max_sev(itype, key, token, Severity::Error);
1288    }
1289
1290    #[cfg(feature = "ck3")]
1291    pub(crate) fn verify_icon(&self, define: &str, token: &Token, suffix: &str) {
1292        if let Some(icon_path) = self.get_defined_string_warn(token, define) {
1293            let pathname = format!("{icon_path}/{token}{suffix}");
1294            // It's `Severity::Warning` because a missing icon is only a UI issue.
1295            self.verify_exists_implied_max_sev(Item::File, &pathname, token, Severity::Warning);
1296        }
1297    }
1298
1299    #[cfg(feature = "ck3")]
1300    pub(crate) fn mark_used_icon(&self, define: &str, token: &Token, suffix: &str) {
1301        if let Some(icon_path) = self.get_defined_string_warn(token, define) {
1302            let pathname = format!("{icon_path}/{token}{suffix}");
1303            self.fileset.mark_used(&pathname);
1304        }
1305    }
1306
1307    #[allow(dead_code)]
1308    pub(crate) fn validate_use(&self, itype: Item, key: &Token, block: &Block) {
1309        self.database.validate_use(itype, key, block, self);
1310    }
1311
1312    #[allow(dead_code)]
1313    pub(crate) fn validate_call(
1314        &self,
1315        itype: Item,
1316        key: &Token,
1317        block: &Block,
1318        sc: &mut ScopeContext,
1319    ) {
1320        self.database.validate_call(itype, key, block, self, sc);
1321    }
1322
1323    /// Validate the use of a localization within a specific `ScopeContext`.
1324    /// This allows validation of the named scopes used within the localization's datafunctions.
1325    pub(crate) fn validate_localization_sc(&self, key: &str, sc: &mut ScopeContext) {
1326        self.localization.validate_use(key, self, sc);
1327    }
1328
1329    #[allow(dead_code)]
1330    pub(crate) fn get_item<T: DbKind>(
1331        &self,
1332        itype: Item,
1333        key: &str,
1334    ) -> Option<(&Token, &Block, &T)> {
1335        self.database.get_item(itype, key)
1336    }
1337
1338    pub(crate) fn get_key_block(&self, itype: Item, key: &str) -> Option<(&Token, &Block)> {
1339        self.database.get_key_block(itype, key)
1340    }
1341
1342    pub(crate) fn get_trigger(&self, key: &Token) -> Option<&Trigger> {
1343        #[cfg(any(feature = "ck3", feature = "eu5"))]
1344        if Game::is_ck3() || Game::is_eu5() {
1345            if let Some(trigger) = self.triggers.get(key.as_str()) {
1346                return Some(trigger);
1347            }
1348            if let Some(trigger) = self.events.get_trigger(key) {
1349                return Some(trigger);
1350            }
1351            return None;
1352        }
1353        self.triggers.get(key.as_str())
1354    }
1355
1356    pub(crate) fn get_effect(&self, key: &Token) -> Option<&Effect> {
1357        #[cfg(any(feature = "ck3", feature = "eu5"))]
1358        if Game::is_ck3() || Game::is_eu5() {
1359            if let Some(effect) = self.effects.get(key.as_str()) {
1360                return Some(effect);
1361            }
1362            if let Some(effect) = self.events.get_effect(key) {
1363                return Some(effect);
1364            }
1365            return None;
1366        }
1367        self.effects.get(key.as_str())
1368    }
1369
1370    #[cfg(feature = "ck3")]
1371    pub(crate) fn get_defined_string(&self, key: &str) -> Option<&Token> {
1372        self.defines.get_bv(key).and_then(BV::get_value)
1373    }
1374
1375    #[cfg(any(feature = "ck3", feature = "vic3"))]
1376    pub(crate) fn get_defined_array(&self, key: &str) -> Option<&Block> {
1377        self.defines.get_bv(key).and_then(BV::get_block)
1378    }
1379
1380    #[allow(clippy::missing_panics_doc)] // only panics on poisoned mutex
1381    #[cfg(feature = "ck3")]
1382    pub(crate) fn get_defined_string_warn(&self, token: &Token, key: &str) -> Option<&Token> {
1383        let result = self.get_defined_string(key);
1384        if result.is_none() {
1385            let mut cache = self.warned_defines.write().unwrap();
1386            if !cache.contains(key) {
1387                let msg = format!("{key} not defined in common/defines/");
1388                err(ErrorKey::MissingItem).msg(msg).loc(token).push();
1389                cache.insert(key.to_string());
1390            }
1391        }
1392        result
1393    }
1394
1395    #[allow(clippy::missing_panics_doc)] // only panics on poisoned mutex
1396    #[cfg(any(feature = "ck3", feature = "vic3"))]
1397    pub(crate) fn get_defined_array_warn(&self, token: &Token, key: &str) -> Option<&Block> {
1398        let result = self.get_defined_array(key);
1399        if result.is_none() {
1400            let mut cache = self.warned_defines.write().unwrap();
1401            if !cache.contains(key) {
1402                let msg = format!("{key} not defined in common/defines/");
1403                err(ErrorKey::MissingItem).msg(msg).loc(token).push();
1404                cache.insert(key.to_string());
1405            }
1406        }
1407        result
1408    }
1409
1410    #[cfg(feature = "ck3")]
1411    pub fn iter_keys_ck3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1412        match itype {
1413            Item::Coa => Box::new(self.coas.iter_keys()),
1414            Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1415            Item::Character => Box::new(self.characters.iter_keys()),
1416            Item::CharacterInteractionCategory => Box::new(self.interaction_cats.iter_keys()),
1417            Item::Doctrine => Box::new(self.doctrines.iter_keys()),
1418            Item::DoctrineBooleanParameter => {
1419                Box::new(self.doctrines.iter_boolean_parameter_keys())
1420            }
1421            Item::DoctrineCategory => Box::new(self.doctrines.iter_category_keys()),
1422            Item::DoctrineParameter => Box::new(self.doctrines.iter_parameter_keys()),
1423            Item::Event => Box::new(self.events.iter_keys()),
1424            Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1425            Item::GameConcept => Box::new(self.gameconcepts.iter_keys()),
1426            Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1427            Item::GeneticConstraint => Box::new(self.traits.iter_constraint_keys()),
1428            Item::MenAtArms => Box::new(self.menatarmstypes.iter_keys()),
1429            Item::MenAtArmsBase => Box::new(self.menatarmstypes.iter_base_keys()),
1430            Item::Music => Box::new(self.music.iter_keys()),
1431            Item::Province => Box::new(self.provinces_ck3.iter_keys()),
1432            Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1433            Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1434            Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1435            Item::Title => Box::new(self.titles.iter_keys()),
1436            Item::TitleHistory => Box::new(self.title_history.iter_keys()),
1437            Item::Trait => Box::new(self.traits.iter_keys()),
1438            Item::TraitFlag => Box::new(self.traits.iter_flag_keys()),
1439            Item::TraitTrack => Box::new(self.traits.iter_track_keys()),
1440            _ => Box::new(self.database.iter_keys(itype)),
1441        }
1442    }
1443
1444    #[cfg(feature = "vic3")]
1445    fn iter_keys_vic3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1446        match itype {
1447            Item::Coa => Box::new(self.coas.iter_keys()),
1448            Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1449            Item::Event => Box::new(self.events.iter_keys()),
1450            Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1451            Item::Music => Box::new(self.music.iter_keys()),
1452            Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1453            Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1454            Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1455            Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1456            _ => Box::new(self.database.iter_keys(itype)),
1457        }
1458    }
1459
1460    #[cfg(feature = "imperator")]
1461    fn iter_keys_imperator<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1462        match itype {
1463            Item::Coa => Box::new(self.coas.iter_keys()),
1464            Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1465            Item::Decision => Box::new(self.decisions_imperator.iter_keys()),
1466            Item::Event => Box::new(self.events.iter_keys()),
1467            Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1468            Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1469            Item::Music => Box::new(self.music.iter_keys()),
1470            Item::Province => Box::new(self.provinces_imperator.iter_keys()),
1471            Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1472            Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1473            Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1474            _ => Box::new(self.database.iter_keys(itype)),
1475        }
1476    }
1477
1478    #[cfg(feature = "eu5")]
1479    fn iter_keys_eu5<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1480        match itype {
1481            Item::Coa => Box::new(self.coas.iter_keys()),
1482            Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1483            Item::Event => Box::new(self.events.iter_keys()),
1484            Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1485            Item::Music => Box::new(self.music.iter_keys()),
1486            Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1487            Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1488            Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1489            Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1490            _ => Box::new(self.database.iter_keys(itype)),
1491        }
1492    }
1493
1494    #[cfg(feature = "hoi4")]
1495    fn iter_keys_hoi4<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1496        match itype {
1497            Item::Event => Box::new(self.events_hoi4.iter_keys()),
1498            Item::EventNamespace => Box::new(self.events_hoi4.iter_namespace_keys()),
1499            Item::Music => Box::new(self.music_hoi4.iter_keys()),
1500            Item::MusicAsset => Box::new(self.assets.iter_music_keys()),
1501            Item::Pdxmesh => Box::new(self.gfx.iter_mesh_keys()),
1502            Item::Province => Box::new(self.provinces_hoi4.iter_keys()),
1503            Item::Sprite => Box::new(self.gfx.iter_sprite_keys()),
1504            _ => Box::new(self.database.iter_keys(itype)),
1505        }
1506    }
1507
1508    pub fn iter_keys<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1509        match itype {
1510            Item::Asset => Box::new(self.assets.iter_asset_keys()),
1511            Item::BlendShape => Box::new(self.assets.iter_blend_shape_keys()),
1512            Item::Define => Box::new(self.defines.iter_keys()),
1513            Item::Entity => Box::new(self.assets.iter_entity_keys()),
1514            Item::File => Box::new(self.fileset.iter_keys()),
1515            Item::GuiLayer => Box::new(self.gui.iter_layer_keys()),
1516            Item::GuiTemplate => Box::new(self.gui.iter_template_keys()),
1517            Item::GuiType => Box::new(self.gui.iter_type_keys()),
1518            Item::Localization => Box::new(self.localization.iter_keys()),
1519            Item::OnAction => Box::new(self.on_actions.iter_keys()),
1520            #[cfg(feature = "jomini")]
1521            Item::Pdxmesh => Box::new(self.assets.iter_mesh_keys()),
1522            Item::ScriptedEffect => Box::new(self.effects.iter_keys()),
1523            Item::ScriptedTrigger => Box::new(self.triggers.iter_keys()),
1524            Item::TextFormat => Box::new(self.gui.iter_textformat_keys()),
1525            Item::TextIcon => Box::new(self.gui.iter_texticon_keys()),
1526            Item::TextureFile => Box::new(self.assets.iter_texture_keys()),
1527            Item::WidgetName => Box::new(self.gui.iter_names()),
1528            _ => match Game::game() {
1529                #[cfg(feature = "ck3")]
1530                Game::Ck3 => self.iter_keys_ck3(itype),
1531                #[cfg(feature = "vic3")]
1532                Game::Vic3 => self.iter_keys_vic3(itype),
1533                #[cfg(feature = "imperator")]
1534                Game::Imperator => self.iter_keys_imperator(itype),
1535                #[cfg(feature = "eu5")]
1536                Game::Eu5 => self.iter_keys_eu5(itype),
1537                #[cfg(feature = "hoi4")]
1538                Game::Hoi4 => self.iter_keys_hoi4(itype),
1539            },
1540        }
1541    }
1542
1543    #[cfg(feature = "jomini")]
1544    fn valid_sound(&self, name: &str) -> bool {
1545        // TODO: verify that file:/ values work
1546        if let Some(filename) = name.strip_prefix("file:/") {
1547            self.fileset.exists(filename)
1548        } else {
1549            let sounds_set = match Game::game() {
1550                #[cfg(feature = "ck3")]
1551                Game::Ck3 => &crate::ck3::tables::sounds::SOUNDS_SET,
1552                #[cfg(feature = "vic3")]
1553                Game::Vic3 => &crate::vic3::tables::sounds::SOUNDS_SET,
1554                #[cfg(feature = "imperator")]
1555                Game::Imperator => &crate::imperator::tables::sounds::SOUNDS_SET,
1556                #[cfg(feature = "eu5")]
1557                Game::Eu5 => &crate::eu5::tables::sounds::SOUNDS_SET,
1558                #[cfg(feature = "hoi4")]
1559                Game::Hoi4 => unimplemented!(),
1560            };
1561            sounds_set.contains(&Lowercase::new(name))
1562        }
1563    }
1564
1565    /// Return true iff a script value of the given name is defined.
1566    #[allow(clippy::unused_self)]
1567    #[allow(unused_variables)] // hoi4 does not use `name`
1568    pub(crate) fn script_value_exists(&self, name: &str) -> bool {
1569        if Game::is_jomini() {
1570            #[cfg(feature = "jomini")]
1571            return self.script_values.exists(name);
1572        }
1573        false
1574    }
1575
1576    pub(crate) fn event_check_scope(&self, id: &Token, sc: &mut ScopeContext) {
1577        if Game::is_hoi4() {
1578            #[cfg(feature = "hoi4")]
1579            self.events_hoi4.check_scope(id, sc, self);
1580        } else {
1581            #[cfg(feature = "jomini")]
1582            self.events.check_scope(id, sc, self);
1583        }
1584    }
1585
1586    pub(crate) fn event_validate_call(&self, id: &Token, sc: &mut ScopeContext) {
1587        if Game::is_hoi4() {
1588            #[cfg(feature = "hoi4")]
1589            self.events_hoi4.validate_call(id, self, sc);
1590        } else {
1591            #[cfg(feature = "jomini")]
1592            self.events.validate_call(id, self, sc);
1593        }
1594    }
1595}
1596
1597impl Drop for Everything {
1598    fn drop(&mut self) {
1599        // For the sake of the benchmark code, restore MACRO_MAP to a clean slate
1600        MACRO_MAP.clear();
1601    }
1602}
1603
1604#[cfg(feature = "internal_benches")]
1605#[divan::bench_group(sample_count = 10)]
1606mod benchmark {
1607    use super::*;
1608    use crate::benches;
1609    use divan::{self, Bencher};
1610
1611    #[cfg(feature = "ck3")]
1612    #[divan::bench(args = benches::ck3::bench_mods())]
1613    fn load_provinces_ck3(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1614        bencher
1615            .with_inputs(|| {
1616                Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1617                    .unwrap()
1618            })
1619            .bench_local_refs(|everything| {
1620                everything.fileset.handle(&mut everything.provinces_ck3, &everything.parser);
1621            });
1622    }
1623
1624    #[cfg(feature = "vic3")]
1625    #[divan::bench(args = benches::vic3::bench_mods())]
1626    fn load_provinces_vic3(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1627        bencher
1628            .with_inputs(|| {
1629                Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1630                    .unwrap()
1631            })
1632            .bench_local_refs(|everything| {
1633                everything.fileset.handle(&mut everything.provinces_vic3, &everything.parser);
1634            });
1635    }
1636
1637    #[divan::bench(args = benches::bench_mods())]
1638    fn load_localization(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1639        bencher
1640            .with_inputs(|| {
1641                Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1642                    .unwrap()
1643            })
1644            .bench_local_refs(|everything| {
1645                everything.fileset.handle(&mut everything.localization, &everything.parser);
1646            });
1647    }
1648}