1
0
mirror of https://github.com/Bluemangoo/sekai-unpacker.git synced 2026-05-06 20:44:47 +08:00

fix tons of bugs

This commit is contained in:
Bluemangoo 2026-04-17 18:56:56 +08:00
parent d83d611ab8
commit 2f1c7eb9d8
Signed by: Bluemangoo
GPG Key ID: F2F7E46880A1C4CF
11 changed files with 99 additions and 74 deletions

10
Cargo.lock generated
View File

@ -122,6 +122,7 @@ dependencies = [
"tempfile",
"thiserror 2.0.18",
"tokio",
"twox-hash",
"yaml_serde",
]
@ -2797,6 +2798,15 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "twox-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
dependencies = [
"rand",
]
[[package]]
name = "typenum"
version = "1.19.0"

View File

@ -34,3 +34,4 @@ lazy_static = "1.5.0"
structopt = "0.3.26"
tokio-util = "0.7.18"
futures-util = "0.3.32"
twox-hash = "2.1.2"

View File

@ -22,3 +22,4 @@ cridecoder = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
cipher = { workspace = true, features = ["block-padding"] }
twox-hash = { workspace = true, features = ["xxhash3_128"] }

View File

@ -262,13 +262,17 @@ impl AssetExecutionContext {
}
RegionProviderConfig::Nuverse {
asset_version_url,
app_version,
asset_info_url_template,
..
} => {
// For nuverse, always fetch the version from asset_version_url.
// The incoming request.asset_version is intentionally ignored here
// to match Go reference behavior.
let app_version = self.sync_context.app_version.as_ref().ok_or_else(|| {
AssetExecutionError::MissingAppVersion {
region: self.region_name.clone(),
}
})?;
let version_url = asset_version_url.replace("{app_version}", app_version);
let resolved_version =
String::from_utf8_lossy(&self.get_with_retry(&version_url).await?)
@ -319,9 +323,13 @@ impl AssetExecutionContext {
}
RegionProviderConfig::Nuverse {
asset_bundle_url_template,
app_version,
..
} => {
let app_version = self.sync_context.app_version.as_ref().ok_or_else(|| {
AssetExecutionError::MissingAppVersion {
region: self.region_name.clone(),
}
})?;
let asset_version = self
.resolved_asset_version
.as_deref()
@ -435,6 +443,7 @@ impl AssetExecutionContext {
&self.region,
&temp_file,
&task.bundle_path,
&task.download_path,
category,
)
.await;

View File

@ -150,7 +150,6 @@ pub enum RegionProviderConfig {
},
Nuverse {
asset_version_url: String,
app_version: String,
asset_info_url_template: String,
asset_bundle_url_template: String,
#[serde(default)]

View File

@ -107,6 +107,8 @@ pub enum AssetExecutionError {
HttpStatus { url: String, status: u16 },
#[error("region `{region}` is missing asset_save_dir")]
MissingAssetSaveDir { region: String },
#[error("nuverse region `{region}` requires asset_version and asset_hash")]
MissingAppVersion { region: String },
#[error("colorful_palette region `{region}` requires asset_version and asset_hash")]
MissingAssetVersionOrHash { region: String },
#[error("colorful_palette region `{region}` is missing profile hash for `{profile}`")]

View File

@ -54,18 +54,26 @@ pub fn get_export_group(export_path: &str) -> &'static str {
"container"
}
pub fn get_hex_index(input: &str) -> String {
let hash_val = twox_hash::XxHash3_128::oneshot(input.as_bytes());
format!("{:032x}", hash_val)
}
pub async fn extract_unity_asset_bundle(
app_config: &AppConfig,
sync_context: &SyncContext,
region: &RegionConfig,
asset_bundle_file: &Path,
export_path: &str,
download_path: &str,
category: &str,
) -> Result<(PathBuf, bool), ExportPipelineError> {
let hash = get_hex_index(export_path);
let output_dir = std::env::temp_dir()
.join("sekai-updater")
.join("extract")
.join(&sync_context.region);
.join(&sync_context.region)
.join(hash);
let Some(asset_studio_cli_path) = app_config.tools.asset_studio_cli_path.as_deref() else {
return Ok((asset_bundle_file.parent().unwrap().to_path_buf(), true));
};
@ -82,7 +90,7 @@ pub async fn extract_unity_asset_bundle(
};
let actual_export_path = if sync_context.export.by_category {
output_dir.join(category.to_lowercase()).join(export_path)
output_dir.join(category.to_lowercase())
} else {
output_dir.join(export_path)
};
@ -126,10 +134,23 @@ pub async fn extract_unity_asset_bundle(
.await?;
let mut count = 0;
walk(&actual_export_path, &mut |_| count += 1)?;
let mut is_bundle_name_file = true;
let download_name = match download_path.rsplit_once("/") {
None => download_path,
Some((_, name)) => name,
};
walk(&output_dir, &mut |f| {
count += 1;
let file_name = f.file_name().unwrap().to_string_lossy();
is_bundle_name_file = is_bundle_name_file
&& match file_name.rsplit_once(".") {
Some((name, _)) => name.eq_ignore_ascii_case(download_name),
None => file_name.eq_ignore_ascii_case(download_name),
}
})?;
post_process_exported_files(app_config, sync_context, region, &actual_export_path).await?;
Ok((actual_export_path, count <= 1))
post_process_exported_files(app_config, sync_context, region, &output_dir).await?;
Ok((output_dir, is_bundle_name_file && count <= 1))
}
pub async fn post_process_exported_files(

View File

@ -12,6 +12,8 @@ pub struct SyncContext {
#[serde(default)]
pub asset_hash: Option<String>,
#[serde(default)]
pub app_version: Option<String>,
#[serde(default)]
pub exact_single_file_bundle: bool,
}

View File

@ -1,19 +1,20 @@
log_level: "DEBUG"
#client:
# - url: "127.0.0.1:3333"
# token: abc
server:
- url: 127.0.0.1:3333
client:
- url: "127.0.0.1:3333"
token: abc
cert: "D:\\WorkDir\\Nginx\\cert\\_.bluemangoo.net\\_.bluemangoo.net-chain.pem"
key: "D:\\WorkDir\\Nginx\\cert\\_.bluemangoo.net\\_.bluemangoo.net-key.pem"
#server:
# - url: 127.0.0.1:3333
# token: abc
# cert: "D:\\WorkDir\\Nginx\\cert\\_.bluemangoo.net\\_.bluemangoo.net-chain.pem"
# key: "D:\\WorkDir\\Nginx\\cert\\_.bluemangoo.net\\_.bluemangoo.net-key.pem"
profiles:
cn:
region: cn
# interval: 3 # seconds
concurrent: 50
app_version: "6.0.0"
filters:
start_app:
- "thumbnail"
@ -44,7 +45,7 @@ profiles:
convert_to_mp3: false
convert_to_flac: false
remove_wav: false
# exact_single_file_bundle: true
exact_single_file_bundle: true
path: "./data/cn"
jp:
region: jp

View File

@ -1,12 +1,12 @@
log_level: "DEBUG"
client:
- url: "127.0.0.1:3333"
token: abc
host: "local.bluemangoo.net"
#server:
# - url: 127.0.0.1:3333
#client:
# - url: "127.0.0.1:3333"
# token: abc
# host: "local.bluemangoo.net"
server:
- url: 127.0.0.1:3333
token: abc
execution:
proxy: ""
@ -49,7 +49,6 @@ regions:
provider:
kind: nuverse
asset_version_url: "https://lf3-mkcncdn-tos.dailygn.com/obj/rt-game-lf/gdl_app_5236/Mainland/{app_version}/Release/cn_online/ios/version"
app_version: "5.2.0"
asset_info_url_template: "https://lf3-mkcncdn-tos.dailygn.com/obj/sf-game-lf/gdl_app_5236/AssetBundle/{app_version}/Release/cn_online/ios{asset_version}/AssetBundleInfoNew.json"
asset_bundle_url_template: "https://lf3-mkcncdn-tos.dailygn.com/obj/sf-game-lf/gdl_app_5236/AssetBundle/{app_version}/Release/cn_online/{bundle_path}"
required_cookies: false

View File

@ -7,6 +7,7 @@ use communicator::http::{json_from_request, send, send_error};
use h2::RecvStream;
use h2::server::SendResponse;
use http::{Request, Response};
use log::debug;
pub async fn download(
mut request: Request<RecvStream>,
@ -43,58 +44,37 @@ pub async fn download(
}
let (dir, single_file) = dir.unwrap();
let dir_base = std::env::temp_dir()
.join("sekai-updater")
.join("extract")
.join(&context.sync_context.region);
let files = if !dir.is_dir() {
if context.sync_context.filters.file_ext.is_empty()
|| dir.extension().is_some_and(|t| {
context
.sync_context
.filters
.file_ext
.contains(&t.to_str().unwrap().to_lowercase())
})
{
vec![(
dir.clone(),
dir.strip_prefix(&dir_base)
.unwrap()
.to_string_lossy()
.to_string(),
)]
} else {
vec![]
}
let files = if context.sync_context.filters.file_ext.is_empty() {
find_files(&dir)
} else {
let files = if context.sync_context.filters.file_ext.is_empty() {
find_files(&dir)
} else {
find_files_by_extensions(&dir, &context.sync_context.filters.file_ext)
};
if let Err(error) = files {
send_error(send_response, error.into());
return Ok(());
}
let base = dir.strip_prefix(&dir_base).unwrap();
let base_str = if single_file {
base.parent().unwrap().to_str().unwrap()
} else {
base.to_str().unwrap()
};
files
.unwrap()
.iter()
.map(|p| {
let raw = p.clone();
let name = p.strip_prefix(&dir).unwrap();
let path = format!("{}/{}", base_str, name.to_string_lossy());
(raw, path)
})
.collect::<Vec<_>>()
find_files_by_extensions(&dir, &context.sync_context.filters.file_ext)
};
if let Err(error) = files {
send_error(send_response, error.into());
return Ok(());
}
let files = files
.unwrap()
.iter()
.map(|p| {
let raw = p.clone();
let name = p.strip_prefix(&dir).unwrap();
debug!("{} {}", p.to_string_lossy(), single_file);
let path = if context.sync_context.exact_single_file_bundle
&& single_file
{
format!(
"{}/{}",
name.parent().unwrap().parent().unwrap().to_string_lossy(),
p.file_name().unwrap().to_string_lossy()
)
} else {
name.to_string_lossy().to_string()
};
(raw, path)
})
.collect::<Vec<_>>();
let response = Response::builder()
.status(200)