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 coa::Coas,
45 defines::Defines,
46 events::Events,
47 gui::Gui,
48 localization::Localization,
49 music::Musics,
50 on_actions::OnActions,
51 script_values::ScriptValues,
52 scripted_effects::{Effect, Effects},
53 scripted_lists::ScriptedLists,
54 scripted_modifiers::ScriptedModifiers,
55 scripted_triggers::{Trigger, Triggers},
56};
57use crate::db::{Db, DbKind};
58use crate::dds::DdsFiles;
59use crate::fileset::{FileEntry, FileKind, Fileset};
60use crate::game::Game;
61#[cfg(any(feature = "ck3", feature = "vic3"))]
62use crate::helpers::TigerHashSet;
63#[cfg(feature = "imperator")]
64use crate::imperator::data::{decisions::Decisions, provinces::ImperatorProvinces};
65#[cfg(feature = "imperator")]
66use crate::imperator::tables::misc::*;
67use crate::item::{Item, ItemLoader};
68use crate::lowercase::Lowercase;
69use crate::macros::MACRO_MAP;
70#[cfg(feature = "vic3")]
71use crate::parse::json::parse_json_file;
72use crate::parse::ParserMemory;
73use crate::pdxfile::PdxFile;
74#[cfg(any(feature = "ck3", feature = "vic3"))]
75use crate::report::err;
76use crate::report::{report, set_output_style, ErrorKey, OutputStyle, Severity};
77use crate::rivers::Rivers;
78use crate::token::{Loc, Token};
79#[cfg(feature = "vic3")]
80use crate::vic3::data::{
81 buy_packages::BuyPackage, history::History, provinces::Vic3Provinces,
82 strategic_regions::StrategicRegion, terrain::TerrainMask,
83};
84#[cfg(feature = "vic3")]
85use crate::vic3::tables::misc::*;
86
87#[derive(Debug, Error)]
88#[allow(clippy::enum_variant_names)]
89pub enum FilesError {
90 #[error("Could not read game files at {path}")]
91 VanillaUnreadable { path: PathBuf, source: walkdir::Error },
92 #[error("Could not read mod files at {path}")]
93 ModUnreadable { path: PathBuf, source: walkdir::Error },
94 #[error("Could not read config file at {path}")]
95 ConfigUnreadable { path: PathBuf },
96}
97
98#[derive(Debug)]
108pub struct Everything {
109 config: Block,
111
112 pub parser: ParserMemory,
116
117 #[cfg(any(feature = "ck3", feature = "vic3"))]
120 warned_defines: RwLock<TigerHashSet<String>>,
121
122 pub(crate) fileset: Fileset,
124
125 pub(crate) dds: DdsFiles,
127
128 pub(crate) database: Db,
131
132 pub(crate) localization: Localization,
133
134 pub(crate) scripted_lists: ScriptedLists,
135
136 pub(crate) defines: Defines,
137
138 pub(crate) events: Events,
139 #[cfg(feature = "imperator")]
140 pub(crate) decisions_imperator: Decisions,
141
142 pub(crate) scripted_modifiers: ScriptedModifiers,
143 pub(crate) on_actions: OnActions,
144
145 #[cfg(feature = "ck3")]
146 pub(crate) interaction_cats: CharacterInteractionCategories,
147
148 #[cfg(feature = "ck3")]
149 pub(crate) provinces_ck3: Ck3Provinces,
150 #[cfg(feature = "vic3")]
151 pub(crate) provinces_vic3: Vic3Provinces,
152 #[cfg(feature = "imperator")]
153 pub(crate) provinces_imperator: ImperatorProvinces,
154
155 #[cfg(feature = "ck3")]
156 pub(crate) province_histories: ProvinceHistories,
157 #[cfg(feature = "ck3")]
158 pub(crate) province_properties: ProvinceProperties,
159 #[cfg(feature = "ck3")]
160 pub(crate) province_terrains: ProvinceTerrains,
161
162 #[cfg(feature = "ck3")]
163 pub(crate) gameconcepts: GameConcepts,
164
165 #[cfg(feature = "ck3")]
166 pub(crate) titles: Titles,
167
168 #[cfg(feature = "ck3")]
169 pub(crate) characters: Characters,
170
171 pub(crate) script_values: ScriptValues,
172
173 pub(crate) triggers: Triggers,
174 pub(crate) effects: Effects,
175
176 #[cfg(feature = "ck3")]
177 pub(crate) traits: Traits,
178
179 #[cfg(feature = "ck3")]
180 pub(crate) title_history: TitleHistories,
181
182 #[cfg(feature = "ck3")]
183 pub(crate) doctrines: Doctrines,
184
185 #[cfg(feature = "ck3")]
186 pub(crate) menatarmstypes: MenAtArmsTypes,
187
188 pub(crate) gui: Gui,
189 #[cfg(any(feature = "ck3", feature = "vic3"))]
190 pub(crate) data_bindings: DataBindings,
191
192 pub(crate) assets: Assets,
193 pub(crate) music: Musics,
194
195 pub(crate) coas: Coas,
196
197 #[cfg(feature = "vic3")]
198 pub(crate) history: History,
199
200 #[cfg(feature = "ck3")]
201 pub(crate) wars: Wars,
202}
203
204impl Everything {
205 pub fn new(
215 config_filepath: Option<&Path>,
216 vanilla_dir: Option<&Path>,
217 mod_root: &Path,
218 replace_paths: Vec<PathBuf>,
219 ) -> Result<Self> {
220 let mut fileset = Fileset::new(vanilla_dir, mod_root.to_path_buf(), replace_paths);
221
222 let config_file_name = match Game::game() {
223 #[cfg(feature = "ck3")]
224 Game::Ck3 => "ck3-tiger.conf",
225 #[cfg(feature = "vic3")]
226 Game::Vic3 => "vic3-tiger.conf",
227 #[cfg(feature = "imperator")]
228 Game::Imperator => "imperator-tiger.conf",
229 };
230
231 let config_file = match config_filepath {
232 Some(path) => path.to_path_buf(),
233 None => mod_root.join(config_file_name),
234 };
235
236 let config = if config_file.is_file() {
237 Self::read_config(config_file_name, &config_file)
238 .ok_or(FilesError::ConfigUnreadable { path: config_file })?
239 } else {
240 Block::new(Loc::for_file(config_file.clone(), FileKind::Mod, config_file.clone()))
241 };
242
243 fileset.config(config.clone())?;
244
245 fileset.scan_all()?;
246 fileset.finalize();
247
248 Ok(Everything {
249 parser: ParserMemory::default(),
250 fileset,
251 dds: DdsFiles::default(),
252 config,
253 #[cfg(any(feature = "ck3", feature = "vic3"))]
254 warned_defines: RwLock::new(TigerHashSet::default()),
255 database: Db::default(),
256 localization: Localization::default(),
257 scripted_lists: ScriptedLists::default(),
258 defines: Defines::default(),
259 events: Events::default(),
260 #[cfg(feature = "imperator")]
261 decisions_imperator: Decisions::default(),
262 scripted_modifiers: ScriptedModifiers::default(),
263 on_actions: OnActions::default(),
264 #[cfg(feature = "ck3")]
265 interaction_cats: CharacterInteractionCategories::default(),
266 #[cfg(feature = "ck3")]
267 provinces_ck3: Ck3Provinces::default(),
268 #[cfg(feature = "vic3")]
269 provinces_vic3: Vic3Provinces::default(),
270 #[cfg(feature = "imperator")]
271 provinces_imperator: ImperatorProvinces::default(),
272 #[cfg(feature = "ck3")]
273 province_histories: ProvinceHistories::default(),
274 #[cfg(feature = "ck3")]
275 province_properties: ProvinceProperties::default(),
276 #[cfg(feature = "ck3")]
277 province_terrains: ProvinceTerrains::default(),
278 #[cfg(feature = "ck3")]
279 gameconcepts: GameConcepts::default(),
280 #[cfg(feature = "ck3")]
281 titles: Titles::default(),
282 #[cfg(feature = "ck3")]
283 characters: Characters::default(),
284 script_values: ScriptValues::default(),
285 triggers: Triggers::default(),
286 effects: Effects::default(),
287 #[cfg(feature = "ck3")]
288 traits: Traits::default(),
289 #[cfg(feature = "ck3")]
290 title_history: TitleHistories::default(),
291 #[cfg(feature = "ck3")]
292 doctrines: Doctrines::default(),
293 #[cfg(feature = "ck3")]
294 menatarmstypes: MenAtArmsTypes::default(),
295 gui: Gui::default(),
296 #[cfg(any(feature = "ck3", feature = "vic3"))]
297 data_bindings: DataBindings::default(),
298 assets: Assets::default(),
299 music: Musics::default(),
300 coas: Coas::default(),
301 #[cfg(feature = "vic3")]
302 history: History::default(),
303 #[cfg(feature = "ck3")]
304 wars: Wars::default(),
305 })
306 }
307
308 fn read_config(name: &str, path: &Path) -> Option<Block> {
309 let entry = FileEntry::new(PathBuf::from(name), FileKind::Mod, path.to_path_buf());
310 PdxFile::read_optional_bom(&entry, &ParserMemory::default())
311 }
312
313 pub fn load_config_filtering_rules(&self) {
314 check_for_legacy_ignore(&self.config);
315 load_filter(&self.config);
316 }
317
318 fn load_output_styles(&self, default_color: bool) -> OutputStyle {
322 let block = match self.config.get_field_block("output_style") {
324 Some(block) => Cow::Borrowed(block),
325 None => Cow::Owned(Block::new(self.config.loc)),
326 };
327 if !block.get_field_bool("enable").unwrap_or(default_color) {
328 return OutputStyle::no_color();
329 }
330 let mut style = OutputStyle::default();
331 for severity in Severity::iter() {
332 if let Some(error_block) =
333 block.get_field_block(format!("{severity}").to_ascii_lowercase().as_str())
334 {
335 if let Some(color) = error_block.get_field_value("color") {
336 style.set(severity, color.as_str());
337 }
338 }
339 }
340 style
341 }
342
343 pub fn load_output_settings(&self, default_colors: bool) {
344 set_output_style(self.load_output_styles(default_colors));
345 }
346
347 #[cfg(feature = "vic3")]
348 fn load_json<F>(&mut self, itype: Item, add_json: F)
349 where
350 F: Fn(&mut Db, Block) + Sync + Send,
351 {
352 for block in self.fileset.filter_map_under(&PathBuf::from(itype.path()), |entry| {
353 if entry.filename().to_string_lossy().ends_with(".json") {
354 parse_json_file(entry)
355 } else {
356 None
357 }
358 }) {
359 add_json(&mut self.database, block);
360 }
361 }
362
363 #[cfg(feature = "ck3")]
364 fn load_reader_export(&mut self) {
365 let path = PathBuf::from("reader_export");
366 for entry in self.fileset.get_files_under(&path) {
367 if entry.filename().to_string_lossy().ends_with(".txt") {
368 PdxFile::reader_export(entry, &mut self.parser.pdxfile);
369 }
370 }
371 }
372
373 fn load_pdx_files(&mut self, loader: &ItemLoader) {
374 let path = PathBuf::from(loader.itype().path());
375 for mut block in self.fileset.filter_map_under(&path, |entry| {
376 if entry.filename().to_string_lossy().ends_with(loader.extension()) {
377 PdxFile::read_encoded(entry, loader.encoding(), &self.parser)
378 } else {
379 None
380 }
381 }) {
382 if loader.whole_file() {
383 let fname = block.loc.filename();
384 let key = fname.strip_suffix(loader.extension()).unwrap();
386 let key = Token::new(key, block.loc);
387 (loader.adder())(&mut self.database, key, block);
388 } else {
389 for (key, block) in block.drain_definitions_warn() {
390 (loader.adder())(&mut self.database, key, block);
391 }
392 }
393 }
394 }
395
396 fn load_all_normal_pdx_files(&mut self) {
397 for loader in inventory::iter::<ItemLoader> {
398 if loader.for_game(Game::game()) {
399 self.load_pdx_files(loader);
400 }
401 }
402 }
403
404 fn load_all_generic(&mut self) {
405 scope(|s| {
406 s.spawn(|_| self.fileset.handle(&mut self.dds, &self.parser));
407 s.spawn(|_| self.fileset.handle(&mut self.events, &self.parser));
408 s.spawn(|_| self.fileset.handle(&mut self.localization, &self.parser));
409 s.spawn(|_| self.fileset.handle(&mut self.scripted_lists, &self.parser));
410 s.spawn(|_| self.fileset.handle(&mut self.defines, &self.parser));
411 s.spawn(|_| self.fileset.handle(&mut self.scripted_modifiers, &self.parser));
412 s.spawn(|_| self.fileset.handle(&mut self.script_values, &self.parser));
413 s.spawn(|_| self.fileset.handle(&mut self.triggers, &self.parser));
414 s.spawn(|_| self.fileset.handle(&mut self.effects, &self.parser));
415 s.spawn(|_| self.fileset.handle(&mut self.assets, &self.parser));
416 s.spawn(|_| self.fileset.handle(&mut self.gui, &self.parser));
417 s.spawn(|_| self.fileset.handle(&mut self.on_actions, &self.parser));
418 s.spawn(|_| self.fileset.handle(&mut self.coas, &self.parser));
419 s.spawn(|_| self.fileset.handle(&mut self.music, &self.parser));
420 });
421
422 self.load_all_normal_pdx_files();
423 }
424
425 #[cfg(feature = "ck3")]
426 fn load_all_ck3(&mut self) {
427 scope(|s| {
428 s.spawn(|_| self.fileset.handle(&mut self.interaction_cats, &self.parser));
429 s.spawn(|_| self.fileset.handle(&mut self.province_histories, &self.parser));
430 s.spawn(|_| self.fileset.handle(&mut self.province_properties, &self.parser));
431 s.spawn(|_| self.fileset.handle(&mut self.province_terrains, &self.parser));
432 s.spawn(|_| self.fileset.handle(&mut self.gameconcepts, &self.parser));
433 s.spawn(|_| self.fileset.handle(&mut self.titles, &self.parser));
434 s.spawn(|_| self.fileset.handle(&mut self.characters, &self.parser));
435 s.spawn(|_| self.fileset.handle(&mut self.traits, &self.parser));
436 s.spawn(|_| self.fileset.handle(&mut self.title_history, &self.parser));
437 s.spawn(|_| self.fileset.handle(&mut self.doctrines, &self.parser));
438 s.spawn(|_| self.fileset.handle(&mut self.menatarmstypes, &self.parser));
439 s.spawn(|_| self.fileset.handle(&mut self.data_bindings, &self.parser));
440 s.spawn(|_| self.fileset.handle(&mut self.provinces_ck3, &self.parser));
441 s.spawn(|_| self.fileset.handle(&mut self.wars, &self.parser));
442 });
443 crate::ck3::data::buildings::Building::finalize(&mut self.database);
444 }
445
446 #[cfg(feature = "vic3")]
447 fn load_all_vic3(&mut self) {
448 self.fileset.handle(&mut self.history, &self.parser);
449 self.fileset.handle(&mut self.provinces_vic3, &self.parser);
450 self.fileset.handle(&mut self.data_bindings, &self.parser);
451 self.load_json(Item::TerrainMask, TerrainMask::add_json);
452 }
453
454 #[cfg(feature = "imperator")]
455 fn load_all_imperator(&mut self) {
456 self.fileset.handle(&mut self.decisions_imperator, &self.parser);
457 self.fileset.handle(&mut self.provinces_imperator, &self.parser);
458 }
459
460 pub fn load_all(&mut self) {
461 #[cfg(feature = "ck3")]
462 self.load_reader_export();
463 self.load_all_generic();
464 match Game::game() {
465 #[cfg(feature = "ck3")]
466 Game::Ck3 => self.load_all_ck3(),
467 #[cfg(feature = "vic3")]
468 Game::Vic3 => self.load_all_vic3(),
469 #[cfg(feature = "imperator")]
470 Game::Imperator => self.load_all_imperator(),
471 }
472 self.database.add_subitems();
473 }
474
475 fn validate_all_generic<'a>(&'a self, s: &Scope<'a>) {
476 s.spawn(|_| self.fileset.validate(self));
477 s.spawn(|_| self.scripted_lists.validate(self));
478 s.spawn(|_| self.defines.validate(self));
479 s.spawn(|_| self.scripted_modifiers.validate(self));
480 s.spawn(|_| self.script_values.validate(self));
481 s.spawn(|_| self.triggers.validate(self));
482 s.spawn(|_| self.effects.validate(self));
483 s.spawn(|_| self.events.validate(self));
484 s.spawn(|_| self.assets.validate(self));
485 s.spawn(|_| self.gui.validate(self));
486 s.spawn(|_| self.on_actions.validate(self));
487 s.spawn(|_| self.coas.validate(self));
488 s.spawn(|_| self.music.validate(self));
489 }
490
491 #[cfg(feature = "ck3")]
492 fn validate_all_ck3<'a>(&'a self, s: &Scope<'a>) {
493 s.spawn(|_| self.interaction_cats.validate(self));
494 s.spawn(|_| self.province_histories.validate(self));
495 s.spawn(|_| self.province_properties.validate(self));
496 s.spawn(|_| self.province_terrains.validate(self));
497 s.spawn(|_| self.gameconcepts.validate(self));
498 s.spawn(|_| self.titles.validate(self));
499 s.spawn(|_| self.characters.validate(self));
500 s.spawn(|_| self.traits.validate(self));
501 s.spawn(|_| self.title_history.validate(self));
502 s.spawn(|_| self.doctrines.validate(self));
503 s.spawn(|_| self.menatarmstypes.validate(self));
504 s.spawn(|_| self.data_bindings.validate(self));
505 s.spawn(|_| self.provinces_ck3.validate(self));
506 s.spawn(|_| self.wars.validate(self));
507 s.spawn(|_| Climate::validate_all(&self.database, self));
508 }
509
510 #[cfg(feature = "vic3")]
511 fn validate_all_vic3<'a>(&'a self, s: &Scope<'a>) {
512 s.spawn(|_| self.history.validate(self));
513 s.spawn(|_| self.provinces_vic3.validate(self));
514 s.spawn(|_| self.data_bindings.validate(self));
515 s.spawn(|_| StrategicRegion::crosscheck(self));
516 s.spawn(|_| BuyPackage::crosscheck(self));
517 }
518
519 #[cfg(feature = "imperator")]
520 fn validate_all_imperator<'a>(&'a self, s: &Scope<'a>) {
521 s.spawn(|_| self.decisions_imperator.validate(self));
522 s.spawn(|_| self.provinces_imperator.validate(self));
523 }
524
525 pub fn validate_all(&self) {
526 scope(|s| {
527 self.validate_all_generic(s);
528 match Game::game() {
529 #[cfg(feature = "ck3")]
530 Game::Ck3 => self.validate_all_ck3(s),
531 #[cfg(feature = "vic3")]
532 Game::Vic3 => self.validate_all_vic3(s),
533 #[cfg(feature = "imperator")]
534 Game::Imperator => self.validate_all_imperator(s),
535 }
536 });
537 self.database.validate(self);
538
539 self.localization.validate_pass2(self);
540 }
541
542 pub fn check_rivers(&mut self) {
543 let mut rivers = Rivers::default();
544 self.fileset.handle(&mut rivers, &self.parser);
545 rivers.validate(self);
546 }
547
548 #[cfg(feature = "ck3")]
549 pub fn check_pod(&mut self) {
550 self.province_histories.check_pod_faiths(self, &self.titles);
551 self.characters.check_pod_flags(self);
552 self.localization.check_pod_loca(self);
553 }
554
555 pub fn check_unused(&mut self) {
556 self.localization.check_unused(self);
557 self.fileset.check_unused_dds(self);
558 }
559
560 pub(crate) fn item_has_property(&self, itype: Item, key: &str, property: &str) -> bool {
561 self.database.has_property(itype, key, property, self)
562 }
563
564 #[cfg(feature = "ck3")] pub(crate) fn item_lc_has_property(
566 &self,
567 itype: Item,
568 key: &Lowercase,
569 property: &str,
570 ) -> bool {
571 self.database.lc_has_property(itype, key, property, self)
572 }
573
574 #[cfg(feature = "ck3")]
575 fn item_exists_ck3(&self, itype: Item, key: &str) -> bool {
576 match itype {
577 Item::ActivityState => ACTIVITY_STATES.contains(&key),
578 Item::ArtifactHistory => ARTIFACT_HISTORY.contains(&key),
579 Item::ArtifactRarity => ARTIFACT_RARITIES.contains(&&*key.to_ascii_lowercase()),
580 Item::Character => self.characters.exists(key),
581 Item::CharacterInteractionCategory => self.interaction_cats.exists(key),
582 Item::DangerType => DANGER_TYPES.contains(&key),
583 Item::DlcFeature => DLC_FEATURES_CK3.contains(&key),
584 Item::Doctrine => self.doctrines.exists(key),
585 Item::DoctrineCategory => self.doctrines.category_exists(key),
586 Item::DoctrineParameter => self.doctrines.parameter_exists(key),
587 Item::GameConcept => self.gameconcepts.exists(key),
588 Item::GeneticConstraint => self.traits.constraint_exists(key),
589 Item::MenAtArms => self.menatarmstypes.exists(key),
590 Item::MenAtArmsBase => self.menatarmstypes.base_exists(key),
591 Item::PrisonType => PRISON_TYPES.contains(&key),
592 Item::Province => self.provinces_ck3.exists(key),
593 Item::RewardItem => REWARD_ITEMS.contains(&key),
594 Item::Sexuality => SEXUALITIES.contains(&key),
595 Item::Skill => SKILLS.contains(&key),
596 Item::Sound => self.valid_sound(key),
597 Item::Title => self.titles.exists(key),
598 Item::TitleHistory => self.title_history.exists(key),
599 Item::TitleHistoryType => TITLE_HISTORY_TYPES.contains(&key),
600 Item::Trait => self.traits.exists(key),
601 Item::TraitFlag => self.traits.flag_exists(key),
602 Item::TraitTrack => self.traits.track_exists(key),
603 Item::TraitCategory => TRAIT_CATEGORIES.contains(&key),
604 _ => self.database.exists(itype, key),
605 }
606 }
607
608 #[cfg(feature = "vic3")]
609 fn item_exists_vic3(&self, itype: Item, key: &str) -> bool {
610 match itype {
611 Item::Approval => APPROVALS.contains(&key),
612 Item::Attitude => ATTITUDES.contains(&&*key.to_lowercase()),
613 Item::CharacterRole => CHARACTER_ROLES.contains(&key),
614 Item::CountryTier => COUNTRY_TIERS.contains(&key),
615 Item::DlcFeature => DLC_FEATURES_VIC3.contains(&key),
616 Item::EventCategory => EVENT_CATEGORIES.contains(&key),
617 Item::InfamyThreshold => INFAMY_THRESHOLDS.contains(&key),
618 Item::Level => LEVELS.contains(&key),
619 Item::RelationsThreshold => RELATIONS.contains(&key),
620 Item::SecretGoal => SECRET_GOALS.contains(&key),
621 Item::Sound => self.valid_sound(key),
622 Item::Strata => STRATA.contains(&key),
623 Item::TerrainKey => TERRAIN_KEYS.contains(&key),
624 Item::TransferOfPower => TRANSFER_OF_POWER.contains(&key),
625 Item::Wargoal => WARGOALS.contains(&key),
626 _ => self.database.exists(itype, key),
627 }
628 }
629
630 #[cfg(feature = "imperator")]
631 fn item_exists_imperator(&self, itype: Item, key: &str) -> bool {
632 match itype {
633 Item::DlcName => DLC_NAME_IMPERATOR.contains(&key),
634 Item::Decision => self.decisions_imperator.exists(key),
635 Item::Province => self.provinces_imperator.exists(key),
636 Item::Sound => self.valid_sound(key),
637 _ => self.database.exists(itype, key),
638 }
639 }
640
641 pub(crate) fn item_exists(&self, itype: Item, key: &str) -> bool {
642 match itype {
643 Item::Asset => self.assets.asset_exists(key),
644 Item::BlendShape => self.assets.blend_shape_exists(key),
645 Item::Coa => self.coas.exists(key),
646 Item::CoaTemplate => self.coas.template_exists(key),
647 Item::Define => self.defines.exists(key),
648 Item::Entity => self.assets.entity_exists(key),
649 Item::Entry => self.fileset.entry_exists(key),
650 Item::Event => self.events.exists(key),
651 Item::EventNamespace => self.events.namespace_exists(key),
652 Item::File => self.fileset.exists(key),
653 Item::GeneAttribute => self.assets.attribute_exists(key),
654 Item::GuiLayer => self.gui.layer_exists(key),
655 Item::GuiTemplate => self.gui.template_exists(key),
656 Item::GuiType => self.gui.type_exists(&Lowercase::new(key)),
657 Item::Localization => self.localization.exists(key),
658 Item::Music => self.music.exists(key),
659 Item::OnAction => self.on_actions.exists(key),
660 Item::Pdxmesh => self.assets.mesh_exists(key),
661 Item::ScriptedEffect => self.effects.exists(key),
662 Item::ScriptedList => self.scripted_lists.exists(key),
663 Item::ScriptedModifier => self.scripted_modifiers.exists(key),
664 Item::ScriptedTrigger => self.triggers.exists(key),
665 Item::ScriptValue => self.script_values.exists(key),
666 Item::TextFormat => self.gui.textformat_exists(key),
667 Item::TextIcon => self.gui.texticon_exists(key),
668 Item::TextureFile => self.assets.texture_exists(key),
669 Item::WidgetName => self.gui.name_exists(key),
670 Item::Directory | Item::Shortcut => true, _ => match Game::game() {
672 #[cfg(feature = "ck3")]
673 Game::Ck3 => self.item_exists_ck3(itype, key),
674 #[cfg(feature = "vic3")]
675 Game::Vic3 => self.item_exists_vic3(itype, key),
676 #[cfg(feature = "imperator")]
677 Game::Imperator => self.item_exists_imperator(itype, key),
678 },
679 }
680 }
681
682 #[cfg(feature = "ck3")]
686 fn item_exists_lc_ck3(&self, itype: Item, key: &Lowercase) -> bool {
687 match itype {
688 Item::MenAtArmsBase => self.menatarmstypes.base_exists_lc(key),
689 Item::Trait => self.traits.exists_lc(key),
690 Item::TraitTrack => self.traits.track_exists_lc(key),
691 _ => self.database.exists_lc(itype, key),
692 }
693 }
694
695 #[cfg(feature = "vic3")]
699 fn item_exists_lc_vic3(&self, itype: Item, key: &Lowercase) -> bool {
700 match itype {
701 Item::TerrainKey => TERRAIN_KEYS.contains(&key.as_str()),
702 _ => self.database.exists_lc(itype, key),
703 }
704 }
705
706 #[cfg(feature = "imperator")]
710 fn item_exists_lc_imperator(&self, itype: Item, key: &Lowercase) -> bool {
711 #[allow(clippy::match_single_binding)]
712 match itype {
713 _ => self.database.exists_lc(itype, key),
714 }
715 }
716
717 pub(crate) fn item_exists_lc(&self, itype: Item, key: &Lowercase) -> bool {
721 #[allow(clippy::match_single_binding)]
722 match itype {
723 _ => match Game::game() {
724 #[cfg(feature = "ck3")]
725 Game::Ck3 => self.item_exists_lc_ck3(itype, key),
726 #[cfg(feature = "vic3")]
727 Game::Vic3 => self.item_exists_lc_vic3(itype, key),
728 #[cfg(feature = "imperator")]
729 Game::Imperator => self.item_exists_lc_imperator(itype, key),
730 },
731 }
732 }
733
734 pub(crate) fn mark_used(&self, itype: Item, key: &str) {
735 match itype {
736 Item::File => self.fileset.mark_used(key),
737 Item::Localization => self.localization.mark_used(key),
738 _ => (),
739 }
740 }
741
742 pub(crate) fn verify_exists(&self, itype: Item, token: &Token) {
743 self.verify_exists_implied(itype, token.as_str(), token);
744 }
745
746 pub(crate) fn verify_exists_max_sev(&self, itype: Item, token: &Token, max_sev: Severity) {
747 self.verify_exists_implied_max_sev(itype, token.as_str(), token, max_sev);
748 }
749
750 pub(crate) fn verify_exists_implied_max_sev(
751 &self,
752 itype: Item,
753 key: &str,
754 token: &Token,
755 max_sev: Severity,
756 ) {
757 match itype {
758 Item::Entry => self.fileset.verify_entry_exists(key, token, max_sev),
759 Item::File => self.fileset.verify_exists_implied(key, token, max_sev),
760 Item::Localization => self.localization.verify_exists_implied(key, token, max_sev),
761 Item::Music => self.music.verify_exists_implied(key, token, max_sev),
762 Item::Province => match Game::game() {
763 #[cfg(feature = "ck3")]
764 Game::Ck3 => self.provinces_ck3.verify_exists_implied(key, token, max_sev),
765 #[cfg(feature = "vic3")]
766 Game::Vic3 => self.provinces_vic3.verify_exists_implied(key, token, max_sev),
767 #[cfg(feature = "imperator")]
768 Game::Imperator => {
769 self.provinces_imperator.verify_exists_implied(key, token, max_sev);
770 }
771 },
772 Item::TextureFile => {
773 if let Some(entry) = self.assets.get_texture(key) {
774 self.fileset.mark_used(&entry.path().to_string_lossy());
776 } else {
777 let msg = format!("no texture file {key} anywhere under {}", itype.path());
778 report(ErrorKey::MissingFile, itype.severity().at_most(max_sev))
779 .conf(itype.confidence())
780 .msg(msg)
781 .loc(token)
782 .push();
783 }
784 }
785 _ => {
786 if !self.item_exists(itype, key) {
787 let path = itype.path();
788 let msg = if path.is_empty() {
789 format!("unknown {itype} {key}")
790 } else {
791 format!("{itype} {key} not defined in {path}")
792 };
793 report(ErrorKey::MissingItem, itype.severity().at_most(max_sev))
794 .conf(itype.confidence())
795 .msg(msg)
796 .loc(token)
797 .push();
798 }
799 }
800 }
801 }
802
803 pub(crate) fn verify_exists_implied(&self, itype: Item, key: &str, token: &Token) {
804 self.verify_exists_implied_max_sev(itype, key, token, Severity::Error);
805 }
806
807 #[cfg(feature = "ck3")]
808 pub(crate) fn verify_icon(&self, define: &str, token: &Token, suffix: &str) {
809 if let Some(icon_path) = self.get_defined_string_warn(token, define) {
810 let pathname = format!("{icon_path}/{token}{suffix}");
811 self.verify_exists_implied_max_sev(Item::File, &pathname, token, Severity::Warning);
813 }
814 }
815
816 #[cfg(feature = "ck3")]
817 pub(crate) fn mark_used_icon(&self, define: &str, token: &Token, suffix: &str) {
818 if let Some(icon_path) = self.get_defined_string_warn(token, define) {
819 let pathname = format!("{icon_path}/{token}{suffix}");
820 self.fileset.mark_used(&pathname);
821 }
822 }
823
824 pub(crate) fn validate_use(&self, itype: Item, key: &Token, block: &Block) {
825 self.database.validate_use(itype, key, block, self);
826 }
827
828 #[cfg(feature = "ck3")] pub(crate) fn validate_call(
830 &self,
831 itype: Item,
832 key: &Token,
833 block: &Block,
834 sc: &mut ScopeContext,
835 ) {
836 self.database.validate_call(itype, key, block, self, sc);
837 }
838
839 pub(crate) fn validate_localization_sc(&self, key: &str, sc: &mut ScopeContext) {
842 self.localization.validate_use(key, self, sc);
843 }
844
845 #[allow(dead_code)]
846 pub(crate) fn get_item<T: DbKind>(
847 &self,
848 itype: Item,
849 key: &str,
850 ) -> Option<(&Token, &Block, &T)> {
851 self.database.get_item(itype, key)
852 }
853
854 pub(crate) fn get_key_block(&self, itype: Item, key: &str) -> Option<(&Token, &Block)> {
855 self.database.get_key_block(itype, key)
856 }
857
858 pub(crate) fn get_trigger(&self, key: &Token) -> Option<&Trigger> {
859 #[cfg(feature = "ck3")]
860 if Game::is_ck3() {
861 if let Some(trigger) = self.triggers.get(key.as_str()) {
862 return Some(trigger);
863 }
864 if let Some(trigger) = self.events.get_trigger(key) {
865 return Some(trigger);
866 }
867 return None;
868 }
869 self.triggers.get(key.as_str())
870 }
871
872 pub(crate) fn get_effect(&self, key: &Token) -> Option<&Effect> {
873 #[cfg(feature = "ck3")]
874 if Game::is_ck3() {
875 if let Some(effect) = self.effects.get(key.as_str()) {
876 return Some(effect);
877 }
878 if let Some(effect) = self.events.get_effect(key) {
879 return Some(effect);
880 }
881 return None;
882 }
883 self.effects.get(key.as_str())
884 }
885
886 #[cfg(feature = "ck3")] pub(crate) fn get_defined_string(&self, key: &str) -> Option<&Token> {
888 self.defines.get_bv(key).and_then(BV::get_value)
889 }
890
891 #[cfg(any(feature = "ck3", feature = "vic3"))]
892 pub(crate) fn get_defined_array(&self, key: &str) -> Option<&Block> {
893 self.defines.get_bv(key).and_then(BV::get_block)
894 }
895
896 #[allow(clippy::missing_panics_doc)] #[cfg(feature = "ck3")] pub(crate) fn get_defined_string_warn(&self, token: &Token, key: &str) -> Option<&Token> {
899 let result = self.get_defined_string(key);
900 let mut cache = self.warned_defines.write().unwrap();
901 if result.is_none() && !cache.contains(key) {
902 let msg = format!("{key} not defined in common/defines/");
903 err(ErrorKey::MissingItem).msg(msg).loc(token).push();
904 cache.insert(key.to_string());
905 }
906 result
907 }
908
909 #[allow(clippy::missing_panics_doc)] #[cfg(any(feature = "ck3", feature = "vic3"))]
911 pub(crate) fn get_defined_array_warn(&self, token: &Token, key: &str) -> Option<&Block> {
912 let result = self.get_defined_array(key);
913 let mut cache = self.warned_defines.write().unwrap();
914 if result.is_none() && !cache.contains(key) {
915 let msg = format!("{key} not defined in common/defines/");
916 err(ErrorKey::MissingItem).msg(msg).loc(token).push();
917 cache.insert(key.to_string());
918 }
919 result
920 }
921
922 #[cfg(feature = "ck3")]
923 pub fn iter_keys_ck3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
924 match itype {
925 Item::Character => Box::new(self.characters.iter_keys()),
926 Item::CharacterInteractionCategory => Box::new(self.interaction_cats.iter_keys()),
927 Item::Doctrine => Box::new(self.doctrines.iter_keys()),
928 Item::DoctrineCategory => Box::new(self.doctrines.iter_category_keys()),
929 Item::DoctrineParameter => Box::new(self.doctrines.iter_parameter_keys()),
930 Item::GameConcept => Box::new(self.gameconcepts.iter_keys()),
931 Item::GeneticConstraint => Box::new(self.traits.iter_constraint_keys()),
932 Item::MenAtArms => Box::new(self.menatarmstypes.iter_keys()),
933 Item::MenAtArmsBase => Box::new(self.menatarmstypes.iter_base_keys()),
934 Item::Province => Box::new(self.provinces_ck3.iter_keys()),
935 Item::Title => Box::new(self.titles.iter_keys()),
936 Item::TitleHistory => Box::new(self.title_history.iter_keys()),
937 Item::Trait => Box::new(self.traits.iter_keys()),
938 Item::TraitFlag => Box::new(self.traits.iter_flag_keys()),
939 Item::TraitTrack => Box::new(self.traits.iter_track_keys()),
940 _ => Box::new(self.database.iter_keys(itype)),
941 }
942 }
943
944 #[cfg(feature = "vic3")]
945 fn iter_keys_vic3<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
946 Box::new(self.database.iter_keys(itype))
947 }
948
949 #[cfg(feature = "imperator")]
950 fn iter_keys_imperator<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
951 match itype {
952 Item::Decision => Box::new(self.decisions_imperator.iter_keys()),
953 Item::Province => Box::new(self.provinces_imperator.iter_keys()),
954 _ => Box::new(self.database.iter_keys(itype)),
955 }
956 }
957
958 pub fn iter_keys<'a>(&'a self, itype: Item) -> Box<dyn Iterator<Item = &'a Token> + 'a> {
959 match itype {
960 Item::Asset => Box::new(self.assets.iter_asset_keys()),
961 Item::BlendShape => Box::new(self.assets.iter_blend_shape_keys()),
962 Item::Coa => Box::new(self.coas.iter_keys()),
963 Item::CoaTemplate => Box::new(self.coas.iter_template_keys()),
964 Item::Define => Box::new(self.defines.iter_keys()),
965 Item::Entity => Box::new(self.assets.iter_entity_keys()),
966 Item::Event => Box::new(self.events.iter_keys()),
967 Item::EventNamespace => Box::new(self.events.iter_namespace_keys()),
968 Item::File => Box::new(self.fileset.iter_keys()),
969 Item::GeneAttribute => Box::new(self.assets.iter_attribute_keys()),
970 Item::GuiLayer => Box::new(self.gui.iter_layer_keys()),
971 Item::GuiTemplate => Box::new(self.gui.iter_template_keys()),
972 Item::GuiType => Box::new(self.gui.iter_type_keys()),
973 Item::Localization => Box::new(self.localization.iter_keys()),
974 Item::Music => Box::new(self.music.iter_keys()),
975 Item::OnAction => Box::new(self.on_actions.iter_keys()),
976 Item::Pdxmesh => Box::new(self.assets.iter_mesh_keys()),
977 Item::ScriptedEffect => Box::new(self.effects.iter_keys()),
978 Item::ScriptedList => Box::new(self.scripted_lists.iter_keys()),
979 Item::ScriptedModifier => Box::new(self.scripted_modifiers.iter_keys()),
980 Item::ScriptedTrigger => Box::new(self.triggers.iter_keys()),
981 Item::ScriptValue => Box::new(self.script_values.iter_keys()),
982 Item::TextFormat => Box::new(self.gui.iter_textformat_keys()),
983 Item::TextIcon => Box::new(self.gui.iter_texticon_keys()),
984 Item::TextureFile => Box::new(self.assets.iter_texture_keys()),
985 Item::WidgetName => Box::new(self.gui.iter_names()),
986 _ => match Game::game() {
987 #[cfg(feature = "ck3")]
988 Game::Ck3 => self.iter_keys_ck3(itype),
989 #[cfg(feature = "vic3")]
990 Game::Vic3 => self.iter_keys_vic3(itype),
991 #[cfg(feature = "imperator")]
992 Game::Imperator => self.iter_keys_imperator(itype),
993 },
994 }
995 }
996
997 fn valid_sound(&self, name: &str) -> bool {
998 if let Some(filename) = name.strip_prefix("file:/") {
1000 self.fileset.exists(filename)
1001 } else {
1002 let sounds_set = match Game::game() {
1003 #[cfg(feature = "ck3")]
1004 Game::Ck3 => &crate::ck3::tables::sounds::SOUNDS_SET,
1005 #[cfg(feature = "vic3")]
1006 Game::Vic3 => &crate::vic3::tables::sounds::SOUNDS_SET,
1007 #[cfg(feature = "imperator")]
1008 Game::Imperator => &crate::imperator::tables::sounds::SOUNDS_SET,
1009 };
1010 sounds_set.contains(&Lowercase::new(name))
1011 }
1012 }
1013}
1014
1015impl Drop for Everything {
1016 fn drop(&mut self) {
1017 MACRO_MAP.clear();
1019 }
1020}