extract_high_level.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import argparse
  2. import os
  3. import sys
  4. import json
  5. from pathlib import Path
  6. # Add the utils directory to the Python path
  7. # This allows importing modules from the 'utils' subfolder
  8. utils_path = Path(__file__).parent / 'utils'
  9. sys.path.append(str(utils_path))
  10. # Now we can import our utility modules
  11. from yaml_utils import load_unity_yaml
  12. from file_utils import find_files_by_extension
  13. def parse_project_settings(input_dir, output_dir):
  14. """
  15. Parses project settings and creates the manifest.json file.
  16. """
  17. print("\n--- Starting Task 2: Project Settings Parser ---")
  18. project_settings_path = input_dir / "ProjectSettings" / "ProjectSettings.asset"
  19. editor_settings_path = input_dir / "ProjectSettings" / "EditorSettings.asset"
  20. manifest_data = {}
  21. # Parse ProjectSettings.asset
  22. if project_settings_path.is_file():
  23. docs = load_unity_yaml(str(project_settings_path))
  24. if docs:
  25. # The main settings are usually in the first document
  26. player_settings = docs[0].get('PlayerSettings', {})
  27. manifest_data['productName'] = player_settings.get('productName')
  28. manifest_data['companyName'] = player_settings.get('companyName')
  29. manifest_data['bundleVersion'] = player_settings.get('bundleVersion')
  30. else:
  31. print(f"Warning: Could not parse {project_settings_path}")
  32. else:
  33. print(f"Warning: {project_settings_path} not found.")
  34. # Determine rendering mode from EditorSettings.asset
  35. if editor_settings_path.is_file():
  36. docs = load_unity_yaml(str(editor_settings_path))
  37. if docs:
  38. editor_settings = docs[0].get('EditorSettings', {})
  39. render_pipeline = editor_settings.get('m_RenderPipelineAsset')
  40. if render_pipeline and render_pipeline.get('guid'):
  41. # This indicates URP or HDRP is likely in use.
  42. # A more robust check would be to map this guid to the actual asset.
  43. manifest_data['renderPipeline'] = 'Scriptable'
  44. else:
  45. manifest_data['renderPipeline'] = 'Built-in'
  46. else:
  47. print(f"Warning: Could not parse {editor_settings_path}")
  48. else:
  49. print(f"Warning: {editor_settings_path} not found.")
  50. # Write the combined data to manifest.json
  51. manifest_output_path = output_dir / "manifest.json"
  52. try:
  53. with open(manifest_output_path, 'w', encoding='utf-8') as f:
  54. json.dump(manifest_data, f, indent=4)
  55. print(f"Successfully created manifest.json at {manifest_output_path}")
  56. except IOError as e:
  57. print(f"Error: Could not write to {manifest_output_path}. {e}", file=sys.stderr)
  58. def parse_package_manifests(input_dir, output_dir):
  59. """
  60. Parses package manifests and creates a combined packages.json file.
  61. """
  62. print("\n--- Starting Task 3: Package Manifest Extractor ---")
  63. manifest_path = input_dir / "Packages" / "manifest.json"
  64. lock_path = input_dir / "Packages" / "packages-lock.json"
  65. packages_data = {}
  66. # Read manifest.json
  67. if manifest_path.is_file():
  68. try:
  69. with open(manifest_path, 'r', encoding='utf-8') as f:
  70. packages_data['manifest'] = json.load(f)
  71. except (IOError, json.JSONDecodeError) as e:
  72. print(f"Error reading {manifest_path}: {e}", file=sys.stderr)
  73. else:
  74. print(f"Warning: {manifest_path} not found.")
  75. # Read packages-lock.json
  76. if lock_path.is_file():
  77. try:
  78. with open(lock_path, 'r', encoding='utf-8') as f:
  79. packages_data['lock'] = json.load(f)
  80. except (IOError, json.JSONDecodeError) as e:
  81. print(f"Error reading {lock_path}: {e}", file=sys.stderr)
  82. else:
  83. print(f"Warning: {lock_path} not found.")
  84. # Write the combined data to packages.json
  85. if packages_data:
  86. packages_output_path = output_dir / "packages.json"
  87. try:
  88. with open(packages_output_path, 'w', encoding='utf-8') as f:
  89. json.dump(packages_data, f, indent=4)
  90. print(f"Successfully created packages.json at {packages_output_path}")
  91. except IOError as e:
  92. print(f"Error: Could not write to {packages_output_path}. {e}", file=sys.stderr)
  93. def generate_guid_mappers(input_dir, output_dir):
  94. """
  95. Finds all .meta files and generates JSON files mapping GUIDs to asset paths.
  96. """
  97. print("\n--- Starting Task 4: GUID Mapper Generator ---")
  98. assets_dir = input_dir / "Assets"
  99. if not assets_dir.is_dir():
  100. print(f"Error: 'Assets' directory not found in '{input_dir}'", file=sys.stderr)
  101. return
  102. meta_files = find_files_by_extension(str(assets_dir), '.meta')
  103. print(f"Found {len(meta_files)} .meta files to process.")
  104. # Asset type mapping based on file extensions
  105. asset_type_map = {
  106. '.prefab': 'prefabs',
  107. '.unity': 'scenes',
  108. '.mat': 'materials',
  109. '.cs': 'scripts',
  110. '.png': 'textures',
  111. '.jpg': 'textures',
  112. '.jpeg': 'textures',
  113. '.asset': 'scriptable_objects',
  114. }
  115. guid_maps = {value: {} for value in asset_type_map.values()}
  116. guid_maps['others'] = {}
  117. for meta_file_path_str in meta_files:
  118. meta_file_path = Path(meta_file_path_str)
  119. asset_file_path = Path(meta_file_path_str.rsplit('.meta', 1)[0])
  120. guid = None
  121. try:
  122. with open(meta_file_path, 'r', encoding='utf-8') as f:
  123. for line in f:
  124. if line.strip().startswith('guid:'):
  125. guid = line.split(':')[1].strip()
  126. break
  127. except Exception as e:
  128. print(f"Warning: Could not read or parse guid from {meta_file_path}. {e}", file=sys.stderr)
  129. continue
  130. if guid:
  131. asset_ext = asset_file_path.suffix.lower()
  132. asset_type = asset_type_map.get(asset_ext, 'others')
  133. # Make path relative to the input directory for consistency
  134. relative_path = asset_file_path.relative_to(input_dir).as_posix()
  135. guid_maps[asset_type][guid] = relative_path
  136. # Write the GUID maps to separate JSON files
  137. mappers_dir = output_dir / "GuidMappers"
  138. try:
  139. mappers_dir.mkdir(parents=True, exist_ok=True)
  140. for asset_type, guid_map in guid_maps.items():
  141. if guid_map: # Only write files for types that have assets
  142. output_path = mappers_dir / f"{asset_type}.json"
  143. with open(output_path, 'w', encoding='utf-8') as f:
  144. json.dump(guid_map, f, indent=4)
  145. print(f"Successfully created GUID mappers in {mappers_dir}")
  146. except OSError as e:
  147. print(f"Error: Could not create GUID mapper directory or files. {e}", file=sys.stderr)
  148. def main():
  149. """
  150. Main function to run the high-level data extraction process.
  151. """
  152. parser = argparse.ArgumentParser(
  153. description="Extracts high-level summary data from a Unity project."
  154. )
  155. parser.add_argument(
  156. "--input",
  157. type=str,
  158. required=True,
  159. help="The root directory of the target Unity project."
  160. )
  161. parser.add_argument(
  162. "--output",
  163. type=str,
  164. required=True,
  165. help="The directory where the generated output folder will be saved."
  166. )
  167. args = parser.parse_args()
  168. # --- 1. Validate inputs and set up paths ---
  169. input_dir = Path(args.input)
  170. output_dir = Path(args.output)
  171. if not input_dir.is_dir():
  172. print(f"Error: Input path '{input_dir}' is not a valid directory.", file=sys.stderr)
  173. sys.exit(1)
  174. # Create the main output folder, named "HighLevel"
  175. high_level_output_dir = output_dir / "HighLevel"
  176. try:
  177. high_level_output_dir.mkdir(parents=True, exist_ok=True)
  178. print(f"Output will be saved to: {high_level_output_dir}")
  179. except OSError as e:
  180. print(f"Error: Could not create output directory '{high_level_output_dir}'. {e}", file=sys.stderr)
  181. sys.exit(1)
  182. # --- Run Extraction Tasks ---
  183. print("\nMilestone 2, Task 1 Complete: Argument parsing and folder creation successful.")
  184. # Run Task 2
  185. parse_project_settings(input_dir, high_level_output_dir)
  186. # Run Task 3
  187. parse_package_manifests(input_dir, high_level_output_dir)
  188. # Run Task 4
  189. generate_guid_mappers(input_dir, high_level_output_dir)
  190. print("\nHigh-level extraction complete.")
  191. if __name__ == "__main__":
  192. main()