Fix SplitFileReader logic for parts

This commit is contained in:
Luke Street
2026-06-09 13:13:01 -06:00
parent 0576dde215
commit 682abf7d1a
+82 -11
View File
@@ -28,21 +28,19 @@ fn split_path_1(input: &Path, index: u32) -> PathBuf {
let input_str = input.to_str().unwrap_or("[INVALID]");
let mut out = input_str.to_string();
out.push('.');
out.push(char::from_digit(index, 10).unwrap());
out.push_str(&index.to_string());
PathBuf::from(out)
}
// .part1.iso, .part2.iso, etc.
fn split_path_2(input: &Path, index: u32) -> PathBuf {
let extension = input.extension().and_then(|s| s.to_str()).unwrap_or("iso");
let input_without_ext = input.with_extension("");
let input_str = input_without_ext.to_str().unwrap_or("[INVALID]");
let mut out = input_str.to_string();
out.push_str(".part");
out.push(char::from_digit(index, 10).unwrap());
out.push('.');
out.push_str(extension);
PathBuf::from(out)
let stem = input.file_stem().and_then(|s| s.to_str()).unwrap_or("[INVALID]");
let base_stem = stem
.rsplit_once(".part")
.filter(|(_, suffix)| !suffix.is_empty() && suffix.chars().all(|c| c.is_ascii_digit()))
.map_or(stem, |(base, _)| base);
input.with_file_name(format!("{base_stem}.part{index}.{extension}"))
}
// .wbf1, .wbf2, etc.
@@ -51,7 +49,7 @@ fn split_path_3(input: &Path, index: u32) -> PathBuf {
let mut chars = input_str.chars();
chars.next_back();
let mut out = chars.as_str().to_string();
out.push(char::from_digit(index, 10).unwrap());
out.push_str(&index.to_string());
PathBuf::from(out)
}
@@ -72,17 +70,23 @@ impl SplitFileReader {
}
for path_fn in [split_path_1, split_path_2, split_path_3] {
let mut index = 1;
let mut found_split = false;
loop {
let path = path_fn(path, index);
if path == files[0].inner {
index += 1;
continue;
}
if let Ok(metadata) = path.metadata() {
files.push(Split { inner: path, begin, size: metadata.len() });
begin += metadata.len();
index += 1;
found_split = true;
} else {
break;
}
}
if index > 1 {
if found_split {
break;
}
}
@@ -140,3 +144,70 @@ impl DiscStream for SplitFileReader {
fn stream_len(&mut self) -> io::Result<u64> { Ok(self.len()) }
}
#[cfg(test)]
mod tests {
use std::{
fs,
time::{SystemTime, UNIX_EPOCH},
};
use super::*;
struct TestDir(PathBuf);
impl TestDir {
fn new() -> Self {
let mut path = std::env::temp_dir();
let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
path.push(format!("nod-split-test-{}-{nanos}", std::process::id()));
fs::create_dir(&path).unwrap();
Self(path)
}
fn path(&self) -> &Path { &self.0 }
}
impl Drop for TestDir {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
#[test]
fn part_zero_input_builds_next_part_paths() {
let input = Path::new("/tmp/game.part0.iso");
assert_eq!(split_path_2(input, 1), PathBuf::from("/tmp/game.part1.iso"));
assert_eq!(split_path_2(input, 10), PathBuf::from("/tmp/game.part10.iso"));
}
#[test]
fn reads_part_zero_sequence_across_multiple_parts() {
let dir = TestDir::new();
for index in 0..=10 {
let path = dir.path().join(format!("game.part{index}.iso"));
fs::write(path, [index as u8]).unwrap();
}
let input = dir.path().join("game.part0.iso");
let mut reader = SplitFileReader::new(&input).unwrap();
let mut buf = [0; 11];
reader.read_exact_at(&mut buf, 0).unwrap();
assert_eq!(reader.len(), 11);
assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}
#[test]
fn numbered_input_is_not_added_twice() {
let dir = TestDir::new();
let input = dir.path().join("game.part1.iso");
fs::write(&input, [1]).unwrap();
let reader = SplitFileReader::new(&input).unwrap();
assert_eq!(reader.len(), 1);
assert_eq!(reader.files.len(), 1);
}
}