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