import os import re game_dir = r"d:\Projects\ichni Official\Assets\ThemeBundles\DepartureToMultiverse\Scripts\Game" datacore_dir = r"d:\Projects\ichni Official\Assets\ThemeBundles\DepartureToMultiverse\Scripts\DataCore" def extract_beatmap_namespace(filepath): try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() except Exception as e: print("Error reading", filepath, e) return # Find the start of namespace Beatmap match = re.search(r'^\s*namespace\s+Beatmap\s*\{', content, re.MULTILINE) if not match: print("No Beatmap namespace in", filepath) return start_idx = match.start() # We need to find the matching closing brace brace_depth = 0 end_idx = -1 for i in range(start_idx, len(content)): if content[i] == '{': brace_depth += 1 elif content[i] == '}': brace_depth -= 1 if brace_depth == 0: end_idx = i + 1 break if end_idx == -1: print("Mismatched braces in", filepath) return bm_namespace_content = content[start_idx:end_idx] # Find the name of the _BM class to name the new file class_match = re.search(r'public\s+(?:partial\s+)?class\s+(\w+_BM)', bm_namespace_content) if not class_match: print("No _BM class found in namespace Beatmap for", filepath) return bm_class_name = class_match.group(1) # Make the original file clean new_game_content = content[:start_idx] + content[end_idx:] new_game_content = new_game_content.rstrip() # close the namespace of the original file if needed, actually it's usually inside the original namespace! # Let's check if "namespace Beatmap" is inside another namespace. # Yes, typically "namespace Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse" wraps everything. # The new_game_content currently might have a trailing `}` because the outer namespace is closed at the end. # E.g. # namespace Outer { # class OuterClass { } # namespace Beatmap { ... } # } # If we remove "namespace Beatmap { ... }", we still need the trailing "}". new_game_content = re.sub(r'[\r\n\s]*namespace\s+Beatmap[\s\S]*\}\s*\}\s*$', '\n}', content) # A quick hacky way but relies on structure. # Better way: just remove the exact substring, then it leaves the outer closing brace intact. # wait, if namespace Beatmap { ... } is inside namespace Outer { ... } # start_idx to end_idx is exactly "namespace Beatmap { ... }" # So content[:start_idx] + content[end_idx:] will leave the outer } perfectly. new_content = content[:start_idx] + content[end_idx:] # Clean up excess newlines new_content = re.sub(r'\n\s*\n\s*\n', '\n\n', new_content) # Ensure it ends with a newline new_content = new_content.strip() + '\n' with open(filepath, 'w', encoding='utf-8') as f: f.write(new_content) # Now create the DataCore file rel_path = os.path.relpath(filepath, game_dir) datacore_file_dir = os.path.dirname(os.path.join(datacore_dir, rel_path)) os.makedirs(datacore_file_dir, exist_ok=True) datacore_filepath = os.path.join(datacore_file_dir, bm_class_name + ".cs") # We need usings. Extract original usings usings = "".join(re.findall(r'^(?:using\s+[\w\.]+;\s*)+', content, re.MULTILINE)) # Format the new DataCore file datacore_content = usings + "\n" # Wrap in outer namespace datacore_content += "namespace Ichni.RhythmGame.ThemeBundles.DepartureToMultiverse.Beatmap\n{\n" # Extract the classes inside "namespace Beatmap" inner_classes = re.search(r'namespace\s+Beatmap\s*\{([\s\S]*)\}', bm_namespace_content).group(1) datacore_content += inner_classes + "}\n" with open(datacore_filepath, 'w', encoding='utf-8') as f: f.write(datacore_content) print(f"Split {os.path.basename(filepath)} -> {bm_class_name}.cs") for root, dirs, files in os.walk(game_dir): for file in files: if file.endswith('.cs'): filepath = os.path.join(root, file) extract_beatmap_namespace(filepath)