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