все заметки
[ru / en]

Добавляем монеты на баланс в LEGO Games

2026.05.05

Внимание! Это не для всех Лего игр.

Естественно это делается правкой сохраненной игры.

Но, происходит проверка данных с контрольной суммой (далее КС).

В этом и будем разбираться.

Но если влом, то можно воспользоваться этим инструментом:

  • LEGO® Indiana Jones™ 2
  • LEGO® Star Wars™: The Complete Saga
  • LEGO® Batman™
Выбери файл сохраненной игры, введи баланс, получи файл.
Файл правится в браузере. Никуда не загружается. Не забываем про бэкапы.

А если интересно, то есть два пути. Найти и использовать алгоритм генерации КС.

Или пропатчить саму игру в месте, где идет проверка КС.

Для примера исследуем LEGO Indiana Jones 2.

Где баланс?

Для начала берем файл сохраненной игры из:

  • %appdata%\LucasArts\LEGO® Indiana Jones™ 2\SavedGames
  • %appdata%\..\Local\LucasArts\LEGO Star Wars - The Complete Saga\SavedGames
  • %appdata%\..\Local\Warner Bros. Interactive Entertainment\LEGO Batman\Saved Games

Сам файл это Rich Saved Games формат со структурой:

typedef struct _RICH_GAME_MEDIA_HEADER\n{\n    DWORD         dwMagicNumber;\n    DWORD         dwHeaderVersion;\n    DWORD         dwHeaderSize;\n    LARGE_INTEGER liThumbnailOffset;\n    DWORD         dwThumbnailSize;\n    GUID          guidGameId;\n    WCHAR         szGameName[RM_MAXLENGTH];\n    WCHAR         szSaveName[RM_MAXLENGTH];\n    WCHAR         szLevelName[RM_MAXLENGTH];\n    WCHAR         szComments[RM_MAXLENGTH];\n} RICH_GAME_MEDIA_HEADER;

Нас интересует два значения:

  • dwMagicNumber - четыре байта должны быть равны HMGR, идентификация, что файл тот
  • dwHeaderSize - это размер всего заголовка, после него идут кастомные данные, которые и суммируются

Смотрим какой текущий баланс, переводит его в HEX и ищем в файле. Не забываем про порядок байтов, у нас little endian. То есть, если HEX = 00005F32, то искать нужно 32 5F 00 00.

В LEGO Indiana Jones 2 это смещение 0x22FF0 от начала файла. Или смещение внутри данных для КС (0x22FF0-dwHeaderSize)=0x20FC8

Алгоритм генерации

Подсчет КС осуществляется простым складыванием (add) по четыре байта (dword) к константе 0x5C0999:

09AB1D2 mov     esi, 05C0999\n09AB1D7 jle     09AB1EA\n09AB1D9 mov     edx, [esp+4+arg_0]\n09AB1DD lea     ecx, [ecx+0]\n09AB1E0 add     esi, [edx+ecx*4]\n09AB1E3 add     ecx, 1\n09AB1E6 cmp     ecx, eax\n09AB1E8 jl      09AB1E0

А начинается он именно после RICH_GAME_MEDIA_HEADER заголовка, то есть со смещения на dwHeaderSize байт, и продолжается до конца файла минус 8 байт, которые:

  • 4 байта - это КС который сравнивается с подсчитанным ранее
  • 4 байта - это процент прохождения в игре

То есть, получаем такой код:

start = dwHeaderSize;\nlength = filesize - dwHeaderSize - 8;\nchecksum = 0x5C0999;\n\nfor (i = start; i < length; i = i + 4) {\n\tchecksum += data[i]; // DWORD, 4 байта\n}

Патч игры

Можно пойти другим путем. Чтобы каждый раз не пересчитывать КС, а просто менять количество монет, нужно пропатчить LEGOIndy2.exe файл.

09AB4FF cmp     eax, [edi+ebx] ; 3B 04 1F\n09AB502 jnz     09AB529 ; 75 25

Если КС не равны, то упрыгивает. Нужно просто не прыгать. Меняем один байт 25 -> 00:

09AB502 jnz     09AB504 ; 75 00

А для LEGOStarWarsSaga.exe и LEGOBatman.exe наоборот, нужно прыгать. Так же ищем 3B 04 1F:

0XXXXXX cmp     eax, [edi+ebx] ; 3B 04 1F\n0XXXXXX jz      0XXXXXX ; 74 12

Такая же проверка КС, и если равны, то прыгает в нужно место. Нужно принудить 74 -> EB:

0XXXXXX jmp     0XXXXXX ; EB 12
еще по темам: реверс инжиниринг, patch