mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 00:55:15 +08:00
Persist official version-state, extend CAS/ResourceRepository import classification, and add offline catalog and failure regression fixtures. Fixes #13 Fixes #12 Fixes #8
123 lines
4.2 KiB
Rust
123 lines
4.2 KiB
Rust
use bat_adapters::manifest::ManifestDriverRegistry;
|
|
use bat_core::repositories::cas_repository::CasRepository;
|
|
use bat_core::repositories::resource_repository::{ResourceQuery, ResourceRepository};
|
|
use bat_infrastructure::{
|
|
BundleSource, FileSystemCasRepository, InMemoryResourceRepository, ResourceImportService,
|
|
};
|
|
use serde_json::json;
|
|
use tempfile::TempDir;
|
|
|
|
fn push_c_string(data: &mut Vec<u8>, value: &str) {
|
|
data.extend_from_slice(value.as_bytes());
|
|
data.push(0);
|
|
}
|
|
|
|
fn push_u16(data: &mut Vec<u8>, value: u16) {
|
|
data.extend_from_slice(&value.to_be_bytes());
|
|
}
|
|
|
|
fn push_u32(data: &mut Vec<u8>, value: u32) {
|
|
data.extend_from_slice(&value.to_be_bytes());
|
|
}
|
|
|
|
fn push_i32(data: &mut Vec<u8>, value: i32) {
|
|
data.extend_from_slice(&value.to_be_bytes());
|
|
}
|
|
|
|
fn push_u64(data: &mut Vec<u8>, value: u64) {
|
|
data.extend_from_slice(&value.to_be_bytes());
|
|
}
|
|
|
|
fn align(data: &mut Vec<u8>, alignment: usize) {
|
|
let remainder = data.len() % alignment;
|
|
if remainder != 0 {
|
|
data.resize(data.len() + alignment - remainder, 0);
|
|
}
|
|
}
|
|
|
|
fn synthetic_minimal_unityfs_bundle() -> Vec<u8> {
|
|
let mut blocks_info = Vec::new();
|
|
blocks_info.extend_from_slice(&[0; 16]);
|
|
push_i32(&mut blocks_info, 1);
|
|
push_u32(&mut blocks_info, 4);
|
|
push_u32(&mut blocks_info, 4);
|
|
push_u16(&mut blocks_info, 0);
|
|
push_i32(&mut blocks_info, 1);
|
|
push_u64(&mut blocks_info, 0);
|
|
push_u64(&mut blocks_info, 4);
|
|
push_u32(&mut blocks_info, 0);
|
|
push_c_string(&mut blocks_info, "SYNTHETIC-GOLDEN-CAB");
|
|
|
|
let mut data = Vec::new();
|
|
push_c_string(&mut data, "UnityFS");
|
|
push_u32(&mut data, 8);
|
|
push_c_string(&mut data, "5.x.x");
|
|
push_c_string(&mut data, "2021.3.56f2");
|
|
push_u64(&mut data, 0);
|
|
push_u32(&mut data, blocks_info.len() as u32);
|
|
push_u32(&mut data, blocks_info.len() as u32);
|
|
push_u32(&mut data, 0);
|
|
align(&mut data, 16);
|
|
data.extend_from_slice(&blocks_info);
|
|
data.extend_from_slice(b"data");
|
|
|
|
let total_size = data.len() as u64;
|
|
let total_size_offset = b"UnityFS\0".len() + 4 + b"5.x.x\0".len() + b"2021.3.56f2\0".len();
|
|
data[total_size_offset..total_size_offset + 8].copy_from_slice(&total_size.to_be_bytes());
|
|
data
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn imports_synthetic_addressables_catalog_bundle_against_golden() {
|
|
let catalog = include_bytes!("fixtures/synthetic_phase2/catalog.json");
|
|
let manifest = ManifestDriverRegistry::with_defaults()
|
|
.parse(catalog)
|
|
.await
|
|
.unwrap();
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let cas = FileSystemCasRepository::new(temp_dir.path().join("cas"));
|
|
let resources = InMemoryResourceRepository::new();
|
|
let service = ResourceImportService::new(&cas, &resources);
|
|
|
|
let report = service
|
|
.import_manifest_bundles(
|
|
&manifest,
|
|
&[BundleSource::new(
|
|
"minimal.bundle",
|
|
synthetic_minimal_unityfs_bundle(),
|
|
)],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(report.imported.len(), 1);
|
|
assert!(cas.exists(&report.imported[0].object_id).await);
|
|
|
|
let indexed = resources.list(ResourceQuery::all()).await.unwrap();
|
|
let actual = json!({
|
|
"imported": report.imported.iter().map(|imported| {
|
|
json!({
|
|
"bytes": imported.bytes,
|
|
"category": imported.category.as_str(),
|
|
"resource_type": format!("{:?}", imported.resource_type),
|
|
"source_path": imported.source_path,
|
|
"unityfs": imported.unityfs.as_ref().map(|unityfs| json!({
|
|
"block_count": unityfs.block_count,
|
|
"directories": unityfs.directories,
|
|
"directory_count": unityfs.directory_count,
|
|
"unity_version": unityfs.unity_version,
|
|
})),
|
|
})
|
|
}).collect::<Vec<_>>(),
|
|
"resource_count": indexed.len(),
|
|
"skipped": report.skipped,
|
|
"category_counts": report.category_counts,
|
|
});
|
|
let expected: serde_json::Value =
|
|
serde_json::from_str(include_str!("golden/synthetic_phase2_import.json")).unwrap();
|
|
|
|
assert_eq!(actual, expected);
|
|
assert_eq!(indexed[0].entry.hash, report.imported[0].object_id);
|
|
}
|