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
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#[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
226impl Everything {
227 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 fn load_output_styles(&self, default_color: bool) -> OutputStyle {
363 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 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 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")] 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, _ => 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 #[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 #[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 #[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 #[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 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 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 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 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)] #[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)] #[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 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 #[allow(clippy::unused_self)]
1320 #[allow(unused_variables)] 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 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}