rom_analyzer/console/
psx.rs1use serde::Serialize;
7
8use crate::error::RomAnalyzerError;
9use crate::region::{Region, check_region_mismatch};
10
11#[derive(Debug, PartialEq, Clone, Serialize)]
13pub struct PsxAnalysis {
14 pub source_name: String,
16 pub region: Region,
18 pub region_string: String,
20 pub region_mismatch: bool,
22 pub code: String,
24}
25
26impl PsxAnalysis {
27 pub fn print(&self) -> String {
29 let executable_prefix_not_found = if self.code == "N/A" {
30 "\nNote: Executable prefix (SLUS/SLES/SLPS) not found in header area. Requires main data track (.bin or .iso)."
31 } else {
32 ""
33 };
34 format!(
35 "{}\n\
36 System: Sony PlayStation (PSX)\n\
37 Region: {}\n\
38 Code: {}\
39 {}",
40 self.source_name, self.region, self.code, executable_prefix_not_found
41 )
42 }
43}
44
45pub fn map_region(region_code: &str) -> (&'static str, Region) {
84 match region_code {
85 "SLUS" => ("North America (NTSC-U)", Region::USA),
86 "SLES" => ("Europe (PAL)", Region::EUROPE),
87 "SLPS" => ("Japan (NTSC-J)", Region::JAPAN),
88 _ => ("Unknown", Region::UNKNOWN),
89 }
90}
91
92pub fn analyze_psx_data(data: &[u8], source_name: &str) -> Result<PsxAnalysis, RomAnalyzerError> {
110 let check_size = std::cmp::min(data.len(), 0x20000);
112 if check_size < 0x2000 {
113 return Err(RomAnalyzerError::DataTooSmall {
115 file_size: data.len(),
116 required_size: 0x2000,
117 details: "PSX boot file analysis".to_string(),
118 });
119 }
120
121 let data_sample = &data[..check_size];
122
123 let mut found_code = "N/A".to_string();
124 let mut region_name = "Unknown";
125 let mut region = Region::UNKNOWN;
126
127 for prefix in ["SLUS", "SLES", "SLPS"] {
130 if data_sample
132 .windows(prefix.len())
133 .any(|window| window.eq_ignore_ascii_case(prefix.as_bytes()))
134 {
135 found_code = prefix.to_string();
136 let (region_str, region_mask) = map_region(prefix);
137 region_name = region_str;
138 region = region_mask;
139 break;
140 }
141 }
142
143 let region_mismatch = check_region_mismatch(source_name, region);
144
145 Ok(PsxAnalysis {
146 source_name: source_name.to_string(),
147 region,
148 region_string: region_name.to_string(),
149 region_mismatch,
150 code: found_code,
151 })
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_analyze_psx_data_slus() -> Result<(), RomAnalyzerError> {
160 let mut data = vec![0; 0x2000];
162 data[0x100..0x104].copy_from_slice(b"SLUS"); let analysis = analyze_psx_data(&data, "test_rom_us.iso")?;
165
166 assert_eq!(analysis.source_name, "test_rom_us.iso");
167 assert_eq!(analysis.region, Region::USA);
168 assert_eq!(analysis.region_string, "North America (NTSC-U)");
169 assert_eq!(analysis.code, "SLUS");
170 assert_eq!(
171 analysis.print(),
172 "test_rom_us.iso\n\
173 System: Sony PlayStation (PSX)\n\
174 Region: USA\n\
175 Code: SLUS"
176 );
177 Ok(())
178 }
179
180 #[test]
181 fn test_analyze_psx_data_sles() -> Result<(), RomAnalyzerError> {
182 let mut data = vec![0; 0x2000];
183 data[0x100..0x104].copy_from_slice(b"SLES"); let analysis = analyze_psx_data(&data, "test_rom_eur.iso")?;
185
186 assert_eq!(analysis.source_name, "test_rom_eur.iso");
187 assert_eq!(analysis.region, Region::EUROPE);
188 assert_eq!(analysis.region_string, "Europe (PAL)");
189 assert_eq!(analysis.code, "SLES");
190 Ok(())
191 }
192
193 #[test]
194 fn test_analyze_psx_data_slps() -> Result<(), RomAnalyzerError> {
195 let mut data = vec![0; 0x2000];
196 data[0x100..0x104].copy_from_slice(b"SLPS"); let analysis = analyze_psx_data(&data, "test_rom_jp.iso")?;
198
199 assert_eq!(analysis.source_name, "test_rom_jp.iso");
200 assert_eq!(analysis.region, Region::JAPAN);
201 assert_eq!(analysis.region_string, "Japan (NTSC-J)");
202 assert_eq!(analysis.code, "SLPS");
203 Ok(())
204 }
205
206 #[test]
207 fn test_analyze_psx_data_unknown() -> Result<(), RomAnalyzerError> {
208 let data = vec![0; 0x2000];
209 let analysis = analyze_psx_data(&data, "test_rom.iso")?;
211
212 assert_eq!(analysis.source_name, "test_rom.iso");
213 assert_eq!(analysis.region, Region::UNKNOWN);
214 assert_eq!(analysis.region_string, "Unknown");
215 assert_eq!(analysis.code, "N/A");
216 assert_eq!(
217 analysis.print(),
218 "test_rom.iso\n\
219 System: Sony PlayStation (PSX)\n\
220 Region: Unknown\n\
221 Code: N/A\n\
222 Note: Executable prefix (SLUS/SLES/SLPS) not found in header area. Requires main data track (.bin or .iso)."
223 );
224 Ok(())
225 }
226
227 #[test]
228 fn test_analyze_psx_data_too_small() {
229 let data = vec![0; 100]; let result = analyze_psx_data(&data, "too_small.iso");
232 assert!(result.is_err());
233 assert!(result.unwrap_err().to_string().contains("too small"));
234 }
235
236 #[test]
237 fn test_analyze_psx_data_case_insensitivity() -> Result<(), RomAnalyzerError> {
238 let mut data = vec![0; 0x2000];
240 data[0x100..0x104].copy_from_slice(b"sLuS"); let analysis = analyze_psx_data(&data, "test_rom_mixedcase.iso")?;
242
243 assert_eq!(analysis.source_name, "test_rom_mixedcase.iso");
244 assert_eq!(analysis.region, Region::USA);
245 assert_eq!(analysis.region_string, "North America (NTSC-U)");
246 assert_eq!(analysis.code, "SLUS");
247 Ok(())
248 }
249}