use openstranded_map_tool::{parse_s2, s2_to_osmap, save_osmap_ron};
fn build_minimal_s2() -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(b"### Stranded II Mapfile \xa9by Unreal Software 2004-2007\r\n");
buf.extend_from_slice(b"1.0.0.1\r\n");
buf.extend_from_slice(b"10 Jul 2026\r\n");
buf.extend_from_slice(b"18:10:56\r\n");
buf.extend_from_slice(b"default\r\n");
buf.extend_from_slice(b"map\r\n");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"###\r\n");
buf.extend(std::iter::repeat(0u8).take(20736));
buf.push(0);
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(&1u32.to_le_bytes()); buf.push(9); buf.push(0); buf.push(3); buf.extend_from_slice(&3u32.to_le_bytes()); buf.extend_from_slice(b"sky");
buf.push(0); buf.push(0); let music = b"amb_jungle.mp3";
buf.extend_from_slice(&(music.len() as u32).to_le_bytes());
buf.extend_from_slice(music);
buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&[205, 210, 245, 0]); buf.push(0);
for _ in 0..10 {
buf.extend_from_slice(&[1, 0, 0, 0, b'0']);
}
buf.extend_from_slice(&128u32.to_le_bytes());
buf.extend(std::iter::repeat(0u8).take(128 * 128 * 3));
buf.extend_from_slice(&16u32.to_le_bytes());
for _ in 0..289 {
buf.extend_from_slice(&0.447_f32.to_le_bytes());
}
buf.extend(std::iter::repeat(0u8).take(16641));
buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&1u32.to_le_bytes());
buf.push(0); buf.push(0); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&6u32.to_le_bytes()); buf.extend_from_slice(&8u32.to_le_bytes()); buf.extend_from_slice(b"lasttime");
buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&5u32.to_le_bytes()); buf.extend_from_slice(b"1,9,2");
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(b"### EOF Map File\r\n");
buf.extend_from_slice(b"www.unrealsoftware.de\r\n");
buf
}
#[test]
fn test_parse_s2_minimal() {
let data = build_minimal_s2();
let s2 = parse_s2(&data).expect("should parse minimal .s2");
assert_eq!(s2.header.version, "1.0.0.1");
assert_eq!(s2.terrain_size, 16);
assert_eq!(s2.colormap_dim, 128);
assert!(s2.objects.is_empty());
assert!(s2.extensions.iter().any(|e| e.key == "lasttime"));
}
#[test]
fn test_convert_minimal_roundtrip() {
let data = build_minimal_s2();
let s2 = parse_s2(&data).expect("parse");
let osmap = s2_to_osmap(s2, "test.s2");
assert_eq!(osmap.meta.name, "test");
assert_eq!(osmap.terrain.size, 16);
assert!(osmap.entities.is_empty());
let ron = ron::to_string(&osmap).expect("serialize");
let deserialized: openstranded_map_tool::OpenStrandedMap =
ron::from_str(&ron).expect("deserialize");
assert_eq!(deserialized.terrain.size, 16);
assert_eq!(deserialized.meta.name, "test");
}
#[test]
fn test_convert_minimal_to_file() {
let tmp = tempfile::NamedTempFile::new().expect("tempfile");
let out_path = tmp.path().with_extension("osmap");
let data = build_minimal_s2();
let s2 = parse_s2(&data).expect("parse");
let osmap = s2_to_osmap(s2, "test.s2");
save_osmap_ron(&osmap, &out_path).expect("save");
let content = std::fs::read_to_string(&out_path).expect("read");
assert!(content.contains("terrain"));
assert!(content.contains("test"));
let parsed: openstranded_map_tool::OpenStrandedMap =
ron::from_str(&content).expect("parse osmap");
assert_eq!(parsed.terrain.size, 16);
}
#[test]
fn test_convert_with_entities() {
let mut buf2 = Vec::new();
buf2.extend_from_slice(b"### Stranded II Mapfile \xa9by Unreal Software 2004-2007\r\n");
buf2.extend_from_slice(b"1.0.0.1\r\n");
buf2.extend_from_slice(b"10 Jul 2026\r\n");
buf2.extend_from_slice(b"18:10:56\r\n");
buf2.extend_from_slice(b"default\r\n");
buf2.extend_from_slice(b"map\r\n");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"###\r\n");
buf2.extend(std::iter::repeat(0u8).take(20736));
buf2.push(0);
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(&1u32.to_le_bytes());
buf2.push(9);
buf2.push(0);
buf2.push(3);
buf2.extend_from_slice(&3u32.to_le_bytes());
buf2.extend_from_slice(b"sky");
buf2.push(0);
buf2.push(0);
let music = b"amb_jungle.mp3";
buf2.extend_from_slice(&(music.len() as u32).to_le_bytes());
buf2.extend_from_slice(music);
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&[205, 210, 245, 0]);
buf2.push(0);
for _ in 0..10 {
buf2.extend_from_slice(&[1, 0, 0, 0, b'0']);
}
buf2.extend_from_slice(&128u32.to_le_bytes());
buf2.extend(std::iter::repeat(0u8).take(128 * 128 * 3));
buf2.extend_from_slice(&16u32.to_le_bytes());
for _ in 0..289 {
buf2.extend_from_slice(&0.447_f32.to_le_bytes());
}
buf2.extend(std::iter::repeat(0u8).take(16641));
buf2.extend_from_slice(&1u32.to_le_bytes()); buf2.extend_from_slice(&1u32.to_le_bytes()); buf2.push(5); buf2.extend_from_slice(&10.0_f32.to_le_bytes()); buf2.extend_from_slice(&20.0_f32.to_le_bytes()); buf2.extend_from_slice(&90.0_f32.to_le_bytes()); buf2.extend_from_slice(&100.0_f32.to_le_bytes()); buf2.extend_from_slice(&100.0_f32.to_le_bytes()); buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&1u32.to_le_bytes());
buf2.push(0);
buf2.push(0);
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&6u32.to_le_bytes());
buf2.extend_from_slice(&8u32.to_le_bytes());
buf2.extend_from_slice(b"lasttime");
buf2.extend_from_slice(&0u32.to_le_bytes());
buf2.extend_from_slice(&5u32.to_le_bytes());
buf2.extend_from_slice(b"1,9,2");
buf2.extend_from_slice(b"\r\n");
buf2.extend_from_slice(b"### EOF Map File\r\n");
buf2.extend_from_slice(b"www.unrealsoftware.de\r\n");
let s2 = parse_s2(&buf2).expect("parse with object");
assert_eq!(s2.objects.len(), 1);
assert_eq!(s2.objects[0].id, 1);
assert_eq!(s2.objects[0].typ, 5);
assert_eq!(s2.objects[0].x, 10.0);
let osmap = s2_to_osmap(s2, "test.s2");
assert_eq!(osmap.entities.len(), 1);
assert_eq!(osmap.entities[0].class, "object");
assert_eq!(osmap.entities[0].type_id, 5);
assert_eq!(osmap.entities[0].id, 1);
let (px, py, pz) = osmap.entities[0].position;
assert!((px - 0.15625).abs() < 1e-4, "expected 0.15625, got {px}");
assert!((py - 5.82).abs() < 1e-4, "expected 5.82, got {py}");
assert!((pz - 0.3125).abs() < 1e-4, "expected 0.3125, got {pz}");
let ron = ron::to_string(&osmap).expect("serialize");
let back: openstranded_map_tool::OpenStrandedMap =
ron::from_str(&ron).expect("deserialize");
assert_eq!(back.entities.len(), 1);
}