rom_analyzer/console/
psx.rs1use std::error::Error;
7
8use serde::Serialize;
9
10use crate::error::RomAnalyzerError;
11use crate::region::{Region, check_region_mismatch};
12
13#[derive(Debug, PartialEq, Clone, Serialize)]
15pub struct PsxAnalysis {
16 pub source_name: String,
18 pub region: Region,
20 pub region_string: String,
22 pub region_mismatch: bool,
24 pub code: String,
26}
27
28impl PsxAnalysis {
29 pub fn print(&self) -> String {
31 let executable_prefix_not_found = if self.code == "N/A" {
32 "\nNote: Executable prefix (SLUS/SLES/SLPS) not found in header area. Requires main data track (.bin or .iso)."
33 } else {
34 ""
35 };
36 format!(
37 "{}\n\
38 System: Sony PlayStation (PSX)\n\
39 Region: {}\n\
40 Code: {}\
41 {}",
42 self.source_name, self.region, self.code, executable_prefix_not_found
43 )
44 }
45}
46
47pub fn map_region(region_code: &str) -> (&'static str, Region) {
86 match region_code {
87 "SLUS" => ("North America (NTSC-U)", Region::USA),
88 "SLES" => ("Europe (PAL)", Region::EUROPE),
89 "SLPS" => ("Japan (NTSC-J)", Region::JAPAN),
90 _ => ("Unknown", Region::UNKNOWN),
91 }
92}
93
94pub fn analyze_psx_data(data: &[u8], source_name: &str) -> Result<PsxAnalysis, Box<dyn Error>> {
112 let check_size = std::cmp::min(data.len(), 0x20000);
114 if check_size < 0x2000 {
115 return Err(Box::new(RomAnalyzerError::new(
117 "PSX boot file too small for reliable analysis.",
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 use std::error::Error;
158
159 #[test]
160 fn test_analyze_psx_data_slus() -> Result<(), Box<dyn Error>> {
161 let mut data = vec![0; 0x2000];
163 data[0x100..0x104].copy_from_slice(b"SLUS"); let analysis = analyze_psx_data(&data, "test_rom_us.iso")?;
166
167 assert_eq!(analysis.source_name, "test_rom_us.iso");
168 assert_eq!(analysis.region, Region::USA);
169 assert_eq!(analysis.region_string, "North America (NTSC-U)");
170 assert_eq!(analysis.code, "SLUS");
171 Ok(())
172 }
173
174 #[test]
175 fn test_analyze_psx_data_sles() -> Result<(), Box<dyn Error>> {
176 let mut data = vec![0; 0x2000];
177 data[0x100..0x104].copy_from_slice(b"SLES"); let analysis = analyze_psx_data(&data, "test_rom_eur.iso")?;
179
180 assert_eq!(analysis.source_name, "test_rom_eur.iso");
181 assert_eq!(analysis.region, Region::EUROPE);
182 assert_eq!(analysis.region_string, "Europe (PAL)");
183 assert_eq!(analysis.code, "SLES");
184 Ok(())
185 }
186
187 #[test]
188 fn test_analyze_psx_data_slps() -> Result<(), Box<dyn Error>> {
189 let mut data = vec![0; 0x2000];
190 data[0x100..0x104].copy_from_slice(b"SLPS"); let analysis = analyze_psx_data(&data, "test_rom_jp.iso")?;
192
193 assert_eq!(analysis.source_name, "test_rom_jp.iso");
194 assert_eq!(analysis.region, Region::JAPAN);
195 assert_eq!(analysis.region_string, "Japan (NTSC-J)");
196 assert_eq!(analysis.code, "SLPS");
197 Ok(())
198 }
199
200 #[test]
201 fn test_analyze_psx_data_unknown() -> Result<(), Box<dyn Error>> {
202 let data = vec![0; 0x2000];
203 let analysis = analyze_psx_data(&data, "test_rom.iso")?;
205
206 assert_eq!(analysis.source_name, "test_rom.iso");
207 assert_eq!(analysis.region, Region::UNKNOWN);
208 assert_eq!(analysis.region_string, "Unknown");
209 assert_eq!(analysis.code, "N/A");
210 Ok(())
211 }
212
213 #[test]
214 fn test_analyze_psx_data_too_small() {
215 let data = vec![0; 100]; let result = analyze_psx_data(&data, "too_small.iso");
218 assert!(result.is_err());
219 assert!(result.unwrap_err().to_string().contains("too small"));
220 }
221
222 #[test]
223 fn test_analyze_psx_data_case_insensitivity() -> Result<(), Box<dyn Error>> {
224 let mut data = vec![0; 0x2000];
226 data[0x100..0x104].copy_from_slice(b"sLuS"); let analysis = analyze_psx_data(&data, "test_rom_mixedcase.iso")?;
228
229 assert_eq!(analysis.source_name, "test_rom_mixedcase.iso");
230 assert_eq!(analysis.region, Region::USA);
231 assert_eq!(analysis.region_string, "North America (NTSC-U)");
232 assert_eq!(analysis.code, "SLUS");
233 Ok(())
234 }
235}