268 lines
No EOL
10 KiB
Python
268 lines
No EOL
10 KiB
Python
#!/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<int, string> (\w+) = new Dictionary<int, string>\s*\n\s*{([^}]+)}',
|
|
r'public static readonly Dictionary<int, string> (\w+) = new Dictionary<int, string>\(\)\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() |