1use 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;
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;
75use crate::parse::ParserMemory;
76#[cfg(feature = "vic3")]
77use crate::parse::json::parse_json_file;
78use crate::pdxfile::PdxFile;
79#[cfg(any(feature = "ck3", feature = "vic3"))]
80use crate::report::err;
81use crate::report::{ErrorKey, OutputStyle, Severity, report, set_output_style};
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#[derive(Debug)]
114pub struct Everything {
115 config: Block,
117
118 pub parser: ParserMemory,
122
123 #[cfg(any(feature = "ck3", feature = "vic3"))]
126 warned_defines: RwLock<TigerHashSet<String>>,
127
128 pub(crate) fileset: Fileset,
130
131 pub(crate) dds: DdsFiles,
133
134 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
226macro_rules! load_all_generic {
227 ($s: ident, $t: ident) => {
228 $s.spawn(|_| $t.fileset.handle(&mut $t.dds, &$t.parser));
229 $s.spawn(|_| $t.fileset.handle(&mut $t.defines, &$t.parser));
230 $s.spawn(|_| $t.fileset.handle(&mut $t.triggers, &$t.parser));
231 $s.spawn(|_| $t.fileset.handle(&mut $t.effects, &$t.parser));
232 $s.spawn(|_| $t.fileset.handle(&mut $t.assets, &$t.parser));
233 $s.spawn(|_| $t.fileset.handle(&mut $t.gui, &$t.parser));
234 $s.spawn(|_| $t.fileset.handle(&mut $t.on_actions, &$t.parser));
235 };
236}
237
238#[cfg(feature = "ck3")]
239macro_rules! load_all_ck3 {
240 ($s: ident, $t: ident) => {
241 $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
242 $s.spawn(|_| $t.fileset.handle(&mut $t.interaction_cats, &$t.parser));
243 $s.spawn(|_| $t.fileset.handle(&mut $t.province_histories, &$t.parser));
244 $s.spawn(|_| $t.fileset.handle(&mut $t.province_properties, &$t.parser));
245 $s.spawn(|_| $t.fileset.handle(&mut $t.province_terrains, &$t.parser));
246 $s.spawn(|_| $t.fileset.handle(&mut $t.gameconcepts, &$t.parser));
247 $s.spawn(|_| $t.fileset.handle(&mut $t.titles, &$t.parser));
248 $s.spawn(|_| $t.fileset.handle(&mut $t.characters, &$t.parser));
249 $s.spawn(|_| $t.fileset.handle(&mut $t.traits, &$t.parser));
250 $s.spawn(|_| $t.fileset.handle(&mut $t.title_history, &$t.parser));
251 $s.spawn(|_| $t.fileset.handle(&mut $t.doctrines, &$t.parser));
252 $s.spawn(|_| $t.fileset.handle(&mut $t.menatarmstypes, &$t.parser));
253 $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
254 $s.spawn(|_| $t.fileset.handle(&mut $t.data_bindings, &$t.parser));
255 $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_ck3, &$t.parser));
256 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
257 $s.spawn(|_| $t.fileset.handle(&mut $t.wars, &$t.parser));
258 $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
259 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
260 $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
261 $s.spawn(|_| crate::ck3::data::buildings::Building::finalize(&mut $t.database));
262 };
263}
264
265#[cfg(feature = "vic3")]
266macro_rules! load_all_vic3 {
267 ($s: ident, $t: ident) => {
268 $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
269 $s.spawn(|_| $t.fileset.handle(&mut $t.history, &$t.parser));
270 $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_vic3, &$t.parser));
271 $s.spawn(|_| $t.fileset.handle(&mut $t.data_bindings, &$t.parser));
272 $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
273 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
274 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
275 $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
276 $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
277 $s.spawn(|_| {
278 Everything::load_json(
279 &$t.fileset,
280 &mut $t.database,
281 Item::TerrainMask,
282 TerrainMask::add_json,
283 );
284 });
285 };
286}
287
288#[cfg(feature = "imperator")]
289macro_rules! load_all_imperator {
290 ($s: ident, $t: ident) => {
291 $s.spawn(|_| $t.fileset.handle(&mut $t.events, &$t.parser));
292 $s.spawn(|_| $t.fileset.handle(&mut $t.decisions_imperator, &$t.parser));
293 $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_imperator, &$t.parser));
294 $s.spawn(|_| $t.fileset.handle(&mut $t.coas, &$t.parser));
295 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_lists, &$t.parser));
296 $s.spawn(|_| $t.fileset.handle(&mut $t.scripted_modifiers, &$t.parser));
297 $s.spawn(|_| $t.fileset.handle(&mut $t.script_values, &$t.parser));
298 $s.spawn(|_| $t.fileset.handle(&mut $t.music, &$t.parser));
299 };
300}
301
302#[cfg(feature = "hoi4")]
303macro_rules! load_all_hoi4 {
304 ($s: ident, $t: ident) => {
305 $s.spawn(|_| $t.fileset.handle(&mut $t.events_hoi4, &$t.parser));
306 $s.spawn(|_| $t.fileset.handle(&mut $t.gfx, &$t.parser));
307 $s.spawn(|_| $t.fileset.handle(&mut $t.provinces_hoi4, &$t.parser));
308 $s.spawn(|_| $t.fileset.handle(&mut $t.music_hoi4, &$t.parser));
309 };
310}
311
312macro_rules! scan_all_generic {
313 ($s: ident) => {
314 $s.triggers.scan_variables(&mut $s.variables);
315 $s.effects.scan_variables(&mut $s.variables);
316 $s.on_actions.scan_variables(&mut $s.variables);
317 };
318}
319
320#[cfg(feature = "ck3")]
321macro_rules! scan_all_ck3 {
322 ($s: ident) => {
323 $s.events.scan_variables(&mut $s.variables);
324 $s.interaction_cats.scan_variables(&mut $s.variables);
325 $s.province_histories.scan_variables(&mut $s.variables);
326 $s.titles.scan_variables(&mut $s.variables);
327 $s.characters.scan_variables(&mut $s.variables);
328 $s.traits.scan_variables(&mut $s.variables);
329 $s.title_history.scan_variables(&mut $s.variables);
330 $s.doctrines.scan_variables(&mut $s.variables);
331 $s.menatarmstypes.scan_variables(&mut $s.variables);
332 $s.music.scan_variables(&mut $s.variables);
333 $s.scripted_lists.scan_variables(&mut $s.variables);
334 $s.coas.scan_variables(&mut $s.variables);
335 $s.scripted_modifiers.scan_variables(&mut $s.variables);
336 $s.script_values.scan_variables(&mut $s.variables);
337 };
338}
339
340#[cfg(feature = "vic3")]
341macro_rules! scan_all_vic3 {
342 ($s: ident) => {
343 $s.events.scan_variables(&mut $s.variables);
344 $s.history.scan_variables(&mut $s.variables);
345 $s.coas.scan_variables(&mut $s.variables);
346 $s.scripted_lists.scan_variables(&mut $s.variables);
347 $s.scripted_modifiers.scan_variables(&mut $s.variables);
348 $s.script_values.scan_variables(&mut $s.variables);
349 $s.music.scan_variables(&mut $s.variables);
350 };
351}
352
353#[cfg(feature = "imperator")]
354macro_rules! scan_all_imperator {
355 ($s: ident) => {
356 $s.events.scan_variables(&mut $s.variables);
357 $s.decisions_imperator.scan_variables(&mut $s.variables);
358 $s.coas.scan_variables(&mut $s.variables);
359 $s.scripted_lists.scan_variables(&mut $s.variables);
360 $s.scripted_modifiers.scan_variables(&mut $s.variables);
361 $s.script_values.scan_variables(&mut $s.variables);
362 $s.music.scan_variables(&mut $s.variables);
363 };
364}
365
366#[cfg(feature = "hoi4")]
367macro_rules! scan_all_hoi4 {
368 ($s: ident) => {
369 $s.events_hoi4.scan_variables(&mut $s.variables);
370 $s.music_hoi4.scan_variables(&mut $s.variables);
371 };
372}
373
374impl Everything {
375 pub fn new(
385 config_filepath: Option<&Path>,
386 vanilla_dir: Option<&Path>,
387 workshop_dir: Option<&Path>,
388 paradox_dir: Option<&Path>,
389 mod_root: &Path,
390 replace_paths: Vec<PathBuf>,
391 ) -> Result<Self> {
392 let mut fileset = Fileset::new(vanilla_dir, mod_root.to_path_buf(), replace_paths);
393
394 let config_file_name = match Game::game() {
395 #[cfg(feature = "ck3")]
396 Game::Ck3 => "ck3-tiger.conf",
397 #[cfg(feature = "vic3")]
398 Game::Vic3 => "vic3-tiger.conf",
399 #[cfg(feature = "imperator")]
400 Game::Imperator => "imperator-tiger.conf",
401 #[cfg(feature = "hoi4")]
402 Game::Hoi4 => "hoi4-tiger.conf",
403 };
404
405 let config_file = match config_filepath {
406 Some(path) => path.to_path_buf(),
407 None => mod_root.join(config_file_name),
408 };
409
410 let config = if config_file.is_file() {
411 Self::read_config(config_file_name, &config_file)
412 .ok_or(FilesError::ConfigUnreadable { path: config_file })?
413 } else {
414 Block::new(Loc::for_file(config_file.clone(), FileKind::Mod, config_file.clone()))
415 };
416
417 fileset.config(config.clone(), workshop_dir, paradox_dir)?;
418
419 fileset.scan_all()?;
420 fileset.finalize();
421
422 Ok(Everything {
423 parser: ParserMemory::default(),
424 fileset,
425 dds: DdsFiles::default(),
426 config,
427 #[cfg(any(feature = "ck3", feature = "vic3"))]
428 warned_defines: RwLock::new(TigerHashSet::default()),
429 database: Db::default(),
430 localization: Localization::default(),
431 #[cfg(feature = "jomini")]
432 scripted_lists: ScriptedLists::default(),
433 defines: Defines::default(),
434 #[cfg(feature = "jomini")]
435 events: Events::default(),
436 #[cfg(feature = "hoi4")]
437 events_hoi4: Hoi4Events::default(),
438 #[cfg(feature = "imperator")]
439 decisions_imperator: Decisions::default(),
440 #[cfg(feature = "jomini")]
441 scripted_modifiers: ScriptedModifiers::default(),
442 on_actions: OnActions::default(),
443 #[cfg(feature = "ck3")]
444 interaction_cats: CharacterInteractionCategories::default(),
445 #[cfg(feature = "ck3")]
446 provinces_ck3: Ck3Provinces::default(),
447 #[cfg(feature = "vic3")]
448 provinces_vic3: Vic3Provinces::default(),
449 #[cfg(feature = "imperator")]
450 provinces_imperator: ImperatorProvinces::default(),
451 #[cfg(feature = "hoi4")]
452 provinces_hoi4: Hoi4Provinces::default(),
453 #[cfg(feature = "ck3")]
454 province_histories: ProvinceHistories::default(),
455 #[cfg(feature = "ck3")]
456 province_properties: ProvinceProperties::default(),
457 #[cfg(feature = "ck3")]
458 province_terrains: ProvinceTerrains::default(),
459 #[cfg(feature = "ck3")]
460 gameconcepts: GameConcepts::default(),
461 #[cfg(feature = "ck3")]
462 titles: Titles::default(),
463 #[cfg(feature = "ck3")]
464 characters: Characters::default(),
465 #[cfg(feature = "jomini")]
466 script_values: ScriptValues::default(),
467 triggers: Triggers::default(),
468 effects: Effects::default(),
469 #[cfg(feature = "ck3")]
470 traits: Traits::default(),
471 #[cfg(feature = "ck3")]
472 title_history: TitleHistories::default(),
473 #[cfg(feature = "ck3")]
474 doctrines: Doctrines::default(),
475 #[cfg(feature = "ck3")]
476 menatarmstypes: MenAtArmsTypes::default(),
477 gui: Gui::default(),
478 #[cfg(any(feature = "ck3", feature = "vic3"))]
479 data_bindings: DataBindings::default(),
480 #[cfg(feature = "hoi4")]
481 gfx: Gfx::default(),
482 assets: Assets::default(),
483 #[cfg(feature = "hoi4")]
484 music_hoi4: Hoi4Musics::default(),
485 #[cfg(feature = "jomini")]
486 music: Musics::default(),
487 #[cfg(feature = "jomini")]
488 coas: Coas::default(),
489 #[cfg(feature = "vic3")]
490 history: History::default(),
491 #[cfg(feature = "ck3")]
492 wars: Wars::default(),
493 variables: Variables::new(),
494 })
495 }
496
497 fn read_config(name: &str, path: &Path) -> Option<Block> {
498 let entry = FileEntry::new(PathBuf::from(name), FileKind::Mod, path.to_path_buf());
499 PdxFile::read_optional_bom(&entry, &ParserMemory::default())
500 }
501
502 pub fn load_config_filtering_rules(&self) {
503 check_for_legacy_ignore(&self.config);
504 load_filter(&self.config);
505 }
506
507 fn load_output_styles(&self, default_color: bool) -> OutputStyle {
511 let block = match self.config.get_field_block("output_style") {
513 Some(block) => Cow::Borrowed(block),
514 None => Cow::Owned(Block::new(self.config.loc)),
515 };
516 if !block.get_field_bool("enable").unwrap_or(default_color) {
517 return OutputStyle::no_color();
518 }
519 let mut style = OutputStyle::default();
520 for severity in Severity::iter() {
521 if let Some(error_block) =
522 block.get_field_block(format!("{severity}").to_ascii_lowercase().as_str())
523 {
524 if let Some(color) = error_block.get_field_value("color") {
525 style.set(severity, color.as_str());
526 }
527 }
528 }
529 style
530 }
531
532 pub fn load_output_settings(&self, default_colors: bool) {
533 set_output_style(self.load_output_styles(default_colors));
534 }
535
536 #[cfg(feature = "vic3")]
537 fn load_json<F>(fileset: &Fileset, db: &mut Db, itype: Item, add_json: F)
538 where
539 F: Fn(&mut Db, Block) + Sync + Send,
540 {
541 for block in fileset.filter_map_under(&PathBuf::from(itype.path()), |entry| {
542 if entry.filename().to_string_lossy().ends_with(".json") {
543 parse_json_file(entry)
544 } else {
545 None
546 }
547 }) {
548 add_json(db, block);
549 }
550 }
551
552 #[cfg(feature = "ck3")]
553 fn load_reader_export(&mut self) {
554 let path = PathBuf::from("reader_export");
555 for entry in self.fileset.get_files_under(&path) {
556 if entry.filename().to_string_lossy().ends_with(".txt") {
557 PdxFile::reader_export(entry, &mut self.parser.pdxfile);
558 }
559 }
560 }
561
562 fn load_pdx_files(&mut self, loader: &ItemLoader) {
563 let path = PathBuf::from(loader.itype().path());
564 let recursive = loader.recursive();
565 let expect_count = path.components().count() + 1;
566 for mut block in self.fileset.filter_map_under(&path, |entry| {
567 if (recursive || entry.path().components().count() <= expect_count)
569 && entry.filename().to_string_lossy().ends_with(loader.extension())
570 {
571 PdxFile::read_encoded(entry, loader.encoding(), &self.parser)
572 } else {
573 None
574 }
575 }) {
576 if loader.whole_file() {
577 let fname = block.loc.filename();
578 let key = fname.strip_suffix(loader.extension()).unwrap();
580 let key = Token::new(key, block.loc);
581 (loader.adder())(&mut self.database, key, block);
582 } else {
583 for (key, block) in block.drain_definitions_warn() {
584 (loader.adder())(&mut self.database, key, block);
585 }
586 }
587 }
588 }
589
590 fn load_all_normal_pdx_files(&mut self) {
591 for loader in inventory::iter::<ItemLoader> {
592 if loader.for_game(Game::game()) {
593 self.load_pdx_files(loader);
594 }
595 }
596 }
597
598 pub fn load_all(&mut self) {
599 #[cfg(feature = "ck3")]
600 self.load_reader_export();
601 self.load_all_normal_pdx_files();
602
603 std::thread::scope(|s| {
604 s.spawn(|| self.fileset.handle(&mut self.localization, &self.parser));
605
606 scope(|s| {
607 load_all_generic!(s, self);
608 match Game::game() {
609 #[cfg(feature = "ck3")]
610 Game::Ck3 => {
611 load_all_ck3!(s, self);
612 }
613 #[cfg(feature = "vic3")]
614 Game::Vic3 => {
615 load_all_vic3!(s, self);
616 }
617 #[cfg(feature = "imperator")]
618 Game::Imperator => {
619 load_all_imperator!(s, self);
620 }
621 #[cfg(feature = "hoi4")]
622 Game::Hoi4 => {
623 load_all_hoi4!(s, self);
624 }
625 }
626 });
627
628 self.database.add_subitems();
629 scan_all_generic!(self);
630 match Game::game() {
631 #[cfg(feature = "ck3")]
632 Game::Ck3 => {
633 scan_all_ck3!(self);
634 }
635 #[cfg(feature = "vic3")]
636 Game::Vic3 => {
637 scan_all_vic3!(self);
638 }
639 #[cfg(feature = "imperator")]
640 Game::Imperator => {
641 scan_all_imperator!(self);
642 }
643 #[cfg(feature = "hoi4")]
644 Game::Hoi4 => {
645 scan_all_hoi4!(self);
646 }
647 }
648 self.database.scan_variables(&mut self.variables);
649 });
650 }
651
652 fn validate_all_generic<'a>(&'a self, s: &Scope<'a>) {
653 s.spawn(|_| self.fileset.validate(self));
654 s.spawn(|_| self.defines.validate(self));
655 s.spawn(|_| self.triggers.validate(self));
656 s.spawn(|_| self.effects.validate(self));
657 s.spawn(|_| self.assets.validate(self));
658 s.spawn(|_| self.gui.validate(self));
659 s.spawn(|_| self.on_actions.validate(self));
660 s.spawn(|_| self.dds.validate());
661 }
662
663 #[cfg(feature = "ck3")]
664 fn validate_all_ck3<'a>(&'a self, s: &Scope<'a>) {
665 s.spawn(|_| self.events.validate(self));
666 s.spawn(|_| self.interaction_cats.validate(self));
667 s.spawn(|_| self.province_histories.validate(self));
668 s.spawn(|_| self.province_properties.validate(self));
669 s.spawn(|_| self.province_terrains.validate(self));
670 s.spawn(|_| self.gameconcepts.validate(self));
671 s.spawn(|_| self.titles.validate(self));
672 s.spawn(|_| self.characters.validate(self));
673 s.spawn(|_| self.traits.validate(self));
674 s.spawn(|_| self.title_history.validate(self));
675 s.spawn(|_| self.doctrines.validate(self));
676 s.spawn(|_| self.menatarmstypes.validate(self));
677 s.spawn(|_| self.data_bindings.validate(self));
678 s.spawn(|_| self.provinces_ck3.validate(self));
679 s.spawn(|_| self.wars.validate(self));
680 s.spawn(|_| self.coas.validate(self));
681 s.spawn(|_| self.scripted_lists.validate(self));
682 s.spawn(|_| self.scripted_modifiers.validate(self));
683 s.spawn(|_| self.script_values.validate(self));
684 s.spawn(|_| self.music.validate(self));
685 s.spawn(|_| Climate::validate_all(&self.database, self));
686 }
687
688 #[cfg(feature = "vic3")]
689 fn validate_all_vic3<'a>(&'a self, s: &Scope<'a>) {
690 if std::env::var("TIGER_CHECK_MODIFS").is_ok() {
691 for line in std::io::stdin().lines().map_while(Result::ok) {
692 if !line.starts_with(' ') {
693 if let Some(name) = line.strip_suffix(":") {
694 eprintln!("checking modif {name}");
695 let loc: Loc =
696 FileEntry::new("stdin".into(), FileKind::Vanilla, "stdin".into())
697 .into();
698 let name_token = Token::new(name, loc);
699 crate::vic3::tables::modifs::lookup_engine_modif(
700 &name_token,
701 &Lowercase::new(name),
702 self,
703 Some(Severity::Error),
704 );
705 }
706 }
707 }
708 }
709 s.spawn(|_| self.events.validate(self));
710 s.spawn(|_| self.history.validate(self));
711 s.spawn(|_| self.provinces_vic3.validate(self));
712 s.spawn(|_| self.data_bindings.validate(self));
713 s.spawn(|_| self.coas.validate(self));
714 s.spawn(|_| self.scripted_lists.validate(self));
715 s.spawn(|_| self.scripted_modifiers.validate(self));
716 s.spawn(|_| self.script_values.validate(self));
717 s.spawn(|_| self.music.validate(self));
718 s.spawn(|_| StrategicRegion::crosscheck(self));
719 s.spawn(|_| BuyPackage::crosscheck(self));
720 }
721
722 #[cfg(feature = "imperator")]
723 fn validate_all_imperator<'a>(&'a self, s: &Scope<'a>) {
724 s.spawn(|_| self.events.validate(self));
725 s.spawn(|_| self.decisions_imperator.validate(self));
726 s.spawn(|_| self.provinces_imperator.validate(self));
727 s.spawn(|_| self.coas.validate(self));
728 s.spawn(|_| self.scripted_lists.validate(self));
729 s.spawn(|_| self.scripted_modifiers.validate(self));
730 s.spawn(|_| self.script_values.validate(self));
731 s.spawn(|_| self.music.validate(self));
732 }
733
734 #[cfg(feature = "hoi4")]
735 fn validate_all_hoi4<'a>(&'a self, s: &Scope<'a>) {
736 s.spawn(|_| self.events_hoi4.validate(self));
737 s.spawn(|_| self.provinces_hoi4.validate(self));
738 s.spawn(|_| self.gfx.validate(self));
739 s.spawn(|_| self.music_hoi4.validate(self));
740 }
741
742 pub fn validate_all(&self) {
743 scope(|s| {
744 self.validate_all_generic(s);
745 match Game::game() {
746 #[cfg(feature = "ck3")]
747 Game::Ck3 => self.validate_all_ck3(s),
748 #[cfg(feature = "vic3")]
749 Game::Vic3 => self.validate_all_vic3(s),
750 #[cfg(feature = "imperator")]
751 Game::Imperator => self.validate_all_imperator(s),
752 #[cfg(feature = "hoi4")]
753 Game::Hoi4 => self.validate_all_hoi4(s),
754 }
755 s.spawn(|_| self.database.validate(self));
756 });
757 self.localization.validate_pass2(self);
758 }
759
760 pub fn check_rivers(&mut self) {
761 let mut rivers = Rivers::default();
762 self.fileset.handle(&mut rivers, &self.parser);
763 rivers.validate(self);
764 }
765
766 #[cfg(feature = "ck3")]
767 pub fn check_pod(&mut self) {
768 self.province_histories.check_pod_faiths(self, &self.titles);
769 self.characters.check_pod_flags(self);
770 self.localization.check_pod_loca(self);
771 }
772
773 pub fn check_unused(&mut self) {
774 self.localization.check_unused(self);
775 self.fileset.check_unused_dds(self);
776 }
777
778 #[allow(dead_code)]
779 pub(crate) fn item_has_property(&self, itype: Item, key: &str, property: &str) -> bool {
780 self.database.has_property(itype, key, property, self)
781 }
782
783 #[cfg(feature = "ck3")] pub(crate) fn item_lc_has_property(
785 &self,
786 itype: Item,
787 key: &Lowercase,
788 property: &str,
789 ) -> bool {
790 self.database.lc_has_property(itype, key, property, self)
791 }
792
793 #[cfg(feature = "ck3")]
794 fn item_exists_ck3(&self, itype: Item, key: &str) -> bool {
795 match itype {
796 Item::ActivityState => ACTIVITY_STATES.contains(&key),
797 Item::ArtifactHistory => ARTIFACT_HISTORY.contains(&key),
798 Item::ArtifactRarity => ARTIFACT_RARITIES.contains(&&*key.to_ascii_lowercase()),
799 Item::Character => self.characters.exists(key),
800 Item::CharacterInteractionCategory => self.interaction_cats.exists(key),
801 Item::Coa => self.coas.exists(key),
802 Item::CoaTemplate => self.coas.template_exists(key),
803 Item::DangerType => DANGER_TYPES.contains(&key),
804 Item::DlcFeature => DLC_FEATURES_CK3.contains(&key),
805 Item::Doctrine => self.doctrines.exists(key),
806 Item::DoctrineBooleanParameter => self.doctrines.boolean_parameter_exists(key),
807 Item::DoctrineCategory => self.doctrines.category_exists(key),
808 Item::DoctrineParameter => self.doctrines.parameter_exists(key),
809 Item::Event => self.events.exists(key),
810 Item::EventNamespace => self.events.namespace_exists(key),
811 Item::GameConcept => self.gameconcepts.exists(key),
812 Item::GeneAttribute => self.assets.attribute_exists(key),
813 Item::GeneticConstraint => self.traits.constraint_exists(key),
814 Item::MenAtArms => self.menatarmstypes.exists(key),
815 Item::MenAtArmsBase => self.menatarmstypes.base_exists(key),
816 Item::Music => self.music.exists(key),
817 Item::PrisonType => PRISON_TYPES.contains(&key),
818 Item::Province => self.provinces_ck3.exists(key),
819 Item::RewardItem => REWARD_ITEMS.contains(&key),
820 Item::ScriptedList => self.scripted_lists.exists(key),
821 Item::ScriptedModifier => self.scripted_modifiers.exists(key),
822 Item::ScriptValue => self.script_values.exists(key),
823 Item::Sexuality => SEXUALITIES.contains(&key),
824 Item::Skill => SKILLS.contains(&key),
825 Item::Sound => self.valid_sound(key),
826 Item::Title => self.titles.exists(key),
827 Item::TitleHistory => self.title_history.exists(key),
828 Item::Trait => self.traits.exists(key),
829 Item::TraitFlag => self.traits.flag_exists(key),
830 Item::TraitTrack => self.traits.track_exists(key),
831 Item::TraitCategory => TRAIT_CATEGORIES.contains(&key),
832 _ => self.database.exists(itype, key),
833 }
834 }
835
836 #[cfg(feature = "vic3")]
837 fn item_exists_vic3(&self, itype: Item, key: &str) -> bool {
838 match itype {
839 Item::Approval => APPROVALS.contains(&key),
840 Item::Attitude => ATTITUDES.contains(&&*key.to_lowercase()),
841 Item::CharacterRole => CHARACTER_ROLES.contains(&key),
842 Item::Coa => self.coas.exists(key),
843 Item::CoaTemplate => self.coas.template_exists(key),
844 Item::CountryTier => COUNTRY_TIERS.contains(&key),
845 Item::DlcFeature => DLC_FEATURES_VIC3.contains(&key),
846 Item::Event => self.events.exists(key),
847 Item::EventCategory => EVENT_CATEGORIES.contains(&key),
848 Item::EventNamespace => self.events.namespace_exists(key),
849 Item::GeneAttribute => self.assets.attribute_exists(key),
850 Item::InfamyThreshold => INFAMY_THRESHOLDS.contains(&key),
851 Item::Level => LEVELS.contains(&key),
852 Item::Music => self.music.exists(key),
853 Item::RelationsThreshold => RELATIONS.contains(&key),
854 Item::ScriptedList => self.scripted_lists.exists(key),
855 Item::ScriptedModifier => self.scripted_modifiers.exists(key),
856 Item::ScriptValue => self.script_values.exists(key),
857 Item::SecretGoal => SECRET_GOALS.contains(&key),
858 Item::Sound => self.valid_sound(key),
859 Item::Strata => STRATA.contains(&key),
860 Item::TerrainKey => TERRAIN_KEYS.contains(&key),
861 Item::TransferOfPower => TRANSFER_OF_POWER.contains(&key),
862 _ => self.database.exists(itype, key),
863 }
864 }
865
866 #[cfg(feature = "imperator")]
867 fn item_exists_imperator(&self, itype: Item, key: &str) -> bool {
868 match itype {
869 Item::Coa => self.coas.exists(key),
870 Item::CoaTemplate => self.coas.template_exists(key),
871 Item::DlcName => DLC_NAME_IMPERATOR.contains(&key),
872 Item::Decision => self.decisions_imperator.exists(key),
873 Item::Event => self.events.exists(key),
874 Item::EventNamespace => self.events.namespace_exists(key),
875 Item::GeneAttribute => self.assets.attribute_exists(key),
876 Item::Music => self.music.exists(key),
877 Item::Province => self.provinces_imperator.exists(key),
878 Item::ScriptedList => self.scripted_lists.exists(key),
879 Item::ScriptedModifier => self.scripted_modifiers.exists(key),
880 Item::ScriptValue => self.script_values.exists(key),
881 Item::Sound => self.valid_sound(key),
882 _ => self.database.exists(itype, key),
883 }
884 }
885
886 #[cfg(feature = "hoi4")]
887 fn item_exists_hoi4(&self, itype: Item, key: &str) -> bool {
888 match itype {
889 Item::AiStrategyType => AI_STRATEGY_TYPES.contains(&key),
890 Item::Event => self.events_hoi4.exists(key),
891 Item::EventNamespace => self.events_hoi4.namespace_exists(key),
892 Item::Music => self.music_hoi4.exists(key),
893 Item::MusicAsset => self.assets.music_exists(key),
894 Item::Pdxmesh => self.gfx.mesh_exists(key),
895 Item::Province => self.provinces_hoi4.exists(key),
896 Item::Sprite => self.gfx.sprite_exists(key),
897 _ => self.database.exists(itype, key),
898 }
899 }
900
901 pub(crate) fn item_exists(&self, itype: Item, key: &str) -> bool {
902 match itype {
903 Item::Asset => self.assets.asset_exists(key),
904 Item::BlendShape => self.assets.blend_shape_exists(key),
905 Item::Define => self.defines.exists(key),
906 Item::Entity => self.assets.entity_exists(key),
907 Item::Entry => self.fileset.entry_exists(key),
908 Item::File => self.fileset.exists(key),
909 Item::GuiLayer => self.gui.layer_exists(key),
910 Item::GuiTemplate => self.gui.template_exists(key),
911 Item::GuiType => self.gui.type_exists(&Lowercase::new(key)),
912 Item::Localization => self.localization.exists(key),
913 Item::OnAction => self.on_actions.exists(key),
914 #[cfg(feature = "jomini")]
915 Item::Pdxmesh => self.assets.mesh_exists(key),
916 Item::ScriptedEffect => self.effects.exists(key),
917 Item::ScriptedTrigger => self.triggers.exists(key),
918 Item::TextFormat => self.gui.textformat_exists(key),
919 Item::TextIcon => self.gui.texticon_exists(key),
920 Item::TextureFile => self.assets.texture_exists(key),
921 Item::WidgetName => self.gui.name_exists(key),
922 Item::Directory | Item::Shortcut => true, _ => match Game::game() {
924 #[cfg(feature = "ck3")]
925 Game::Ck3 => self.item_exists_ck3(itype, key),
926 #[cfg(feature = "vic3")]
927 Game::Vic3 => self.item_exists_vic3(itype, key),
928 #[cfg(feature = "imperator")]
929 Game::Imperator => self.item_exists_imperator(itype, key),
930 #[cfg(feature = "hoi4")]
931 Game::Hoi4 => self.item_exists_hoi4(itype, key),
932 },
933 }
934 }
935
936 #[cfg(feature = "ck3")]
940 fn item_exists_lc_ck3(&self, itype: Item, key: &Lowercase) -> bool {
941 match itype {
942 Item::MenAtArmsBase => self.menatarmstypes.base_exists_lc(key),
943 Item::Trait => self.traits.exists_lc(key),
944 Item::TraitTrack => self.traits.track_exists_lc(key),
945 _ => self.database.exists_lc(itype, key),
946 }
947 }
948
949 #[cfg(feature = "vic3")]
953 fn item_exists_lc_vic3(&self, itype: Item, key: &Lowercase) -> bool {
954 match itype {
955 Item::TerrainKey => TERRAIN_KEYS.contains(&key.as_str()),
956 _ => self.database.exists_lc(itype, key),
957 }
958 }
959
960 #[cfg(feature = "imperator")]
964 fn item_exists_lc_imperator(&self, itype: Item, key: &Lowercase) -> bool {
965 #[allow(clippy::match_single_binding)]
966 match itype {
967 _ => self.database.exists_lc(itype, key),
968 }
969 }
970
971 #[cfg(feature = "hoi4")]
975 fn item_exists_lc_hoi4(&self, itype: Item, key: &Lowercase) -> bool {
976 #[allow(clippy::match_single_binding)]
977 match itype {
978 Item::EventNamespace => self.events_hoi4.namespace_exists_lc(key),
979 _ => self.database.exists_lc(itype, key),
980 }
981 }
982 pub(crate) fn item_exists_lc(&self, itype: Item, key: &Lowercase) -> bool {
986 #[allow(clippy::match_single_binding)]
987 match itype {
988 _ => match Game::game() {
989 #[cfg(feature = "ck3")]
990 Game::Ck3 => self.item_exists_lc_ck3(itype, key),
991 #[cfg(feature = "vic3")]
992 Game::Vic3 => self.item_exists_lc_vic3(itype, key),
993 #[cfg(feature = "imperator")]
994 Game::Imperator => self.item_exists_lc_imperator(itype, key),
995 #[cfg(feature = "hoi4")]
996 Game::Hoi4 => self.item_exists_lc_hoi4(itype, key),
997 },
998 }
999 }
1000
1001 pub(crate) fn mark_used(&self, itype: Item, key: &str) {
1002 match itype {
1003 Item::File => self.fileset.mark_used(key),
1004 Item::Localization => {
1005 self.localization.mark_used_return_exists(key);
1006 }
1007 _ => (),
1008 }
1009 }
1010
1011 pub(crate) fn verify_exists(&self, itype: Item, token: &Token) {
1012 self.verify_exists_implied(itype, token.as_str(), token);
1013 }
1014
1015 pub(crate) fn verify_exists_max_sev(&self, itype: Item, token: &Token, max_sev: Severity) {
1016 self.verify_exists_implied_max_sev(itype, token.as_str(), token, max_sev);
1017 }
1018
1019 pub(crate) fn verify_exists_implied_max_sev(
1020 &self,
1021 itype: Item,
1022 key: &str,
1023 token: &Token,
1024 max_sev: Severity,
1025 ) {
1026 match itype {
1027 Item::Entry => self.fileset.verify_entry_exists(key, token, max_sev),
1028 Item::File => self.fileset.verify_exists_implied(key, token, max_sev),
1029 Item::Localization => self.localization.verify_exists_implied(key, token, max_sev),
1030 Item::Music => match Game::game() {
1031 #[cfg(feature = "ck3")]
1032 Game::Ck3 => self.music.verify_exists_implied(key, token, max_sev),
1033 #[cfg(feature = "vic3")]
1034 Game::Vic3 => self.music.verify_exists_implied(key, token, max_sev),
1035 #[cfg(feature = "imperator")]
1036 Game::Imperator => self.music.verify_exists_implied(key, token, max_sev),
1037 #[cfg(feature = "hoi4")]
1038 Game::Hoi4 => self.music_hoi4.verify_exists_implied(key, token, max_sev),
1039 },
1040 Item::Province => match Game::game() {
1041 #[cfg(feature = "ck3")]
1042 Game::Ck3 => self.provinces_ck3.verify_exists_implied(key, token, max_sev),
1043 #[cfg(feature = "vic3")]
1044 Game::Vic3 => self.provinces_vic3.verify_exists_implied(key, token, max_sev),
1045 #[cfg(feature = "imperator")]
1046 Game::Imperator => {
1047 self.provinces_imperator.verify_exists_implied(key, token, max_sev);
1048 }
1049 #[cfg(feature = "hoi4")]
1050 Game::Hoi4 => {
1051 self.provinces_hoi4.verify_exists_implied(key, token, max_sev);
1052 }
1053 },
1054 Item::TextureFile => {
1055 if let Some(entry) = self.assets.get_texture(key) {
1056 self.fileset.mark_used(&entry.path().to_string_lossy());
1058 } else {
1059 let msg = format!("no texture file {key} anywhere under {}", itype.path());
1060 report(ErrorKey::MissingFile, itype.severity().at_most(max_sev))
1061 .conf(itype.confidence())
1062 .msg(msg)
1063 .loc(token)
1064 .push();
1065 }
1066 }
1067 _ => {
1068 if !self.item_exists(itype, key) {
1069 let path = itype.path();
1070 let msg = if path.is_empty() {
1071 format!("unknown {itype} {key}")
1072 } else {
1073 format!("{itype} {key} not defined in {path}")
1074 };
1075 report(ErrorKey::MissingItem, itype.severity().at_most(max_sev))
1076 .conf(itype.confidence())
1077 .msg(msg)
1078 .loc(token)
1079 .push();
1080 }
1081 }
1082 }
1083 }
1084
1085 #[allow(dead_code)]
1086 pub(crate) fn verify_exists_implied_max_sev_lc(
1087 &self,
1088 itype: Item,
1089 key: &Lowercase,
1090 token: &Token,
1091 max_sev: Severity,
1092 ) {
1093 if !self.item_exists_lc(itype, key) {
1094 let path = itype.path();
1095 let msg = if path.is_empty() {
1096 format!("unknown {itype} {key}")
1097 } else {
1098 format!("{itype} {key} not defined in {path}")
1099 };
1100 report(ErrorKey::MissingItem, itype.severity().at_most(max_sev))
1101 .conf(itype.confidence())
1102 .msg(msg)
1103 .loc(token)
1104 .push();
1105 }
1106 }
1107
1108 pub(crate) fn verify_exists_implied(&self, itype: Item, key: &str, token: &Token) {
1109 self.verify_exists_implied_max_sev(itype, key, token, Severity::Error);
1110 }
1111
1112 #[cfg(feature = "ck3")]
1113 pub(crate) fn verify_icon(&self, define: &str, token: &Token, suffix: &str) {
1114 if let Some(icon_path) = self.get_defined_string_warn(token, define) {
1115 let pathname = format!("{icon_path}/{token}{suffix}");
1116 self.verify_exists_implied_max_sev(Item::File, &pathname, token, Severity::Warning);
1118 }
1119 }
1120
1121 #[cfg(feature = "ck3")]
1122 pub(crate) fn mark_used_icon(&self, define: &str, token: &Token, suffix: &str) {
1123 if let Some(icon_path) = self.get_defined_string_warn(token, define) {
1124 let pathname = format!("{icon_path}/{token}{suffix}");
1125 self.fileset.mark_used(&pathname);
1126 }
1127 }
1128
1129 #[allow(dead_code)]
1130 pub(crate) fn validate_use(&self, itype: Item, key: &Token, block: &Block) {
1131 self.database.validate_use(itype, key, block, self);
1132 }
1133
1134 #[allow(dead_code)]
1135 pub(crate) fn validate_call(
1136 &self,
1137 itype: Item,
1138 key: &Token,
1139 block: &Block,
1140 sc: &mut ScopeContext,
1141 ) {
1142 self.database.validate_call(itype, key, block, self, sc);
1143 }
1144
1145 pub(crate) fn validate_localization_sc(&self, key: &str, sc: &mut ScopeContext) {
1148 self.localization.validate_use(key, self, sc);
1149 }
1150
1151 #[allow(dead_code)]
1152 pub(crate) fn get_item<T: DbKind>(
1153 &self,
1154 itype: Item,
1155 key: &str,
1156 ) -> Option<(&Token, &Block, &T)> {
1157 self.database.get_item(itype, key)
1158 }
1159
1160 pub(crate) fn get_key_block(&self, itype: Item, key: &str) -> Option<(&Token, &Block)> {
1161 self.database.get_key_block(itype, key)
1162 }
1163
1164 pub(crate) fn get_trigger(&self, key: &Token) -> Option<&Trigger> {
1165 #[cfg(feature = "ck3")]
1166 if Game::is_ck3() {
1167 if let Some(trigger) = self.triggers.get(key.as_str()) {
1168 return Some(trigger);
1169 }
1170 if let Some(trigger) = self.events.get_trigger(key) {
1171 return Some(trigger);
1172 }
1173 return None;
1174 }
1175 self.triggers.get(key.as_str())
1176 }
1177
1178 pub(crate) fn get_effect(&self, key: &Token) -> Option<&Effect> {
1179 #[cfg(feature = "ck3")]
1180 if Game::is_ck3() {
1181 if let Some(effect) = self.effects.get(key.as_str()) {
1182 return Some(effect);
1183 }
1184 if let Some(effect) = self.events.get_effect(key) {
1185 return Some(effect);
1186 }
1187 return None;
1188 }
1189 self.effects.get(key.as_str())
1190 }
1191
1192 #[cfg(feature = "ck3")]
1193 pub(crate) fn get_defined_string(&self, key: &str) -> Option<&Token> {
1194 self.defines.get_bv(key).and_then(BV::get_value)
1195 }
1196
1197 #[cfg(any(feature = "ck3", feature = "vic3"))]
1198 pub(crate) fn get_defined_array(&self, key: &str) -> Option<&Block> {
1199 self.defines.get_bv(key).and_then(BV::get_block)
1200 }
1201
1202 #[allow(clippy::missing_panics_doc)] #[cfg(feature = "ck3")]
1204 pub(crate) fn get_defined_string_warn(&self, token: &Token, key: &str) -> Option<&Token> {
1205 let result = self.get_defined_string(key);
1206 if result.is_none() {
1207 let mut cache = self.warned_defines.write().unwrap();
1208 if !cache.contains(key) {
1209 let msg = format!("{key} not defined in common/defines/");
1210 err(ErrorKey::MissingItem).msg(msg).loc(token).push();
1211 cache.insert(key.to_string());
1212 }
1213 }
1214 result
1215 }
1216
1217 #[allow(clippy::missing_panics_doc)] #[cfg(any(feature = "ck3", feature = "vic3"))]
1219 pub(crate) fn get_defined_array_warn(&self, token: &Token, key: &str) -> Option<&Block> {
1220 let result = self.get_defined_array(key);
1221 if result.is_none() {
1222 let mut cache = self.warned_defines.write().unwrap();
1223 if !cache.contains(key) {
1224 let msg = format!("{key} not defined in common/defines/");
1225 err(ErrorKey::MissingItem).msg(msg).loc(token).push();
1226 cache.insert(key.to_string());
1227 }
1228 }
1229 result
1230 }
1231
1232 #[cfg(feature = "ck3")]
1233 pub fn iter_keys_ck3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1234 match itype {
1235 Item::Coa => Box::new(self.coas.iter_keys()),
1236 Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1237 Item::Character => Box::new(self.characters.iter_keys()),
1238 Item::CharacterInteractionCategory => Box::new(self.interaction_cats.iter_keys()),
1239 Item::Doctrine => Box::new(self.doctrines.iter_keys()),
1240 Item::DoctrineBooleanParameter => {
1241 Box::new(self.doctrines.iter_boolean_parameter_keys())
1242 }
1243 Item::DoctrineCategory => Box::new(self.doctrines.iter_category_keys()),
1244 Item::DoctrineParameter => Box::new(self.doctrines.iter_parameter_keys()),
1245 Item::Event => Box::new(self.events.iter_keys()),
1246 Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1247 Item::GameConcept => Box::new(self.gameconcepts.iter_keys()),
1248 Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1249 Item::GeneticConstraint => Box::new(self.traits.iter_constraint_keys()),
1250 Item::MenAtArms => Box::new(self.menatarmstypes.iter_keys()),
1251 Item::MenAtArmsBase => Box::new(self.menatarmstypes.iter_base_keys()),
1252 Item::Music => Box::new(self.music.iter_keys()),
1253 Item::Province => Box::new(self.provinces_ck3.iter_keys()),
1254 Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1255 Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1256 Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1257 Item::Title => Box::new(self.titles.iter_keys()),
1258 Item::TitleHistory => Box::new(self.title_history.iter_keys()),
1259 Item::Trait => Box::new(self.traits.iter_keys()),
1260 Item::TraitFlag => Box::new(self.traits.iter_flag_keys()),
1261 Item::TraitTrack => Box::new(self.traits.iter_track_keys()),
1262 _ => Box::new(self.database.iter_keys(itype)),
1263 }
1264 }
1265
1266 #[cfg(feature = "vic3")]
1267 fn iter_keys_vic3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1268 match itype {
1269 Item::Coa => Box::new(self.coas.iter_keys()),
1270 Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1271 Item::Event => Box::new(self.events.iter_keys()),
1272 Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1273 Item::Music => Box::new(self.music.iter_keys()),
1274 Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1275 Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1276 Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1277 Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1278 _ => Box::new(self.database.iter_keys(itype)),
1279 }
1280 }
1281
1282 #[cfg(feature = "imperator")]
1283 fn iter_keys_imperator<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1284 match itype {
1285 Item::Coa => Box::new(self.coas.iter_keys()),
1286 Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
1287 Item::Decision => Box::new(self.decisions_imperator.iter_keys()),
1288 Item::Event => Box::new(self.events.iter_keys()),
1289 Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
1290 Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
1291 Item::Music => Box::new(self.music.iter_keys()),
1292 Item::Province => Box::new(self.provinces_imperator.iter_keys()),
1293 Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
1294 Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
1295 Item::ScriptValue => Box::new(self.script_values.iter_keys()),
1296 _ => Box::new(self.database.iter_keys(itype)),
1297 }
1298 }
1299
1300 #[cfg(feature = "hoi4")]
1301 fn iter_keys_hoi4<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1302 match itype {
1303 Item::Event => Box::new(self.events_hoi4.iter_keys()),
1304 Item::EventNamespace => Box::new(self.events_hoi4.iter_namespace_keys()),
1305 Item::Music => Box::new(self.music_hoi4.iter_keys()),
1306 Item::MusicAsset => Box::new(self.assets.iter_music_keys()),
1307 Item::Pdxmesh => Box::new(self.gfx.iter_mesh_keys()),
1308 Item::Province => Box::new(self.provinces_hoi4.iter_keys()),
1309 Item::Sprite => Box::new(self.gfx.iter_sprite_keys()),
1310 _ => Box::new(self.database.iter_keys(itype)),
1311 }
1312 }
1313
1314 pub fn iter_keys<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
1315 match itype {
1316 Item::Asset => Box::new(self.assets.iter_asset_keys()),
1317 Item::BlendShape => Box::new(self.assets.iter_blend_shape_keys()),
1318 Item::Define => Box::new(self.defines.iter_keys()),
1319 Item::Entity => Box::new(self.assets.iter_entity_keys()),
1320 Item::File => Box::new(self.fileset.iter_keys()),
1321 Item::GuiLayer => Box::new(self.gui.iter_layer_keys()),
1322 Item::GuiTemplate => Box::new(self.gui.iter_template_keys()),
1323 Item::GuiType => Box::new(self.gui.iter_type_keys()),
1324 Item::Localization => Box::new(self.localization.iter_keys()),
1325 Item::OnAction => Box::new(self.on_actions.iter_keys()),
1326 #[cfg(feature = "jomini")]
1327 Item::Pdxmesh => Box::new(self.assets.iter_mesh_keys()),
1328 Item::ScriptedEffect => Box::new(self.effects.iter_keys()),
1329 Item::ScriptedTrigger => Box::new(self.triggers.iter_keys()),
1330 Item::TextFormat => Box::new(self.gui.iter_textformat_keys()),
1331 Item::TextIcon => Box::new(self.gui.iter_texticon_keys()),
1332 Item::TextureFile => Box::new(self.assets.iter_texture_keys()),
1333 Item::WidgetName => Box::new(self.gui.iter_names()),
1334 _ => match Game::game() {
1335 #[cfg(feature = "ck3")]
1336 Game::Ck3 => self.iter_keys_ck3(itype),
1337 #[cfg(feature = "vic3")]
1338 Game::Vic3 => self.iter_keys_vic3(itype),
1339 #[cfg(feature = "imperator")]
1340 Game::Imperator => self.iter_keys_imperator(itype),
1341 #[cfg(feature = "hoi4")]
1342 Game::Hoi4 => self.iter_keys_hoi4(itype),
1343 },
1344 }
1345 }
1346
1347 #[cfg(feature = "jomini")]
1348 fn valid_sound(&self, name: &str) -> bool {
1349 if let Some(filename) = name.strip_prefix("file:/") {
1351 self.fileset.exists(filename)
1352 } else {
1353 let sounds_set = match Game::game() {
1354 #[cfg(feature = "ck3")]
1355 Game::Ck3 => &crate::ck3::tables::sounds::SOUNDS_SET,
1356 #[cfg(feature = "vic3")]
1357 Game::Vic3 => &crate::vic3::tables::sounds::SOUNDS_SET,
1358 #[cfg(feature = "imperator")]
1359 Game::Imperator => &crate::imperator::tables::sounds::SOUNDS_SET,
1360 #[cfg(feature = "hoi4")]
1361 Game::Hoi4 => unimplemented!(),
1362 };
1363 sounds_set.contains(&Lowercase::new(name))
1364 }
1365 }
1366
1367 #[allow(clippy::unused_self)]
1369 #[allow(unused_variables)] pub(crate) fn script_value_exists(&self, name: &str) -> bool {
1371 if Game::is_jomini() {
1372 #[cfg(feature = "jomini")]
1373 return self.script_values.exists(name);
1374 }
1375 false
1376 }
1377
1378 pub(crate) fn event_check_scope(&self, id: &Token, sc: &mut ScopeContext) {
1379 if Game::is_hoi4() {
1380 #[cfg(feature = "hoi4")]
1381 self.events_hoi4.check_scope(id, sc);
1382 } else {
1383 #[cfg(feature = "jomini")]
1384 self.events.check_scope(id, sc);
1385 }
1386 }
1387
1388 pub(crate) fn event_validate_call(&self, id: &Token, sc: &mut ScopeContext) {
1389 if Game::is_hoi4() {
1390 #[cfg(feature = "hoi4")]
1391 self.events_hoi4.validate_call(id, self, sc);
1392 } else {
1393 #[cfg(feature = "jomini")]
1394 self.events.validate_call(id, self, sc);
1395 }
1396 }
1397}
1398
1399impl Drop for Everything {
1400 fn drop(&mut self) {
1401 MACRO_MAP.clear();
1403 }
1404}
1405
1406#[cfg(feature = "internal_benches")]
1407#[divan::bench_group(sample_count = 10)]
1408mod benchmark {
1409 use super::*;
1410 use crate::benches;
1411 use divan::{self, Bencher};
1412
1413 #[cfg(feature = "ck3")]
1414 #[divan::bench(args = benches::ck3::bench_mods())]
1415 fn load_provinces_ck3(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1416 bencher
1417 .with_inputs(|| {
1418 Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1419 .unwrap()
1420 })
1421 .bench_local_refs(|everything| {
1422 everything.fileset.handle(&mut everything.provinces_ck3, &everything.parser);
1423 });
1424 }
1425
1426 #[cfg(feature = "vic3")]
1427 #[divan::bench(args = benches::vic3::bench_mods())]
1428 fn load_provinces_vic3(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1429 bencher
1430 .with_inputs(|| {
1431 Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1432 .unwrap()
1433 })
1434 .bench_local_refs(|everything| {
1435 everything.fileset.handle(&mut everything.provinces_vic3, &everything.parser);
1436 });
1437 }
1438
1439 #[divan::bench(args = benches::bench_mods())]
1440 fn load_localization(bencher: Bencher, (vanilla_dir, modpath): (&str, &PathBuf)) {
1441 bencher
1442 .with_inputs(|| {
1443 Everything::new(None, Some(Path::new(vanilla_dir)), None, None, modpath, vec![])
1444 .unwrap()
1445 })
1446 .bench_local_refs(|everything| {
1447 everything.fileset.handle(&mut everything.localization, &everything.parser);
1448 });
1449 }
1450}