Skip to content

Instantly share code, notes, and snippets.

@tuokri
Last active October 3, 2025 15:51
Show Gist options
  • Select an option

  • Save tuokri/2730987c0216b9ca5e98dfa378858d4a to your computer and use it in GitHub Desktop.

Select an option

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.
# 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