Предлагаю вниманию читателей некоторые подробности относительно внутреннего устройства сейвов Heroes of Might & Magic I, полученные мною экспериментальным путём. Возможно, кому-то это всё давно известно, но лично мне ранее не встречалось в Сети чего-то подобного. Все эксперименты проводились на английской DOS версии 1.0(не думаю, что между версиями будут какие-либо различия). Конкретно меня интересовала следующая задача: имея сейв с выигранной миссией в кампании, поменять свою расу и миссию, которая будет следующей в кампании (а точнее, пройдя кампанию за рыцаря и учитывая то, что 7 из 8 миссий во всех кампаниях одинаковы и что я очень не люблю миссии на выкапывание клада, пройти последнюю карту Castle Ironfist). Для реализации подобной задачи оказалось достаточно понять значения всего нескольких байт в самом начале файла, которые я и опишу в данном посте вместе с некоторыми дополнительными наблюдениями.
Итак, общая информация. Как известно, игра разделяет карты (и соответственно сейвы) на стандартные (одиночные карты) и кампанию. Сейвы компании имеют расширение cgm, для стандартных в Интернете обычно указывают расширение как gm*, хотя лично на моей системе оно всегда gm1. Это различие в расширениях достаточно искусственное, поскольку на самом деле формат этих файлов один и тот же, различаются лишь значения некоторых байт в файле.
Поскольку меня интересовала только кампания, в дальнейшем я буду описывать только сейвы кампании и упоминать отличия "стандартных" сейвов, обнаруженные при беглом их просмотре.
Размер файла сейва первых героев всегда равен 77663 байт. Это важно - если размер файла будет меньше, игра загрузится со странными результатами (герой за пределами карты, полностью изменившиеся типы и численность войск у героя и т.п.) и вылетит при попытке совершить любое действие. С файлами большего размера не экспериментировал.
Первый байт файла (смещение 0x00 от его начала) - один из признаков того, является ли сейв "стандартным" или нет. Более того, он является признаком конца кампании, т.к. его значение равно 1 в последней миссии и 0 во всех остальных случаях.
Следующий байт (смещение 0x01) - собственно признак кампании. Для сейвов кампании он всегда равен 0x64, для стандартных карт - другим числам, зависящим от карты. Я не проверял подробно все стандартные карты, поэтому не могу сказать, насколько он уникально идентифицирует карту. Так для карт Around the Bay и Two If By Sea он равен 0x50, а для Claw и обучающей миссии (фактически проходящей на этой же карте) значения разные - 0x3C и 0x23 соответственно.
Байт по смещению 0x02 в моих сейвах всегда был равен 0. Возможно он относится к предыдущему параметру.
Байты 0x03-0x06 - признак начала миссии. Если игра только начата (и в первые несколько ходов) все они равны 0xFF. В дальнейшем их значения изменяются каждые несколько ходов, но я не смог увидеть в этом каких-либо закономерностей. Возможно, это что-то вроде информации из гильдии воров или какие-то другие параметры, которые учитываются для всех четырёх игроков.
Байт 0x07 - раса игрока.
1 - рыцарь
2 - варвар
3 - волшебница
4 - warlock
(Неупомянутые в посте байты либо не несут полезной информации, либо я не заметил в них какой-либо системы)
Байт 0x0B - вот тут и кодируется в сейвах кампании, на какой карте мы в данный момент играем.
0 - карта Gateway в кампании или любая из стандартных карт.
1 - Archipelago
2 - Wounded Land
3 - Free-for-all
4 - Castle Ironfist
5 - Castle Slayer
6 - Castle Lamanda
7 - Castle Alamar
8 - Dragon Island
Байты 0x0F-0x10 - счётчик игрового времени в кампании для расчёта рейтинга. Хранящееся здесь число соответствует количеству игровых дней, посчитанному по формуле (№ месяца-1)*28 + (№ недели-1)*7 + (№ дня).
Хотя здесь я не очень уверен, сохраняется ли эта закономерность для стандартных карт или нет. Возможно, эти байты - счётчик времени, потраченного на все предыдущие миссии в кампании, а время текущей миссии хранится в каком-то другом, неизвестном мне месте.
Байт 0x13 - номер текущей миссии в кампании-1. Т.е. 0 для первой миссии, 7 для последней.
(Следующее описание чисто информационное)
Байты 0x14-0xBB - в кампаниях равны 0, в стандартных картах здесь лежит текстовое описание карты (отображающееся справа внизу при выборе).
Байты 0xBC-0xBD - возможно ещё признак того, кампания это или нет. Во всяком случае, во всех сейвах кампании они равны 0x0101 (хотя и на стандартной карте Around the Bay равны этому же числу), а в большинстве стандартных карт в них нули (хотя иногда встречаются и другие значения без явной системы).
Байты 0xBE-0xCE - здесь находится имя карты для сейвов на стандартных картах или строка Unknown в кампании
Байты 0xCF-0xDC - имя файла под которым будет сохранена игра (т.е. по умолчанию здесь строка NEWGAME , если пользователь её изменял, то имя файла сейва). Что интересно, для кампании имя файла указывается без расширения.
С байта 0xF2 начинается запись параметров первого игрока. Формат этих записей давно опубликован, поэтому для целей полноты описания я скопирую информацию "как есть" , без дополнительных проверок, из http://www.maps4heroes.com/heroes1/hex_codes_eng.php и немного дополненную из http://www.celestialheavens.com/forums/viewtopic.php?p=184560#184560 (странно, здесь коды для юнитов, артефактов и заклинаний приведены на единицу меньшие. Возможно описаны разные версии игры?)
Теперь собственно, к исходной задаче. Для того чтобы сыграть недоступную обычным способом карту в кампании (т.е. сыграть против замка той расы, за которую проходилась кампания) и при этом не переигрывать 4 первых миссии, достаточно иметь выигрышный сейв 4й миссии free-for-all и изменить в нём всего два байта: в 0x07 выставить расу, за которую хочется попробовать сыграть, а в 0x0B - нужную карту. После этого можно загружать игру, перезапускать сценарий для чистоты эксперимента и нормально играть, сменив собственную расу посередине кампании (как во вторых Героях, где перед 5й миссией в кампании можно дезертировать и продолжить за противника)! А вот если сценарий не перезапускать (хотя это я уже не проверял на работоспособность), можно добиться ещё более впечатляющего и даже несколько противоестественного для первых Героев эффекта - возможности сыграть например, рыцарем против рыцаря!
Что пока остаётся неисследованным (или неопубликованным - возможно, разработчики утилит поделятся информацией хотя бы из соображений документирования всего в одном месте ?):
1) Не до конца ясен вопрос с хранением игрового времени
2) Данные о замках - принадлежность игрокам, возведённые постройки, заклинания в гильдии магов, количество невыкупленных существ
3) Объекты на картах - принадлежность шахт, текущее количество войск в отрядах нейтралов, подобранные кучки ресурсов и т.п.
4) Информация из гильдии воров (если она вообще хранится отдельно, а не пересчитывается каждый раз при заходе в неё).