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