» » Мануал по взлому любых SMD игры [Romhacking]
Навигация по сайту
Случайная игра

Вступай!!!
Облако тегов
****************** Незаконченная (-демо) статья v02 *********************************************************
*************************************************************************************************************
Мануал по взлому любых SMD игры [Romhacking]

Ромхакинг для начинающих. Sega Mega Drive.

Гайд по взлому игр Sega. Автор Ti.

В этом гайде будут рассмотрены методики взлома игр с приставки Sega Mega Drive. Взлом игр с других платформ может значительно отличаться, поэтому не рекомендуется использовать тут написанное, как шаблон, если только вы не понимаете что делаете. Гайд ориентирован на новичков, к тому же автор не является професcиональным программистои и программистом вообще, поэтому все определения и названия могут и будут отличаться. Под взломом понимается изучение и изменение самой игры, а не замена графики или звуков, это будет затронуто лишь вкратце.
Также автор не отвечает за причиненный вред психологическому состоянию, в случае неудачных попыток хакинга.

Важно:

Что необходимо знать:
16-ричная и двоичная система счисления и их отличия от десятичной.
Что такое ROM.
Что такое RAM.
Что такое байт(byte),бит.
Иметь общее представление о работе компьютера.

Если вы не знаете что это такое или не уверены лучше не читайте этот гайд!

Для взлома понадобятся следующие программы:
IDAPro v5.2 с sega loader'ом. (Альтернативы нет!)
WinHex или любой другой HEX-редактор. (В примерах будет рассмотрен WinHEX, но принцип работы у всех одинаков)
GENSVKNTrace - модификицая эмулятора GENS c расширенным дебаггером.
Gens-mk2 (с сайта шедевра) - эмулятор с безглючным поиском читов(значений в памяти).
Gens11/GensKMOD - также можно запастись и этими.
FixCheckSum.exe - программа исправления контрольной суммы рома, требуется для игр с защитой.
GGConv.exe - конвертер гейм гени кодов(читов) в формат Адресс:значение.
Goldwave - редактор звуков если вдруг решили заменить звук.(имеется ввиду речь).
Тайловый редактор - Он будет нужен чтобы узнать где именно в роме лежит графика. Примеры редакторов - YY-CHR V.098, djinn Tile mapper, tile molester, tile layer pro и т.д.
Если вы не смогли скачать("где это скачать", "у меня не работает ссылка" и т.д.) данные программы не переходите к
дальнейшим пунктам! Научитесь скачивать потом ломать ромы!

Многие ссылки можно найти тут:
romhacking


Введение.


Форматы представления данных.

В сеге все значения можно поделить на 3-типа =
byte = значение в 1байт. (.b)
word = значение 2байта. (.w)
longword = значение из 4байтов. (.l)

Именно такими значениями оперирует процессор.

байт это число от 0 до $FF (0-255) (в бол-ве случаев от -128 до +127) \ это важно учитывать!.
ворд это число от 0 до $FFFF (0-65535) (в бол-ве случаев от -32768 до +32767) /
символ $ перед числом говорит о его 16-ричном представление.


Железо сеги.

ROM (read-only-memory)-память только чтения - пзу картиржа (картирдж) - то что на нем записано - ром-файл.
RAM (random access memory) -оперативная память (временная) - работает только при включение питания.

Процессор сеги (Motorola 68000) - выполняет операции между ram, rom и собственными регистрами и другими системными устройствами.
VDP (видеопроцессор),
Z80 (доп.процессор), используемый для звуковых программ.
Порты джойстиков.
YM2612 - звуковой чип.
PSG-еще один звуковой чип (шумогенератор).

Прежде чем вскрыть ром.

Распакуйте его если он в архиве, также ром должен быть формата .BIN (или .GEN)

Что содержится в роме? В роме нет файлов. Все данные, расположенные в роме - графика, музыки, звуки, код (программа), могут быть расположены как угодно, а их точное располжение (адрес) определяется программой.

Вскрытие рома.

Для начала откроем РОМ с помощью WINHEX. (File-Open).

Откроется окно, где слева отображается некоторая информация о файле, чуть правее Offset - адрес текущего расположения, посередине большое окно с кучей цифр - 16ричное представление, и справа ASCII-представление. Обычно вначале картирджа располагается информация(заголовок) об игре, который можно прочитать в текстовом (ascii)-виде.

Можно прокрутить ползунок по всему рому, чтобы поискать нет ли еще где такого текста. Если есть его как правило можно безболезненно поменять. Не пытайтесь менять другие цифры в надежде получить хороший результат.

Важно:
Если нажать на колонку offset мышкой колонка адресов преставиться в 10-чной системе, это очень частая ошибка когда случайно так нажимают, мы будем использоваться только 16-ричную систему, если нажали надо нажать еще раз чтобы вернуть обратно, это станет заметно по наличию букв в адресах.

Итак, как же работает игра? Начальный адрес программы задается по адресу 4.

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000000 00 FF FF 00 00 00 02 10


$00000210 - тип longword (полный адрес) в вашем случае адрес может отличаться (как правило $200)

Теперь если перейти к этому адресу (Position-go to offset-210-ok), мы окажемся вначале программы

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000210 4A B9 00 A1 00 08 66 06 4A 79 00 A1 00 0C 66 7C
00000220 4B FA 00 7C 4C 9D 00 E0 4C DD 1F 00 10 29 EF 01

Эти цифры представляют собой машинные коды, которые читает и выполняет процессор, отображенные в 'удобном' 16-ричном виде.
К счастью любой такой код можно перевести в код языка Ассемблера, однако следует помнить что ром не состоит из одних кодов, цифры могут означать и что угодно (графика, данные и т.д.).

например данный код: 4A B9 00 A1 00 08
до компиляции (преобразовании из ассемблера в машинные коды) выглядит так:
tst.l    ($A10008).l


Процессор начинает считывать и выполнять программу с картиджа сразу при включении. Все коды читаются один за другим непрерывно, также программа перейти из одного адреса в другой если это указано или условие перехода выполняется (переход, прыжок).

Самым лучшим способом отделения программной части игры от всего остального (графики, звуков) является дизассемблинг.

В этом нам поможет IDAPro. (используем IDAv5.2 с sega loader'ом(c) от HardwareMan'a)
Запускаем idag.exe
OK.
NEW-disassemble new file.
Выбираем РОМ-файл.
OK.
OK.
Появится окно IDA View-A.
Немного прокрутим ползунок до адресса $210. (в данном случае)

Увидим следующее (или похожее) :

ROM:00000210; =============== S U B R O U T I N E =======================================
ROM:00000210
ROM:00000210
ROM:00000210                 global RESET
ROM:00000210 RESET:
ROM:00000210                 tst.l   (unk_0_A10008).l
ROM:00000216                 bne.s   loc_0_21E
ROM:00000218                 tst.w   (word_0_A1000C).l
ROM:0000021E
ROM:0000021E loc_0_21E:                            ; CODE XREF: RESET+6j
ROM:0000021E                 bne.s   loc_0_29C
ROM:00000220                 lea     unk_0_29E,a5
ROM:00000224                 movem.w (a5)+,d5-d7
ROM:00000228                 movem.l (a5)+,a0-a4
ROM:0000022C                 move.b  byte_0_FFFFEF01(a1),d0
ROM:00000230                 andi.b  #$F,d0
ROM:00000234                 beq.s   loc_0_23E
ROM:00000236                 move.l  #$53454741,$2F00(a1)


Вот это и есть программа(код) игры на языка Ассемблера, точнее только ее маленькая часть (самое начало).

Рекомендуется включить сразу текстовый вид (правая кнопка - text view), иначе при прокрутке начнут вылезать графики.

Каждая строчка представляет собой команду для процессора, где слева - сама команда, а справа, то над чем её проводить (адрес, число, регистры). Также в среде программистов именуются инструкциями, опкодами и прочее.

Теперь если выделить любую команду мышью, а затем перейти во вторую вкладку (Hex view-A), мы увидим её отображение в РОМЕ в 16-ричном виде и место расположения. Команда будет выделена зеленым и может быть разной длины (от 2 до 10байт и более). Данная "фишка" позволит нам в дальнейшем с легкостью их находить и менять если нужно.

К сожалению IDA не может определить сама расположение всех частей программы (т.е. отделить весь игровой код, от всего остального), поэтому доделывать за неё это придётся нам.

В зависимости от игры и наших целей это можно делать, а можно и не делать.
Опция в IDA , Options-General-Kernel Options 1- Make final analysis pass (поставить галочку) и далее нажать Reanalyze Program позволяет отделить больше кода чем обычно, но это вызывает и излишки когда куски не кода, определяются тоже как код.
Это не страшно так как в IDA всегда можно преобразовывать данные в код и обратно. Используйте эту опцию (можно еще нажать её н-ко раз) если IDA отделила слишком мало кода (кол-во определяется по большой полосе сверху на вкладкой).

Есть два простых хоткея преобразования туда-обратно: это 'C' (code) и 'U' (undefine) - отмена.
Попробуйте сразу нажать 'U' на место где есть код, а потом снова 'C', чтобы понять как это работает.


Теперь прокрутим ползунок чуть дальше, до первого места где программа пропадет.

ROM:00000298                 move    #$2700,sr
ROM:0000029C
ROM:0000029C loc_0_29C:                            ; CODE XREF: RESET:loc_0_21Ej
ROM:0000029C                 bra.s   loc_0_30A
ROM:0000029C; ---------------------------------------------------------------------------
ROM:0000029E unk_0_29E:      dc.b $80; ?          ; DATA XREF: RESET+10o
ROM:0000029F                 dc.b   0
ROM:000002A0                 dc.b $3F; ?
ROM:000002A1                 dc.b $FF
ROM:000002A2                 dc.b   1
ROM:000002A3                 dc.b   0
ROM:000002A4                 dc.b   0
ROM:000002A5                 dc.b $A0; ?
ROM:000002A6                 dc.b   0
ROM:000002A7                 dc.b   0
ROM:000002A8                 dc.b   0


$0000029E - числа начиная с этого адреса не являются кодом. Это данные, ну или как можно назвать 'gamedata'.
Суда может входить что угодно (и графика и звуки, конфиги уровней, хар-ки персонажей, комбинации кнопок, ascii текст, архивы, цены игровых предметов и т.д.)

Однако можно 'насильно' попробовать их представить как код (нажав C).

ROM:0000029E loc_0_29E:                            ; DATA XREF: RESET+10o
ROM:0000029E                 or.b    d0,d0
ROM:0000029E; ---------------------------------------------------------------------------
ROM:000002A0                 dc.b $3F; ?
ROM:000002A1                 dc.b $FF
ROM:000002A2; ---------------------------------------------------------------------------
ROM:000002A2                 btst    d0,d0
ROM:000002A4                 ori.l   #$A1,-(a0); '?'
ROM:000002AA                 move.b  d0,-(a0)
ROM:000002AC                 ori.l   #$120000C0,-(a1)
ROM:000002B2                 ori.b   #(dword_0_0+$C0),d0
ROM:000002B6                 ori.b   #$14,d4
ROM:000002BA                 move.w  #$76C,d0
ROM:000002BE                 ori.b   #0,d0
ROM:000002BE; -------------------------------------------------------------------


Данные представились как код, но это неправильно, т.к. это просто совпадение. Поэтому надо сделать Undefine обратно.
Как точно определить данные это или же код, будет рассмотрено немного позже.
Однако скажу что любой код заканчивается на команды:
jmp (прыжок)
bra (прыжок)
rts (возврат)
rte (возврат из исключения)

Если же у вас участок кода закончился на любую другую, а далее идет 'разрыв' и 'C' не срабатывает, то это не КОД.
ROM:0000029E                 or.b    d0,d0
ROM:0000029E; ---------------------------------------------------------------------------
ROM:000002A0                 dc.b $3F; ?
ROM:000002A1                 dc.b $FF


or.b d0,d0 - не код. жмем 'U'. следующий кусок тоже не код, т.к. заканчивается на ori.b #0,d0

Почему так? если бы процессор выполнял команду or.b d0, d0; следующая за ней шла бы 3FFF, а она не является командой процессор и игра бы просто повисла или перезапустилась. Именно поэтому нельзя в РОМ-файле менять цифры от 'балды', т.к. задевая программную часть мы портим последовательность действий.
Важно знать!, что хоть data и не является кодом, однако может представлять т.н. ссылки на код или ресурсы (offsets) - то есть адреса.(отделение адресов будет рассмотрено позже)

Так что же нам это всё дает? Наверное вы уже слышали понятия ASM-hacking и Data-hacking.
Так вот асм-хакинг это изменение(хакинг) кода, требует знания программирования (на самом деле можно хакать и не зная, достаточно запомнить парочку команд, а как я расскажу позже).

Дата-хакинг это изменение игровых данных, простой способ определить за что отвечали те или иные данные, заполнить эту область нулями (не задевая код), и посмотреть что изменилось.
Рекомендуется искать такие области начиная с адреса $1500 (но всё зависит от конкретной игры!), обычно вначале идут системные данные (связанные с информацией о регионах), которые менять ненужно или нельзя.
Чтож если вы поняли и вам уже захотелось попробовать, ищем в IDA-участки 'Data', переходим по этим же адресам в WinHex'e, выделяем область , жмем Edit-Fill block-Fill with hex values - 00, OK. Сохраняем и открываем ром в эмуляторе. Если РОМ не запускается, зайдите в опции эмулятора и поставьте галочку Auto Fix Chechsum / Править CRC Рома. Если РОМ все равно не запускается, отмените в WinHEx изменения (CTRL+Z) и пересохраните файл. Если игра теперь запускается значит, область была важной и просто так её изменять нельзя, попрбуйте найти другую область данных. Если игра от Electronic Arts**, то стоит
дополнительная защита, которую нужно предварительно снять, также доп. защита может быть и в других играх. Определить есть защита или нет просто - если игра не запускается при любом изменении - значит есть.
В случае удачных изменений, т.е. что-то изменилось и вы поняли что, следует изучать данный участок.(попробовать залить не 'нулями', редактировать цифры по одной и выявлять закономерности):

Пример области 'data':

ROM:00007328                 add.b   d1,(a1,a0.w)
ROM:0000732C                 rts
ROM:0000732C; ---------------------------------------------------------------------------
ROM:0000732E unk_0_732E:     dc.b   1
ROM:0000732F                 dc.b   0
ROM:00007330                 dc.b   1
ROM:00007331                 dc.b   0
ROM:00007332                 dc.b   0
ROM:00007333                 dc.b   1
ROM:00007334                 dc.b   1
ROM:00007335 unk_0_7335:     dc.b $10
ROM:00007336                 dc.b $10
ROM:00007337                 dc.b   0
ROM:00007338                 dc.b   0
ROM:00007339                 dc.b $10



И её отображение в HEX. (т.е. пробуем менять цифры с адреса 732E до 7369)

00007320 43 EC 51 9A 12 3B 00 1D D3 31 80 00 4E 75 01 00
00007330 01 00 00 01 01 10 10 00 00 10 00 10 00 08 08 08
00007340 00 00 08 00 00 00 01 01 01 01 00 03 00 02 00 03
00007350 00 04 00 00 00 04 00 03 01 00 08 00 01 00 08 00
00007360 02 00 04 00 02 00 04 00 08 00 FF FF E5 40 41 EC

Примечание: в IDA значения менять нельзя, это только просмотрщик. Меняем через WinHEx.

Если вы не поняли, или хотите перейти дальше к более совершенным методам читаем.

Карта памяти:
ROM:0000000 - ROM:003FFFFF   (размера РОМа до 4мб)
Z80_RAM:00A00000 - Z80_RAM:00A01FFF            (память процессора Z80)

VDP_data:00C000002        (видеопроцессор)
VDP_CONTROL:00C00004

RAM_:00FF0000        (ОЗУ (RAM))
RAM_:00FFFFFF

RAM:FFFF0000      (ОЗУ (RAM))  *в данном случае 00FF0000 тоже самое, что FFFF0000. (и то, и то является памятью и одним
RAM:FFFFFFFF                    и тем же адресом, это происходит потому что первый байт не читается, т.к. процессор
                                имеет лишь 24битную шину адресации. (до 16мегабайт)

Также есть куча других системных адресов (порты и т.д.). Процессор имеет доступ ко всем этим адресам.

В Loader который мы используем уже прописаны все эти адреса. Теперь мы начнем заполнение базы IDA для конкретной игры, начав с этих системных адресов, это даст нам в дальнейшем много преимществ.

VDP_data:00C00000; Segment type: Pure data
VDP_data:00C00000; segment "VDP_DATA"
VDP_data:00C00000 unk_0_C00000:   ds.b 1                ; DATA XREF: sub_0_7D0:loc_0_7D6w
VDP_data:00C00000                                        ; sub_0_7E2+4o ...
VDP_data:00C00001                 ds.b 1
VDP_data:00C00001; end of 'VDP_DATA'


Жмем правой кнопкой на unk_0_C00000 - Rename(N) переименовываем в VDP_DATA_, OK.

Это нужно проделать со всеми этими адресами где есть ukn_0_ (они пойдут после области ROM). (в IDA черные области на полосе)

Теперь выделим наш переименованный адрес правой кнопкой, и найдем там строчку Jump to Xref to operand. Должен появится список из одной или нескольких строк. Можем перейти по любой - попадем в участок кода, где этот адрес запрашивается, (там тоже будет автоматически переименовано), это подскажет нам о том, что данный участок используется для чего-то связанного с выводом графики. (т.к. VDP-видеопроцессор). Участки с переходами к Z80, будут связаны со звуком. А IO_CT1_DATA - с опросом джойстиков.

Это главная 'фишка' IDA, засчет которой можно быстро разобраться в игре и переименовать не только системные адреса, но и любые другие. В дальнейшем мы будем часто пользоваться Jump to Xref to operand.

Я не рекомендую менять(да и не умеем ещё) участки кода с 'системными' адресами, т.к. ничего полезного вы в этом не найдете.
Мы их отделяли как раз для того, чтобы не 'задеть'.
В других участках (отвечающих за игровые 'фишки'), как правило нет этих запросов.

Так как же их найти? Конечно можно проверять всё подряд. Но мы еще не умеем правильно менять код. Да и конкретную вещь в данном случае найти непросто.

Есть два простых способа найти нужную вещь:
Дебаг (с установкой брейкпоинтов) на адрес (ROM или RAM).
Поиск адреса RAM (ID) конкретной вещи через поиск кодов.
Или оба сразу.

Если вы умеете искать читы (game genie), то для вас не составит проблему найти кучу адресов в RAM, отвечающих за что-либо.
Чем больше переменных мы найдем, тем лучше.
Какие это могут быть значения:
Номер (ID) персонажа.
Номер (ID) уровня, трассы,машины, мотоцикла.
Кол-во патронов.
Кол-во жизней, денег и т.д.
Таймер.
Координаты.
Всё что меняется и можно отследить.

Эмулятор который применяю для поиска я - Gens-mk2.

Подробное описание поиска 'читов' вы можете найти в интернете, таких описаний составлено очень много. Также вы можете попросить кого-то научить вас их искать (не меня).
Читы-это всего лишь значения в оперативной памяти, которые меняются. Само читерство заключается что мы можем залезть в память и заблокировать их (перезаписывая всё время своё значение) или просто один раз изменить.

Опишу простой способ поиска:
Выбираем первого персонажа, заходим cheats-search gg codes-reset.
в поле comparison type ставим больше > (greater than) и нажимаем OK.
Теперь выбираем второго персонажа , опять заходим в search gg codes и нажимаем reset.
Список адресов значительно уменьшится.
Теперь выбираем третьего , опять заходим и опять reset.
Еще меньше. Так делаем пока значений не останется по минимуму. это и будет искомый адрес. (адрес ID персонажа)
Почти всегда значение ID будет равен персонажу -1 , т.к. счет идет с 'нулевого' перса , а не первого.
Записываем эти значения (их может быть н-ко) , а также добавляем как чит.
add cheat - new value, в new value прописываем с любой из реальных ID и жмем ок.
Теперь включая чит, мы можем проверить какой-же именно адресс отвечал за порядковый номер персонажа.
Стоит попрбовать вписать другой 'несуществующий' ID, это позволит выбрать несуществующих или бажных персонажей (которые получаются 'случайно').
В случае если вы искали кол-во жизней или патронов то надо сравнивать на уменьшение char_id
Теперь если нажать правой кнопкой и выбрать Jump to Xref's, мы увидим переходы к участкам кода игры, связанные с данным параметром. Эти 'куски' кода и будут нас интересовать больше всего.

Если же ничего не нашлось (пусто по обоим адресам) - значит:
1) участок в котором запрашивается этот адрес не был дизасмнут.
2) этот адрес запрашивается относительно другого адреса. (относительная адресация)
3) вся игра использует относительный метод адресации к RAM.
(пример - Rock n' Roll Racing, все значения берутся от-но адреса FF8000,который записан в регистре A4 процессора)
4) 1+3 варианты сразу.
В этом случае на помощь приходит дебаг.

Эмулятор который мы будем использовать для дебага GENSVKNTrace.
Запускаем, открываем РОМ файл , потом переходим в опции Дебаг-Main 68k Debugger, откроется окно.
Находим брейкпоинты на ОЗУ. В первое или любое поле вписываем наш адрес FFxxxx. Ставим галочки 'чтение' или 'запись', или обе сразу и галочку 'установить брейкпоинт'. Нажимаем ОК.
Игра продолжается. теперь просто надо 'прокрутить' игру или поиграть до того момента , пока не срабатоет брейкпоинт (само откроется окно дебага с надписью 'сработал брейкопоинт на чтение (запись)!'
После того как окно откроется мы увидим код, по которому этот адрес запросился. (например чтение номера(ID) игрока), команда по которой он запросился находится в самой первой строчке. Запомним этот адрес и теперь перейдем к этому участку в базе IDA, и поставим там метку (rename_ read_char_id) или (write_char_id). Хоткей ренейма - 'N'.
Стоит отметить что таких участков может быть достаточно много. (т.е. нужно ждать другого брейкпоинта, при других игровых условиях, пробовать ставить на запись отдельно, на чтение отдельно и ждать.

Брейкпоинт для PC (program counter) - т.н. программный счетчик, необоходим для определения чтения исполняемого кода.
Несмотря на то, что в эмуляторе написано 'брейкпоинты на озу', можно спокойно использовать дебаг и на ROM (т.е. область кода и данных). Теперь если написать любой адрес ROM в поле дебага и поставить галочку 'PC', то если в этом находится программа, то сработает брекпоинт на PC (в случае если эта часть программы будет выполняться) - ну например код выполнения выстрела или спецприема. Если же поставить ROM-адресс на 'чтение' и сработает брейкпоинт значит в этом месте не код, а данные. На 'запись' для rom ставить не нужно, т.к. ни о какой запись в (read-only-memory) картидж не может идти и речи, это используется толко для RAM(ОЗУ), а 'pc', т.е код, практически не встречается в области RAM.
PC (program counter) - счетчик который определяет адресс текущего расположения выполняемой процессором команды. Очень часто применяется для относительной адресации. (от текущего места +$xxxx = итоговый адрес).

Примеры адресации:

move.w ($FFCECE).l,d0 - абсолютная полная (32битная запись, но сега 'держит' только 24)


move.w ($CECE).w,d0 или при этом два байта move.w ($FFFFCECE).w,d0 (но в hex 'FFFF' не будет) - абсолютный адрес но задается 2 байтами. трюк состоит в том что первые считаются $FFFF и мы попадаем тоже в RAM, но это соблюдается только для адресов >$8000, при меньших мы попадем в ROM. (это особенность 16-битной адресации)


move.w $4ECE(a4),d0 - адрес равняется $4ECE + число, в регистре a4 процессора. в данном случае a4=$FF8000+$4ECE=$FFCECE. В других случаях в a4 может быть что угодно, регистры могут быть от a0 до a6 и в них может быть записано что угодно, и может постоянно менятся в зависимости от программы. Что точно находиться в регистре в данный момент времени можно посмотреть в дебаггере.


Во всех трех случаях значение из памяти (RAM) читается и записывается в регистр d0 процессора.
Запись же выглядит 'наоборот': move.w d0,($FFCECE).l
Команда может быть не обязательно move, а другой (add, sub, и т.д.)

move.w unkn_00(pc,d1.w),d0 - чтение данных от текущего места + PC + d1. (IDA автоматически определяет расположение unkn_00 при условии что d1=0)
Подробнее о командах и регистрах будет рассмотрено позже.

Hot trick:
Используете GG-коды и GGConv.exe (game genie converter), чтобы найти уже найденные другими адреса в ROM и RAM, и допишите их в базу переименовав и поставив метки.
GG-коды можно найти в книге кодов codebook.exe, и на сайтах.
Запустим конвертер и выберем вкладку GEN (sega GENesis).
Вбиваем GG-code и получаем HEX-code.
Многие адреса уже представлены в виде HEX-code и их конвертировать не нужно.
HEX-code выглядит в формате adress:data.

пример FF1234:0005 (интересуемый адрес FF1234, значение вприцнипе нас не интересует), так если в описании чита указано что это жизни, следует переименовать, или начать с 5-ого (4-ого) уровня - значить это уровень, а значение его и определяет
его номер)
пример 00A6b0:6000 (интересуемый код находится в области ROM, суда следует перейти и тоже поставить метку.), также в этом случае число может являются только номером , но и кодом для процессора. (например, 6000, 4E75 или 4E71)


Модификация(хакинг) участков кода:

Теперь найдя интересующие участки кода мы будем их переделывать. Если с вышеописанными способами ничего не получилось, то мы их все равно будем переделывать, а что именно мы будем изменять узнаем по ходу дела. (сравнивая после и до изменения)

Перейдем к интересующему нас коду. (там где есть обращения к найденным переменным).
Весь код программы можно условно разделить на функции (процедуры), в IDA они разделяются строками ====SUBROUTINE====.
У любой такой подпрограммы есть начало и конец. в 95% случаях в конце находится команда .RTS (return from subroutine)- то есть возврат из этой подпрограммы к предыдущей.
HEX-код данной команды выглядит как 4E75. Запомните эту команду. в 97% случаях эта комбинация цифр и говорит, о том что тут находится имеенно код и конец подпрограммы.
Посмотрев ROM в 16-ричном виде в WinHexe, и вбив в поиск search-find hex values - 4e75, можно найти
огромное их кол-во. Следует отметить что адрес должен быть четным. То есть если 4e75 нашлась по адресу $251 (нечетн и т.д.), то это не Rts. Это полезно учитывать при нахождении кода в базе.

Теперь перейдем к началу подпрограммы (разделенной ====SUBROUTINE====), теперь надо заменить первую команду на RTS.
Что это даст? Мы полностью отключим данный участок. И таким образом сможем узнать за что, же эта часть кода отвечала.

Замена:
Просто пишем,использую в winhex, '4e75' по адресу который соотв. началу подпрограммы. (т.е. по адресу самой первой команды, то есть фактически мы ее заменим-т.е. уже asm-hacking)

Если ничего не изменилось или игра зависает, пробуйте тоже самое с другими участками.
Если получилось, и вы поняли что изменилось (напр. отключилась стрельба) переименовывайте (называйте) данную функцию.

Примеры названий и названных функций:
AI_weapon_nitro_using
car_model_gfx
weapon_moving
weapon_sprites
car1_life
car1_topspeed
xz_1
weap_table


Функции с code+data:
Если в функции есть следующие или похожие команды:
move.w unkn_1234(pc,d1.w),d0
lea unnk_5678,a1

И при переходе по адресам unnk_5678 в ROM, скорее всего содержится 'data'. Т.е. это могут быть игровые параметры такие как характерситики персонажа (скорость,сила и т.д.). Стоит попрбовать позаменять эти значения в РОМЕ через Winhex.
(заменять значения по этим адресам, как и случае data-хакинга, а не сами команды).
Особенно хорошо, если вы уже определили за что отвечает ваша функция).
Незабудьте что сама же функция должна быть при этом включена (т.е. никаких RTS вначале).
В данном примере команда загружает число с адреса 1234, от-но значения регистре d1 и записывает в d0.
В d1 например может быть ID (порядковый номер) персонажа, а по адресу хар-ка 'скорость'.


Функции только с code:
В этом случае стоит посмотреть на ту команду над которой происходит действия над нашим найденным и переименованным адресом.

Пример:
add.w #$14,($FFCECE).l
или
sub.w #1,($FFCECE).l

Адресс с назначенным именем (меткой):
add.w #$14,(LIFES).l


В данном случае это ADD (англ. прибавить), SUB (substract-вычесть). #1 - символ # говорит о том, чо это число. если # нет, значит это адрес. Результирующие действие проводится только над тем что,находится справа.
Т.е. в первом случае мы добавляем к значению в адресе $FFCECE число #$14.
Во-втором: вычитание из значения в адресе $FFCECE вычитаем 1. (значение в этом адресе памяти сразу же меняется, уменьшаясь на 1)
Например станет 13 жизней, а не 14.

Следующий код можно изменить следующим способом: вырезать (удалить) данную команду.
Для этого предусмотрена команда NOP (No Operate) - т.е. не проивозодить никаких действий. Данная команда записывается в HEx-виде как 4E71. Учитывая что команда nop занимает всего 2байта, а команда которую мы будет заменять может занимать значительно больше байт (для чего выделим ее в IDA и перейдем во-вторую вкладку), мы должны записать н-ко Nop'ов., чтобы не нарушить структуру программы.
Открываем ROM в Winhex, перейдем к адресу этой команды и впишем 4e71 4e71 4e71 4e71. (т.е. если ориентироваться по вкладке в IDA hex-view-A соотв. залить всю зеленую область nop'ами).
Таким способом можно вырезать любые команды и сравнивая результат до и после, и добиться каких-то 'эффектов', от положительных до зависания игры.
Также можно пробовать менять сами цифры в коде. Т.е. если в команде была операция с числом #$14, и выделив ее вы в hex-виде нашли 0014, можно попрбовать заменить на 0000 или 0028.
Можно ещё попрбовать поменять местами соседние команды.
Также если вы увидите команды beq или bne, пробуйте переделать их. (в hex начинаются на 67 и 66, т.е. нужно пробовать заменить 66 на 67, и наоборот- это превратит команду bne в beq).

Несмотря на простоту эти методы являются самыми эффективными, в случае если надо просто что-то 'хакнуть'.

Если же у вас есть конкретная цель, то без знания ассемблера или хотя бы н-ких команд не обойтись.

Вкратце рассмотрим что за ассемблер:
Несмотря на то, что у всех представление о ассемблере как о самом сложном языке, это не совсем так. Я бы даже сказал что ассемблер сеги самый простой.(достаточно знать десяток команд и можно написать что угодно). Другое дело что само написание дело долгое и трудоемкое. (т.к. все действия выполянются над числами и адресами).

Обзор регистров процессора:
Процессор MC68000 содержит 8 регистров данных (для чисел) - это регистры d0, d1, d2, d3, d4, d5, d6, d7 и 8 регистров для адресов - это регистры a0, a1, a2, a3, a4, a5, a6, a7

Регистры нужны для быстрых рассчетов, и бол-во операций выполняются между ними или между ними и памятью.

Также есть регистр SR (status registr), он меняется автоматически после каждого действия и отображает результат, который нужен для выполнения условий (напр.условие перехода).
Регистр a7 является железным 'stack pointer'ом и поэтому в коде его записывают как sp.
пример move.w d0,(sp)+. что это такое будет рассомтрено.

Основные команды ассемблера:
Буква после точки любой команды, означают что операция проводится с этим типом данных.
(.w - 2байта, .b 1 - байт, .l = 4 байта) , .s (short) также означает байт(но в случае переходов и прыжков).

Пример:
move.b d0,d1
move.w d0,d1
move.l d0,d1
bra.s  loc_100
bra.w  loc_100
move.b  ($FFCABA).l
move.l  ($FFFFCECE).w


А буква после адреса может быть только .w или .l (это говорит лишь о виде адресации полная или 16-битная) - это не влияет, на тип заносимых данных. (т.к. некоторые новички думают что если move.w адрес надо писать тоже ($1234).w - это не так)

В результате выполнения команд всегда меняется только значение справа (в примере d1).Это справедливо для процессоров Motorola.

Теперь рассмотрим сами команды.
Команды которые мы уже знаем:
RTS (return from subroutine) - возврат из подпрограммы
NOP (no operate) - не произв.действий.(процессор выполняет команду, но ничего не меняется)
ADD - сложение
SUB - (substract) вычитание

Другие команды (эти команды необходмо знать!):
MOVE - копирование
CMP - (compare) сравнение
TST - (test) - сравнение с нулем.
CLR - (clear) очистка - то есть запись нуля.
LEA - (load effective adress) - загрузка адреса в указанный адресный регистр.
JMP (или BRA) - прыжок. (jump, branch)
JSR (или BSR) - переход в подпрограмму с последующим возвратом (после rts). (jump to subroutine, branch to subroutine)

если к команде добавлено 'i' (cmpi.b) это лишь значит что операция выполняется над числом+регистр.
если стоит 'a' (movea.w) , это говорит лишь о том что выполняется над адресным регистром.
а 'q' (addq.b), говорит об упрощенном варианте команды, которая отличается тем, что выполняется процессором быстрее, но имеет некоторые ограничения.

Команды переходов (используется в связке сравнения CMP. или TST.), также очень важные команды:
beq (branch if equialent) - переход (прыжок), если равно.
bne (not equialent) - переход(прыжок), если не равно.
bge (greater или equialent) - если больше или равно.
ble (lower or equialent) - меньше или равно
bpl (plus) - результат положительный.(число полученное в результате имеет знак +, т.е. если байт то от 0 до 127 ($7F).
bmi (minus) - отрицательный.

Логические команды: (частые команды, но чтобы их понять их логику надо считать число по битам)
or ('ИЛИ') - логическое 'сложение' 0+1 = 1, 0+0 = 0 , 1+1=1
and ('И') - логическое 'умножение' 0x1= 0 , 0x0= 0 , 1x1 =1
eor (' исключающее ИЛИ') - 'сложение' , но в этом варинате 1+1=0 (также назыв. xor)
not ('НЕТ') - инвертирует все биты.

lsr (logical shift right) - сдвиг битов вправо
lsl (logical shift left) - влево
asr (arithmetical shift right) - вариант сдвига.
asl (arithmetical shift left)
Сдвиги чаще всего используются для умножения или деления.

Чтобы представить число в двочином виде, откройте калькулятор наберите число в hex или dec, а потом поставьте точку где bin.
Калькулятор (WINDOWS) должен быть установлен в вид->инженерный. Тут же можно и проверить их действие.
Поначалу можно обойтись и без понимая этих команд.

Все остальные команды более редкие, и для начала изучения не требует. Вообщем если попадаются просто смотрим в мануалах.

Выполнение команд:
Рассмотрим пример выполнения и важную особенность команд add,sub и других.
Пример: addi.b #$C0,d0 (добавить C0 к числу в регисте d0)
Пусть в нашем пример до этого регистр был равен d0=002a00B0.
Что должно получится? то есть к числу $B0 прибавить $C0 (т.к. тип данных - байт), если вы используете калькулятор,то получите $170, но это будет неверно. особенность в том,что если вы производили операцию над типом BYTE, другие байты не будут затронуты!, т.е. 002A00 останется как и есть. Чтобы посчитать правильно надо выставить в калькуляторе 1байт.
Получается что все числа как-бы замкнуты по кругу ($FF+2 = 1).
результат d0=002a0070. (этим объясняется например замкнутость координат, т.е. если вспомнить tmnt2 и баг с выходом за экран, когда проходили появлялись сверху или снизу наполовину, после максимума координата 255 попадали в 0 и наоборот )

Eсли же мы будем использовать команду addi.w или addi.l мы получим d0=002a0170.

Команда move.
Обратно же если в команде используется word или longword мы изменим 2 или 4 байта:
Пример move.l #4,d0
Результат: d0=00000004. (т.е. мы скопировали не 4, а 00000004)

Команда clr.
clr.b запишет 00 (т.е обнулит 1 байт)
clr.w запишет 0000 (обнулит 2 байта)
clr.l обнулит весь регистр.


Теперь если вы скачивали эмулятор Gens11(gensmovie) или GensKMOD, запустите этот эмулятор , откройте ром , найдите в пунктах Trace. Включим trace на пару секунд и выключим. Получится файл trace.log. Если вы ждали долго файл может получится огромным. Открываем блокнотом и смотрим. Перед вами результат выполнения программы, т.е. весь текущийвыполненный за эти 2секунды код, а справа то, чтобы находилось в регистрах.

За счет trace.log мы можем:
Отследить как работают команды.
Как от их выполнения меняются регистры.
Отследить последовательность выполнения программы.
Какие участки кода использовались в этот раз, и где они находятся. (что тоже полезно при дизасме в базе IDA).
Буквы xnzvc соотв. состоянию регистра состояния SR.

Операции с адресными регистрами:
movea.w  d0,a0
move.w   d0,(a0)
move.w   d0,(a0)+
move.w   d0,$1234(a0)
move.w   d0,-(a0)
jmp      (a0)


случай 1) в этом случае меняется сам регистр a0.
случаи 2-5) 'скобки' - так как в регистре содержится адрес, например FF1000, поменяется содержимое памяти по этому адресу. то есть число из d0, запишется в FF1000. это и есть от-ная адресация. (относительно адреса в регистре a0).
случай 3) (a0)+ - в этом случае адрес в регистре a0 еще и автоматически увеличится (станет FF1002), то есть меняется и регистр и значение в памяти.
случай 4) запись в память по адресу FF1000+1234 = FF2234, сам a0 при этом остается = FF1000.
от-ная адресация для них работает как word. то есть максимум до $7FFF(a0), а минимум -$8000(a0). если укажете число $8500, то оно будет считаться как отрицательное. Однако если a4=FF8000 как в rock n' roll racing, мы получаем как раз доступ ко всей RAM, от-но этого одного регистра. Однако чаще встречаются 1(a0) или -2(a0), с небольшим смещением.
5) тоже что 3, только уменьшение адреса.
6) прыжок (переход программы) к адресу, который содержится в регистре a0.

ROM:000006C2    lea     ($FF8000).l,a4

загрузка адреса $FF8000 в a4.

Stack pointer и Stack.
Как уже упоминалось a7 является stack pointer'ом и записывается как sp.
Он нужен, т.к. в нем записываются точки возврата , необходимые для команды JSR (ответ на вопрос откуда процессор знает, куда ему вернуться после команды rts).
stack - это как бы системная память (часть ram), stack pointer - адрес стека задается вначале, далее может сменен программой, в эту память автоматически и записываются адреса возрврата.
stack устроен по принципу стопки книг.(то есть вначале записывается одно, потом сверху другое, и разбирается вначале сверху потом вниз.), поэтому чаще всего стек находится в самом конце области RAM. ($FFFF00)
Кроме того в стек , можно записывать данные и самому, используя как временную память.
Чаще всего туда заталкивают регистры (содержимое регистров), если его надо 'сохранить'.
Очень часто подпрограмма как раз и начинается с сейва регистра одного или н-ких. Это объясняется тем что нужно сохранить их значения для предыдущей более главной программы. А заканчивается как раз взятием их 'обратно ', прямо перед rts.

Пример:
ROM:00000A10 sub_0_A10:                            ; CODE XREF: sub_0_A22:loc_0_A28p
ROM:00000A10                                        ; sub_0_A22:loc_0_A4Cp ...
ROM:00000A10                 movem.l d0-a6,-(sp)
ROM:00000A14                 bsr.w   sub_0_10C8
ROM:00000A18                 bsr.w   sub_0_10B8
ROM:00000A1C                 movem.l (sp)+,d0-a6
ROM:00000A20                 rts


cейв всех регистров от d0 до a6 (сохраняют в стек), bsr.w - переходы к другим подрограммам.
movem означает (move multiply)- множественное копирование, дополнительный вариант команды move.


ROM:0000154E                 global VBLANK
ROM:0000154E VBLANK:
ROM:0000154E                 movem.l d0-d2/d7-a1,-(sp)
ROM:00001552                 addq.w  #1,$425E(a4)
ROM:00001556                 tst.w   $38D8(a4)
ROM:0000155A                 beq.s   loc_0_1570
ROM:0000155C                 move.w  $4256(a4),d0
ROM:00001560                 add.w   d0,d0
ROM:00001562                 lea     off_0_1576,a0
ROM:00001566                 movea.l (a0,d0.w),a0
ROM:0000156A                 jsr     (a0)
ROM:0000156C                 bsr.w   sub_0_6F8
ROM:00001570
ROM:00001570 loc_0_1570:                            ; CODE XREF: VBLANK+Cj
ROM:00001570                 movem.l (sp)+,d0-d2/d7-a1
ROM:00001574                 rte


сохраняет регистры от d0 до d2, d7 до a1 (т,е. еще и a0).
Обращаю внимание в этом участке , не rts , а rte (т.н. возврат из исключения). VBLANK -кадровое прерывание, которое происходит само 50 или 60 раз в секунду. (PAL или NTSC сега). Поэтому к нему нет переходов. Текущая выполняем процессором программа в любой момент может остановиться и начнет выполенения кода отсуда, к нему нет переходв (это происходит на уровне железа), однако адрес VBLANK задается в заголовке картриджа. (IDA определяет автоматически).
Этот код связан в основном с выводом графики для следующего кадра, поэтому трогать его не советую.
rte вернет вас назад к вашей программе.

Кроме сохранения регистров, стек может быть использован и в других целях. Зависит от конретной игры, в некоторых доходит до того что его используют по 4 строчки подряд. (что не совсем удобно для хакинга).

Также важно если будете менять код, сохраняйте принцип стакинга, (т.е. на затрите с команды sp+ или -sp), иначе нарушится последовательность, программа вернется не в то место и в 90% случаях будет зависание или баги.

* Если вы отключаете функцию (то есть через 4e75 вначале, а там находился сейв в sp), то это как раз нормально, так как мы сразу возвращаемся, а если вписать как раз после сейва, то зависнет (т.к. не 'заберем' из стека обратно).

Ну вот и весь ассемблер.

На вопрос, а что же мне с ними делать? Куда сувать команды и регистры? Ответ - изучите как работает чужая программа (участок кода), от и до (пусть он будет хоть 10строчек), тогда вы сможете понять как его можно изменить с желаемым результатом или написать по-своему.

Для написания вашего собственного участка кода я рекомендую ассембелер ASM68K.exe, который надо запускать с ключом /p.
работает из командой строки (консоли виндовс).
asm68k /p test.asm, test.bin

где test.asm - текстовый файл с вашим кодом, test.bin - готовый hex-код. test.bin надо открыть в WINHEX'e и полученный hex-cod вставить на место старого. если он не влез - используйте команду JSR и найдите пустое место в Rome.
Команда JSR выглядит как 4EB9 00 xx xx xx, где xxxxxx - абсолютный адрес в РОМе.
Пример: 4E B9 00 00 B8 2E - прыжок к подрограмме по адресу B82E.
Эту команду надо Запомнить также как RTS(4e75) и NOP(4e71), то есть если раньше вы вписывали вручную 4e75- теперь вписывайте 4eb9 00 0F A0 00 (где FA000 - адрес найднного вами пустого места, куда вы скоптируете свой код).

* В WINHEX копирование - CTRL+B (то есть запись поверх старого) , не путайте с CTRL+V - это сдвинет весь ром разрушив структуру.

Не советую использовать sega-asm, т.к. в нем есть определенные баги с некоторыми командами и баг c "org".
Что же касается SNASM68K.exe, можете попробовать и его (в нем на мой взгляд тоже есть неясность с полурабочим 'org', к тому же он у меня закидывал свои 'копирайты' в бинарник, что может мешать при копировании кода)

Что делать если вы не нашли пустого места в РОМЕ? SEGA позволяет безболезенно 'расширять' РОМ-файл до 4 мегабайт.
Модифицированные эмуляторы позволят запускать и до 13 мегабайт ром. (но такой ром не будет работать на железной сеге), также не приветствуется многими пользвателями.

Расширение рома:
Для увелечиния размера рома, достаточно приклеить к нему пустое место (нули) к концу файла.

Идем к winhex последнему байту в РОМЕ, выделяем его , заходим Edit->Paste Zero bytes. Появится надпись append to end of file? жмем YES. Добавляем 1 048 576 байт (1 мегабайт). Если ром был 1 мегабайт станет 2 мегабайта.
Теперь проверим запускается ли игра. Если игра перестала запускаться, и игра была с проверкой контрольной суммы (вы выставляли fix checksum), используйте программу FIXCHECKSUM.exe, она автоматически исправляет и сумму и размер рома, которые записываются в начале ром-файла. Эмуляторы умеют исправлять только сумму (но не размер).

Code+Data hacking. Часть2.
Ключевым аспектом является использование(чтение) данных кодом и с последующей их обработкой и/или записью в память.
Впринципе все что не является кодом, является данными. Отдельно можно расположить лищь оффсеты(ссылки на адреса).
Обычно все данные расположены в РОМЕ таблицами, т.н. 'массивы' данных.
Т.е. графика, хар-ки персонажей, пос-ти нажатий кнопок, игровые цены, палитры, конфиги уровней, текст и прочее.

Пример чтения данных:
ROM:00007300                 move.b  unk_0_732E(pc,d0.w),d1
ROM:00007304                 add.b   d1,(a1,a0.w)
ROM:00007308                 lea     $51A8(a4),a1
ROM:0000730C                 move.b  unk_0_7335(pc,d0.w),d1
ROM:00007310                 add.b   d1,(a1,a0.w)
ROM:00007314                 lea     $51AC(a4),a1
ROM:00007318                 move.b  unk_0_733C(pc,d0.w),d1
ROM:0000731C                 add.b   d1,(a1,a0.w)
ROM:00007320                 lea     $519A(a4),a1
ROM:00007324                 move.b  unk_0_7343(pc,d0.w),d1
ROM:00007328                 add.b   d1,(a1,a0.w)
ROM:0000732C                 rts
ROM:0000732C; ---------------------------------------------------------------------------
ROM:0000732E unk_0_732E:     dc.b   1
ROM:0000732F                 dc.b   0
ROM:00007330                 dc.b   1
ROM:00007331                 dc.b   0
ROM:00007332                 dc.b   0
ROM:00007333                 dc.b   1
ROM:00007334                 dc.b   1
ROM:00007335 unk_0_7335:     dc.b $10
ROM:00007336                 dc.b $10
ROM:00007337                 dc.b   0
ROM:00007338                 dc.b   0



Если вы уже занимались data-hacking'ом (где мы 'заливали' эти данные, т.е. массивы данными нулями для проверки), вы бы уже скорее всего знали что они отвечают.

В примере move.b считывает (копирует) значение с адреса 732E (адрес в программе от-ный, но IDA считает его автоматически - т.к. относительно pc (текущего положения)). Также дополнительно считывает и от-но значения в регистре d0.
Что это значит?
Если в регистре d0 значение = 00000000, то в d1 запишется значение с адреса 732E, то есть 1.
Если в регистре d0 = 00000001, то в d1 запишется значение с адреса 732E+1, т.е. с 732F, то есть 0.

Для объяснения открою секрет - по адресу $732E, находится таблица хар-ки + скорость, а в d0 порядковый номер (ID) персонажа.
Что же происходит? Если у нас персонаж номер 1 (то есть с id=0, т.к. счет идет с нуля), то прочитается 1, если второй то 0, если третий то 1., т.е. таблица хар-ки + скорость.
То есть целиком куска 'персонажа' не существует. А все его параметры определяются от-но ID (порядкового номера), т.е. такие как таблицы спецприемов и т.д.
По адресу $7335 расположена таблица хар-ки +ускорение. Как видно они в примере они состоят из 7 значений (т.к. всего семь персонажей). Если же ID персонажа = будет 8-ой (id=07), то для первого случая (+скорость) прочитается значение с адреса $7335 (732E+7), т.е. не из рассчитаного места.
(если вы играли в RRR за 'невидимку', теперь вам ясно откуда у него такая завышенная скорость).
То есть если вы хотите добавлять персонажей, то нужно смещать адреса и расширять эти таблицы. Если адрес абсолютный то это сделать несложно, если же от-ный намного сложнее, т.к. придется либо перемещать весь участок в новое место, либо делать какие-то вставки через JSR. Но это пока не рассматривается, т.к. сложность заключается в том, что если вы хотите что-то добавить, то нужно найти и расширить ВСЕ эти 'таблицы', коих может быть от 10 до 100. (и даже после этого никакой персонаж не появится, т.к. нужно указать что персонажей не 7, а 8.) Засчет паролей в RRR получается что задается
не рассчитаный 8-ой персонаж, и результат его обработки. (читаются другие данные), вместо графики все черное и т.д.

Однако может встретить и 'классический' вариант - когда область 'data' отвечает за многие хар-ки одного персонажа.
(пример dune 2), для каждого здания и юнита есть 'data' конфиг. Но при этом все конфиги аналогичны. (т.е. в конфиге не прописывает dmg=100, скорость=20., там есть только числа 100 и 20., а то что за что оно отвечает определяется, в зависимости от его расположения (т.е. например во всех конфигах юнитов первое число будет отвечать за скорость, второе за цену и т.д.)
Программа не читает все сразу (она читает нужное число,) т.е., в программе при чтении цены будет указано смещение +1, от-но начала конфига. (если первое значение типа байт, если же word то +2)

Команда lea.
Самой частой командой загрузки data, является команда lea (load effective adress). Т.е. фактически команда только загружает адрес расположения 'data' в адресный регистр , а в дальнейшем читать уже будет команда move или add.
Пример чтения (ukn_000 переименованы, в найденные опытным путем параметры):
Таблица физ. хар-к для пяти машин:

Код:
ROM:0000DF0C                 lea     car_topspeed,a1
ROM:0000DF10                 lea     $51A4(a4),a2
ROM:0000DF14                 move.b  (a1,d1.w),(a2,a0.w)
ROM:0000DF1A                 lea     car_accelerat,a1
ROM:0000DF1E                 lea     $51A8(a4),a2
ROM:0000DF22                 move.b  (a1,d1.w),(a2,a0.w)


Данные:
ROM:0000E150 car_topspeed:   dc.b $28              ; DATA XREF: CARS_STATS+Co
ROM:0000E151                 dc.b $2A
ROM:0000E152                 dc.b $24
ROM:0000E153                 dc.b $24
ROM:0000E154                 dc.b $26
ROM:0000E155 car_accelerat:  dc.b $C0              ; DATA XREF: CARS_STATS+1Ao
ROM:0000E156                 dc.b $D0
ROM:0000E157                 dc.b $A0
ROM:0000E158                 dc.b $A0
ROM:0000E159                 dc.b $B0


Как видно в этом примере код и данные располжены далековато друг от друга, но чаще всего они бывают рядом.
Для нахождения кода (одного или н-ких участков) , который читает 'data' , надо выделять метку правой кнопкой, т.е. в пример car_topspeed: и выбирать Jump To Xref to Operand, покажет список всех участков кода и также позволит туда перейти. Как уже упоминалось это главная особенность IDAPro.
В этом примере адресация тоже относительная, хоть это и не написано мы можем посмотреть, выделив команду lea и перейдя во вторую вкладку мы увидим 43FA 0242, где 0242 -от-ный адрес (от-но pc-program counter) , который нужно счиать по формуле DF0C(текущий)+242(указанный от-ный в hex)+2(длина команды) (Ida считает автоматически).
Примеры 43FA xxxx, 41FA xxxx, 45FA xxx, 47FA xxxx. - от-ные.

Абсолютные: в данном варианте полный адрес в виде longword.

ROM:000051FA                 lea     (cars_costs_pla).l,a0


в HEx выглядит так: 41F9 00 00 55 20, загрузка адреса $00005520, по которому располагаются данные. Такие адреса гораздо проще менять. (т.к ненадо считать и мы можем указать любое новое место, без изменения в коде).

41F9 xx xx xx xx
43F9 xx xx xx xx и т.д.


Как уже упоминалось все данные являются либо byte, либо word, либо longword.
данные типа byte = dc.b
2 байта word = dc.w
longword = dc.l

Тип н-кых данных IDA определяет автоматически. Остальные вы должны определить сами. Менять типы данных можно нажимая хот-кей 'D'. Какой именно тип узнать можно по команде которая пойдет после lea, (т.е. команде чтения данных), если это будет move.w значит надо данные перевести в dc.w.

Пример переназченного типа:
ROM:00005520 cars_costs_pla: dc.l $70000            ; DATA XREF: sub_0_507E+76o
ROM:00005520                                        ; sub_0_51CE:cars_costso
ROM:00005524                 dc.l $70000
ROM:00005528                 dc.l $110000
ROM:0000552C                 dc.l $110000
ROM:00005530                 dc.l $130000



Offsets.
Кроме кода и данных в роме также есть и оффсеты, это так называемые 'ссылки' на адреса. А уже по этим ссылкам могут располагаться как данные, так и код , и даже другие ссылки.
Ссылки почти всегда расположены массивами, (т.е. как таблицы статов).

Пример массива ссылок на подпрограммы:
ROM:00001576 off_0_1576:     dc.l locret_0_15BE    ; DATA XREF: VBLANK+14o
ROM:0000157A                 dc.l loc_0_B66C
ROM:0000157E                 dc.l unk_0_829C
ROM:00001582                 dc.l unk_0_535A
ROM:00001586                 dc.l sub_0_93C6
ROM:0000158A                 dc.l sub_0_3146
ROM:0000158E                 dc.l sub_0_8888
ROM:00001592                 dc.l sub_0_13B60
ROM:00001596                 dc.l sub_0_13B88
ROM:0000159A                 dc.l sub_0_9C44
ROM:0000159E                 dc.l sub_0_1A42
ROM:000015A2                 dc.l sub_0_7996
ROM:000015A6                 dc.l sub_0_7210
ROM:000015AA                 dc.l sub_0_B666
ROM:000015AE                 dc.l sub_0_B672
ROM:000015B2                 dc.l sub_0_B68A
ROM:000015B6                 dc.l sub_0_B6FA
ROM:000015BA                 dc.l sub_0_15600


Оффсеты определяются не хоткеем 'D', а хоткеем 'O'.

До нажатия 'O', это место выглядело так:
ROM:00001576 unk_0_1576:     dc.b   0              ; DATA XREF: VBLANK+14o
ROM:00001577                 dc.b   0
ROM:00001578                 dc.b $15
ROM:00001579                 dc.b $BE; -
ROM:0000157A                 dc.b   0
ROM:0000157B                 dc.b   0
ROM:0000157C                 dc.b $B6; ¦
ROM:0000157D                 dc.b $6C; l
ROM:0000157E                 dc.b   0
ROM:0000157F                 dc.b   0
ROM:00001580                 dc.b $82; ?
ROM:00001581                 dc.b $9C; ?
ROM:00001582                 dc.b   0
ROM:00001583                 dc.b   0
ROM:00001584                 dc.b $53; S
ROM:00001585                 dc.b $5A; Z
ROM:00001586                 dc.b   0
ROM:00001587                 dc.b   0


Т.е. если можно легко определить на 'глаз'.


Массив оффсетов на участки 'data'.
ROM:00002124 off_0_2124:     dc.l unk_0_2148        ; DATA XREF: sub_0_2064+20o
ROM:00002128                 dc.l unk_0_2182
ROM:0000212C                 dc.l unk_0_2194
ROM:00002130                 dc.l unk_0_21A2
ROM:00002134                 dc.l unk_0_21B0
ROM:00002138                 dc.l unk_0_2156
ROM:0000213C                 dc.l unk_0_2160
ROM:00002140                 dc.l unk_0_216A
ROM:00002144                 dc.l unk_0_2176
ROM:00002148 unk_0_2148:     dc.b  $B              ; DATA XREF: ROM:off_0_2124o
ROM:00002149                 dc.b  $F
ROM:0000214A                 dc.b   8
ROM:0000214B                 dc.b   1
ROM:0000214C                 dc.b   4


Как видно все оффсеты имеют тип longword. Однако в некоторых играх есть и двух-байтовые оффсеты, это не очень удобно, т.к. такие базы очень плохо сама дизасмит IDA (нужно определять самому), да и сами оффсеты от-ные. (эти оффсеты нельзя определить хоткеем 'O', только 'd')

Пример golden-axe 3:
ROM:00003E18                 lea     (word_0_3E26).l,a4
ROM:00003E1E                 move.b  $6D(a0),d0
ROM:00003E22                 bra.w   sub_0_41CC
ROM:00003E22; End of function sub_0_3DF8
ROM:00003E22
ROM:00003E22; ---------------------------------------------------------------------------
ROM:00003E26 word_0_3E26:    dc.w $E                ; DATA XREF: sub_0_3DF8+20o
ROM:00003E28                 dc.w $12
ROM:00003E2A                 dc.w $22
ROM:00003E2C                 dc.w $22
ROM:00003E2E                 dc.w $3A
ROM:00003E30                 dc.w $6C
ROM:00003E32                 dc.w $6A


ROM:000041CC sub_0_41CC:                            ; CODE XREF: sub_0_12AA+2Aj
ROM:000041CC                                        ; ROM:0000136Ej ...
ROM:000041CC                 ext.w   d0
ROM:000041CE                 add.w   d0,d0
ROM:000041D0                 adda.w  (a4,d0.w),a4
ROM:000041D4                 jmp     (a4)


Массив от-ных оффсетов на код с последующим JMP по этим ссылкам.
Как считать: обычно считаем от первого + значение: т.е. 3E26+E = 4134
3E26+12 = 3e38
3e26+22 = 3e48


По этим адресам расположились подпрограммы. (причем фактически они расположены в другом месте, тут расположен только код прыжка к ним (bra, jmp)-прыжки
ROM:00003E34; ---------------------------------------------------------------------------
ROM:00003E34                 bra.w   loc_0_3EDA
ROM:00003E38; ---------------------------------------------------------------------------
ROM:00003E38                 move.b  #2,$6D(a0)
ROM:00003E3E                 moveq   #$E,d0
ROM:00003E40                 clr.b   $135(a0)
ROM:00003E44                 bra.w   loc_0_3EF6
ROM:00003E48; ---------------------------------------------------------------------------
ROM:00003E48                 bra.w   loc_0_3EDA


Данная организация хоть и неудобна (для дизасма), но достаточно часто встречается, поэтому надо ее понять.

Отмечу, если вы читали какие-то доки где упоминается понятие поинтер, то под поинтерами понимают команды lea и оффсеты.
Некоторые ромхакеры считают поинтерами только оффсеты. Н-кые вообще понимают под ними любые адреса.
LEA и offsets могут быть как на ROM, так и на RAM.

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

К таким данным относятся: код процессора Z80, звуки и графика.

Процессор Z80 и его код.
Я уже упоминал что в сеге содержится второй процессор - ZILOG Z80. Процессор 8-битный и в 2 раза меньше по частоте, но это удачное решение, т.к. его использовали для проигрывания звуков и музыки.
Мы не будем его изучать, т.к. звуки можно менять и без этого, а изменение музыки достаточно сложная вещь, (т.к. музыка 'трекерная', вроде midi).
Но мы должны отделить его код.
Чтобы найти его мы должны перейти в IDA вначале к адресу A00000. Если вы уже сделали переименование системных адресов как описано в статье это место будет выглядеть так:

Z80_RAM:00A00000; Segment type: Pure data
Z80_RAM:00A00000; segment "Z80_RAM"
Z80_RAM:00A00000 Z80_RAM_:       ds.b 1                ; DATA XREF: sub_0_12AAC


Делаем Jump to Xref to Operand:

Единственный переход и попадаем в сегмент:
ROM:00012AAC sub_0_12AAC:                          ; CODE XREF: sub_0_12980p
ROM:00012AAC                 move.w  #$100,(word_0_A11200).l
ROM:00012AB4                 bsr.w   sub_0_12D36
ROM:00012AB8                 lea     (unk_0_19000).l,a0
ROM:00012ABE                 lea     (Z80_RAM_).l,a1
ROM:00012AC4                 move.w  #$1FFF,d7
ROM:00012AC8
ROM:00012AC8 loc_0_12AC8:                          ; CODE XREF: sub_0_12AAC+1Ej
ROM:00012AC8                 move.b  (a0)+,(a1)+
ROM:00012ACA                 dbf     d7,loc_0_12AC8


(unk_0_19000) в этом случае адрес ROM, где находится код Z80. Подпрограмма выполняет копирование кода для процессора Z80 из картиджа (ROM) в RAM Z80, который имеет свою собственную память(отдельную микросхему), но она всего 8кб, против 65к основной.

Процессор MC68000 имеет сводобный доступ к этой микросхеме (т.е. к памяти z80), если вы смотрели карту памяти, адресное пространство выглядит для него A00000-A1FFFF. Копирование проводят так как z80 не читает программу с картриджа, а из собственной памяти (имеет ограниченный доступ к картриджу). Это соблюдается почти во всех ромах (если не во всех.)
В дальнейшем в эту память процессором mc68000 просто так 'лазить' нельзя, так как предварительно необоходимо делать 'захват шин'. (но в ходе программ это используется часто, т.к. если бы этого не было музыка и звуки играли независимо от игры, а приходится каждый раз указаывать z80, что ему играть.)
адреса вроде
ROM:00000C00                 move.w  #$100,(word_0_A11100).l
ROM:00000C08                 move.w  #$100,(word_0_A11200).l
и говорят об этом 'захвате' , а также говорит , о том что данный код связан со звуком.
ROM:00000C00                 move.w  #$100,(IO_Z80BUS_).l
ROM:00000C08                 move.w  #$100,(IO_Z80RES_).l


Переходим к 19000:
ROM:00019000 Z80_code:       dc.b $F3; ?          ; DATA XREF: sub_0_12AAC+Co
ROM:00019001                 dc.b $31; 1
ROM:00019002                 dc.b $F0; ?
ROM:00019003                 dc.b $13
ROM:00019004                 dc.b $ED; ?


В базе его нельзя дизасмнуть, т.к. это другой процессор и его команды другие. (можно открыть файл заного, и указать тип процессора Z80), но можно ли в одной базе дизассемблить 2 процессора я не знаю, скорее всего нельзя.
Код z80 представляет собой лишь один кусок, (т.е. в роме его больше нигде нет), теперь надо на глаз определить размер этого куска , выделить и сделать 'hide':

примерно тут конец:
ROM:0001A1E9                 dc.b   1
ROM:0001A1EA                 dc.b   5
ROM:0001A1EB                 dc.b   0
ROM:0001A1EC                 dc.b   0
ROM:0001A1ED                 dc.b   0
ROM:0001A1EE                 dc.b   0
ROM:0001A1EF                 dc.b   0
ROM:0001A1F0                 dc.b   0
ROM:0001A1F1                 dc.b   0
ROM:0001A1F2                 dc.b   0
ROM:0001A1F3                 dc.b   0

(пошли нули).
выделяем область и заходим в view - hide. OK.

Результат:
ROM:00018FFC                 dc.b   0
ROM:00018FFD                 dc.b   0
ROM:00018FFE                 dc.b   0
ROM:00018FFF                 dc.b   0
ROM:00019000; Z80_code
ROM:0001A1EC                 dc.b   0
ROM:0001A1ED                 dc.b   0
ROM:0001A1EE                 dc.b   0
ROM:0001A1EF                 dc.b   0
ROM:0001A1F0                 dc.b   0
ROM:0001A1F1                 dc.b   0


Т.е. нам не пришлось ломать этот участок. (пытаясь понять, что же там).

Звук.
Теперь стоит выделить звуки. К тем звукам что можно открыть через редактор относится лишь оцифрованная речь,
пример озвучка larry в RRR, озвучка действия юнитов в Dune. Остальные звуки и музыку можно взломать лишь зная программирование.
Если ром содержит такую речь, открываем ром-файл в редакторе goldwave.

Следует использовать следующие настройки:
File type:  Raw (snd,raw)
Attributes:  PCM, unsigned 8bit,mono
Rate(HZ) : от 8000 до 16000.


Требуемый рейт можно определить на слух, т.к. он определяет скорость проигрывания. Т.е. нужно подобрать, если проигрывается слишком медленно или слишком быстро.
Теперь прослушаем РОМ, наличие звука можно увидеть также на графике, в виде характерной синусоиды. Остальные области просто 'залиты зеленым', скрипы и писки говорят о том что мы просто слушаем код, данные, графику (т.е. читаем их как будто бы они бы звук).
Теперь найдем и выделим эту же область и сделаем в базе IDA 'hide' аналогично как с случае с Z80. Типичное представление звука в 16-ричном виде:
ROM:000236D4                 dc.b $80; ?
ROM:000236D5                 dc.b $80; ?
ROM:000236D6                 dc.b $80; ?
ROM:000236D7                 dc.b $80; ?
ROM:000236D8                 dc.b $80; ?
ROM:000236D9                 dc.b $80; ?
ROM:000236DA                 dc.b $80; ?
ROM:000236DB                 dc.b $80; ?
ROM:000236DC                 dc.b $80; ?
ROM:000236DD                 dc.b $7F;
ROM:000236DE                 dc.b $81; ?
ROM:000236DF                 dc.b $90; ?
ROM:000236E0                 dc.b $93; ?


80 - говорит о тишине (начало звука), т.к. 8 битный звук (1 байт). где 80 как бы середина , а 0 и FF максимальный значения амлитуды колебаний.

Звук PCM (pulse code modulation), соотв. звукам типа WAV, т.е. и те и те 'несжатые' в отиличи от mp3.(mp3-сжатый WAV).
WAV отличается наличием заголвка в котором содержится информация о битности и размере.
В роме размер определяется программой.

Поскольку звук cчитывается процессором z80, мы не найдем на 'lea' на начальный адрес массива звуков. Однако могут быть массивы оффсетов на адреса для каждого звука. (абсолютные или от-ные).
Оффсеты могут быть и на музыку.

Пример 'взломанных' относительных оффсетов:
ROM:000234D2                 dc.w $2A              ; 42sounds
ROM:000234D4 larry_adr_lngt: dc.l $36CC
ROM:000234D8                 dc.w $1A70
ROM:000234DA                 dc.w 0
ROM:000234DC                 dc.l 0
ROM:000234E0                 dc.l $513C
ROM:000234E4                 dc.w $309F
ROM:000234E6                 dc.l 0


$36CC адрес начала звука, от-но адреса $20000.
ROM:00020000 SOUND_MUSIC:

ROM:00012ACE                 move.l  #SOUND_MUSIC,d0
ROM:00012AD4                 move.b  d0,(z80_xz_5).l


$1A70 - размер (длительность) звука.

Это требует доли смекалки, т.е. сразу так догадаться ни у кого не получается.

Хайд:
ROM:00020000; snd_music
ROM:0006B000 unk_0_6B000:    dc.b $20              ; DATA XREF: sub_0_E38:loc_0_E3Co
ROM:0006B000                                        ; sub_0_E74+4o ...


* Вы можете заменять звуки как угодно через goldwave. (т.е. это можно делать и без изучения рома через IDA и WINHEX)

Графика:
Для нахождения и изменения графики в роме используются т.н. тайловые редакторы.
Откроем ROM с помощь тайлового редактора:
YY-CHR V0.98
File->open
Во вкладах выставим 4Bpp MSX/GENESIS.
Теперь используя стрелки найдем нечто похожее на графику. 'Каша' означает что это не графика (а данные, код, звук).
Т.е. как в goldwave мы слышли скрипы, тут видим кашу.

В случае нахождения графики (например машины или персонажа), стоит прокрутить все опции (вместо Normal другие), это выбирает порядок расположения тайлов. (блоков графики).
Нужный порядок(карта тайлов) задается в программе игры. Сама карта является 'data'ой.
Также палитра (набор цветов) задается в программе. Палитры расположены в других, отедельных от графики местах.
в YY-CHR на глаз можно подобрать нужную палитру.

Пример в роме Rock n' Roll Racing машины (их части) можно увидеть в конце рома.
Конец их графики уже известен, теперь надо определить начало, адрес с которого примерно начинается графика машин BF400.
(это видно в YY-CHR) сверху на вкладке на file open и т.д.
yy-CHR (BF400/100000).

Теперь идем к этому адресу в IDA.

Теперь будем искать переход (запрос) на чтение графики программой.

XREF нашелся по адресу BF500

ROM:000BF500 unk_0_BF500:    dc.b   0              ; DATA XREF: sub_0_6394+Eo
ROM:000BF501                 dc.b   0
ROM:000BF502                 dc.b   0
ROM:000BF503                 dc.b   0
ROM:000BF504                 dc.b   0
ROM:000BF505                 dc.b   0
ROM:000BF506                 dc.b   0


код после jump to xref.

ROM:000063A2                 addi.l  #unk_0_BF500,d0
ROM:000063A8                 movea.l d0,a0


В этом случае использовалось не lea или move, а addi. Это более редкий вариант. Также Xref может идти к оффсету (а от него уже к программе).

Если вы ничего не нашли кроме 'каши', значит графика сжата. (т.е. заархивирона как winrar). Также часть графики может быть сжата, а часть не сжата . Пример RRR - (cжата графика планет и аватары).
Это сделано для экономии места, т.к. картирджная память была дорогая и место было ограничено.
Сжата может быть не только графика, (в RRR сжаты также конфиги трасс, и палитры планет).
Графика 'расжимается' программно подпрограммой-распаковщиком, переносясь в RAM. Расжимается только что-то одно конкретное (графика одной планеты), т.к. место в RAM еще более ограничено.
С помощью winrar или zip, эти архивы открыть нельзя. В каждой игре использовались свои аглоритмы архивирования, но тем не менее есть многих похожих (напр. LZSS).
Без специальных программ распаковки их вскрыть нельзя. Для бол-ва форматов и игр можно найти соотв. программы распаковщики.
Также в редких случаях может использоваться 2 или 3 разных способа сжатия.
Чтобы вскрыть архивы, если программа-распаковщик открывает только их, а не ром целиком, надо найти их точные адреса.
Адреса и размеры могут быть заданы оффсетами:

Пример организации архивов в RRR, от-ные ссылки на архивы:
ROM:0006B000 dword_0_6B000:  dc.l $2000037C        ; DATA XREF: sub_0_E38:loc_0_E3Co
ROM:0006B000                                        ; sub_0_E74+4o ...
ROM:0006B004                 dc.l $20000688
ROM:0006B008                 dc.l $B2A
ROM:0006B00C                 dc.l $20000B50
ROM:0006B010                 dc.l $2F30
ROM:0006B014                 dc.w $2000
ROM:0006B016                 dc.w $2F7A

6B000 + 37C = первый архив по адресу 6b000+37c = 6b37c второй 6B000+688 = 6b688, размер архива не задан - высчитывается разницей между соседними архивами.
Размер также может быть указан рядом, быть задан в программе, находится в самом архиве, быть заданным (фиксированным) - для однотипных архивов.
цифра $20 в данном случае не используется, видимо в н-кых писали как пробел вместо 00.
Чтобы понять как именно устроено надо изучать программу.

"Хайд":
ROM:0001FFFE                 dc.b   0
ROM:0001FFFF                 dc.b   0
ROM:00020000; sound_music
ROM:0006B000; archives
ROM:000BF500; cars_gfx


"Отрезали солидные куски", Во всех остальных частях РОМа находятся код, данные и пустое место, которые нужно отделить друг от друга.

Пустое место может быть не только 'Нулями' '00', может быть 'залито' 'FF'.
Замечу что в несжатой графике тоже много нулей, но это не пустое место. (т.е. если мы начнем туда что-то совать,
например свой код, мы получим подпорченную графику - модели машин с 'артефактами'.). Чтобы не гадать просто расширяйте РОМ как описано в гайде. К тому же при расширении вы будет знать, что записывали в новую облсть.
(а не сунули там куда-то в середину , а куда забыли.)


Data hacking. часть3.
Кроме описанных данных в РОМе содержатся 'типичные' данные (т.е. содержатся в любой игре) :
К этим данным можно отнести:
Палитры (наборы цветов)
Комбинации кнопок (игры с комбо-ударами или пос-ти на читы)
Значения от нажатий кнопок в RAM.
Ascii - текст, если игра использует данный вид текста.


Палитры.
Это наборы цветов которые необходимы для 'раскраски' графики.
в HEX-виде палитры выглдят следующим образом:
0EEE, 0AC8, 0246, 0E00, 000A. (тип данных word).
разобраться в них достаточно просто - '0BGR' , (blue green red) т.е. первая цифра соотв. кол-ву синего, вторая зеленего, третья - красный.
Комбинируя 3 составляющие можно получить любой цвет 000 - черный 0EEE (белый) 0888 - (серый)
Цифры могут быть только четными (0,2,4,6,8,a,c,e) - это говорит о 9 битности цвета. 8^3=512 цветов.
(но одновременно железо сеги позволяет отображать только от 16 до 61 (в лучшем случаем)) цветов.
Поэтому и используются палитры.

Типичный вид палитр: (в примере палитры уже переведены в вид word - кнопка 'D' , если забыли)

Палитра в RRR для синих машин:
ROM:00006776 word_0_6776:    dc.w 0                ; DATA XREF: sub_0_5DAE:loc_0_5DE0o
ROM:00006778                 dc.w $C86
ROM:0000677A                 dc.w $C44
ROM:0000677C                 dc.w $A22
ROM:0000677E                 dc.w $444
ROM:00006780                 dc.w $222
ROM:00006782                 dc.w $460
ROM:00006784                 dc.w $460
ROM:00006786                 dc.w $460
ROM:00006788                 dc.w $460
ROM:0000678A                 dc.w $460
ROM:0000678C                 dc.w $EEE
ROM:0000678E                 dc.w $AAA

В некоторых случаях палитры могут быть сжаты (запакованы).

Некоторые ромхакеры для быстрого поиска палитры используют сейв-файлы эмулятора. (в них хранится текущая палитра).
Скопировав через WINHEX и вбив в поиск соотв. цифры можно найти эту палитру.
Можно просто поискать в ром-файле популярные цвета (0EEE), в winhex search-find-hex values - 0EEE, ok.

Текст в формате ascii. (это обычный текст на английском).
Его можно найти в winhex в правой вкладке, о чем упоминалось вначале.
в IDA текст по умолчанию выглядит так:

ROM:0000398C unk_0_398C:     dc.b $50; P          ; DATA XREF: text_string+10o
ROM:0000398D                 dc.b $4C; L
ROM:0000398E                 dc.b $41; A
ROM:0000398F                 dc.b $59; Y
ROM:00003990                 dc.b $45; E
ROM:00003991                 dc.b $52; R
ROM:00003992                 dc.b   0
ROM:00003993                 dc.b $54; T
ROM:00003994                 dc.b $52; R
ROM:00003995                 dc.b $41; A
ROM:00003996                 dc.b $43; C
ROM:00003997                 dc.b $4B; K
ROM:00003998                 dc.b $3A; :
ROM:00003999                 dc.b   0
ROM:0000399A                 dc.b $52; R
ROM:0000399B                 dc.b $41; A
ROM:0000399C                 dc.b $43; C
ROM:0000399D                 dc.b $45; E
ROM:0000399E                 dc.b $53; S


Для преобразования в строку, выделите нужное слово и нажмите хоткей 'a'.

Итог:
ROM:0000398C aPlayer:        dc.b 'PLAYER'          ; DATA XREF: text_string+10o
ROM:00003992                 dc.b   0
ROM:00003993 aTrack:         dc.b 'TRACK:'
ROM:00003999                 dc.b   0
ROM:0000399A aRaces:         dc.b 'RACES'


Рекомендуется сразу же удалить, образовавшиеся в результате метки. (aPlayer) , т.е. делаем rename и оставляем пустое поле.
Иначе ваша база меток (вкладка NAMES в IDA) , окажется забитой ненужной кучей ссылок на текст.
Кроме того если строка получилась длинной, и метка тоже, это может вызвать баг.
Как я уже упоминал в названиях следует использовать короткие имена - не более 14 символов.
Если текста много можно скрыть область ('hide').


Последовательности кнопок.
Каждому нажатию кнопки соотв. определенный код. Этот код записывается в RAM.
Самый частый вариант комбинации 'нажатие - код'.
ВВЕРХ - 0100
ВНИЗ - 0200
ВЛЕВО - 0400
ВПРАВО - 0800
A - 4000
B - 1000
C - 2000
start - 8000

Если игра использует 3-кнопчный контроллер, значения скорее всего будут 1-байтные. т.е. 01, 02, 04, 40, 20 и т.д.
Второй байт нужен для кнопок X Y Z MODE.
при нажатии A+B получается 'сумма битов' нажатий , т.е 5000.

Где находятся значения в RAM следует искать с помощью поиска читов. (зажимайте вверх + нажмайте esc для паузы эмуляции, теперь можно искать это значение в памяти не удерживая 'вверх' :) )
Если вы нашли верное значение, то при нажатии кнопок оно будет меняться в соотв. с этими кодами.
Значение может быть в нескольких адресах (на нажатие, отжатие и т.д.).

в IDA эти значения можно переименовать как Joy_data. Т.е. также как мы искали и переименовывали другие переменные.

Также их можно найти через IDA, по xrefs'y от адреса 00A10002 (порты), попав к программе-драйверу джойстика.
(может еще быть a10005). И изучив эту программу (куда в RAM она записывает значения).

IO_CT1_data:00A10002; Segment type: Pure data
IO_CT1_data:00A10002; segment "IO_CT1_DATA"
IO_CT1_data:00A10002                 ds.b 1
IO_CT1_data:00A10003 IO_CT1_DATA_:   ds.b 1                ; DATA XREF: JOYSTICKS_DRV_1+2Ao



Заданные последовательности кнопок: (эти последовательности прописаны в ROM) .
Программа устроена так что сверяет значения из RAM cо значениям из ROM. (и если всё совпало - условие выполнено - идет переход к коду спецудара или включению чита).

RRR - Секретный код выбор 'Олафа'.

ROM:00007358 char_secr_code: dc.w $100              ; DATA XREF: char_select_frameo
ROM:0000735A                 dc.w $800
ROM:0000735C                 dc.w $100
ROM:0000735E                 dc.w $800
ROM:00007360                 dc.w $200
ROM:00007362                 dc.w $400
ROM:00007364                 dc.w $200
ROM:00007366                 dc.w $400
ROM:00007368                 dc.w $800
ROM:0000736A                 dc.w $FFFF


$FFFF в этом случае означает 'конец'. Данные комбинации можно найти и без IDA, (как и в случае с палитрами).
Однако если значения были 1-байтные , обычным поиском через hex это будет практически невозможно.

Подводные камни и прочие трюки.
** игры от EA и другие игры которые не запускаются при любом изменении.
Снятие 'защиты'. Hot trick.
Используйте 'MASTER' code из книги гейм-гени кодов Codebook.exe или с сайтов где описаны гейм-гени коды.
Этот код описывается как необходимый для того,чтобы работали все остальные коды.
На самом деле он является кодом отключения защиты.
Переведя этот код в формат Adress:data, и вписав это значение через WINHEX в ром-файл мы отключим код проверки дополнительной чексуммы.

Прежде чем продолжать дальше дизасмить базу (определять данные и код).

Важно знать следующие баги в IDA:

"-1(a0) bug"
При дизасме IDA автоматически определяет отрицательные от-но регистров (a0), (a1) и т.д. смещения как адрес RAM FFFFxxxx. На самом деле это не так, так произойдет только в том, случае если в регистр был равен a0=00000000. А как мы уже знаем, он может быть равен чему угодно.

Пример(dune):
ROM:00025708                 move.w  (word_0_FFFFF3BE).w,d1
ROM:0002570C                 sub.w   dword_0_FFFFFFF4(a4),d1
ROM:00025710                 move.w  d1,(word_0_FFFFF3BE).w


a4 не обязательно равен 00000000.

Исрпавляем (нажав хоткей 'q') :
ROM:00025708                 move.w  (word_0_FFFFF3BE).w,d1
ROM:0002570C                 sub.w   -$C(a4),d1
ROM:00025710                 move.w  d1,(word_0_FFFFF3BE).w


В случаях если игра не использует данный метод обращения к 'копии' RAM (FFFFxxxx), можно вообще удалить этот сегмент.
Вы должны быть уверены что вы делаете, и что точно не использует прежде чем удалять. Dune использует все три варианта обращения к RAM.

* сегмент RAM FFFF0000 - FFFFFFFF, можно удалить в базе Rock n' Roll Racing.

"offset bug"
IDA автоматически определяет многие адреса как оффсеты, хотя они ими не являются.

Пример (dune):
ROM:0002EC7A off_0_2EC7A:    dc.l off_0_10018      ; DATA XREF: sub_0_2EBFCo
ROM:0002EC7E                 dc.b   0
ROM:0002EC7F                 dc.b $18
ROM:0002EC80                 dc.b $FF
ROM:0002EC81                 dc.b $F4; ?
ROM:0002EC82                 dc.b  $A
ROM:0002EC83                 dc.b   0

ROM:00009DB6 off_0_9DB6:     dc.l loc_0_20016+2    ; DATA XREF: ROM:00009D94o
ROM:00009DBA                 dc.b   0
ROM:00009DBB                 dc.b $20
ROM:00009DBC                 dc.b   0
ROM:00009DBD                 dc.b   0
ROM:00009DBE                 dc.b  $A

Из идущих вслед значений нельзя получить нормальные оффсеты (нельзя продолжить массив - таблицу адресов).
Исправление:
Провести undefine на данные значение (хоткей 'U')

ROM:00009DB6 unk_0_9DB6:     dc.b   0              ; DATA XREF: ROM:00009D94o
ROM:00009DB7                 dc.b   2
ROM:00009DB8                 dc.b   0
ROM:00009DB9                 dc.b $18


В большом кол-ве этот вид бага можно получить если вы делали 'final analisys pass'.


"move.w #loc_ bug. "
Баг - число принимается за адрес (метку).

Пример:
ROM:00000D6A                 move.w  #loc_0_8124,(word_0_C00004).l
ROM:00000D72                 move.w  #loc_0_8124,(word_0_FFFFE00C).w
ROM:00000D78                 bsr.w   sub_0_4E8
ROM:00000D7C                 bsr.w   sub_0_536
ROM:00000D80                 bsr.w   sub_0_41C
ROM:00000D84                 move.w  #unk_0_8004,(a5)
ROM:00000D88                 move.w  #unk_0_8700,(a5)
ROM:00000D8C
ROM:00000D8C loc_0_D8C:                            ; DATA XREF: ROM:000198C8o
ROM:00000D8C                 move.w  #loc_0_8B08,(a5)
ROM:00000D90                 move.b  #(dword_0_0+$80),(word_0_A1000C+1).l
ROM:00000D98                 move.w  #(loc_0_8FFC+5),(a5)



Исправление (нажать q на число):
ROM:00000D6A                 move.w  #$8124,(word_0_C00004).l
ROM:00000D72                 move.w  #$8124,(word_0_FFFFE00C).w
ROM:00000D78                 bsr.w   sub_0_4E8
ROM:00000D7C                 bsr.w   sub_0_536
ROM:00000D80                 bsr.w   sub_0_41C
ROM:00000D84                 move.w  #$8004,(a5)
ROM:00000D88                 move.w  #$8700,(a5)
ROM:00000D8C
ROM:00000D8C loc_0_D8C:                            ; DATA XREF: ROM:000198C8o
ROM:00000D8C                 move.w  #$8B08,(a5)
ROM:00000D90                 move.b  #$80,(word_0_A1000C+1).l


# - говорит о том что, это число, а не адрес. В некоторых случаях число все же может являются адресом (или его частью).
Это можно понять лишь значительно изучив программу и ассемблер. в 90% случаях это является багом.
Поэтому его нужно фиксить. Этот вид 'бага' может присутсвовать в огромном кол-ве. Поэтому не старайтесь сразу его исправить.

"Баг строк"
Этот баг может возникнуть в случае использования длинных ascii строк или создания такой без выделения, а также если вы делали 'final analysis pass'. Баг состоит в том, что начинает глючить список имен (+забивается ненужными именами).
Также можно схватить и 'too many lines' баг.

Баг too many lines' в Dune. (IDA определила как строку автоматически при final analysis).

ROM:000E0000 aTPqkleqamiompfdMoLkklepHvm9$:dc.b '}?-L------+=+¬??????????????¦L¦+¬T-LT¦??¬¦??kleP;HVM9//$         !$ (2@NYd|?shu?????¦-??????ztx}???-???¬T-+¦T¦¦T-TL+¦---¬????'
ROM:000E0000                                        ; DATA XREF: ROM:000061C4o
ROM:000E0000                                        ; ROM:000483BAo ...
ROM:000E0000                 dc.b '}^NFA7#  #,>V[SS\kqkrzw_RV[djbSSJ?>MY\VYm???????--¦????????????????????????????-L+T+-L¬??--¬¦????zkUPV__P8#+22* ,/-(!       &'
ROM:000E0000                 dc.b '5EOS]XOSj??zb^n???-T¦¦¦¦L¦T-T-¬¬¦+T¦??-?+¦----++¦????-??pPA4(   &8A51Jag_X^\TPKB1/;A@BJRQSZbZ^k}????-+T-¦¬¬¬¦¦¦????yninttoged'
ROM:000E0000                 dc.b 'diq???????-??¦¦??zm_SLC>@AHWhtz|?????¦¦+?????????uz~wfRKLRVTPWf|???????-???zqhT\e^PL@8DVdoz?????-¦¦¦++¦?????|qw?????????z}??'


Решение:
Сделать undefine на всю область.
А также удалить названия(имя метки ascii).
Также если вы не заполняли базу, создайте новую с нуля и не делайте final analysis.


Дизасм данных как кода:
Баг когда IDA автоматически дизасмит данные как код.
Как мы уже помним любой код заканчивается на rts, jmp, bra или rte.
Решение - сделать undefine.

Трюки:
Исправление адреса от-но регистра. Это очень важный трюк. Он позволит получить абсолютный доступ к RAM в IDA, даже для от-ного метода адресации. (а значит мы сможем задать и метки и видеть точный адрес).

Этот способ я использовал для фикса адресов в Rock n' Roll Racing.

ДО:
ROM:0000DFEA                 move.b  $395E(a4),$3984(a4)
ROM:0000DFF0                 clr.w   d1
ROM:0000DFF2                 move.b  $390F(a4),d1
ROM:0000DFF6                 add.b   d1,d1
ROM:0000DFF8                 add.b   $395F(a4),d1
ROM:0000DFFC                 asl.b   #2,d1
ROM:0000DFFE                 cmpi.b  #2,$4ECE(a4)


ПОСЛЕ:

ROM:0000DFEA                 move.b  current_planet-$FF8000(a4),word_0_FFB984-$FF8000(a4)
ROM:0000DFF0                 clr.w   d1
ROM:0000DFF2                 move.b  difficlt_level-$FF8000(a4),d1
ROM:0000DFF6                 add.b   d1,d1
ROM:0000DFF8                 add.b   divison_num-$FF8000(a4),d1
ROM:0000DFFC                 asl.b   #2,d1
ROM:0000DFFE                 cmpi.b  #2,num_players-$FF8000(a4)


Получил доступ к меткам и абсолютным адресам в RAM, где подписаны найденные мною переменные.
В данном случае я знал a4=FF8000, т.к. в RRR он всегда равен FF8000.
Если я нажму на метку я перейду в IDA в абсолютный адрес: $FFB95E. (395E+FF800)., т.е. реальный адрес RAM куда пишется значение.

Во всех остальных случаях регистр быть равен чему угодно, но и там его можно 'отследить'. Для этого надо изучить код, или использовать дебаг на pc на этот ром-адрес. Регистр может быть равен всегда чему-то одному, а может принимать разные значения. (во-втором случае исправление 'неточное', т.к. даст переход только к одному адресу, но это тоже бывает полезно).

Dune:
ROM:00004E74                 lea     (word_0_FFBF4C).l,a2
ROM:00004E7A                 move.b  #$FC,4(a2)

ROM:00004E74                 lea     (word_0_FFBF4C).l,a2
ROM:00004E7A                 move.b  #$FC,byte_0_FFBF50-$FFBF4C(a2)



Трюк выполняется следующим способом:
Выделяем нужный адрес (цифру $395E) и нажимаем CTRL+R. В поле Base adress вписываем адресс который находится в регистре.
Ставим галочку treat base adress as plain number. OK.

Некоторые могут заявить что это не трюк, однако многие известные мне ромхакеры этого не знали, поэтому назыв. так.

Трюк с созданием таблиц:
Допустим вы нашли какие-то данные, знаете за что, они отвечают и хотите их упорядочить.
Для этого выделяем область и жмем правой кнопкой->array. Тут указывается что-то вроде длины и ширины таблицы.

Таблица порядка номеров трасс в RRR данным способом:
(для первой планеты по 4, для второй по 5 и т.д.)

ROM:0000189A trak_sort_vs_m: dc.b 1, 2, 3, 4        ; DATA XREF: tracks_init+1Eo
ROM:0000189A                 dc.b $25, $26, $27, $28
ROM:000018A2                 dc.b 5, 6, 7, 8, 9
ROM:000018A2                 dc.b $29, $2A, $2B, $2C, $2D
ROM:000018AC                 dc.b $A, $B, $C, $D, $E, $F
ROM:000018AC                 dc.b $2E, $2F, $30, $31, $32, $33
ROM:000018B8                 dc.b $10, $11, $12, $13, $14, $15, $16
ROM:000018B8                 dc.b $34, $35, $36, $37, $38, $39, $3A
ROM:000018C6                 dc.b $17, $18, $19, $1A, $1B, $1C, $1D
ROM:000018C6                 dc.b $3B, $3C, $3D, $3E, $3F, $40, $41
ROM:000018D4                 dc.b $1E, $1F, $20, $21, $22, $23, $24
ROM:000018D4                 dc.b $42, $43, $44, $45, $46, $47, $48


Трюки и хоткеи в winhex.
Простой поиск данных.
Например вы ищите точное значение (конретное число), и это число достаточно длинное, т.е. например цена машины.
Число может быть представлено как в 10-чной системе, так и 16-ричной.
в RRR для цен используется десятичная система. цена 24000$.
в поиск вбиваем '00240000', если значений нашлось н-ко следует проверить по группам, или каждое по очереди.
используйте 'replace hex values' для замещения и поиска всех сразу. (для поиска например в части рома выделите область и используйте поиск только в блоке - галочка search in block only.)
Если число 16-ричное (в бол-ве игр именно такое), его надо перевести на калькуляторе и потом искать.
24000= $5DC0, т.е. следовательно надо было вбивать в поиск '5dc0' или '00005dc0'

Если игра содержит много таблиц, (как например rock-n-roll racing - таблица порядка трасс, хар-ки тачек, игроков, список цен, таблицы хар-к для AI (уровни сложности).
в WinHEx в ascii-виде (правая колонка) эти таблицы выглядит в виде точек. Либо других повторяющихся последовательностей

Пример (таблица бонусов игрока + комбо на чит) - точки в ascii. (правая колонка).
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00007320 43 EC 51 9A 12 3B 00 1D D3 31 80 00 4E 75 01 00 C?Q?.;..?1€.Nu..
00007330 01 00 00 01 01 10 10 00 00 10 00 10 00 08 08 08 ................
00007340 00 00 08 00 00 00 01 01 01 01 00 03 00 02 00 03 ................
00007350 00 04 00 00 00 04 00 03 01 00 08 00 01 00 08 00 ................
00007360 02 00 04 00 02 00 04 00 08 00 FF FF E5 40 41 EC ..........???@A?

То есть 'data'-hacking возможен в чистом хекс. Но все зависит от конретной игры. Я например нашел эти 'gamedata' таблицы используя только winhex, также в dune2 я разобрался в конфиге карты, без изучения кода. (т.е. каждое значение соотв. в карте номеру юнита, его координатам на карте и жизням) - что легче определить опытным путем., чем взламывать код.

Хоткеи WINHEX:
CTRL+C - копировать
CTRL+B - 'вставить' (записать), не следует использовать CTRL+V, иначе мы сдвинем все данные.
CTRL+SHIFT+C и ALT+SHIFT+C - копировать значения, для переноса в текстовый файл. (.txt). например на форум.
ctrl -копирует только сами значения, alt - все отображение (ненадо делать скриншот)

ctrl+s - сейв файла.
ctrl+z - отмена последних изменений (очень полезный хоткей, когда игра после изменений зависла, и надо отменить, позволяет обходится без бек-апов).

ALT+G - прыжок к нужному адресу.

Также можно включить режим мнговенного редактирования (ненадо будет делать сейв):
Нажать F6, и выбрать Default Edit Mode (editable).


Также в winhex есть опция открытия жестого диска, и можно восстановить удаленные н-кые файлы.(и ненадо применять программы для восстановления). Хотя это и выходит за рамки гайда (но считаю полезным), т.к. если что-то 'полетит' вы сможете восстанавить базу с помощью winhex (но не 100%) - так что делайте бекап баз на CD-диск.

Трюки с ромами:
Используйте total commander для сравнения двух файлов. Например оригинал и пиратский хак, или просто хак.
Таким образом вы можете узнать где, и что менять, чтобы получить такой же результат. Также это поможет взломать защиту, если другие методы не помогли.
Также сравнивайте свой хак с оригиналом, если забыли где и что меняли, а также на наличие ошибок (когда забыли отменить изменения и они остались).


Заключение:
Таким методом можно взломать любую игру на Sega, также можно использовать некоторые подходы и для других платформ.

В случае если вы ничего не поняли из сего гайда, Вам остается курить бамбук. Так как понятнее может быть лишь с
картинками, а автору это пока что не под силам, т.к. сам мануал получился немаленьким.
Если вы всё поняли, и вам этого недостаточно, то стоит искать более сложные мануалы, ориентированные на что-то одно (например тех. документация на сегу, или руководство программиста по ассемблеру mc68000), т.е. не надейтесь что всё сделают за вас. Если Вы с чем-то несогласны это остается с вами. Если Вы нашли ошибку вы можете написать ее в ПМ автору.
Также не следует задавать вопросов автору (он не обучает хакингу, а только предоставляет сей мануал). Также не следует спорить, о том как нужно писать мануал и что в нем надо переделать. А также запрещается копировать текст из мануала и куда-то его заливать.

Удачного ромхакинга!


Hack guide by Ti. (с) 2010.


*********************************************************************************************************************
****************** demo (not finished) version of guide *************************************************************



Вернуться
  • Комментарий: 6
  • Просмотров: 18273
Ti_
#1
  • 2 июня 2016 19:44
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
ну это пиздец... и кто такой умный?
  • 18 июля 2018 16:56
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
Подскажите пожалуйста GG код или читы на деньги RRR_Hack_v16alpha?, оч нужно.
Sheraz
#3
  • 17 марта 2019 14:14
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
Цитата: Саша
Подскажите пожалуйста GG код или читы на деньги RRR_Hack_v16alpha?, оч нужно.

Да,в этой игре нет сложностей с деньгами,просто нужно проанализировать то,как меняется пароль игры в зависимости от нужной суммы,пароль в этой игре решает фактически всё.Например,когда я его анализировал его,то открыл для себя несколько новых машин,планет,Олафа и ещё одного секретного персонажа.
  • 1 апреля 2019 01:45
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
Столько много теории и очень жаль что нет практики. Честно, для меня это заумно и эта теория для меня тёмный лес. Практика мудрее и она гораздо проще, неждели это нкенужная теория. Однако незнание данной теории мне не помешало взщламывать игры.
Простите, но данную теорию считаю "дурдомом".
Sheraz
#5
  • 7 апреля 2022 22:16
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
Цитата: sergei1204
Столько много теории и очень жаль что нет практики. Честно, для меня это заумно и эта теория для меня тёмный лес. Практика мудрее и она гораздо проще, неждели это нкенужная теория. Однако незнание данной теории мне не помешало взщламывать игры.
Простите, но данную теорию считаю "дурдомом".

"Дурдом" - это написанное тобою,прочти ещё раз свой комментарий,думаю,что найдёшь ошибки,которые указывают на уровень твоего интеллекта.Практика без теории слепа,теория без практики мертва.Ещё скажи,что ты в школе не учился и не читал мануалов по взлому,они тоже являются теорией,без которой ты врятли что-нибудь взломал бы.Да,знание ДАННОЙ теории тебе не помешало взломать ОПРЕДЕЛЁННЫЕ игры,но не другие игры.По крайней мере,мой предыдущий пост ВПОЛНЕ ПРАКТИЧЕН...ЗАРАБОТАЙТЕ ОПРЕДЕЛЁННОЕ КОЛИЧЕСТВО ДЕНЕГ НА ПЕРВОЙ ПЛАНЕТЕ В ПЕРВОМ ДИВИЗИОНЕ И СДЕЛАЙТЕ ТАК,ЧТОБЫ НЕ ВЫПОЛНИТЬ УСЛОВИЯ ДЛЯ ПЕРЕХОДА В СЛЕДУЮЩИЙ ДИВИЗИОН И ТОГДА ПОЛУЧИТЕ ПАРОЛЬ НА,ЗАРАБОТАННУЮ ВАМИ,СУММУ ДЕНЕГ.ДАЛЕЕ-СРАВНИТЕ НАЧАЛЬНЫЙ И,ПОЛУЧЕННЫЙ ВАМИ,ПАРОЛЬ и ВЫЯВИТЕ РАЗЛИЧИЯ В ОПРЕДЕЛЁННЫХ УЧАСТКАХ КОДА,ЭТО ДАСТ ВАМ КЛЮЧ К ПОЛУЧЕНИЮ,НУЖНОЙ ВАМ СУММЫ ДЕНЕГ.
  • 7 октября 2023 23:50
  • Регистрация: --
  • ICQ: {icq}
  • Комментариев: 0
  • Новостей: 0
Статья такой же набор бесполезной хуйни, как реформы в стране, поменяли милицию на полицаев, но ни чего не поменялось. Такое ощущение, что программисты СЕГА специально создавали архитектуру консоли так, чтобы в ней смогли разобраться только такие же конченые как и они сами долбаёбы. И НЕТ Я не буду просить прощения за мат. Можете забанить как обычно и удалить мой пост, мне по хуй. Это просто покажет всем, что вы такие же неадекватные как и они. Блядь за 30 с лишним лет ни кто не смог написать даже удобный инструмент для замены графики, ПОЗОРИЩЕ. Не говоря уже о нормальном КОНСТРУКТОРЕ ИГР БЕЗ ПРОГРАММИРОВАНИЯ ДЛЯ НЕЁ. Я в ахуе.

Комментарии:

Оставить комментарий