Опыт: как я разбирал SAV-файлы Half-Life ради лазера у RPG
От окна с оружием до лазерного прицела
Я делал программу по заказу фаната этих игр. В интерфейсе для 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 заказчик прислал пары файлов. Я уточнил подписи: в 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 понятнее, но не доказывает совместимость записи.
Сборки с исправлениями я передал. Подтверждения окончательной приёмки всех доработок у меня нет. История с лазером и фонарём для меня как раз об этой границе: найти похожий флаг полезно, а доказать его поведение можно только на правильно подготовленных сохранениях и проверке в игре.





Обсудить
Добавить комментарий