
Введение
Мне стало любопытно, можно ли создать парсер карт HotA, способный «на лету» отвечать на игровые вопросы: «Где искать свиток «Городского портала»?», «В каком месте спрятан Чёрный шар?» или «Томится ли герой Джелу в тюрьме?».
Вместо поиска готовых спецификаций в сети я решил пойти сложным путем — разобраться во внутреннем устройстве формата самостоятельно. Будем считать, что интернета не существует, а в распоряжении есть только сами карты, hex-редактор и здоровое любопытство.
Эта статья — отчет о моих «низкоуровневых мучениях»: изучении байт-кода, сопоставлении файлов, поиске закономерностей и извлечении полезной информации. Если эта цифровая археология покажется вам утомительной, можете смело переходить к финалу — там лежит готовое решение, которое поможет вам находить нужные заклинания без лишних усилий.
Сразу оговорюсь: я не претендую на звание эксперта по реверс-инжинирингу, просто захотелось немного поиграть с шестнадцатеричным редактором, да и вручную «ковырять» карты в редакторе надоело.
Подготовка
Для работы я взял ImHex и HxD. Чтобы не запутаться в огромном массиве данных, я создал серию тестовых карт, меняя в каждой ровно один параметр: название, наличие подземелья или сложность. Если менять всё сразу, разобраться в «простыне» байтов будет невозможно, а при пошаговых изменениях файл сам начинает «рассказывать», где спрятаны нужные настройки.
Открыв первую карту через ImHex, я столкнулся с ожидаемым препятствием.

Карта упакована в gzip. Распаковав все файлы через WinRAR, я приступил к их сравнительному анализу.
Проверка на «шум»
Сначала я убедился, что редактор карт предсказуем: создал карту, сохранил её, а затем сделал копию под другим именем. Побайтное сравнение подтвердило, что файлы идентичны — лишнего «мусора», метаданных или случайных таймстампов программа не добавляет.


Отлично, фундамент чист.
Извлечение заголовка: название карты
Сравнивая карты с разными названиями (например, «A» и «ABC»), я заметил изменение в ячейке 0x2B. В первом случае там стояло 01, во втором — 03. Попробовав более длинное название, я увидел значение 10, что в шестнадцатеричной системе как раз соответствует десятичному 16 (количеству символов). Логика проста: этот байт хранит длину названия.

С русскими названиями всё также предсказуемо — используется стандартная кодировка Windows-1251.
Первые шаги в Python
Определив, что длина названия лежит по адресу 0x2B, а само имя начинается с 0x2F, я написал базовый скрипт для его извлечения.
import gzip
data = gzip.open("hota_lab_03_name_LONG.h3m", "rb").read()
# Читаем длину, используя little-endian
n = int.from_bytes(data[0x2B:0x2F], "little")
name = data[0x2F:0x2F + n]
print(name.decode("cp1251"))
Использование little-endian здесь критично: именно так младший байт числа записывается первым. Это универсальное правило для формата H3M.
Исследование объектов: свитки и заклинания
Самое интересное началось, когда я перешел к объектам. Положив на карту свиток с «Волшебной стрелой», а затем заменив его на «Городской портал», я вычислил байт, отвечающий за ID заклинания. Сравнивая другие заклинания, я подтвердил закономерность.
Magic Arrow -> Town Portal : 0F -> 09
Magic Arrow -> Lightning Bolt : 0F -> 11
Magic Arrow -> Implosion : 0F -> 12
...
Удивительно, но даже при смене типа объекта (скажем, со свитка на объект «Ученый») логика хранения данных сохраняется, хотя и усложняется наличием дополнительных флагов.
Итоговый парсер
Последовательно изучая каждый блок данных — координаты объекта, тип награды, наличие подземелья — я собрал библиотеку для анализа файлов.
Полный код доступен на GitHub: hota-map-parser.
Запуск предельно прост:
python .\analyze_h3m.py "my_map.h3m" spell "Town Portal"
Помимо прямого поиска, я интегрировал систему взаимодействия через ИИ-агентов (Codex). Теперь можно просто спросить у консоли: «Где на этой карте Джелу?», и агент сам просканирует файл, используя мой парсер.

Надеюсь, этот опыт поможет тем, кто тоже любит заглядывать «под капот» любимых игр. Enjoy!


