Опыт: как я разбирал SAV-файлы Half-Life ради лазера у RPG

Пост опубликован в блогах iXBT.com, его автор не имеет отношения к редакции iXBT.com

От окна с оружием до лазерного прицела

Я делал программу по заказу фаната этих игр. В интерфейсе для Half-Life были слоты оружия с первого по пятый, подпись главы и карты, время текущей карты и выбор русского или английского языка. За полями окна стояли разные задачи: добавить оружие, записать патроны, сохранить режим прицеливания.

В части Counter-Strike речь шла о Condition Zero: Deleted Scenes, дальше буду сокращать до CZDS. Также в работе были сохранения Blue Shift и Opposing Force.

Потом заказчик спросил о сохранённом состоянии лазера у RPG и Desert Eagle. В проверенном сейве Opposing Force у записей CRpg и CEagle нашлось поле m_fSpotVisible. В обоих случаях оно равнялось 1: режим был включён. Я объяснил, что его можно вывести отдельными переключателями в редакторе.

Это была находка в конкретном сохранении. Для RPG в Half-Life и Blue Shift ещё понадобились отдельные образцы с включённым и выключенным лазером.

Меню редактора: выбор игры и раздел «Персонаж».
Меню редактора: выбор игры и раздел «Персонаж».

Почему оружие в редакторе есть, а в игре его нет

Одна из ошибок выглядела как подмена: Glock, SG-550 и SG-552 отображались как TMP, MP5 и Galil. Я нашёл конфликт между игровыми идентификаторами и битами HUD, то есть флагами отображения оружия в игровом интерфейсе. После правки чужие пушки перестали появляться. Но заказчик проверил тот же сейв и сообщил: нужные тоже не восстановились.

После добавления SG-550 или SG-552 через консоль редактор видел оружие и мог удалить его. Но в HUD оставалась неактивная иконка.

Сначала я использовал шаблон из другого сохранения. Затем попробовал клонировать сущность из самого редактируемого файла: для Glock взял USP, для SG-550 и SG-552 — M4A1. Переписывал класс, идентификатор, боезапас и модель. Локальные проверки проходили.

Следующий файл от заказчика показал: редактор записал сущности и связал их с инвентарём. Игра их отбрасывала. Мой код чтения SAV соглашался с моим же кодом записи, но движок ожидал другую структуру.

Эталон должен создать сам движок

Я попросил выдать нужное оружие через игровую консоль и прислать сохранение, которое ещё не открывали в редакторе. По результату собственной обработки нельзя было судить о том, как оружие записывает игра.

Побайтное сравнение сначала привело меня к неверной гипотезе. Я решил, что Glock нужен ID 17, SG-550 — ID 12, а для восстановления обязателен полный набор полей CBasePlayerWeapon. Чистый игровой образец это опроверг.

В нём Glock имел ID 11 и пустой блок CGLOCK18. У SG-550 был ID 9 без блока подкласса, у SG-552 — ID 20 и пустой CSG552. Игра нормально опускала поля с нулевыми значениями. Сохранение, на которое я ориентировался раньше, нельзя было считать чистым эталоном.

После этого я поправил создание записей и добавил исправление старых при сохранении. Отдельно учёл оружие и аксессуары при удалении и очистке HUD. При этом сохранялись патроны в магазине, запас боеприпасов и связи предметов в инвентаре.

Исходный игровой файл после чтения и записи без правок побайтно совпадал с оригиналом. Повторное исправление тоже не меняло уже исправленную запись. Запуск в игре оставался отдельным этапом.

Пустое поле и отсутствующий блок — разные вещи

При разборе вылетов в Opposing Force нашлась ещё одна ошибка. Старая версия редактора обрывала запись после CBasePlayerWeapon, без обязательного блока полей производного класса.

Это касалось, например, CM249, CSniperRifle и CSporeLauncher. При восстановлении GoldSrc мог прочитать следующую сущность как продолжение оружия. Здесь уже недостаточно было подобрать правильный идентификатор.

Противоречия с пропущенными нулевыми полями здесь нет. Одно дело — опустить отдельное поле. Другое — потерять обязательный блок класса и нарушить границу между сущностями. Я добавил запись таких блоков и исправление старых записей.

В интерфейсе тоже потребовались поправки. Для LAW в CZDS оставил одну ячейку со значением 0 или 1, без резерва. У оружия, которое не выбрано, выключил поля патронов. Для раннего небезопасного сохранения Blue Shift сохранил запрет на добавление оружия.

Главное окно редактора: персонаж, позиция игрока, слоты оружия с патронами и переключатель лазера у RPG.
Главное окно редактора: персонаж, позиция игрока, слоты оружия с патронами и переключатель лазера у RPG.

Зачем понадобились три сейва с фонарём

Для проверки лазера у RPG заказчик прислал пары файлов. Я уточнил подписи: в Half-Life файл 000 был без лазера, 001 — с лазером. В Blue Shift выключенному состоянию соответствовал 001, включённому — 002. Это номера файлов в наборе, не значения игрового параметра.

С фонарём у M3 и MP5 сначала пришлось устранить неоднозначность в подписях. Для MP5 были указаны 000 и 009: выключен и включён. У M3 один номер оказался подписан обоими состояниями. Я переспросил и получил подтверждение: 010 — выключен, 011 — включён. Перепутанные подписи испортили бы дальнейшее сравнение.

Затем я попросил для каждого оружия серию на одной карте: фонарь выключен, только что включён и всё ещё включён спустя 10 секунд. Между сохранениями попросил не менять оружие и не делать других действий. По возможности нужен был ещё файл после выключения. Просил целые SAV с подписью оружия и состояния.

Смысл такого запроса — отделить переключение от изменений, которые происходят со временем. Пара «выключено/включено» показывает различия между двумя снимками. Третий снимок позволяет проверить, какие различия сохраняются при том же режиме, а какие продолжают меняться. Обратное переключение помогает проверить догадку ещё раз.

Результата проверки всей серии у меня нет. Утверждать, что включение фонаря у M3 и MP5 доведено до рабочего результата, пока не могу.

Замечание заказчика по таблице «Время на локациях»: стрелкой показан порядок колонок.
Замечание заказчика по таблице «Время на локациях»: стрелкой показан порядок колонок.

Режим оружия нельзя записывать поверх остальных

В сборке 02.10.2026.1 я добавил работу с сохранёнными режимами CZDS: глушитель M4A1 и USP, очередь по три выстрела у Glock 18 и FAMAS. Для поля зрения игрока, FOV, были предусмотрены значения 90, 55, 40, 15 и 10.

При записи одного режима остальные биты m_WeaponState сохранялись: изменение выбранного состояния не должно затронуть другие флаги. На контрольном сохранении проверил, что соседнее поле CM4A1. iShellOn остаётся прежним после записи и повторного чтения.

У этой сборки прошли 62 теста, ещё 14 были условно пропущены. Архив проверил и передал заказчику. В игре локально сборка не запускалась, дальше требовалась его игровая проверка. Пропуски в число успешных тестов я не включаю.

Вывод: версия сборки тоже часть проверки

Я вынес номер сборки в заголовок программы и README. Появились версии 28.09.2026.2, 29.09.2026.4 и 30.09.2026.2. По замечанию об исчезнувшем оружии нужно понимать, какую сборку проверяли. Новую версию я просил распаковывать в пустую папку.

Из этой работы я вынес простое правило: сначала нужен образец, созданный игрой, затем аккуратная правка и проверка сохранности остальных данных. После этого файл должен принять сам движок. Графический интерфейс делает работу с SAV понятнее, но не доказывает совместимость записи.

Сборки с исправлениями я передал. Подтверждения окончательной приёмки всех доработок у меня нет. История с лазером и фонарём для меня как раз об этой границе: найти похожий флаг полезно, а доказать его поведение можно только на правильно подготовленных сохранениях и проверке в игре.

ШутерPC
Автор не входит в состав редакции iXBT.com (подробнее »)

Сейчас на главной

Новости

Публикации

Мультиметр для автодиагностики FNIRSI TMP-610S: обзор осциллографа, цифрового генератора и тестера сигналов в одном корпусе

В одном корпусе осциллограф, цифровой генератор и тестер сигналов — такое устройство позволяет не только видеть статические значения амплитуды, но и анализировать форму сигналов,...

Обзор и тест мини-ПК Ninkear L20: Core Ultra 5 226V, Intel Arc 130V и локальный ИИ

Когда-то компактный компьютер приходилось выбирать по принципу «лишь бы хватило мощности для браузера и документов». Сейчас ситуация изменилась. В корпусе объёмом меньше литра уже можно...

Эта птица глотает кости целиком: как бородач умудряется их переваривать

Когда от погибшего животного остаются почти одни кости, для большинства падальщиков еда уже закончилась. Но бородач всё ещё может здесь пообедать. Эта крупная горная птица подбирает кости и глотает...

Зачем NASA отправило на орбиту серебристый шар размером с дом

В начале 1960-х спутниковая связь ещё была экспериментом. Инженерам предстояло выяснить, можно ли передавать через космос обычный телефонный разговор и какое оборудование для этого...

Зачем крекеру дырки: история и немного морской романтики

Крекеры — это лаконичный, но невероятно практичный продукт, который давно занял своё место в рационе многих людей. Их ценят за нейтральный вкус и приятную текстуру: они отлично оттеняют...