fix: harden official resource sync retries

This commit is contained in:
2026-07-07 01:14:54 +08:00
parent e0d43fb994
commit b2d6871926
14 changed files with 333 additions and 72 deletions
+42 -8
View File
@@ -1,7 +1,7 @@
//! Official JP download inventory extraction.
use super::yostar_jp::{verified_official_platforms, PatchPlatform, YostarJpResourceRoot};
use std::collections::{BTreeSet, HashSet};
use std::collections::{BTreeMap, BTreeSet, HashSet};
/// Download inventory extracted from the official JP catalog bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -79,7 +79,7 @@ impl YostarJpPlatformDownloadInventory {
platform_catalogs: Vec<YostarJpPlatformCatalogInventory>,
) -> Self {
Self {
table_file_names: extract_file_names(table_catalog, &["db", "zip"]),
table_file_names: extract_table_file_names(table_catalog),
platform_catalogs: merge_platform_catalogs(platform_catalogs),
}
}
@@ -171,7 +171,7 @@ impl YostarJpDownloadInventory {
) -> Self {
Self {
bundle_patch_pack_names: extract_full_patch_pack_names(bundle_packing_info),
table_file_names: extract_file_names(table_catalog, &["db", "zip"]),
table_file_names: extract_table_file_names(table_catalog),
media_file_names: extract_file_names(
media_catalog,
&["zip", "mp4", "png", "ogg", "wav"],
@@ -297,6 +297,24 @@ fn extract_full_patch_pack_names(data: &[u8]) -> Vec<String> {
.collect()
}
fn extract_table_file_names(data: &[u8]) -> Vec<String> {
let mut counts = BTreeMap::<String, usize>::new();
// Real TableCatalog bytes list top-level downloadable table files twice,
// while package-internal stage/resource .zip names normally appear once
// next to rawdata paths and are not direct TableBundles URLs.
for string in extract_printable_strings(data, 4) {
for name in candidate_file_names(&string, &["db", "zip"]) {
*counts.entry(name).or_default() += 1;
}
}
counts
.into_iter()
.filter_map(|(name, count)| (count >= 2).then_some(name))
.collect()
}
fn extract_file_names(data: &[u8], extensions: &[&str]) -> Vec<String> {
let mut names = BTreeSet::new();
@@ -422,7 +440,7 @@ mod tests {
fn extracts_download_names_from_synthetic_bytes() {
let inventory = YostarJpDownloadInventory::from_catalog_bytes(
b"prefix FullPatch_000.zip noise FullPatch_114.zip suffix",
b"GameData\\Table\\ExcelDB.db\0rawdata/table/excel/ignored.bytes\0Battle.zip8",
b"GameData\\Table\\ExcelDB.db\0ExcelDB.db\0rawdata/table/excel/ignored.bytes\0Battle.zip\0Battle.zip8",
b"audio/voc_jp/jp_airi/jp_airi\0GameData\\Audio\\VOC_JP\\JP_Airi.zip8\0JP_Akane.zip",
);
@@ -443,11 +461,27 @@ mod tests {
);
}
#[test]
fn extracts_only_top_level_table_files_from_table_catalog() {
let inventory = YostarJpPlatformDownloadInventory::from_catalog_bytes(
b"ExcelDB.db\0ExcelDB.db\0TablePatchPack_Prologue_GroundStage_1.zip\0TablePatchPack_Prologue_GroundStage_1.zip|1011101_01_s1_01_mainstreet_p01_d.zip\0rawdata/ground/stage/bytes/1011101_01_s1_01_mainstreet_p01_d.bytes",
Vec::new(),
);
assert_eq!(
inventory.table_file_names,
vec![
"ExcelDB.db".to_string(),
"TablePatchPack_Prologue_GroundStage_1.zip".to_string()
]
);
}
#[test]
fn builds_direct_download_urls() {
let inventory = YostarJpDownloadInventory::from_catalog_bytes(
b"FullPatch_000.zip FullPatch_001.zip",
b"ExcelDB.db Battle.zip",
b"ExcelDB.db ExcelDB.db Battle.zip Battle.zip",
b"JP_Airi.zip JP_Akane.zip",
);
let root = YostarJpResourceRoot::from_root_token(ROOT).unwrap();
@@ -469,7 +503,7 @@ mod tests {
fn builds_direct_download_urls_for_verified_platforms() {
let inventory = YostarJpDownloadInventory::from_catalog_bytes(
b"FullPatch_000.zip",
b"ExcelDB.db",
b"ExcelDB.db ExcelDB.db",
b"JP_Airi.zip",
);
let root = YostarJpResourceRoot::from_root_token(ROOT).unwrap();
@@ -499,7 +533,7 @@ mod tests {
#[test]
fn builds_platform_specific_download_urls_without_cross_mixing_catalogs() {
let inventory = YostarJpPlatformDownloadInventory::from_catalog_bytes(
b"ExcelDB.db",
b"ExcelDB.db ExcelDB.db",
vec![
YostarJpPlatformCatalogInventory::from_catalog_bytes(
PatchPlatform::Windows,
@@ -556,7 +590,7 @@ mod tests {
);
assert_eq!(inventory.bundle_patch_pack_names.len(), 142);
assert_eq!(inventory.table_file_names.len(), 6351);
assert!(inventory.table_file_names.len() < 1000);
assert_eq!(inventory.media_file_names.len(), 1887);
assert!(inventory
.bundle_patch_pack_names