#!/usr/bin/env python3 """ Comprehensive enum extraction from Mag-Plugins/Shared/Constants directory. This script extracts ALL enum definitions from the Mag-Plugins source code and creates structured JSON mappings for the inventory service. """ import re import json import os from pathlib import Path from typing import Dict, List, Any, Tuple, Optional def extract_enum_from_file(file_path: Path) -> Tuple[str, Dict[str, Any]]: """Extract enum definition from a C# file.""" with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Find enum declaration enum_match = re.search(r'public enum (\w+)(?:\s*:\s*(\w+))?\s*{', content) if not enum_match: return None, {} enum_name = enum_match.group(1) enum_type = enum_match.group(2) or 'int' print(f"Extracting {enum_name} from {file_path.name}") # Extract enum entries entries = {} comments = {} attributes = {} # Pattern to match enum entries with various formats # Supports: Name = Value, Name, Name = 0x12345, etc. pattern = r'^\s*(?:\[([^\]]+)\])?\s*(?:/// \s*([^<]*?)\s*/// \s*)?(?:\[([^\]]+)\])?\s*(\w+)(?:\s*=\s*([^,]+?))?\s*,?\s*(?://\s*(.*))?$' lines = content.split('\n') in_enum = False current_value = 0 for line in lines: # Check if we're entering the enum if f'enum {enum_name}' in line: in_enum = True continue # Check if we're exiting the enum if in_enum and '}' in line and not line.strip().startswith('//'): break if in_enum: match = re.match(pattern, line) if match: attr1, summary, attr2, name, value, comment = match.groups() # Parse value if value: value = value.strip() if value.startswith('0x') and ' ' not in value: # Simple hex value parsed_value = int(value, 16) elif value.isdigit(): # Simple integer parsed_value = int(value) elif '|' in value or '<<' in value or '+' in value: # Complex expression - store as string for now parsed_value = f"EXPR:{value}" else: # Try simple conversion try: parsed_value = int(value) except ValueError: parsed_value = f"VALUE:{value}" else: parsed_value = current_value entries[parsed_value] = name # Update current_value for auto-increment if isinstance(parsed_value, int): current_value = parsed_value + 1 else: # For non-numeric values, increment anyway for next entry current_value += 1 # Store metadata if summary: comments[name] = summary.strip() if comment: comments[name] = comment.strip() if attr1 or attr2: attributes[name] = (attr1 or '') + ' ' + (attr2 or '') return enum_name, { 'type': enum_type, 'values': entries, 'comments': comments, 'attributes': attributes, 'source_file': file_path.name } def extract_skill_names(): """Extract skill names with their indices from Skill.cs""" skill_file = Path('/home/erik/MosswartOverlord/Mag-Plugins/Shared/Constants/Skill.cs') with open(skill_file, 'r') as f: content = f.read() skills = {} lines = content.split('\n') in_enum = False index = 0 for line in lines: if 'enum Skill' in line: in_enum = True continue if in_enum and '}' in line: break if in_enum: # Match skill entries like "None," or "Axe, /* Retired */" match = re.match(r'^\s*(\w+),?\s*(?:/\*\s*([^*]*)\s*\*/)?', line) if match: skill_name = match.group(1) status = match.group(2) or "Active" skills[index] = { 'name': skill_name, 'status': status.strip() } index += 1 return skills def categorize_enums(): """Create logical categories for different enum types.""" return { 'item_properties': { 'description': 'Item property value keys for different data types', 'enums': ['IntValueKey', 'DoubleValueKey', 'StringValueKey', 'BoolValueKey', 'QuadValueKey'] }, 'item_classification': { 'description': 'Item type and material classification', 'enums': ['ItemType', 'MaterialType', 'WeenieType'] }, 'game_mechanics': { 'description': 'Skills, spells, and game system enums', 'enums': ['Skill', 'SpellCategory', 'WieldRequirement'] }, 'technical': { 'description': 'Technical flags and masks for game engine', 'enums': ['CoverageMask', 'EquipMask'] } } def create_translation_mappings(): """Create user-friendly translations for important enum values.""" # Map IntValueKey values to more descriptive names int_value_translations = { 5: 'Encumbrance', 19: 'Value (Pyreals)', 25: 'Level', 28: 'Armor Level', 44: 'Base Damage', 45: 'Damage Type', 48: 'Weapon Skill', 158: 'Wield Requirements', 160: 'Wield Difficulty', 218103842: 'Maximum Damage', 218103849: 'Icon Overlay', 218103850: 'Icon Underlay' } # Material type friendly names material_translations = { 0: 'Unknown', 1: 'Ceramic', 20: 'Diamond', 21: 'Emerald', 31: 'Ruby', 32: 'Sapphire' } return { 'int_values': int_value_translations, 'materials': material_translations } def main(): """Main extraction process.""" constants_dir = Path('/home/erik/MosswartOverlord/Mag-Plugins/Shared/Constants') output_dir = Path('/home/erik/MosswartOverlord/inventory-service') # Ensure output directory exists output_dir.mkdir(exist_ok=True) print("Starting comprehensive enum extraction from Mag-Plugins/Shared/Constants...") # Extract all enum files all_enums = {} enum_files = list(constants_dir.glob('*.cs')) for enum_file in enum_files: if enum_file.name == 'Dictionaries.cs': # Skip non-enum files continue enum_name, enum_data = extract_enum_from_file(enum_file) if enum_name and enum_data: all_enums[enum_name] = enum_data # Special handling for Skills (has complex structure) skill_data = extract_skill_names() all_enums['Skill'] = { 'type': 'indexed', 'values': skill_data, 'source_file': 'Skill.cs' } # Create categorized structure categories = categorize_enums() translations = create_translation_mappings() # Save complete enum database complete_data = { 'metadata': { 'extracted_at': '2025-06-10', 'source': 'Mag-Plugins/Shared/Constants', 'total_enums': len(all_enums), 'version': '1.0.0' }, 'categories': categories, 'translations': translations, 'enums': all_enums } # Save main database main_output = output_dir / 'complete_enum_database.json' with open(main_output, 'w') as f: json.dump(complete_data, f, indent=2) print(f"Saved complete enum database to {main_output}") # Save individual enum files for backward compatibility individual_dir = output_dir / 'enums' individual_dir.mkdir(exist_ok=True) for enum_name, enum_data in all_enums.items(): enum_file = individual_dir / f'{enum_name}.json' with open(enum_file, 'w') as f: json.dump({enum_name: enum_data}, f, indent=2) # Update existing files for backward compatibility if 'IntValueKey' in all_enums: legacy_int_file = output_dir / 'int_value_enums.json' with open(legacy_int_file, 'w') as f: json.dump(all_enums['IntValueKey']['values'], f, indent=2) print(f"Updated legacy file: {legacy_int_file}") # Create comprehensive summary print("\n" + "="*60) print("EXTRACTION SUMMARY") print("="*60) for enum_name, enum_data in all_enums.items(): value_count = len(enum_data.get('values', {})) enum_type = enum_data.get('type', 'unknown') print(f"{enum_name:20} | {value_count:4} values | Type: {enum_type}") print(f"\nTotal enums extracted: {len(all_enums)}") print(f"Files created:") print(f" - {main_output}") print(f" - {len(all_enums)} individual enum files in {individual_dir}/") # Show sample translations print("\nSample enum translations:") for enum_name in ['IntValueKey', 'MaterialType', 'ItemType', 'Skill']: if enum_name in all_enums: values = all_enums[enum_name]['values'] if isinstance(values, dict): sample_keys = list(values.keys())[:3] for key in sample_keys: if isinstance(values[key], dict): print(f" {enum_name}[{key}] = {values[key].get('name', values[key])}") else: print(f" {enum_name}[{key}] = {values[key]}") if __name__ == '__main__': main()