From 191d7af25d4025dd42e22d3be3ffebfe33a7dde9 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 6 May 2026 10:44:52 +0800 Subject: [PATCH] Now support to detect default path --- Cargo.lock | 1 + Cargo.toml | 3 +- msg_tool_xp3data/crypt.json | 78 ++++++++++++++++++- msg_tool_xp3data/cx_cb/amb_after_ep1.bin | Bin 0 -> 4096 bytes msg_tool_xp3data/cx_cb/cafe_stella_steam.bin | Bin 0 -> 4096 bytes src/scripts/kirikiri/archive/xp3/crypt/cx.rs | 30 ++++++- 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 msg_tool_xp3data/cx_cb/amb_after_ep1.bin create mode 100644 msg_tool_xp3data/cx_cb/cafe_stella_steam.bin diff --git a/Cargo.lock b/Cargo.lock index 6b804f3..5211df1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1573,6 +1573,7 @@ dependencies = [ "serde_yaml_ng", "sha1", "sha2", + "siphasher", "stylua", "tendril", "unicode-segmentation", diff --git a/Cargo.toml b/Cargo.toml index bb46143..50a33c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ serde_json = "1" serde_yaml_ng = "0.10" sha1 = { version = "0.11", optional = true } sha2 = { version = "0.11", optional = true } +siphasher = { version = "1.0", optional = true } stylua = { version = "2.1", optional = true, default-features = false} tendril = { version = "0.5", optional = true } unicode-segmentation = "1.12" @@ -101,7 +102,7 @@ hexen-haus = ["memchr", "utils-str"] hexen-haus-arc = ["hexen-haus"] hexen-haus-img = ["hexen-haus", "image"] kirikiri = ["emote-psb", "fancy-regex", "flate2", "json", "lz4", "utils-escape"] -kirikiri-arc = ["kirikiri", "adler", "aes", "base64", "blake2", "bytes", "cbc", "chacha20/legacy", "fastcdc", "flate2", "hex", "int-enum", "md5", "msg_tool_macro/kirikiri-arc", "msg_tool_xp3data", "parse-size", "sha2", "utils-case-insensitive-string", "utils-lzss", "utils-serde-base64bytes", "utils-simple-pack", "zopfli", "zstd"] +kirikiri-arc = ["kirikiri", "adler", "aes", "base64", "blake2", "bytes", "cbc", "chacha20/legacy", "fastcdc", "flate2", "hex", "int-enum", "md5", "msg_tool_macro/kirikiri-arc", "msg_tool_xp3data", "parse-size", "sha2", "siphasher", "utils-case-insensitive-string", "utils-lzss", "utils-serde-base64bytes", "utils-simple-pack", "zopfli", "zstd"] kirikiri-img = ["kirikiri", "image", "libtlg-rs"] musica = [] musica-arc = ["musica", "crc32fast", "flate2", "include-flate", "utils-blowfish", "utils-rc4", "utils-serde-base64bytes", "utils-xored-stream"] diff --git a/msg_tool_xp3data/crypt.json b/msg_tool_xp3data/crypt.json index c14a495..63d07a3 100644 --- a/msg_tool_xp3data/crypt.json +++ b/msg_tool_xp3data/crypt.json @@ -151,6 +151,20 @@ "TpmFileName": "plugin/AmairoIsleNauts.tpm", "Title": "天色*アイルノーツ | 天色幻想岛" }, + "AMBITIOUS MISSION After Episode 1": { + "$type": "HxCrypt", + "Mask": 454, + "Offset": 656, + "PrologOrder": "AAEC", + "OddBranchOrder": "BAMCBQAB", + "EvenBranchOrder": "AAIHAwQBBgU=", + "Key": "Wa7phHMk/a0hEztfLDpfcWbN1n7e0DYitcdCV1m/2EQ=", + "Nonce": "DDEouJ4BezmeMVKZf7WVqA==", + "FilterKey": 375708990042069900, + "RandomType": 1, + "ControlBlockName": "amb_after_ep1.bin", + "Title": "AMBITIOUS MISSION アフターエピソード1 かぐや&あてな | Ambitious Mission FD1" + }, "Anata no Milk Kudasai na": { "$type": "HashCrypt", "Title": "あなたの精液くださいな ~私とボクとどっちがお好み?~" @@ -310,6 +324,67 @@ "ControlBlockName": "cafe_stella.bin", "Title": "喫茶ステラと死神の蝶 | 星光咖啡馆与死神之蝶 | 星光咖啡館與死神之蝶" }, + "Café Stella to Shinigami no Chou [Steam]": { + "$type": "HxCrypt", + "Mask": 486, + "Offset": 101, + "PrologOrder": "AgAB", + "OddBranchOrder": "BAEFAAMC", + "EvenBranchOrder": "BgMBAgQFAAc=", + "FilterKey": 9413765695581088110, + "IndexKeyDict": { + "data.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "video.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "bgm.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "fgimage.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "bgimage.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "scn.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "voice.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "main.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "evimage.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "uipsd.xp3": { + "Key": "sd8wA9myXvbA4ATJnq6JYia3V5otV84MkuSiW4aYOog=", + "Nonce": "HXJsNtEkHsOPDBcx/QODdQ==" + }, + "video2.xp3": { + "Key": "9lU4Kgw+IvRM3Zm0ubsa161RAxURD9ghXmSvYH7f/VE=", + "Nonce": "r5eGhOPufNjqgZMwtggGhg==" + }, + "adult.xp3": { + "Key": "9lU4Kgw+IvRM3Zm0ubsa161RAxURD9ghXmSvYH7f/VE=", + "Nonce": "r5eGhOPufNjqgZMwtggGhg==" + } + }, + "ControlBlockName": "cafe_stella_steam.bin", + "Title": "喫茶ステラと死神の蝶 [Steam] | 星光咖啡馆与死神之蝶 [Steam] | 星光咖啡館與死神之蝶 [Steam]" + }, "CharaBration!": { "$type": "AkabeiCrypt", "Seed": 3854429322, @@ -420,7 +495,8 @@ "Key": "4ozUw00nStHvknH8ECpqL1BVC9nOrpdryY4wHKtBTFE=", "Nonce": "pjhv8KmiW/gnbdoy6c5J6Q==", "FilterKey": 12271333071625965214, - "ControlBlockName": "dc5.bin" + "ControlBlockName": "dc5.bin", + "Title": "D.C.5 ~ダ・カーポ5~ | 初音岛5 | D.C.5 Plus Happiness ~ダ・カーポ5~プラスハピネス | D.C.5 ~Da Capo 5~ Future Link | D.C.5 Future Link ~ダ・カーポ5~ フューチャーリンク | D.C.5 Sweet Happiness ~ダ・カーポ5~スイートハピネス" }, "Deatte 5-fun wa Ore no Mono!": { "$type": "XorCrypt", diff --git a/msg_tool_xp3data/cx_cb/amb_after_ep1.bin b/msg_tool_xp3data/cx_cb/amb_after_ep1.bin new file mode 100644 index 0000000000000000000000000000000000000000..ccdd0a71028570322e8ccabce4f159c57c407b43 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs index 5e940c5..0d6b947 100644 --- a/src/scripts/kirikiri/archive/xp3/crypt/cx.rs +++ b/src/scripts/kirikiri/archive/xp3/crypt/cx.rs @@ -2111,7 +2111,7 @@ impl HxCrypt { index_key = Some(ind.clone()) } let index_key = index_key.ok_or_else(|| anyhow::anyhow!("Can not find index key."))?; - let (file_map, path_map) = if let Some(path) = file_list_path { + let (file_map, mut path_map) = if let Some(path) = file_list_path { let data = std::fs::read(path)?; let data = decode_to_string(Encoding::Utf8, &data, true)?; Self::read_names(&data, s)? @@ -2128,6 +2128,10 @@ impl HxCrypt { (HashMap::new(), HashMap::new()) } }; + let default_path_hash = calculate_path_hash("", "xp3hnp"); + if !path_map.contains_key(&default_path_hash) { + path_map.insert(default_path_hash, String::new()); + } Ok(Self { base: CxEncryption::new_inner( base, @@ -2952,6 +2956,18 @@ fn create_garbage_filename_set(file_hash_salt: &str) -> HashSet { set } +fn calculate_path_hash(pathname: &str, path_hash_salt: &str) -> PathHash { + use std::hash::Hasher; + let mut hasher = siphasher::sip::SipHasher24::new(); + (pathname.to_lowercase() + path_hash_salt) + .encode_utf16() + .for_each(|b| { + hasher.write(&b.to_le_bytes()); + }); + let data = hasher.finish().to_ne_bytes(); + PathHash(u64::from_be_bytes(data)) +} + #[test] fn test_filehash_deserialize() { assert_eq!( @@ -2965,3 +2981,15 @@ fn test_filehash_deserialize() { .unwrap() ); } + +#[test] +fn test_calculate_path_hash() { + assert_eq!( + calculate_path_hash("", "xp3hnp"), + PathHash::try_from("94d4a97c61498621").unwrap(), + ); + assert_eq!( + calculate_path_hash("scenario/scripts/", "xp3hnp"), + PathHash::try_from("c81c19411c1a5e54").unwrap(), + ); +}