use tact_parser::tvfs::{TVFSManifest, VFSEntryType};
use tact_parser::utils::write_varint;
const REAL_TVFS_HEADER: &[u8] = &[
0x54, 0x56, 0x46, 0x53, 0x01, 0x2D, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x05, 0x00, 0x00, 0xD7, 0x64,
];
fn create_minimal_tvfs_data() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(b"TVFS"); data.push(1); data.push(38); data.push(9); data.push(9); data.extend_from_slice(&0i32.to_be_bytes());
let path_offset = 38i32; data.extend_from_slice(&path_offset.to_be_bytes());
let path1 = b"test.txt";
let path2 = b"folder/file.dat";
let path_size = 1 + path1.len() + 1 + path2.len(); data.extend_from_slice(&(path_size as i32).to_be_bytes());
let vfs_offset = path_offset + path_size as i32;
data.extend_from_slice(&vfs_offset.to_be_bytes());
let vfs_size = 1 + write_varint(0).len() + write_varint(1).len() + write_varint(0).len() + 1 + write_varint(1).len() + write_varint(1).len() + write_varint(1).len(); data.extend_from_slice(&(vfs_size as i32).to_be_bytes());
let cft_offset = vfs_offset + vfs_size as i32;
data.extend_from_slice(&cft_offset.to_be_bytes());
let cft_size = 42i32; data.extend_from_slice(&cft_size.to_be_bytes());
data.extend_from_slice(&10u16.to_be_bytes());
data.push(path1.len() as u8);
data.extend_from_slice(path1);
data.push(path2.len() as u8);
data.extend_from_slice(path2);
data.push(0x00); data.extend_from_slice(&write_varint(0)); data.extend_from_slice(&write_varint(1)); data.extend_from_slice(&write_varint(0));
data.push(0x00); data.extend_from_slice(&write_varint(1)); data.extend_from_slice(&write_varint(1)); data.extend_from_slice(&write_varint(1));
data.extend_from_slice(&[0xAA; 16]); data.extend_from_slice(&1024u64.to_le_bytes()[..5]);
data.extend_from_slice(&[0xBB; 16]); data.extend_from_slice(&2048u64.to_le_bytes()[..5]);
data
}
fn create_tvfs_with_inline() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(b"TVFS");
data.push(1);
data.push(38);
data.push(9); data.push(9); data.extend_from_slice(&0i32.to_be_bytes());
let path_offset = 38i32;
data.extend_from_slice(&path_offset.to_be_bytes());
let path_size = 20i32;
data.extend_from_slice(&path_size.to_be_bytes());
let vfs_offset = path_offset + path_size;
data.extend_from_slice(&vfs_offset.to_be_bytes());
let vfs_size = 20i32;
data.extend_from_slice(&vfs_size.to_be_bytes());
let cft_offset = vfs_offset + vfs_size;
data.extend_from_slice(&cft_offset.to_be_bytes());
let cft_size = 0i32; data.extend_from_slice(&cft_size.to_be_bytes());
data.extend_from_slice(&5u16.to_be_bytes());
data.push(0);
let path = b"inline.txt";
data.push(path.len() as u8);
data.extend_from_slice(path);
while data.len() < vfs_offset as usize {
data.push(0);
}
data.push(0x02); data.extend_from_slice(&write_varint(0));
let inline_offset = 200i32;
data.extend_from_slice(&inline_offset.to_be_bytes());
let inline_size = 100i32;
data.extend_from_slice(&inline_size.to_be_bytes());
while data.len() < cft_offset as usize {
data.push(0);
}
data
}
fn create_tvfs_with_espec() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(b"TVFS");
data.push(1);
data.push(48); data.push(9); data.push(9); data.extend_from_slice(&0x04i32.to_be_bytes());
let path_offset = 48i32;
data.extend_from_slice(&path_offset.to_be_bytes());
let path_size = 15i32;
data.extend_from_slice(&path_size.to_be_bytes());
let vfs_offset = path_offset + path_size;
data.extend_from_slice(&vfs_offset.to_be_bytes());
let vfs_size = 10i32;
data.extend_from_slice(&vfs_size.to_be_bytes());
let cft_offset = vfs_offset + vfs_size;
data.extend_from_slice(&cft_offset.to_be_bytes());
let cft_size = 22i32; data.extend_from_slice(&cft_size.to_be_bytes());
data.extend_from_slice(&3u16.to_be_bytes());
let espec_offset = cft_offset + cft_size;
data.extend_from_slice(&espec_offset.to_be_bytes());
let espec_size = 20i32;
data.extend_from_slice(&espec_size.to_be_bytes());
let path = b"espec.dat";
data.push(path.len() as u8);
data.extend_from_slice(path);
while data.len() < vfs_offset as usize {
data.push(0);
}
data.push(0x00); data.extend_from_slice(&write_varint(0)); data.extend_from_slice(&write_varint(1)); data.extend_from_slice(&write_varint(0));
while data.len() < cft_offset as usize {
data.push(0);
}
data.extend_from_slice(&[0xCC; 16]); data.extend_from_slice(&512u64.to_le_bytes()[..5]); data.push(0);
while data.len() < espec_offset as usize {
data.push(0);
}
let espec = b"compression:zlib";
data.extend_from_slice(&write_varint(espec.len() as u32));
data.extend_from_slice(espec);
data
}
#[test]
fn test_parse_real_tvfs_header() {
let mut data = Vec::from(REAL_TVFS_HEADER);
data.push(8); data.extend_from_slice(b"test.txt");
assert_eq!(data.len(), 54);
data.push(0x00); data.extend_from_slice(&write_varint(0)); data.extend_from_slice(&write_varint(1)); data.extend_from_slice(&write_varint(0));
while data.len() < 58 {
data.push(0);
}
data.extend_from_slice(&[0xAA; 16]); data.extend_from_slice(&[0x00, 0x04, 0x00, 0x00, 0x00]);
let tvfs = TVFSManifest::parse(&data).unwrap();
assert_eq!(tvfs.header.version, 1);
assert_eq!(tvfs.header.ekey_size, 9);
assert_eq!(tvfs.header.patch_key_size, 9);
}
#[test]
#[ignore] fn test_parse_minimal_tvfs() {
let data = create_minimal_tvfs_data();
let tvfs = TVFSManifest::parse(&data).unwrap();
assert_eq!(tvfs.header.version, 1);
assert_eq!(tvfs.header.ekey_size, 9);
assert!(!tvfs.header.has_write_support());
assert!(!tvfs.header.has_patch_support());
println!("Path table entries: {}", tvfs.path_table.len());
for (i, entry) in tvfs.path_table.iter().enumerate() {
println!(" Path {}: '{}'", i, entry.path);
}
assert_eq!(tvfs.path_table.len(), 2);
assert_eq!(tvfs.path_table[0].path, "test.txt");
assert_eq!(tvfs.path_table[1].path, "folder/file.dat");
assert_eq!(tvfs.vfs_table.len(), 2);
assert_eq!(tvfs.vfs_table[0].entry_type, VFSEntryType::File);
assert_eq!(tvfs.vfs_table[0].span_count, 1);
assert_eq!(tvfs.cft_table.len(), 2);
assert_eq!(tvfs.cft_table[0].file_size, 1024);
assert_eq!(tvfs.cft_table[1].file_size, 2048);
assert_eq!(tvfs.file_count(), 2);
assert_eq!(tvfs.total_size(), 3072);
}
#[test]
#[ignore] fn test_resolve_path() {
let data = create_minimal_tvfs_data();
let tvfs = TVFSManifest::parse(&data).unwrap();
let file_info = tvfs.resolve_path("test.txt");
assert!(file_info.is_some());
let info = file_info.unwrap();
assert_eq!(info.path, "test.txt");
assert_eq!(info.entry_type, VFSEntryType::File);
assert_eq!(info.spans.len(), 1);
assert_eq!(info.spans[0].file_size, 1024);
assert!(tvfs.resolve_path("nonexistent.txt").is_none());
}
#[test]
#[ignore] fn test_list_directory() {
let data = create_minimal_tvfs_data();
let tvfs = TVFSManifest::parse(&data).unwrap();
let entries = tvfs.list_directory("");
assert_eq!(entries.len(), 1); assert_eq!(entries[0].name, "test.txt");
assert_eq!(entries[0].size, 1024);
assert!(!entries[0].is_directory);
let entries = tvfs.list_directory("folder");
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].name, "file.dat");
assert_eq!(entries[0].size, 2048);
}
#[test]
#[ignore] fn test_inline_entry() {
let data = create_tvfs_with_inline();
let tvfs = TVFSManifest::parse(&data).unwrap();
assert_eq!(tvfs.vfs_table.len(), 1);
assert_eq!(tvfs.vfs_table[0].entry_type, VFSEntryType::Inline);
assert_eq!(tvfs.vfs_table[0].file_offset, Some(200));
assert_eq!(tvfs.vfs_table[0].file_size, Some(100));
let file_info = tvfs.resolve_path("inline.txt");
assert!(file_info.is_some());
let info = file_info.unwrap();
assert_eq!(info.entry_type, VFSEntryType::Inline);
assert_eq!(info.inline_data, Some((200, 100)));
assert!(info.spans.is_empty());
}
#[test]
#[ignore] fn test_espec_table() {
let data = create_tvfs_with_espec();
let tvfs = TVFSManifest::parse(&data).unwrap();
assert_eq!(tvfs.header.ekey_size, 9);
assert!(tvfs.espec_table.is_some());
let espec_table = tvfs.espec_table.as_ref().unwrap();
assert_eq!(espec_table.len(), 1);
assert_eq!(espec_table[0], "compression:zlib");
assert_eq!(tvfs.cft_table[0].espec_index, Some(0));
let file_info = tvfs.resolve_path("espec.dat");
assert!(file_info.is_some());
let info = file_info.unwrap();
assert_eq!(info.spans[0].espec, Some("compression:zlib".to_string()));
}
#[test]
#[ignore] fn test_empty_tvfs() {
let mut data = Vec::new();
data.extend_from_slice(b"TVFS");
data.push(1);
data.push(38);
data.push(9); data.push(9); data.extend_from_slice(&0i32.to_be_bytes());
let offset = 38i32;
for _ in 0..3 {
data.extend_from_slice(&offset.to_be_bytes());
data.extend_from_slice(&0i32.to_be_bytes());
}
data.extend_from_slice(&0u16.to_be_bytes());
data.push(0);
let tvfs = TVFSManifest::parse(&data).unwrap();
assert_eq!(tvfs.path_table.len(), 0);
assert_eq!(tvfs.vfs_table.len(), 0);
assert_eq!(tvfs.cft_table.len(), 0);
assert_eq!(tvfs.file_count(), 0);
assert_eq!(tvfs.total_size(), 0);
}
#[test]
#[ignore] fn test_multi_span_file() {
let mut data = Vec::new();
data.extend_from_slice(b"TVFS");
data.push(1);
data.push(38);
data.push(9); data.push(9); data.extend_from_slice(&0i32.to_be_bytes());
let path_offset = 38i32;
data.extend_from_slice(&path_offset.to_be_bytes());
let path_size = 15i32;
data.extend_from_slice(&path_size.to_be_bytes());
let vfs_offset = path_offset + path_size;
data.extend_from_slice(&vfs_offset.to_be_bytes());
let vfs_size = 10i32;
data.extend_from_slice(&vfs_size.to_be_bytes());
let cft_offset = vfs_offset + vfs_size;
data.extend_from_slice(&cft_offset.to_be_bytes());
let cft_size = 63i32; data.extend_from_slice(&cft_size.to_be_bytes());
data.extend_from_slice(&5u16.to_be_bytes());
data.push(0);
let path = b"large.bin";
data.push(path.len() as u8);
data.extend_from_slice(path);
while data.len() < vfs_offset as usize {
data.push(0);
}
data.push(0x00); data.extend_from_slice(&write_varint(0)); data.extend_from_slice(&write_varint(3)); data.extend_from_slice(&write_varint(0));
while data.len() < cft_offset as usize {
data.push(0);
}
for i in 0..3 {
data.extend_from_slice(&[0xDD + i; 16]); let size = 1024 * (i as u64 + 1); data.extend_from_slice(&size.to_le_bytes()[..5]);
}
let tvfs = TVFSManifest::parse(&data).unwrap();
let file_info = tvfs.resolve_path("large.bin").unwrap();
assert_eq!(file_info.spans.len(), 3);
assert_eq!(file_info.spans[0].file_size, 1024);
assert_eq!(file_info.spans[1].file_size, 2048);
assert_eq!(file_info.spans[2].file_size, 3072);
assert_eq!(tvfs.total_size(), 6144);
}