use crate::archive::WszArchive;
use crate::error::{Result, WszError};
enum RegionType {
Main,
MainShade,
Equalizer,
EqualizerShade,
}
impl RegionType {
fn from_str(s: &str) -> Result<Self> {
match s {
"Normal" => Ok(Self::Main),
"WindowShade" => Ok(Self::MainShade),
"Equalizer" => Ok(Self::Equalizer),
"EqualizerWS" => Ok(Self::EqualizerShade),
_ => Err(WszError::InvalidFormat {
line: 0,
error: format!("Invalid region type: '{}'", s),
}),
}
}
}
struct Region {
pub num_points: Vec<usize>,
pub points: Vec<u32>,
}
impl Region {
fn new() -> Self {
Self {
num_points: Vec::new(),
points: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct Regions {
pub main: Option<Vec<Vec<(u32, u32)>>>,
pub main_shade: Option<Vec<Vec<(u32, u32)>>>,
pub equalizer: Option<Vec<Vec<(u32, u32)>>>,
pub equalizer_shade: Option<Vec<Vec<(u32, u32)>>>,
}
impl Regions {
fn new() -> Self {
Self {
main: None,
main_shade: None,
equalizer: None,
equalizer_shade: None,
}
}
pub fn from_archive(archive: &WszArchive) -> Result<Self> {
let region_txt = archive
.iter()
.find(|(name, _)| name.split('/').last().unwrap().to_lowercase() == "region.txt")
.ok_or(WszError::NotFound("region.txt".to_string()))?;
Self::from_string(&String::from_utf8_lossy(region_txt.1).to_string())
}
pub fn from_string(content: &str) -> Result<Self> {
let mut regions = Self::new();
let mut current_section = None;
let mut current_region = Region::new();
for (line_num, line) in content.lines().enumerate() {
let line = line.trim();
if line.is_empty() {
continue;
}
if line.starts_with(";") {
continue;
}
if line.starts_with('[') && line.ends_with(']') {
current_section = Some(RegionType::from_str(line[1..line.len() - 1].to_string().as_str())?);
current_region = Region::new();
continue;
}
if let Some(pos) = line.find('=') {
let key = line[..pos].trim();
let value = line[pos + 1..].trim();
match current_section {
Some(_) => {
match key.to_lowercase().as_str() {
"numpoints" => {
current_region.num_points =
value.split(',').map(|s| s.trim().parse::<usize>().unwrap()).collect();
}
"pointlist" => {
current_region.points = value
.split(|c: char| c.is_whitespace() || c == ',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse::<u32>().unwrap())
.collect();
}
_ => {
return Err(WszError::InvalidFormat {
line: line_num + 1,
error: format!("Invalid key: '{}'", key),
});
}
}
}
_ => {
return Err(WszError::InvalidFormat {
line: line_num + 1,
error: "Key-value pair outside of any section".to_string(),
});
}
}
} else {
return Err(WszError::InvalidFormat {
line: line_num + 1,
error: format!("Invalid line format: '{}'", line),
});
}
if current_section.is_some() && current_region.num_points.len() > 0 && current_region.points.len() > 0 {
if current_region.num_points.iter().sum::<usize>() != current_region.points.len() / 2 {
return Err(WszError::InvalidFormat {
line: line_num + 1,
error: "Number of points does not match number of points in the region".to_string(),
});
}
let mut points_list = Vec::new();
let mut current_offset = 0;
for i in 0..current_region.num_points.len() {
let mut points = Vec::new();
for j in 0..current_region.num_points[i] {
let index = (current_offset + j) * 2;
points.push((current_region.points[index], current_region.points[index + 1]));
}
points_list.push(points);
current_offset += current_region.num_points[i];
}
match current_section {
Some(RegionType::Main) => {
regions.main = Some(points_list);
}
Some(RegionType::MainShade) => {
regions.main_shade = Some(points_list);
}
Some(RegionType::Equalizer) => {
regions.equalizer = Some(points_list);
}
Some(RegionType::EqualizerShade) => {
regions.equalizer_shade = Some(points_list);
}
_ => {
unreachable!()
}
}
current_section = None;
current_region = Region::new();
}
}
Ok(regions)
}
}
impl Default for Regions {
fn default() -> Self {
Self::new()
}
}