#!/usr/bin/env python3 """ Comprehensive enum extraction from Mag-Plugins/Shared directory. Extracts all enums, dictionaries, and data needed for rich tooltip display. """ import json import re import csv from pathlib import Path from typing import Dict, Any def extract_csharp_enum(file_path: Path) -> Dict[str, Any]: """Extract enum definition from C# file.""" try: content = file_path.read_text(encoding='utf-8') # Find enum declaration enum_match = re.search(r'public enum (\w+)(?:\s*:\s*\w+)?\s*{([^}]+)}', content, re.DOTALL) if not enum_match: return {} enum_name = enum_match.group(1) enum_body = enum_match.group(2) values = {} for line in enum_body.split('\n'): line = line.strip() if not line or line.startswith('//'): continue # Parse enum values (handle various formats) if '=' in line: parts = line.split('=') if len(parts) >= 2: name = parts[0].strip().rstrip(',') value_part = parts[1].strip().rstrip(',') # Handle hex values if value_part.startswith('0x'): try: value = int(value_part, 16) values[str(value)] = name except ValueError: pass # Handle decimal values elif value_part.isdigit(): values[value_part] = name # Handle expressions (for flags) elif '|' in value_part: values[f"EXPR:{value_part}"] = name else: # Auto-increment enum name = line.rstrip(',').strip() if name and not name.startswith('//'): values[str(len(values))] = name return { 'name': enum_name, 'values': values, 'source_file': str(file_path.name) } except Exception as e: print(f"Error extracting enum from {file_path}: {e}") return {} def extract_dictionaries_cs(file_path: Path) -> Dict[str, Any]: """Extract dictionary mappings from Dictionaries.cs file.""" try: content = file_path.read_text(encoding='utf-8') dictionaries = {} # Look for dictionary definitions with various patterns dict_patterns = [ r'public static readonly Dictionary (\w+) = new Dictionary\s*\n\s*{([^}]+)}', r'public static readonly Dictionary (\w+) = new Dictionary\(\)\s*\n\s*{([^}]+)}' ] for pattern in dict_patterns: for match in re.finditer(pattern, content, re.DOTALL): dict_name = match.group(1) dict_body = match.group(2) values = {} for line in dict_body.split('\n'): line = line.strip() if not line or line.startswith('//'): continue # Parse dictionary entries: { 0x1, "value" } or { 1, "value" } entry_patterns = [ r'{\s*0x([0-9A-Fa-f]+),\s*"([^"]+)"\s*}', # Hex format r'{\s*(\d+),\s*"([^"]+)"\s*}' # Decimal format ] for entry_pattern in entry_patterns: entry_match = re.search(entry_pattern, line) if entry_match: key_str = entry_match.group(1) value = entry_match.group(2) # Convert hex to decimal if needed if 'x' in entry_pattern: key = str(int(key_str, 16)) else: key = key_str values[key] = value break if values: dictionaries[dict_name] = { 'name': dict_name, 'values': values, 'source_file': 'Dictionaries.cs' } return dictionaries except Exception as e: print(f"Error extracting dictionaries: {e}") return {} def extract_spells_csv(file_path: Path) -> Dict[str, Any]: """Extract spell data from Spells.csv.""" try: spells = {} # Try different encodings for encoding in ['utf-8', 'latin1', 'cp1252']: try: with open(file_path, 'r', encoding=encoding) as f: reader = csv.DictReader(f) for row in reader: spell_id = row.get('Id', '').strip() if spell_id and spell_id.isdigit(): spells[spell_id] = { 'id': int(spell_id), 'name': row.get('Name', '').strip(), 'description': row.get('Description', '').strip(), 'school': row.get('School', '').strip(), 'difficulty': row.get('Difficulty', '').strip(), 'duration': row.get('Duration', '').strip(), 'mana': row.get('Mana', '').strip(), 'family': row.get('Family', '').strip(), 'speed': row.get('Speed', '').strip(), 'target_effect': row.get('TargetEffect', '').strip(), 'target_mask': row.get('TargetMask', '').strip() } break except UnicodeDecodeError: continue return { 'name': 'Spells', 'values': spells, 'source_file': 'Spells.csv' } except Exception as e: print(f"Error extracting spells: {e}") return {} def extract_object_class_cs(file_path: Path) -> Dict[str, Any]: """Extract ObjectClass enum specifically.""" try: content = file_path.read_text(encoding='utf-8') # Find ObjectClass enum enum_match = re.search(r'public enum ObjectClass\s*{([^}]+)}', content, re.DOTALL) if not enum_match: return {} enum_body = enum_match.group(1) values = {} current_value = 0 for line in enum_body.split('\n'): line = line.strip() if not line or line.startswith('//'): continue if '=' in line: parts = line.split('=') if len(parts) >= 2: name = parts[0].strip().rstrip(',') value_str = parts[1].strip().rstrip(',') try: current_value = int(value_str) values[str(current_value)] = name current_value += 1 except ValueError: pass else: name = line.rstrip(',').strip() if name and not name.startswith('//'): values[str(current_value)] = name current_value += 1 return { 'name': 'ObjectClass', 'values': values, 'source_file': 'ObjectClass.cs' } except Exception as e: print(f"Error extracting ObjectClass: {e}") return {} def main(): base_path = Path('/home/erik/MosswartOverlord/Mag-Plugins/Shared') constants_path = base_path / 'Constants' spells_path = base_path / 'Spells' comprehensive_database = { 'metadata': { 'extracted_at': '2025-06-10', 'source': 'Mag-Plugins/Shared (comprehensive)', 'version': '2.0.0' }, 'enums': {}, 'dictionaries': {}, 'spells': {}, 'object_classes': {} } # Extract all constant enums for cs_file in constants_path.glob('*.cs'): if cs_file.name in ['Dictionaries.cs']: continue # Handle separately enum_data = extract_csharp_enum(cs_file) if enum_data: comprehensive_database['enums'][enum_data['name']] = enum_data print(f"✓ Extracted enum: {enum_data['name']} ({len(enum_data['values'])} values)") # Extract ObjectClass enum object_class_file = base_path / 'ObjectClass.cs' if object_class_file.exists(): object_class_data = extract_object_class_cs(object_class_file) if object_class_data: comprehensive_database['object_classes'] = object_class_data print(f"✓ Extracted ObjectClass: {len(object_class_data['values'])} values") # Extract dictionaries dict_file = constants_path / 'Dictionaries.cs' if dict_file.exists(): dict_data = extract_dictionaries_cs(dict_file) comprehensive_database['dictionaries'] = dict_data print(f"✓ Extracted dictionaries: {len(dict_data)} dictionaries") # Extract spells spells_file = spells_path / 'Spells.csv' if spells_file.exists(): spell_data = extract_spells_csv(spells_file) if spell_data: comprehensive_database['spells'] = spell_data print(f"✓ Extracted spells: {len(spell_data['values'])} spells") # Save comprehensive database output_file = Path('/home/erik/MosswartOverlord/inventory-service/comprehensive_enum_database_v2.json') with open(output_file, 'w') as f: json.dump(comprehensive_database, f, indent=2) print(f"\n✅ Comprehensive enum database saved to: {output_file}") print(f"📊 Total enums: {len(comprehensive_database['enums'])}") print(f"📊 Total dictionaries: {len(comprehensive_database['dictionaries'])}") print(f"📊 Total spells: {len(comprehensive_database['spells'].get('values', {}))}") print(f"📊 Object classes: {len(comprehensive_database['object_classes'].get('values', {}))}") if __name__ == '__main__': main()