Last active
October 3, 2025 15:51
-
-
Save tuokri/2730987c0216b9ca5e98dfa378858d4a to your computer and use it in GitHub Desktop.
Organizes a non-CNS Stellar Blade outfit mod (Nikke Cooling Suit Skin Pack) into a proper folder structure with all possible variants so it can be processed by CNSRepacker to create a CNS compatible outfit mod.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Organizes a non-CNS Stellar Blade outfit mod (Nikke Cooling Suit Skin Pack) | |
| # into a proper folder structure with all possible variants so it can be | |
| # processed by CNSRepacker to create a CNS compatible outfit mod. | |
| # | |
| # See: https://www.nexusmods.com/stellarblade/mods/1936 [CNS Repacker] | |
| # See: https://www.nexusmods.com/stellarblade/mods/1496 [CNS - Custom Nanosuit System] | |
| # See: https://www.nexusmods.com/stellarblade/mods/669 [Nikke Cooling Suit Skin Pack] | |
| # | |
| # MIT License | |
| # | |
| # Copyright (c) 2025 Tuomo Kriikkula | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |
| # of this software and associated documentation files (the "Software"), to deal | |
| # in the Software without restriction, including without limitation the rights | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| # copies of the Software, and to permit persons to whom the Software is | |
| # furnished to do so, subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in all | |
| # copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| # | |
| # Expects the following folder structure: | |
| # │ organize.py | |
| # │ | |
| # ├───Clear Trans Doro | |
| # │ CoolingSuit_Trans_Clear_Doro_P.pak | |
| # │ CoolingSuit_Trans_Clear_Doro_P.ucas | |
| # │ CoolingSuit_Trans_Clear_Doro_P.utoc | |
| # │ | |
| # ├───Dirty Trans Doro | |
| # │ CoolingSuit_Trans_Dirty_Doro_P.pak | |
| # │ CoolingSuit_Trans_Dirty_Doro_P.ucas | |
| # │ CoolingSuit_Trans_Dirty_Doro_P.utoc | |
| # │ | |
| # ├───Full Body Pink Trans 20 | |
| # │ CoolingSuit_Body_Trans_Pink_20_Doro_P.pak | |
| # │ CoolingSuit_Body_Trans_Pink_20_Doro_P.ucas | |
| # │ CoolingSuit_Body_Trans_Pink_20_Doro_P.utoc | |
| # │ | |
| # ├───Full Body Pink Trans 40 | |
| # │ CoolingSuit_Body_Trans_Pink_40_Doro_P.pak | |
| # │ CoolingSuit_Body_Trans_Pink_40_Doro_P.ucas | |
| # │ CoolingSuit_Body_Trans_Pink_40_Doro_P.utoc | |
| # │ | |
| # ├───Leotard Black and Tights 40 | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_40_P.pak | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_40_P.ucas | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_40_P.utoc | |
| # │ | |
| # ├───Leotard Black and Tights 60 | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_P.pak | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_P.ucas | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_P.utoc | |
| # │ | |
| # ├───Leotard Black and Tights 60 v2 | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_v2_P.pak | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_v2_P.ucas | |
| # │ CoolingSuit_Body_Leotard_Black_Distressed_Tights_60_v2_P.utoc | |
| # │ | |
| # ├───Leotard Dark Gray and Tights 40 | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_40_P.pak | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_40_P.ucas | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_40_P.utoc | |
| # │ | |
| # ├───Leotard Dark Gray and Tights 60 | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_P.pak | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_P.ucas | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_P.utoc | |
| # │ | |
| # ├───Leotard Dark Gray and Tights 60 v2 | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_v2_P.pak | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_v2_P.ucas | |
| # │ CoolingSuit_Body_Leotard_DarkGray_Distressed_Tights_60_v2_P.utoc | |
| # │ | |
| # ├───Leotard Light Black and Tights 40 | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_40_P.pak | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_40_P.ucas | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_40_P.utoc | |
| # │ | |
| # ├───Leotard Light Black and Tights 60 | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_P.pak | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_P.ucas | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_P.utoc | |
| # │ | |
| # ├───Leotard Light Black and Tights 60 v2 | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_v2_P.pak | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_v2_P.ucas | |
| # │ CoolingSuit_Body_Leotard_LightBlack_Distressed_Tights_60_v2_P.utoc | |
| # │ | |
| # ├───Leotard Pink and Tights 60 v2 | |
| # │ CoolingSuit_Body_Leotard_Distressed_Tights_60_v2_P.pak | |
| # │ CoolingSuit_Body_Leotard_Distressed_Tights_60_v2_P.ucas | |
| # │ CoolingSuit_Body_Leotard_Distressed_Tights_60_v2_P.utoc | |
| # │ | |
| # ├───Pink Leotard and Tights | |
| # │ CoolingSuit_Leotard_Distressed_Tights_P.pak | |
| # │ CoolingSuit_Leotard_Distressed_Tights_P.ucas | |
| # │ CoolingSuit_Leotard_Distressed_Tights_P.utoc | |
| # │ | |
| # ├───Pink Leotard and Tights 40 | |
| # │ CoolingSuit_Leotard_Distressed_Tights_40_P.pak | |
| # │ CoolingSuit_Leotard_Distressed_Tights_40_P.ucas | |
| # │ CoolingSuit_Leotard_Distressed_Tights_40_P.utoc | |
| # │ | |
| # ├───Pink Trans 60 Doro | |
| # │ CoolingSuit_Trans_Pink_60_Doro_P.pak | |
| # │ CoolingSuit_Trans_Pink_60_Doro_P.ucas | |
| # │ CoolingSuit_Trans_Pink_60_Doro_P.utoc | |
| # │ | |
| # ├───Pink Trans 80 Doro | |
| # │ CoolingSuit_Trans_Pink_80_Doro_P.pak | |
| # │ CoolingSuit_Trans_Pink_80_Doro_P.ucas | |
| # │ CoolingSuit_Trans_Pink_80_Doro_P.utoc | |
| # │ | |
| # └───_Jackets | |
| # ├───Black Jacket and Black Label | |
| # │ CoolingSuit_Jacket_Black_Label_Black_P.pak | |
| # │ CoolingSuit_Jacket_Black_Label_Black_P.ucas | |
| # │ CoolingSuit_Jacket_Black_Label_Black_P.utoc | |
| # │ | |
| # ├───Black Jacket and White Label | |
| # │ CoolingSuit_Jacket_Black_Label_White_P.pak | |
| # │ CoolingSuit_Jacket_Black_Label_White_P.ucas | |
| # │ CoolingSuit_Jacket_Black_Label_White_P.utoc | |
| # │ | |
| # ├───Gray Jacket | |
| # │ CoolingSuit_Jacket_Gray_P.pak | |
| # │ CoolingSuit_Jacket_Gray_P.ucas | |
| # │ CoolingSuit_Jacket_Gray_P.utoc | |
| # │ | |
| # ├───No Jacket | |
| # └───White Jacket | |
| # CoolingSuit_Jacket_White_P.pak | |
| # CoolingSuit_Jacket_White_P.ucas | |
| # CoolingSuit_Jacket_White_P.utoc | |
| # | |
| # Output files and folders will be written to ./output/ | |
| # Output will contain all possible outfit variants such that | |
| # each outfit variant will be combined by each jacket option. | |
| # | |
| # Example output folders: | |
| # "./output/Clear Trans Doro (Black Jacket and Black Label)/" | |
| # "./output/Clear Trans Doro (Black Jacket and White Label)/" | |
| # "./output/Clear Trans Doro (Gray Jacket)/" | |
| # "./output/Clear Trans Doro (No Jacket)/" | |
| # "./output/Clear Trans Doro (White Jacket)/" | |
| # "./output/Dirty Trans Doro (Black Jacket and Black Label)/" | |
| # "./output/Dirty Trans Doro (Black Jacket and White Label)/" | |
| # "./output/Dirty Trans Doro (Gray Jacket)/" | |
| # "./output/Dirty Trans Doro (No Jacket)/" | |
| # "./output/Dirty Trans Doro (White Jacket)/" | |
| # "./output/Full Body Pink Trans 20 (Black Jacket and Black Label)/" | |
| # "./output/Full Body Pink Trans 20 (Black Jacket and White Label)/" | |
| # "./output/Full Body Pink Trans 20 (Gray Jacket)/" | |
| # "./output/Full Body Pink Trans 20 (No Jacket)/" | |
| # "./output/Full Body Pink Trans 20 (White Jacket)/" | |
| # "./output/Full Body Pink Trans 40 (Black Jacket and Black Label)/" | |
| # "./output/Full Body Pink Trans 40 (Black Jacket and White Label)/" | |
| # "./output/Full Body Pink Trans 40 (Gray Jacket)/" | |
| # "./output/Full Body Pink Trans 40 (No Jacket)/" | |
| # "./output/Full Body Pink Trans 40 (White Jacket)/" | |
| # "./output/Leotard Black and Tights 40 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Black and Tights 40 (Black Jacket and White Label)/" | |
| # "./output/Leotard Black and Tights 40 (Gray Jacket)/" | |
| # "./output/Leotard Black and Tights 40 (No Jacket)/" | |
| # "./output/Leotard Black and Tights 40 (White Jacket)/" | |
| # "./output/Leotard Black and Tights 60 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Black and Tights 60 (Black Jacket and White Label)/" | |
| # "./output/Leotard Black and Tights 60 (Gray Jacket)/" | |
| # "./output/Leotard Black and Tights 60 (No Jacket)/" | |
| # "./output/Leotard Black and Tights 60 (White Jacket)/" | |
| # "./output/Leotard Black and Tights 60 v2 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Black and Tights 60 v2 (Black Jacket and White Label)/" | |
| # "./output/Leotard Black and Tights 60 v2 (Gray Jacket)/" | |
| # "./output/Leotard Black and Tights 60 v2 (No Jacket)/" | |
| # "./output/Leotard Black and Tights 60 v2 (White Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 40 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Dark Gray and Tights 40 (Black Jacket and White Label)/" | |
| # "./output/Leotard Dark Gray and Tights 40 (Gray Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 40 (No Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 40 (White Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Dark Gray and Tights 60 (Black Jacket and White Label)/" | |
| # "./output/Leotard Dark Gray and Tights 60 (Gray Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 (No Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 (White Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 v2 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Dark Gray and Tights 60 v2 (Black Jacket and White Label)/" | |
| # "./output/Leotard Dark Gray and Tights 60 v2 (Gray Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 v2 (No Jacket)/" | |
| # "./output/Leotard Dark Gray and Tights 60 v2 (White Jacket)/" | |
| # "./output/Leotard Light Black and Tights 40 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Light Black and Tights 40 (Black Jacket and White Label)/" | |
| # "./output/Leotard Light Black and Tights 40 (Gray Jacket)/" | |
| # "./output/Leotard Light Black and Tights 40 (No Jacket)/" | |
| # "./output/Leotard Light Black and Tights 40 (White Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Light Black and Tights 60 (Black Jacket and White Label)/" | |
| # "./output/Leotard Light Black and Tights 60 (Gray Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 (No Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 (White Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 v2 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Light Black and Tights 60 v2 (Black Jacket and White Label)/" | |
| # "./output/Leotard Light Black and Tights 60 v2 (Gray Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 v2 (No Jacket)/" | |
| # "./output/Leotard Light Black and Tights 60 v2 (White Jacket)/" | |
| # "./output/Leotard Pink and Tights 60 v2 (Black Jacket and Black Label)/" | |
| # "./output/Leotard Pink and Tights 60 v2 (Black Jacket and White Label)/" | |
| # "./output/Leotard Pink and Tights 60 v2 (Gray Jacket)/" | |
| # "./output/Leotard Pink and Tights 60 v2 (No Jacket)/" | |
| # "./output/Leotard Pink and Tights 60 v2 (White Jacket)/" | |
| # "./output/Pink Leotard and Tights (Black Jacket and Black Label)/" | |
| # "./output/Pink Leotard and Tights (Black Jacket and White Label)/" | |
| # "./output/Pink Leotard and Tights (Gray Jacket)/" | |
| # "./output/Pink Leotard and Tights (No Jacket)/" | |
| # "./output/Pink Leotard and Tights (White Jacket)/" | |
| # "./output/Pink Leotard and Tights 40 (Black Jacket and Black Label)/" | |
| # "./output/Pink Leotard and Tights 40 (Black Jacket and White Label)/" | |
| # "./output/Pink Leotard and Tights 40 (Gray Jacket)/" | |
| # "./output/Pink Leotard and Tights 40 (No Jacket)/" | |
| # "./output/Pink Leotard and Tights 40 (White Jacket)/" | |
| # "./output/Pink Trans 60 Doro (Black Jacket and Black Label)/" | |
| # "./output/Pink Trans 60 Doro (Black Jacket and White Label)/" | |
| # "./output/Pink Trans 60 Doro (Gray Jacket)/" | |
| # "./output/Pink Trans 60 Doro (No Jacket)/" | |
| # "./output/Pink Trans 60 Doro (White Jacket)/" | |
| # "./output/Pink Trans 80 Doro (Black Jacket and Black Label)/" | |
| # "./output/Pink Trans 80 Doro (Black Jacket and White Label)/" | |
| # "./output/Pink Trans 80 Doro (Gray Jacket)/" | |
| # "./output/Pink Trans 80 Doro (No Jacket)/" | |
| # "./output/Pink Trans 80 Doro (White Jacket)/" | |
| # | |
| # Tested on Python 3.13. | |
| import shutil | |
| from pathlib import Path | |
| from pprint import pprint | |
| def contains_paks(path: Path) -> bool: | |
| return path.is_dir() and any(path.rglob("*.pak")) | |
| def main() -> None: | |
| root = Path(__file__).parent.resolve() | |
| print(f"working in folder: '{root}'") | |
| out_dir = root / "output" | |
| print(f"ensuring output folder exists: '{out_dir}'") | |
| out_dir.mkdir(exist_ok=True) | |
| jackets_dir = root / "_Jackets" | |
| jacket_vars = [ | |
| x for x in | |
| jackets_dir.glob("*") | |
| if x.is_dir() | |
| ] | |
| print("jacket variants:") | |
| pprint([x.name for x in jacket_vars]) | |
| base_outfits = [ | |
| x for x in root.glob("*") | |
| if not x.name.startswith(".") | |
| if contains_paks(x) | |
| ] | |
| base_outfits.remove(jackets_dir) | |
| print("base outfits:") | |
| pprint([x.name for x in base_outfits]) | |
| all_variants: list[Path] = [] | |
| for base_outfit in base_outfits: | |
| for jacket_var in jacket_vars: | |
| variant = out_dir / f"{base_outfit.name} ({jacket_var.name})" | |
| all_variants.append(variant) | |
| variant.mkdir(exist_ok=True, parents=True) | |
| shutil.copytree(base_outfit, variant, dirs_exist_ok=True) | |
| shutil.copytree(jacket_var, variant, dirs_exist_ok=True) | |
| pprint("all variants:") | |
| pprint(all_variants) | |
| print(f"total variants: {len(all_variants)}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment