~quf/tocs

b47f195da0e88ec81e6a29e357f46f899b5c73fe — Lukas Himbert 2 months ago b5ea391
roundtrip tests for reverie scripts
3 files changed, 62 insertions(+), 41 deletions(-)

M tocs/src/aldat/deser.rs
M tocs/src/aldat/ser.rs
M tocs/src/aldat/test.rs
M tocs/src/aldat/deser.rs => tocs/src/aldat/deser.rs +7 -1
@@ 48,26 48,32 @@ pub fn parse(data: &[u8]) -> Option<AlDat> {
    let unk2 = reader.read_chunk::<4>()?;
    let script_name = reader.read_c_ascii_str().ok()?;

    println!("offset after script name = 0x{:x}, table list offset = 0x{:?}", reader.offset(), table_list_offset);

    // read offsets of table names and data
    reader.set_offset(usize::try_from(table_list_offset).unwrap());

    let mut table_name_offsets = std::vec::Vec::with_capacity(n_tables);
    let mut table_data_offsets = std::vec::Vec::with_capacity(n_tables);

    println!("before data offsets 0x{:x}", reader.offset());
    for _ in 0..n_tables {
        let off = reader.read_u32_le()?;
        ensure!(off % 4 == 0);
        table_data_offsets.push(off);
    }
    println!("after data offsets 0x{:x}", reader.offset());
    for _ in 0..n_tables {
        table_name_offsets.push(reader.read_u16_le()?);
    }

    println!("after name offsets 0x{:x}", reader.offset());

    // read table names and contents, record alignments
    let mut tables = std::vec::Vec::with_capacity(n_tables);
    let mut alignment = std::collections::BTreeMap::new();
    for i in 0..n_tables {
        println!("start {i}");
        println!("start {i} (name offset 0x{:x})", table_name_offsets[i]);
        let name = reader.clone_at_offset(table_name_offsets[i].try_into().unwrap()).read_c_ascii_str().ok()?;

        // we assume the table data is always sorted. it seems to work out.

M tocs/src/aldat/ser.rs => tocs/src/aldat/ser.rs +2 -0
@@ 20,6 20,7 @@ pub fn serialize(aldat: &AlDat) -> Option<std::vec::Vec<u8>> {
    };

    let table_list_offset: usize = 33 + aldat.name.len_with_padding().get() + padding_before_table_list;
    println!("ser table_list_offset = 0x{table_list_offset:x}");
    writer.write_u32_le(table_list_offset.try_into().unwrap());

    // redundant stuff


@@ 32,6 33,7 @@ pub fn serialize(aldat: &AlDat) -> Option<std::vec::Vec<u8>> {

    // calculate table data offsets and name offsets
    let table_names_offset: usize = table_list_offset + 6 * aldat.entries.len();
    dbg!(table_names_offset);
    let table_names_end_offset = table_names_offset + aldat.entries.iter().map(|ent| ent.name_len_with_padding()).sum::<usize>();
    let table_data_padding = if table_names_end_offset % 4 == 0 { 0 } else { 4 - table_names_end_offset % 4 };
    let table_data_offset = table_names_end_offset + table_data_padding;

M tocs/src/aldat/test.rs => tocs/src/aldat/test.rs +53 -40
@@ 11,48 11,61 @@ fn compare_data(expected: &[u8], actual: &[u8]) {
}

#[test]
fn test_battle_scripts_reverie() {
    let dir = "private-test-data/cs5/nisa-pc-1.0.6/scripts/battle/dat_en/";
    //let dir = "private-test-data/cs5/nisa-pc-1.0.6/scripts/scena/dat_en/";
    //let dir = "private-test-data/cs5/nisa-pc-1.0.6/scripts/ani/dat_en/";
    for dirent in std::fs::read_dir(dir).unwrap() {
        let path = dirent.unwrap().path();
        println!("{}", path.display());
        let data = std::fs::read(path).unwrap();
        let aldat = parse(data.as_slice()).unwrap();
        //println!("{:?}", aldat);
        let roundtripped = serialize(&aldat).unwrap();
        std::fs::write("/tmp/foo.tmp", &roundtripped).unwrap();
        compare_data(&data, &roundtripped);
    }
}
fn test_scripts_reverie() {
    let roundtrip_fails: std::collections::BTreeSet<&'static str> = [
        "ani/dat_en/chr003_mg16.dat",
        "ani/dat_en/chr970_c00.dat",
        "ani/dat_en/mon000s.dat",
        "ani/dat_en/mon027_c00.dat",
        "ani/dat_en/mon037_c00.dat",
        "ani/dat_en/mon042_c00.dat",
        "ani/dat_en/mon042_c01.dat",
        "ani/dat_en/mon046_c00.dat",
        "ani/dat_en/mon093.dat",
        "ani/dat_en/mon426.dat",
        "ani/dat_en/mon_171.dat",
        "ani/dat_en/mon_template.dat",
        "ani/dat_en/npcx00.dat",
        "ani/dat_en/npcx02.dat",
        "ani/dat_en/npcx03.dat",
        "ani/dat_en/npcx04.dat",
        "ani/dat_en/ply000.dat",
        "ani/dat_en/rob030.dat",
        "ani/dat_en/chr_enemy_template.dat",
        "ani/dat_en/rob013_c00.dat",
        "ani/dat_en/ply001.dat",
        "scena/dat_en/a0106.dat",
    ]
    .into_iter()
    .collect();

#[test]
fn test_tmp() {
    /*
    let dir = "private-test-data/cs5/nisa-pc-1.0.6/scripts/battle/dat_en/";
    let skip = [
        "btl1000.dat",
        "btl1001.dat",
        "btlwin.dat",
        "btl_00_01_02.dat",
        "btl_A1_11_01.dat",
        "btl_A4_20_01.dat",
        "btl_TU_00_00_00.dat",
        "btl_TU_00_02_00.dat",
        "btl_TU_00_03_00.dat",
        "btl_TU_A1_03_00.dat",
        "btl_TU_B1_06_00.dat",
        "btlsys.dat",
    ];
    for dirent in std::fs::read_dir(dir).unwrap() {
        let path = dirent.unwrap().path();
        if skip.iter().any(|f| path.ends_with(f)) {
            println!("{}", path.display());
    let base = std::path::Path::new("private-test-data/cs5/nisa-pc-1.0.6/scripts/");
    for script_folder_dirent in std::fs::read_dir(base).unwrap() {
        let dir = script_folder_dirent.unwrap().path().join("dat_en");
        for dirent in std::fs::read_dir(dir).unwrap() {
            let path = dirent.unwrap().path();
            let relative_path = path.strip_prefix(base).unwrap().to_str().unwrap();
            if roundtrip_fails.contains(relative_path) {
                println!("SKIPPING {relative_path}");
                continue;
            }
            println!("{relative_path}");
            let data = std::fs::read(path).unwrap();
            assert!(parse(data.as_slice()).is_none());
            let aldat = parse(data.as_slice()).unwrap();
            //println!("{:?}", aldat);
            let roundtripped = serialize(&aldat).unwrap();
            //std::fs::write("/tmp/foo.tmp", &roundtripped).unwrap();
            compare_data(&data, &roundtripped);
        }
    }
    todo!()
    */

    for relative_path in roundtrip_fails.iter().copied() {
        let path = base.join(relative_path);
        println!("{relative_path}");
        let data = std::fs::read(&path).unwrap();
        let aldat = parse(data.as_slice()).unwrap();
        //println!("{:?}", aldat);
        let roundtripped = serialize(&aldat).unwrap();
        assert!(data != roundtripped);
    }
}