def upgrade_bnp(self, params=None): path = self.window.create_file_dialog( file_types=tuple(["BOTW Nano Patch (*.bnp)"])) if not path: return path = Path(path if isinstance(path, str) else path[0]) if not path.exists(): return tmp_dir = install.open_mod(path) output = self.window.create_file_dialog( webviewb.SAVE_DIALOG, file_types=tuple(["BOTW Nano Patch (*.bnp)"])) if not output: return output = Path(output if isinstance(output, str) else output[0]) print(f"Saving output file to {str(output)}...") x_args = [install.ZPATH, "a", str(output), f'{str(tmp_dir / "*")}'] if SYSTEM == "Windows": run( x_args, stdout=PIPE, stderr=PIPE, creationflags=util.CREATE_NO_WINDOW, check=True, ) else: run(x_args, stdout=PIPE, stderr=PIPE, check=True)
def convert_bnp(self, params) -> List[str]: bnp = Path(params["mod"]) mod = install.open_mod(bnp) warnings = dev.convert_mod(mod, params["wiiu"], params["warn"]) out = self.window.create_file_dialog( webviewb.SAVE_DIALOG, file_types=("BOTW Nano Patch (*.bnp)", "All files (*.*)"), save_filename=bnp.stem + f"_{'wiiu' if params['wiiu'] else 'switch'}.bnp", ) if not out: raise Exception("canceled") x_args = [ util.get_7z_path(), "a", out if isinstance(out, str) else out[0], f'{str(mod / "*")}', ] if system() == "Windows": result = run( x_args, capture_output=True, universal_newlines=True, creationflags=util.CREATE_NO_WINDOW, check=False, ) else: result = run(x_args, capture_output=True, universal_newlines=True, check=False) if result.stderr: raise RuntimeError(result.stderr) rmtree(mod, ignore_errors=True) return warnings
def create_bnp_mod(mod: Path, output: Path, meta: dict, options: Optional[dict] = None): if isinstance(mod, str): mod = Path(mod) if not options: options = {"options": {}, "disable": []} if mod.is_file(): print("Extracting mod...") tmp_dir: Path = install.open_mod(mod) elif mod.is_dir(): print(f"Loading mod from {str(mod)}...") tmp_dir = Path(TemporaryDirectory().name) shutil.copytree(mod, tmp_dir) else: print(f"Error: {str(mod)} is neither a valid file nor a directory") return if not ((tmp_dir / util.get_content_path()).exists() or (tmp_dir / util.get_dlc_path()).exists()): if (tmp_dir.parent / util.get_content_path()).exists(): tmp_dir = tmp_dir.parent elif util.get_settings("wiiu") and (tmp_dir / "Content").exists(): (tmp_dir / "Content").rename(tmp_dir / "content") else: raise FileNotFoundError( "This mod does not appear to have a valid folder structure") if (tmp_dir / "rules.txt").exists(): (tmp_dir / "rules.txt").unlink() if "showDepends" in meta: del meta["showDepends"] depend_string = f"{meta['name']}=={meta['version']}" meta["id"] = urlsafe_b64encode(depend_string.encode("utf8")).decode("utf8") any_platform = (options.get("options", dict()).get("general", dict()).get("agnostic", False)) meta["platform"] = ("any" if any_platform else "wiiu" if util.get_settings("wiiu") else "switch") (tmp_dir / "info.json").write_text(dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8") with Pool(maxtasksperchild=500) as pool: yml_files = set(tmp_dir.glob("**/*.yml")) if yml_files: print("Compiling YAML documents...") pool.map(_do_yml, yml_files) hashes = util.get_hash_table(util.get_settings("wiiu")) print("Packing SARCs...") _pack_sarcs(tmp_dir, hashes, pool) for folder in {d for d in tmp_dir.glob("options/*") if d.is_dir()}: _pack_sarcs(folder, hashes, pool) for option_dir in tmp_dir.glob("options/*"): for file in { f for f in option_dir.rglob("**/*") if (f.is_file() and (tmp_dir / f.relative_to(option_dir)).exists()) }: data1 = (tmp_dir / file.relative_to(option_dir)).read_bytes() data2 = file.read_bytes() if data1 == data2: util.vprint( f"Removing {file} from option {option_dir.name}, " "identical to base mod") file.unlink() del data1 del data2 if not options: options = {"disable": [], "options": {}} options["options"]["texts"] = {"all_langs": True} try: _make_bnp_logs(tmp_dir, options) for option_dir in { d for d in tmp_dir.glob("options/*") if d.is_dir() }: _make_bnp_logs(option_dir, options) except Exception as err: # pylint: disable=broad-except pool.terminate() raise Exception( f"There was an error generating change logs for your mod. {str(err)}" ) _clean_sarcs(tmp_dir, hashes, pool) for folder in {d for d in tmp_dir.glob("options/*") if d.is_dir()}: _clean_sarcs(folder, hashes, pool) print("Cleaning any junk files...") for file in {f for f in tmp_dir.rglob("**/*") if f.is_file()}: if "logs" in file.parts: continue if (file.suffix in {".yml", ".json", ".bak", ".tmp", ".old"} and file.stem != "info"): file.unlink() print("Removing blank folders...") for folder in reversed(list(tmp_dir.rglob("**/*"))): if folder.is_dir() and not list(folder.glob("*")): shutil.rmtree(folder) print(f"Saving output file to {str(output)}...") x_args = [util.get_7z_path(), "a", str(output), f'{str(tmp_dir / "*")}'] if system() == "Windows": subprocess.run( x_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=util.CREATE_NO_WINDOW, check=True, ) else: subprocess.run(x_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) shutil.rmtree(tmp_dir, ignore_errors=True) print("Conversion complete.")