# -*- coding: utf-8 -*- """ CustomProperties 문자열 파싱 유틸리티 AnimMontage의 AnimNotifies에 있는 CustomProperties는 모두 문자열로 저장되어 있어서 타입 변환 및 파싱이 필요합니다. """ import re from typing import Any, Optional def parse_float(value: str) -> Optional[float]: """문자열을 float로 변환 (실패 시 None)""" try: return float(value.strip()) except (ValueError, AttributeError): return None def parse_tag_name(value: str) -> Optional[str]: """ (TagName="Event.Attack.Normal") 형태에서 태그 이름 추출 Example: '(TagName="Event.Attack.Normal")' -> 'Event.Attack.Normal' """ if not value: return None match = re.search(r'TagName="([^"]+)"', value) if match: return match.group(1) return None def parse_vector(value: str) -> Optional[dict]: """ (X=0.0,Y=1.0,Z=0.0) 형태를 dict로 변환 Example: '(X=0.0,Y=1.0,Z=0.0)' -> {'X': 0.0, 'Y': 1.0, 'Z': 0.0} """ if not value: return None try: x_match = re.search(r'X=([-\d.]+)', value) y_match = re.search(r'Y=([-\d.]+)', value) z_match = re.search(r'Z=([-\d.]+)', value) if x_match and y_match and z_match: return { 'X': float(x_match.group(1)), 'Y': float(y_match.group(1)), 'Z': float(z_match.group(1)) } except (ValueError, AttributeError): pass return None def parse_array(value: str) -> Optional[list]: """ (0.1,0.2,0.3) 형태를 리스트로 변환 Example: '(0.1,0.2,0.3)' -> [0.1, 0.2, 0.3] """ if not value: return None try: # 괄호 제거 cleaned = value.strip().strip('()') if not cleaned: return [] # 쉼표로 분리 parts = cleaned.split(',') return [float(p.strip()) for p in parts if p.strip()] except (ValueError, AttributeError): return None def parse_custom_property(value: str, field_name: str) -> Any: """ CustomProperties 필드의 값을 적절한 타입으로 변환 Args: value: 원본 문자열 값 field_name: 필드 이름 (타입 추론에 사용) Returns: 파싱된 값 (float, str, dict, list 등) """ if not isinstance(value, str): return value value = value.strip() # 빈 문자열 if not value or value == '': return None # TagName 파싱 if 'TagName=' in value: tag = parse_tag_name(value) if tag and tag != 'None': return tag return None # Vector 파싱 if value.startswith('(') and ('X=' in value or 'Y=' in value or 'Z=' in value): vec = parse_vector(value) if vec: return vec # Array 파싱 (TimeArray, LocationArray 등) if 'Array' in field_name and value.startswith('('): arr = parse_array(value) if arr is not None: return arr # Boolean 파싱 if value.lower() in ['true', 'false']: return value.lower() == 'true' # Float 파싱 시도 float_val = parse_float(value) if float_val is not None: return float_val # 기본: 문자열 그대로 반환 return value def extract_notify_properties(notify: dict, target_fields: list) -> dict: """ AnimNotify에서 CustomProperties의 특정 필드만 추출 Args: notify: AnimNotify 딕셔너리 target_fields: 추출할 필드 이름 리스트 Returns: 추출된 프로퍼티 딕셔너리 """ custom_props = notify.get('CustomProperties', {}) result = {} for field in target_fields: if field in custom_props: value = custom_props[field] parsed = parse_custom_property(value, field) if parsed is not None: result[field] = parsed return result