diff --git a/Cargo.lock b/Cargo.lock index 58b435f..d4df7e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,26 +116,43 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "serde" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "simplelog" version = "0.12.1" @@ -206,6 +223,8 @@ version = "0.1.0" dependencies = [ "Inflector", "log", + "serde", + "serde_json", "simplelog", ] diff --git a/Cargo.toml b/Cargo.toml index 43d4ad4..2c5ed3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" [dependencies] Inflector = "*" log = "0.4.20" +serde = { version = "1.0.201", features = ["derive"] } +serde_json = "1.0.117" simplelog = "0.12.1" diff --git a/build.py b/build.py index 0c98114..b021fcf 100644 --- a/build.py +++ b/build.py @@ -54,7 +54,7 @@ def findWorkingExecutablePath(executable_paths, flags): } class BuildVariant: - def __init__(self, short_description, chapter, unity, system, target_crc32=None, translation_default=False): + def __init__(self, short_description, chapter, unity, system, target_crc32=None, translation_default=False, path_id_overrides=None): self.chapter = chapter self.unity = unity self.system = system @@ -63,14 +63,21 @@ def __init__(self, short_description, chapter, unity, system, target_crc32=None, self.data_dir = f"HigurashiEp{self.chapter_number:02}_Data" self.translation_default = translation_default self.short_description = short_description - - def get_build_command(self) -> str: - args = [self.chapter, self.unity, self.system] - - if self.target_crc32 is not None: - args.append(self.target_crc32) - - return " ".join(args) + self.path_id_overrides = [] + if path_id_overrides is not None: + self.path_id_overrides = path_id_overrides + + def get_build_settings(self) -> dict[str, object]: + return { + "chapter": self.chapter, + "unity": self.unity, + "system": self.system, + "crc32": self.target_crc32, + "path_id_overrides": self.path_id_overrides, + } + + def get_build_settings_json(self) -> str: + return json.dumps(self.get_build_settings()) def get_translation_sharedassets_name(self) -> str: operatingSystem = None @@ -356,7 +363,7 @@ def save(self): # Get a list of build variants (like 'onikakushi 5.2.2f1 win') depending on commmand line arguments build_variants = get_build_variants(chapter_name) -build_variants_list = "\n - ".join([b.get_build_command() for b in build_variants]) +build_variants_list = "\n - ".join([b.get_build_settings_json() for b in build_variants]) print(f"-------- Build Started --------") print(f"Chapter: [{chapter_name}] | Translation Archive Output: [{('Enabled' if translation else 'Disabled')}]") print(f"Variants:") @@ -440,11 +447,29 @@ def save(self): # Build all the requested variants for build_variant in build_variants: - print(f"Building .assets for {build_variant.get_build_command()}...") + print(f"Building .assets for {build_variant.get_build_settings_json()}...") + + # Delete any old .json settings to make sure we don't reuse it accidentally + settings_path = 'rust_script_settings.json' + if os.path.exists(settings_path): + os.remove(settings_path) + + # Write json settings file for rust to use + json_settings = build_variant.get_build_settings_json() + print(f"Calling rust script with settings: {json_settings}") + with open(settings_path, 'w', encoding='utf-8') as f: + f.write(json_settings) + + # Build command line arguments + command = [] if working_cargo: - call(f"cargo run {build_variant.get_build_command()}") + command += ["cargo", "run"] else: - call(f"ui-compiler.exe {build_variant.get_build_command()}") + command += ["ui-compiler.exe"] + command += [settings_path] + + # Call the rust script + call(command) if translation: source_sharedassets = os.path.join("output", build_variant.data_dir, "sharedassets0.assets") diff --git a/src/main.rs b/src/main.rs index fdad2f1..13884ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,65 @@ use std::process; use inflector::Inflector; use log::*; use simplelog::{TermLogger, TerminalMode, ColorChoice,Config}; +use serde::{Deserialize, Serialize}; + +// The settings this program takes from the .json file +// The .json file should be structured matching this struct +#[derive(Serialize, Deserialize, Debug)] +struct Settings { + chapter: String, + unity: String, + system: String, + checksum: Option, + path_id_overrides: Vec<(String, usize)> +} + +// Force a particular file to use a Unity .assets PathID by renaming the file, typically in the 'output/assets' folder +fn force_path_id(directory_assets: &str, original_name: &str, path_id_override: usize) -> std::io::Result<()> +{ + let new_name = format!("{} {}", path_id_override, original_name); + let original_path = format!("{}/{}", directory_assets, original_name); + let new_path = format!("{}/{}", directory_assets, new_name); + + println!("Forcing path id {}: {} -> {}", path_id_override, original_path, new_path); + fs::rename(original_path, new_path) +} + +// Previously settings were read from cmd line, but now we load from .json +// When loading from .json, the first argument should be the path to the .json file. +fn read_settings_from_args_or_json() -> Settings +{ + let args: Vec = env::args().collect(); + + if args[1].ends_with(".json") + { + let json_path = args[1].to_string(); + println!("Loading settings from json file {}", json_path); + let json_str = fs::read_to_string(json_path).expect("Unable to read json config file"); + serde_json::from_str(json_str.as_str()).expect("Unable to parse json file") + } + else + { + let chapter = args[1].to_string(); + let unity = args[2].to_string(); + let system = args[3].to_string(); + let checksum = if args.len() > 4 { + Some(args[4].to_string()) + } else { + None + }; + + Settings { + chapter, + unity, + system, + checksum, + path_id_overrides: Vec::new() + } + } +} + + fn main() -> ExitCode { TermLogger::init( @@ -17,15 +76,12 @@ fn main() -> ExitCode { ) .expect("Failed to init logger"); - let args: Vec = env::args().collect(); - let chapter = &args[1]; - let unity = &args[2]; - let system = &args[3]; - let checksum = if args.len() > 4 { - Some(&args[4]) - } else { - None - }; + let settings = read_settings_from_args_or_json(); + let chapter = &settings.chapter; + let unity = &settings.unity; + let system = &settings.system; + let checksum = settings.checksum.as_ref(); + println!("Rust Settings: {:?}", settings); // Check if python is correctly installed println!("Running 'python --version' to check if python is correctly installed..."); @@ -207,6 +263,14 @@ fn main() -> ExitCode { println!(); + // 4a. Force certain assets to have specific PathIDs + if settings.path_id_overrides.len() > 0 { + println!("------ Forcing PathIDs ------"); + } + for (original_name, path_id_override) in settings.path_id_overrides { + force_path_id(&directory_assets, &original_name, path_id_override).expect("Failed to force ID"); + } + // 5. generate emip let status = Command::new("python") .env("PYTHONIOENCODING", "utf-8")