pattern_searcher.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from abc import ABC, abstractmethod
  2. from typing import List, Tuple, Dict, Optional
  3. import sys
  4. import os
  5. # Agregar el directorio padre al path para importar config
  6. sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  7. from config import MEMORY_SEARCH_CONFIG, UI_MESSAGES
  8. from core.memory_manager import MemoryManager
  9. class PatternSearcher(ABC):
  10. """Interfaz base para buscadores de patrones (Strategy Pattern)"""
  11. def __init__(self):
  12. pass
  13. @abstractmethod
  14. def get_patterns(self) -> Dict[str, Tuple[bytes, List[str]]]:
  15. """Retorna los patrones a buscar"""
  16. pass
  17. @abstractmethod
  18. def search(self, memory_manager: MemoryManager) -> List[Tuple[str, int]]:
  19. """Busca los patrones en memoria"""
  20. pass
  21. def pattern_matches(self, data: bytes, pattern: bytes, mask: List[str], offset: int) -> bool:
  22. """Verifica si un patrón coincide en una posición específica"""
  23. pattern_len = len(pattern)
  24. if offset + pattern_len > len(data):
  25. return False
  26. data_slice = data[offset:offset + pattern_len]
  27. for i, (p_byte, m_char) in enumerate(zip(pattern, mask)):
  28. if m_char != '?' and data_slice[i] != p_byte:
  29. return False
  30. return True
  31. class StaminaPatternSearcher(PatternSearcher):
  32. """Buscador específico para patrones de stamina"""
  33. def get_patterns(self) -> Dict[str, Tuple[bytes, List[str]]]:
  34. """Define los patrones específicos para stamina"""
  35. return {
  36. "stamina_pattern_1": (
  37. bytes([0x75, 0x00, 0xF3, 0x0F, 0x10, 0x44, 0x24, 0x08,
  38. 0xF3, 0x0F, 0x5A, 0xC0, 0xF2, 0x0F, 0x5A, 0xE8,
  39. 0xF3, 0x0F, 0x11, 0xAE, 0x94, 0x01, 0x00, 0x00]),
  40. ['x', '?', 'x', 'x', 'x', 'x', 'x', 'x',
  41. 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
  42. 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x']
  43. )
  44. }
  45. def search(self, memory_manager: MemoryManager) -> List[Tuple[str, int]]:
  46. """Busca patrones de stamina en memoria"""
  47. patterns = self.get_patterns()
  48. regions = memory_manager.get_memory_regions()
  49. found_addresses = []
  50. print(UI_MESSAGES['info']['searching_patterns'].format(region_count=len(regions)))
  51. # Pre-calcular longitudes de patrones
  52. pattern_info = {name: (pattern, mask, len(pattern))
  53. for name, (pattern, mask) in patterns.items()}
  54. for region_idx, region in enumerate(regions):
  55. print(f" Región {region_idx + 1}/{len(regions)}: 0x{region['base']:X} - 0x{region['base'] + region['size']:X}")
  56. # Configuración optimizada de chunks desde config
  57. chunk_size = MEMORY_SEARCH_CONFIG['chunk_size']
  58. overlap = MEMORY_SEARCH_CONFIG['overlap']
  59. for offset in range(0, region['size'], chunk_size):
  60. current_address = region['base'] + offset
  61. read_size = min(chunk_size + overlap, region['size'] - offset)
  62. data = memory_manager.read_memory(current_address, read_size)
  63. if not data:
  64. continue
  65. # Buscar cada patrón
  66. data_len = len(data)
  67. for pattern_name, (pattern, mask, pattern_len) in pattern_info.items():
  68. search_limit = data_len - pattern_len + 1
  69. for i in range(search_limit):
  70. if self.pattern_matches(data, pattern, mask, i):
  71. found_address = current_address + i
  72. found_addresses.append((pattern_name, found_address))
  73. print(UI_MESSAGES['info']['pattern_found'].format(pattern_name=pattern_name, address=found_address))
  74. print(" 🛑 Deteniendo búsqueda - primer patrón encontrado")
  75. return found_addresses
  76. class SpeedPatternSearcher(PatternSearcher):
  77. """Buscador específico para patrones de velocidad"""
  78. def get_patterns(self) -> Dict[str, Tuple[bytes, List[str]]]:
  79. """Define los patrones específicos para velocidad"""
  80. return {
  81. "speed_pattern_1": (
  82. bytes([0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00,
  83. 0x00, 0x00, 0xC8, 0x41, 0x00, 0x00, 0x80, 0x3F]),
  84. ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'x',
  85. 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x']
  86. )
  87. }
  88. def search(self, memory_manager: MemoryManager) -> List[Tuple[str, int]]:
  89. """Busca patrones de velocidad en memoria"""
  90. patterns = self.get_patterns()
  91. regions = memory_manager.get_memory_regions()
  92. found_addresses = []
  93. print(UI_MESSAGES['info']['searching_patterns'].format(region_count=len(regions)))
  94. # Pre-calcular longitudes de patrones
  95. pattern_info = {name: (pattern, mask, len(pattern))
  96. for name, (pattern, mask) in patterns.items()}
  97. for region_idx, region in enumerate(regions):
  98. print(f" Región {region_idx + 1}/{len(regions)}: 0x{region['base']:X} - 0x{region['base'] + region['size']:X}")
  99. # Configuración optimizada de chunks desde config
  100. chunk_size = MEMORY_SEARCH_CONFIG['chunk_size']
  101. overlap = MEMORY_SEARCH_CONFIG['overlap']
  102. for offset in range(0, region['size'], chunk_size):
  103. current_address = region['base'] + offset
  104. read_size = min(chunk_size + overlap, region['size'] - offset)
  105. data = memory_manager.read_memory(current_address, read_size)
  106. if not data:
  107. continue
  108. # Buscar cada patrón
  109. data_len = len(data)
  110. for pattern_name, (pattern, mask, pattern_len) in pattern_info.items():
  111. search_limit = data_len - pattern_len + 1
  112. for i in range(search_limit):
  113. if self.pattern_matches(data, pattern, mask, i):
  114. found_address = current_address + i
  115. found_addresses.append((pattern_name, found_address))
  116. print(UI_MESSAGES['info']['pattern_found'].format(pattern_name=pattern_name, address=found_address))
  117. print(" 🛑 Deteniendo búsqueda - primer patrón encontrado")
  118. return found_addresses
  119. return found_addresses