extract_low_level.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import argparse
  2. import sys
  3. import json
  4. import shutil
  5. from pathlib import Path
  6. # Add the utils directory to the Python path
  7. utils_path = Path(__file__).parent / 'utils'
  8. sys.path.append(str(utils_path))
  9. from file_utils import find_files_by_extension
  10. from deep_parser import parse_scene_or_prefab
  11. from json_utils import write_json
  12. from yaml_utils import load_unity_yaml, convert_to_plain_python_types
  13. from hierarchy_utils import HierarchyParser
  14. def copy_scripts(assets_dir, output_assets_dir):
  15. """
  16. Copies all C# scripts (.cs) to the target directory.
  17. """
  18. print("\n--- Starting Script Handling ---")
  19. cs_files = find_files_by_extension(str(assets_dir), '.cs')
  20. print(f"Found {len(cs_files)} C# script files to copy.")
  21. for script_path_str in cs_files:
  22. script_path = Path(script_path_str)
  23. relative_path = script_path.relative_to(assets_dir)
  24. destination_path = output_assets_dir / relative_path
  25. destination_path.parent.mkdir(parents=True, exist_ok=True)
  26. try:
  27. shutil.copy(script_path, destination_path)
  28. except IOError as e:
  29. print(f"Error copying {script_path} to {destination_path}: {e}", file=sys.stderr)
  30. print("Script copying complete.")
  31. def main():
  32. """
  33. Main function to run the low-level data extraction process.
  34. """
  35. parser = argparse.ArgumentParser(
  36. description="Generates a deeply detailed, per-GameObject breakdown of scenes and prefabs."
  37. )
  38. parser.add_argument(
  39. "--input",
  40. type=str,
  41. required=True,
  42. help="The root directory of the target Unity project."
  43. )
  44. parser.add_argument(
  45. "--output",
  46. type=str,
  47. required=True,
  48. help="The directory where the generated output folder will be saved."
  49. )
  50. parser.add_argument(
  51. "--indent",
  52. type=int,
  53. default=None,
  54. help="Indentation level for JSON output. Defaults to None (compact)."
  55. )
  56. args = parser.parse_args()
  57. input_dir = Path(args.input).resolve()
  58. output_dir = Path(args.output).resolve()
  59. if not input_dir.is_dir():
  60. print(f"Error: Input path '{input_dir}' is not a valid directory.", file=sys.stderr)
  61. sys.exit(1)
  62. # Create the main output folder, named "LowLevel"
  63. low_level_output_dir = output_dir / "LowLevel"
  64. output_assets_dir = low_level_output_dir / "Assets"
  65. try:
  66. output_assets_dir.mkdir(parents=True, exist_ok=True)
  67. print(f"Output will be saved to: {low_level_output_dir}")
  68. except OSError as e:
  69. print(f"Error: Could not create output directory '{low_level_output_dir}'. {e}", file=sys.stderr)
  70. sys.exit(1)
  71. # --- Orchestration ---
  72. assets_dir = input_dir / "Assets"
  73. if not assets_dir.is_dir():
  74. print(f"Error: 'Assets' directory not found in '{input_dir}'.", file=sys.stderr)
  75. return
  76. # --- Task 1: Copy C# Scripts ---
  77. copy_scripts(assets_dir, output_assets_dir)
  78. # --- Task 2: Process Scenes and Prefabs ---
  79. scene_files = find_files_by_extension(str(assets_dir), '.unity')
  80. prefab_files = find_files_by_extension(str(assets_dir), '.prefab')
  81. files_to_process = scene_files + prefab_files
  82. print(f"\nFound {len(files_to_process)} scene/prefab files to process.")
  83. for file_path_str in files_to_process:
  84. file_path = Path(file_path_str)
  85. print(f"\nProcessing Scene/Prefab: {file_path.name}")
  86. # --- Deep Parsing for Individual GameObjects ---
  87. gameobject_list = parse_scene_or_prefab(str(file_path))
  88. # Create the output subdirectory for this asset
  89. relative_path = file_path.relative_to(input_dir)
  90. asset_output_dir = low_level_output_dir / relative_path
  91. try:
  92. asset_output_dir.mkdir(parents=True, exist_ok=True)
  93. except OSError as e:
  94. print(f"Error creating directory {asset_output_dir}: {e}", file=sys.stderr)
  95. continue
  96. if gameobject_list:
  97. print(f"Saving {len(gameobject_list)} GameObjects to {asset_output_dir}")
  98. for go_data in gameobject_list:
  99. file_id = go_data.get('fileID')
  100. if file_id:
  101. output_json_path = asset_output_dir / f"{file_id}.json"
  102. try:
  103. write_json(go_data, output_json_path, indent=args.indent)
  104. except Exception as e:
  105. print(f"Error writing to {output_json_path}: {e}", file=sys.stderr)
  106. else:
  107. print(f"Skipped deep parsing for {file_path.name}.")
  108. # --- Hierarchy Parsing for Root Object Identification ---
  109. try:
  110. documents = load_unity_yaml(file_path)
  111. if not documents:
  112. print(f"Could not load YAML from {file_path.name} for hierarchy parsing.")
  113. continue
  114. raw_object_map = {int(doc.anchor.value): doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
  115. object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
  116. parser = HierarchyParser(object_map)
  117. root_object_ids = parser.get_root_object_ids()
  118. root_ids_list = [file_id for file_id, _ in root_object_ids]
  119. if root_ids_list:
  120. roots_output_path = asset_output_dir / "root_objects.json"
  121. write_json(root_ids_list, roots_output_path, indent=args.indent)
  122. print(f"Successfully saved root object list to {roots_output_path}")
  123. except Exception as e:
  124. print(f"Error during hierarchy parsing for {file_path.name}: {e}", file=sys.stderr)
  125. # --- Task 3: Process .asset files ---
  126. asset_files = find_files_by_extension(str(assets_dir), '.asset')
  127. print(f"\nFound {len(asset_files)} .asset files to process.")
  128. for file_path_str in asset_files:
  129. file_path = Path(file_path_str)
  130. print(f"\nProcessing Asset: {file_path.name}")
  131. relative_path = file_path.relative_to(input_dir)
  132. asset_output_dir = low_level_output_dir / relative_path
  133. try:
  134. asset_output_dir.mkdir(parents=True, exist_ok=True)
  135. except OSError as e:
  136. print(f"Error creating directory {asset_output_dir}: {e}", file=sys.stderr)
  137. continue
  138. try:
  139. documents = load_unity_yaml(file_path)
  140. if not documents:
  141. print(f"Skipped {file_path.name} as it's empty or could not be parsed.")
  142. continue
  143. print(f"Saving {len(documents)} objects from {file_path.name} to {asset_output_dir}")
  144. for doc in documents:
  145. if not hasattr(doc, 'anchor') or doc.anchor is None:
  146. continue
  147. file_id = int(doc.anchor.value)
  148. obj_data = convert_to_plain_python_types(doc)
  149. final_obj_data = {}
  150. for key, value in obj_data.items():
  151. if isinstance(value, dict):
  152. new_value = value.copy()
  153. new_value['fileID'] = file_id
  154. final_obj_data[key] = new_value
  155. else:
  156. final_obj_data[key] = value
  157. output_json_path = asset_output_dir / f"{file_id}.json"
  158. write_json(final_obj_data, output_json_path, indent=args.indent)
  159. except Exception as e:
  160. print(f"Error processing asset file {file_path.name}: {e}", file=sys.stderr)
  161. print("\nLow-level extraction complete.")
  162. if __name__ == "__main__":
  163. main()