Skip to main content

rpfm_extensions/diagnostics/
anim_fragment_battle.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Module with the structs and functions specific for `AnimFragmentBattle` diagnostics.
12
13use getset::{Getters, MutGetters};
14use serde_derive::{Serialize, Deserialize};
15
16use std::collections::{HashMap, HashSet};
17use std::{fmt, fmt::Display};
18
19use rpfm_lib::files::{RFile, RFileDecoded};
20
21use crate::dependencies::Dependencies;
22use crate::diagnostics::*;
23
24//-------------------------------------------------------------------------------//
25//                              Enums & Structs
26//-------------------------------------------------------------------------------//
27
28/// This struct contains the results of an anim fragment battle diagnostic.
29#[derive(Debug, Clone, Default, Getters, MutGetters, Serialize, Deserialize)]
30#[getset(get = "pub", get_mut = "pub")]
31pub struct AnimFragmentBattleDiagnostic {
32    path: String,
33    pack: String,
34    results: Vec<AnimFragmentBattleDiagnosticReport>
35}
36
37/// Location of a flagged sub-entry within a fragment, with which path field tripped the check.
38///
39/// `(subrow, is_file_path, is_meta_file_path, is_snd_file_path)`.
40pub type AnimFragmentBattleAnimRefRef = (usize, bool, bool, bool);
41
42/// Location of a flagged entry within a fragment: row index and (optionally) the offending sub-entry.
43pub type AnimFragmentBattleEntryRef = (usize, Option<AnimFragmentBattleAnimRefRef>);
44
45/// This struct defines an individual anim fragment battle diagnostic result.
46#[derive(Debug, Clone, Getters, MutGetters, Serialize, Deserialize)]
47#[getset(get = "pub", get_mut = "pub")]
48pub struct AnimFragmentBattleDiagnosticReport {
49    locomotion_graph: bool,
50    entry: Option<AnimFragmentBattleEntryRef>,
51    report_type: AnimFragmentBattleDiagnosticReportType,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub enum AnimFragmentBattleDiagnosticReportType {
56    LocomotionGraphPathNotFound(String),
57    FilePathNotFound(String),
58    MetaFilePathNotFound(String),
59    SndFilePathNotFound(String),
60}
61
62//-------------------------------------------------------------------------------//
63//                             Implementations
64//-------------------------------------------------------------------------------//
65
66impl AnimFragmentBattleDiagnosticReport {
67    pub fn new(report_type: AnimFragmentBattleDiagnosticReportType, locomotion_graph: bool, entry: Option<AnimFragmentBattleEntryRef>) -> Self {
68        Self {
69            locomotion_graph,
70            entry,
71            report_type
72        }
73    }
74}
75
76impl DiagnosticReport for AnimFragmentBattleDiagnosticReport {
77    fn message(&self) -> String {
78        match &self.report_type {
79            AnimFragmentBattleDiagnosticReportType::LocomotionGraphPathNotFound(path) => format!("Locomotion Graph file not found: {path}."),
80            AnimFragmentBattleDiagnosticReportType::FilePathNotFound(path) => format!("'File Path' file not found: {path}."),
81            AnimFragmentBattleDiagnosticReportType::MetaFilePathNotFound(path) => format!("'Meta File Path' file not found: {path}."),
82            AnimFragmentBattleDiagnosticReportType::SndFilePathNotFound(path) => format!("'Snd File Path' file not found: {path}."),
83        }
84    }
85
86    fn level(&self) -> DiagnosticLevel {
87        match self.report_type {
88            AnimFragmentBattleDiagnosticReportType::LocomotionGraphPathNotFound(_) => DiagnosticLevel::Warning,
89            AnimFragmentBattleDiagnosticReportType::FilePathNotFound(_) => DiagnosticLevel::Warning,
90            AnimFragmentBattleDiagnosticReportType::MetaFilePathNotFound(_) => DiagnosticLevel::Warning,
91            AnimFragmentBattleDiagnosticReportType::SndFilePathNotFound(_) => DiagnosticLevel::Warning,
92        }
93    }
94}
95
96impl Display for AnimFragmentBattleDiagnosticReportType {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        Display::fmt(match self {
99            Self::LocomotionGraphPathNotFound(_) => "LocomotionGraphPathNotFound",
100            Self::FilePathNotFound(_) => "FilePathNotFound",
101            Self::MetaFilePathNotFound(_) => "MetaFilePathNotFound",
102            Self::SndFilePathNotFound(_) => "SndFilePathNotFound",
103        }, f)
104    }
105}
106
107impl AnimFragmentBattleDiagnostic {
108    pub fn new(path: &str, pack: &str) -> Self {
109        Self {
110            path: path.to_owned(),
111            pack: pack.to_owned(),
112            results: vec![],
113        }
114    }
115
116    /// This function takes care of checking the loc tables of your mod for errors.
117    #[allow(clippy::too_many_arguments)]
118    pub fn check(
119        pack_key: &str,
120        file: &RFile,
121        dependencies: &Dependencies,
122        global_ignored_diagnostics: &[String],
123        ignored_fields: &[String],
124        ignored_diagnostics: &HashSet<String>,
125        ignored_diagnostics_for_fields: &HashMap<String, Vec<String>>,
126        local_path_list: &HashMap<String, Vec<String>>,
127    ) ->Option<DiagnosticType> {
128        if let Ok(RFileDecoded::AnimFragmentBattle(fragment)) = file.decoded() {
129            let mut diagnostic = AnimFragmentBattleDiagnostic::new(file.path_in_container_raw(), pack_key);
130
131            if !Diagnostics::ignore_diagnostic(global_ignored_diagnostics, None, Some("LocomotionGraphPathNotFound"), ignored_fields, ignored_diagnostics, ignored_diagnostics_for_fields) && !fragment.locomotion_graph().is_empty() {
132                let path = fragment.locomotion_graph().replace('\\', "/");
133                let mut path_found = false;
134
135                if !path_found && local_path_list.get(&path.to_lowercase()).is_some() {
136                    path_found = true;
137                }
138
139                if !path_found && dependencies.file_exists(&path, true, true, true) {
140                    path_found = true;
141                }
142
143                if !path_found {
144                    let result = AnimFragmentBattleDiagnosticReport::new(AnimFragmentBattleDiagnosticReportType::LocomotionGraphPathNotFound(fragment.locomotion_graph().to_owned()), true, None);
145                    diagnostic.results_mut().push(result);
146                }
147            }
148
149            for (row, entry) in fragment.entries().iter().enumerate() {
150                for (subrow, anim_ref) in entry.anim_refs().iter().enumerate() {
151                    if !Diagnostics::ignore_diagnostic(global_ignored_diagnostics, None, Some("FilePathNotFound"), ignored_fields, ignored_diagnostics, ignored_diagnostics_for_fields) && !anim_ref.file_path().is_empty() {
152                        let path = anim_ref.file_path().replace('\\', "/");
153                        let mut path_found = false;
154
155                        if !path_found && local_path_list.get(&path.to_lowercase()).is_some() {
156                            path_found = true;
157                        }
158
159                        if !path_found && dependencies.file_exists(&path, true, true, true) {
160                            path_found = true;
161                        }
162
163                        if !path_found {
164                            let result = AnimFragmentBattleDiagnosticReport::new(AnimFragmentBattleDiagnosticReportType::FilePathNotFound(anim_ref.file_path().to_owned()), false, Some((row, Some((subrow, true, false, false)))));
165                            diagnostic.results_mut().push(result);
166                        }
167                    }
168
169                    if !Diagnostics::ignore_diagnostic(global_ignored_diagnostics, None, Some("MetaFilePathNotFound"), ignored_fields, ignored_diagnostics, ignored_diagnostics_for_fields) && !anim_ref.meta_file_path().is_empty() {
170                        let path = anim_ref.meta_file_path().replace('\\', "/");
171                        let mut path_found = false;
172
173                        if !path_found && local_path_list.get(&path.to_lowercase()).is_some() {
174                            path_found = true;
175                        }
176
177                        if !path_found && dependencies.file_exists(&path, true, true, true) {
178                            path_found = true;
179                        }
180
181                        if !path_found {
182                            let result = AnimFragmentBattleDiagnosticReport::new(AnimFragmentBattleDiagnosticReportType::MetaFilePathNotFound(anim_ref.meta_file_path().to_owned()), false, Some((row, Some((subrow, false, true, false)))));
183                            diagnostic.results_mut().push(result);
184                        }
185                    }
186
187                    if !Diagnostics::ignore_diagnostic(global_ignored_diagnostics, None, Some("SndFilePathNotFound"), ignored_fields, ignored_diagnostics, ignored_diagnostics_for_fields) && !anim_ref.snd_file_path().is_empty() {
188                        let path = anim_ref.snd_file_path().replace('\\', "/");
189                        let mut path_found = false;
190
191                        if !path_found && local_path_list.get(&path.to_lowercase()).is_some() {
192                            path_found = true;
193                        }
194
195                        if !path_found && dependencies.file_exists(&path, true, true, true) {
196                            path_found = true;
197                        }
198
199                        if !path_found {
200                            let result = AnimFragmentBattleDiagnosticReport::new(AnimFragmentBattleDiagnosticReportType::SndFilePathNotFound(anim_ref.snd_file_path().to_owned()), false, Some((row, Some((subrow, false, false, true)))));
201                            diagnostic.results_mut().push(result);
202                        }
203                    }
204                }
205            }
206
207            if !diagnostic.results().is_empty() {
208                Some(DiagnosticType::AnimFragmentBattle(diagnostic))
209            } else { None }
210        } else { None }
211    }
212}