|
|
Страница не найденаСтраница, которую вы запрашиваете, не существует. |
Введение в хакинг для начинающихАвтор гида: vladikcomperВы решили заняться хакингом классических Сониковских игр, но не знаете с чего начать? В этом гиде я постараюсь помочь вам и рассказать про хакинг в общих чертах. В мире уже существует множество специальных программ, которые заметно упрощают процесс хакинга Сониковских игр и расширают просторы для творчества. После недолгого изучения некоторых программ, вы сможете уверенно редактировать уровни, палитры и спрайты, причем для некоторых вещей вам даже не понадобятся навыки программирования и работы в HEX редакторе. Итак, начнем наше погружение в мир хакинга! Для начала, определимся с инструментами. ДизасемблГлавное, что Вам потребуется для хакинга - это дизасембл. Дизасембл - это "разобранный" на части ROM игры, где многие данные (графика, палитры и др.) разбиты на отдельные файлы, которые удобно редактировать, а код игры дизассемблирован (переведен на язык ассемблера). В дизасембле есть компилятор, который компилирует из исходного кода игры новый ROM. Имея полный код игры и все данные, разбитые на файлы, возможности хакинга безграничны. Можно полностью переделать игру, добавлять абсолютно новый контент, все, что угодно (в пределах возможностей приставки, разумеется). Впрочем, этому еще надо научиться. На начальных порах вам предстоит познакомиться с великолепными программами, редакторами уровней, спрайтов, которые будут изменять файлы за вас. Они отчасти помогут разобраться, как устроена игра, которую вы хакаете. Но вначале давайте выберем себе дизасембл. На данный момент дизасемблированы все классические Сониковские игры, и к ним существуют даже по несколько версий дизасемблов. За дизасемблами обратимся к замечательному и огромному сайту - Sonic Retro. http://info.sonicretro.org/Disassemblies Как видите, на каждую игру существует множество версий дизасемблов, одни из них довольно старые, другие посвежее и полнее - легко запутаться в этом разнообразии. Дизасемблы Соник 1Всем новичкам рекомендую начинать хакинг именно с Соник 1. Игра относительно мала и изучена больше всех остальных. Именно для первого Соника написано большинство гидов и программ. Ее движок намного проще для изучения, чем движки сиквелов. Если вы решили начать свой хакерский путь с Соник 1, рекоммендую два варианта: Sonic 1 (Split and Text by Hivebrain) (ASM68K) — Очень старый (2005 год), но до сих пор популярный дизасембл. Главный его плюс — организованность кода и простота. Под него на данный момент написано большинство гидов, и на моем сайте вся информация по Соник 1 дается для него. На смену ему вот уже много лет приходит Sonic 1 SVN Disassembly, но все никак не придет. Последний никак не может избавится от неорганизованности и страдает прегруженностью константами. |
|||
S1 Hacking Studio 2 — это готовый инструментарий для хакинга, включающий все нужные редакторы игры (которые будут описаны позже в этой статье). Программа работает с обновленным дизасемблом Hivebrain'а, он основан на версии указанной выше, но более подробно комментирован и усовершенствован. Обновления никак не сказываются на совместимости - абсолютно все гиды, написанные под старый дизасембл, будут работать и в новом. С помощью программы вы также можете одним кликом добавлять в игру Spin Dash, Jump Dash, исправлять оригинальные баги. Все уже настроено и готово к работе. Дизасемблы Соник 2Движок Соник 2 основан на доработаном и улучшенном движке первой части, поэтому с точки зрения хакинга эти две игры очень похожи. Кстати, когда хакинг только зарождался, Соник 2, был напротив, самой популярной в хакинге игрой, но стоило появиться одному интересному гиду, и дела изменились. Sonic 2 (Split and Text by Xenowhirl) — самый рекоммендуемый дизасембл для этой игры. К слову, он выглядит намного лучше и разобраннее дизасембла Hivebrain'а на Соник 1, но это и понятно - дизасембл вышел в 2007 году. Хотя все это компенсируется усложненностью движка и большим количеством кода. Все гиды для Соник 2 на моем сайте рассчитаны имено на этот дизасембл. Хакинг игр Sonic 3, Sonic & Knuckles и Sonic 3 & Knuckles, к сожалению, очень слабо развит на данной момент, так что он в рамках этой статьи рассмотрен не будет. Редакторы ассемблераЕсли вы хотите всерьез заняться изучением Ассемблера, вам не обойтись без хорошего редактора с подсветкой синтаксиса. Если же программирование - не ваш конек, и вы просто ограничитесь применением гидов, можете пропустить этот раздел и использовать для редактирования кода обычный Блокнот. Сега использует процессор Motorola 68000, или, сокращенно, M68K. Ассемблер M68K мало распрастранен, поэтому мало какой редактор поддерживает подсветку его синтаксиса. Когда мне понадобился такой редактор, я потратил немало усилий, чтобы найти его. Esrael Neto Assembler EditorКогда-то я пользовался специальным для этого ассемблера редактором - Esrael Neto Assembler Editor, который написал небезысвестный Esrael. В нем очень хорошая подсветка синтаксиса, она так мне понравилась, что примерно те же цвета я стал использовать для посветки кода на страницах сайта (в теме Синюшная). К сожалению, мне пришлось от него отказаться из-за багов. Впрочем, может баги были только у меня, так что посмотрите его. |
S1 Hacking Studio 2.0 изменяет компоненты игры |
||
ConTEXTConTEXT - это лучший вариант, который я могу предложить на данный момент. Очень советую использовать именно его. Скачайте редактор с официального сайта (http://www.contexteditor.org/) и установите его. Для удобной работы предлагаю немного настроить его. Откройте окно настроек среды (Options->Enviroment Options). Зайдите во вкладку Editor. Убедитесь, что включены опции Auto Indent и Tab Indent, поставьте Tab Width на 8, а Tab Mode на Hard Tabs. Выключите Smart Tabs. Кстати, на ConTEXT есть русская локализация (официальная), однако перевод неполный и местами неточный, поэтому я, во избежании путаницы, давал названия на английском языке. Этот редактор очень гибкий, и позволяет настроить подсветку синтаксиса со всеми тонкостями. На этот редактор я даже нашел готовую подсветку синтаксиса, сделанную Ambil'ом. Я дополнил ее отсутсвующими словами и изменил цвета - и получил наконец точную копию подсветки, что была в ESE ASM. Чтобы установить подсветку синтаксиса M68K, скачайте этот архив: http://vladikcomper.scanf.su/dump/download/M68000_Assembler.zip. Распакуйте CHL-файл из него в папку Highlighters программы.
HEX-редакторыКак известно, все файлы - это массивы из байтов определенной длины, будь то текстовичок, программа или РОМ Сеговской игры. Работая над хаком, вам часто может понадобиться изменять данные, которыми оперирует игра. Данные состоят из байтов, следовательно, вам нужно будет изменять значения байтов. Для этого потребуется шестнадцетиричный редактор, или HEX-редактор (HEX = Hexadecimal). HEX WorkshopОчень известный HEX-редактор, который обладает огромной кучей функций (многие из которых вам вряд ли пригодятся). Я им не пользовался, но многие говорят, что он очень хорош и горячо рекомендуют. Единственный минус - он платный, но при сильном желании это не будет помехой. Официальный сайт редактора — http://www.hexworkshop.com/ |
ConTEXT редактирует сверх-секретные исходники SQWA2. |
||
WinHEXЕще один известный и многофункциональный редактор. Я пользуюсь им уже очень давно, и мне его хватает, поэтому я не перехожу на другие редакторы и даже не присматривался к ним. WinHEX довольно удобен в обращении, интерфейс редактора чист и опрятен, функции поиска, замены и перехода к оффсету в наличии имеются, для меня это главное. :) Он тоже платен, есть бесплатная версия, но она ни на что не годится. Ищите кряк. Официальный сайт редактора — http://www.winhex.com/winhex/ Редакторы Соников и программы для хакингаTODOh |
WinHEX знает, из чего состоит Соник 1. |
Основы ассемблера Motorola 68000Автор гида: vladikcomperОсновано на гиде «SCHG How-to:Work with Motorola 68000 assembly» Обновление от 2023
Этот гид был значительно обновлен 17.11.2023. Обновление исправило ранее допущенные ошибки и неточности формулировок, особенно в первой части. Настоящий гид все еще не претендует на полноту и пытается подать огромное количество информации в рамках одной статьи. Возможно, в будущем автор напишет полноценный гид с несколькими главами. Этот гид для тех, кто слабо знает ассемблер или не знает его совсем. Из него вы узнаете что такое ассемблер, изучите самые базовые его команды. Ассемблер (сокращенно: АСМ) — язык программирования низкого уровня, а также программа, которая переводит мнемоники этого языка в машинный код. Ассемблер исключительно близок к машинному коду, с той лишь разницей, что вместо нулей и единиц используются более удобные для восприятия людьми мнемоники (команды). Например, ассемблерная команда BRA (branch - прыжок) транслируется в байт со значением 60 (шестнадцетеричное) в машинном коде. Код ассемблера представляется в текстовом формате и редактируется в текстовом редакторе. Машинный код имеет двоичный (бинарный) формат и для его редактирования можно использовать разве что HEX-редактор. По сравнению с языками высокого уровня, в ассемблере очень мало команд, следовательно его очень легко выучить, однако на нем довольно трудно программировать, чтобы реализовать какую-нибудь простую вещь, у вас может утйи много времени и страниц кода. Список команд, которые Вы изучите в этом гиде, даст вам мощный толчок, так что Вы вполне сможете изучить остальные команды самостоятельно, лишь узнав их назначение из справочника. Команда MOVEmove.w #$1234, $FF0000 «Move» дословно значит «перемещать». Эта команда записывает значение в заданный адрес памяти. В данном случае в адрес памяти $FF0000 будет записано число «$1234». Весь код, выполняемый процессором так или иначе связан с записью чисел в разные места памяти. На что за «.w» на после команды и адреса памяти? Это размер данных, в данном случае word. Процессор Motorola 68000 поддерживает следующие размеры команд:
Внимательный читатель может сразу спросить: а какой размер у адреса памяти, в который мы записали число из примера выше? Здесь кроется подвох. Если вы еще раз посмотрите таблицу размеров, то увидите, что инструкция MOVE с размером «.w» (или word) запишет целых 2 байта. Однако, размер одной «ячейки памяти» в компьютерах - 1 байт. Так что же произойдет? Наше значение $1234 состоит из 2 байт: $12 и $34. Первый байт будет записан по адресу $FF0000, второй — по следующему адресу $FF0001. Именно в таком порядке читаются и записываются 16-битные числа в Motorola 68000 (M68K). Такой порядок байт называется Big-endian. И напротив, есть little-endian процессоры (например, Z80), где порядок байт строго обратный (т.е. в первый адрес запишется $34, а во второй — $12). Теперь изменим размер команды с word на long: move.l #$00001234, $FF0000 Что изменилось? Теперь мы записываем не 2 байта, а целых 4! Давайте разобъем значение на байты и проследим, как оно затронет адреса памяти с новым размером:
Команды ADD и SUBЭто базовые алгебраические операции, ADD реализует сложение, SUB — вычитание. move.w #$1010, ($FF0000) add.w #2, ($FF0000) ; Сложение: $1010 + $0002 = $1012 sub.w #4, ($FF0000) ; Вычитание: $1012 - $0004 = $100E Обратите внимание на знак доллара ($) перед значением «1010» в первой строке. Он обозначает, что число записано в шеснадцатеричной системе счисления. Эту же роль он выполняет и перед адресом памяти $FF0000. В исходном коде Сеговских игр при работе с числами используется в основном именно эта система. Этот знак обычно опускают перед числами от 0 до 9, так как они в обоих системах обозначают одни и те же числа. Однако никто вам не мешает опустить знак доллара перед числом, чтобы записать его в десятичной системе. А символ «;» начинает комментарий. Весь текст на строке после этого знака игнорируется компилятором. Комментарии, однако, очень полезны, они помогают ориентироваться в коде и узнавать, что делает тот или иной его участок (если комментарии есть). Давайте попробуем поиграть с размерами команд: move.w #$1010, ($FF0000) add.b #2, ($FF0000) sub.b #4, ($FF0000) Что теперь будет записано в $FF0000 и $FF0001, и почему? Ответ: После add.b #2 в $FF0000 будет $12, в $FF0001 так и останется $10 (16-битное значение: $1210). После sub.b #4 в $FF0000 будет $0E, в $FF0001 так и останется $10 (16-битное значение: $0E10). Если у вас возникает сложность в понимании этого, перечитайте про размеры данных и как записываются числа в память на примере команды MOVE выше. Регистры данных и адресовРегистры данных (от d0 до d7) — подобно адресам памяти, содержат в себе числовые значение. Они вмещают до 32 бит или 4 байт данных (в зависимости от размера инструкции). Регистры используются чаще всего для промежуточных вычислений, многим напоминая переменные в языках высокого уровня. Некоторые команды асемблера работают исключительно с регистрами, или имеют ограничения на адресацию памяти. В отличие от внешней памяти доступ к регистрам — моментальный, соответственно команды над регистрами работают быстрее. Приведу простой пример их использования. Допустим, нам нужно взять число из одного адреса памяти, увеличить его и записать в другой: move.b ($FFFFFEB8).w,d0 ; записываем число из указанного адреса в регистр add.b #2,d0 ; увеличиваем это число на 2 move.b d0,($FFFFFEE6).w ; записываем это число в другой адрес памяти Регистры адресов (от a0 до a6) — ссылаются на адреса памяти, многим напоминая указатели в языках высокого уровня. Скажем, в коде внутриигровых объектов они ссылаются на адреса памяти, в которых и хранятся все данные об объекте. Их так же можно использовать вместо адресов, при записи в них данных, эти данные отправлются в адрес памяти, на который они указывают. Внимательный читатель может заметить, что регистры данных идут до d7 включительно, а регистры адресов заканчиваются на a6. Куда делся a7? Он здесь, но трогать его не следует: он зарезервирован под стек. Команды сравнения и переходаИменно эти команды оживляют ассемблер, они выполняют роль конструкции «if» из языков высокого уровня. Небольшой пример: Subroutine1: cmp.w #$1010,($FFFFFEB8).w ; сравнить значение в адресе $FFFFFEB8 с $1010 beq Subroutine2 ; если значения равны, переходим rts ; завершить суброутину Subroutine2: move.w ($FFFFFEB8).w,d0 add.w d0,($FFFFFEB8).w ; увеличиваем число в 2 раза rts В этом коде присутсвует немало новых вещей. Давайте все разберем. Команда «cmp» сравнивает заданное значение (в данном случае это — число $1010) с тем, что записано по адресу $FFFFFEB8. Потом в работу включается команда «beq», которая переходит к суброутине «Subroutine2», если «cmp» сказал, что значения равны. Если переход состоялся, то все что следует за «beq», выполняться не будет. «beq» — команда перехода. На них стоит остановиться подробнее, так как именно они определяют условие перехода и являются важными, частоиспользуемыми командами. Команд перехода очень много, вот самые нужные из них: BEQ — Branch if EQual — Перейти, если равно Используя команду BNE можно составить код, эквивалентный примеру выше: Subroutine1: cmp.w #$1010,($FFFFFEB8).w ; сравнить значение в адресе $FFFFFEB8 с $1010 bne Subroutine2 ; если значения не равны, переходим move.w ($FFFFFEB8).w,d0 add.w d0,($FFFFFEB8).w ; увеличиваем число в 2 раза Subroutine2: rts Результат работы кода будет таким же, но вглядитесь — он получился меньше, потому что пропала одна команда «rts». Вы, должно быть сейчас думаете: «Что за фигня?». Не бойтесь, до меня все тоже не сразу дошло. Так, давайте разберемся. Если одного «rts» нет, то что завершает Subroutine1? «Rts» из Subroutine2! Многие люди, знающие языки высокого уровня вначале принимают Subroutine1 и Subroutine2 за функции, у которых есть начало и конец. Subroutine1 и Subroutine2 — это лейбелы. Лейбелы могут содержать из латинские символы и цифры. Пробелов и цифры в начале названия не допустимы. Они не несут на код никакой смысловой нагрузки, это лишь «закладки» для навигации по коду. Так что когда выполнится команда «add.w d0,($FFFFFEB8).w», процессор перейдет к команде «rts». Команды безусловного переходаКоманды безусловного перехода выполняют переход вне зависимости от ситуации, поэтому перед ними не нужна команда «cmp». Их немного: BRA/JMP, BSR/JSR. Мнемоники BRA/BSR образованы от слова «Branch», а JMP/JSR — от «Jump». И BRA, и JMP делают одно и то же: прыгают в заданное место кода. Аналогично, BSR и JSR тоже идентичны: переходят в заданное место, но когда в той ветке кода встречается команда «rts», они возвращаются обратно и продолжают выполнять код после себя. У читателя может возникнуть вопрос: почему BRA и JMP разные команды, если они делают фактически одно и то же? Аналогично с BSR и JSR. Команды «Branch» (BRA, BSR и другие) имеют ограничение на «дальность» прыжка. Они чаще используются для «локальных» переходов — в место программы, не слишком далекое от самой инструкции перехода. Команды «Jump» (JMP и JSR) поддерживают разные режимы адресации и, в общем-то, имеют власть прыгнуть в абсолютно любое место кода, неважно насколько далекое. Разумеется, у «далеких» прыжков есть цена — инструкция занимает больше байт и выполняется чуть медленее. Некоторые нюансыПоздравьте себя, у вас есть базовые знания ассемблера! Отдохните, почитайте исходник Сониковских игр, потренируйтесь и переварите всё вышепрочитанное. Тогда вы поймете многие вещи и дальнейшее изучание ассемблера будет легким и приятным. Пока можете расслабиться и принять к сведению некоторые нюансы: Существуют разновидности некоторых знакомых вам комманд. Скажем, у MOVE есть разновидность MOVEQ, у команд CMP, ADD и SUB разновидностей еще больше: ADDI, ADDQ, SUBI, SUBQ, CMPI— только основные из них. Когда увидите такие команды, не пугайтесь — эти команды выполняют тоже самое. Скажем, приставки -Q и -I означают «Quick» и «Immediate» соответственно. MOVEQ, ADDQ и SUBQ работают быстрее, но имеют ограничения на размер чисел. Так же помните, что многие адреса памяти заняты и жизненно важны для правильной работы игры. Так что если вам нужен новый адрес, куда вы хотите записывать свои данные, потрать время на поиск свободного. Занятые адреса и их назначение указано в справочниках. Вот, скажем таблица адресов для первого Соника: RAM-адреса. Все, гид окончен, можете свободно вздохнуть. |
Как добавить Spin DashАвтор гида: vladikcomperОсновано на гиде «SCHG How-to:Add Spin Dash to Sonic 1/Part 1» Spin Dash — самая известная способность Соника, которая дебютировала в Sonic 2. В Первом Сонике этой способности не было, но ходят слухи, что подобие Spin Dash все-таки было, так как в РОМе найдены неиспользованные спрайты Соника, занимающегося Спин Дэшем. Однако правда до сих пор неизвестна, а спрайты могли использоваться для других целей. Движок Sonic the Hedgehog 2 во многом сходен с движком Sonic 1, так что можно портировать Спин Дэш из Sonic 2, немного адаптировав его код для работы на другом движке. Все эти адаптации уже сделаны для Вас в этом гиде, вы даже можете проверить сходства предоставленых кодов, откопав суброутину Спин Дэша в коде Второго Соника. Вставка основы Спин ДэшаНайдите в исходном коде лейбел Obj01_MdNormal и добавьте прямо после него строчку: bsr.w Sonic_SpinDash ; ++ branch to Spin Dash Теперь каждый раз, когда ежик стоит, будет запукаться суброутина Спин Дэша, которая в свою очередь будет проверять, можно ли выполнять Спин Дэш. Настало время вставлять саму суброутину Sonic_SpinDash. Вставьте ее после суброутины Sonic_JumpHeight. Найдите конец суброутины, «; End of function Sonic_JumpHeight» и вставьте после него код: ; --------------------------------------------------------------------------- ; Subroutine allowing Sonic to do Spin Dash ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Sonic_SpinDash: tst.b $39(a0) ; is Spin Dash flag set? bne.s SpinDash_Process ; if yes, branch cmpi.b #8,$1C(a0) ; is animation Duck? bne.s Sonic_SpinDash_Rts ; if not, branch move.b ($FFFFF603).w,d0 andi.b #$70,d0 ; is A/B/C pressed? beq.w Sonic_SpinDash_Rts ; if not, branch move.b #2,$1C(a0) ; use Spin Dashing animation move.w #$BE,d0 jsr (PlaySound_Special).l addq.l #4,sp ; avoid Sonic_Jump call move.b #1,$39(a0) ; set Spin Dash flag move.w #0,$3A(a0) bsr.w Sonic_LevelBound bsr.w Sonic_AnglePos Sonic_SpinDash_Rts: rts ; --------------------------------------------------------------------------- SpinDash_Process: move.b ($FFFFF602).w,d0 btst #1,d0 ; is Down pressed? bne.w SpinDash_Charge ; if yes, branch SpinDash_Release: move.b #$E,$16(a0) ; decrease Sonic's height move.b #7,$17(a0) move.b #2,$1C(a0) ; use rolling animation addq.w #5,$C(a0) ; add 5 to Y coordinate move.b #0,$39(a0) ; clear Spin Dash flag moveq #0,d0 move.b $3A(a0),d0 add.w d0,d0 move.w DashSpeeds(pc,d0.w),$14(a0) ; get Spin Dash speed btst #0,$22(a0) ; is Sonic facing right? beq.s SpinDash_ReleaseSound ; if yes, branch neg.w $14(a0) ; negate inertia SpinDash_ReleaseSound: bset #2,$22(a0) move.w #$BC,d0 jsr (PlaySound_Special).l bra.s loc_1AD78 ; --------------------------------------------------------------------------- DashSpeeds: dc.w $800 ; 0 dc.w $880 ; 1 dc.w $900 ; 2 dc.w $980 ; 3 dc.w $A00 ; 4 dc.w $A80 ; 5 dc.w $B00 ; 6 dc.w $B80 ; 7 dc.w $C00 ; 8 ; --------------------------------------------------------------------------- SpinDash_Charge: tst.w $3A(a0) ; test charge counter beq.s loc_1AD48 ; if it's zero, branch move.w $3A(a0),d0 ; move it to d0 lsr.w #5,d0 ; divide by 32 sub.w d0,$3A(a0) bcc.s loc_1AD48 move.w #0,$3A(a0) loc_1AD48: move.b ($FFFFF603).w,d0 andi.b #$70,d0 ; is A/B/C pressed? beq.w loc_1AD78 ; if not, branch ;move.w #$1F00,$1C(a0) move.w #$BE,d0 jsr (PlaySound_Special).l addi.w #$200,$3A(a0) cmpi.w #$800,$3A(a0) bcs.s loc_1AD78 move.w #$800,$3A(a0) loc_1AD78: addq.l #4,sp ; avoid Sonic_Jump call cmpi.w #$60,($FFFFEED8).w beq.s loc_1AD8C bcc.s loc_1AD88 addq.w #4,($FFFFEED8).w loc_1AD88: subq.w #2,($FFFFEED8).w loc_1AD8C: bsr.w Sonic_LevelBound bsr.w Sonic_AnglePos move.w #$60,($FFFFF73E).w ; reset looking up/down rts ; End of subroutine Sonic_SpinDash Отлично! Теперь у нас есть подобие Спин Дэша, с виду похожее на Спин Дэш из Sonic CD. Исправление баговКамера не всегда может успевать прокручиваться вниз за Соником, особенно при прохождении двойных S-образных труб в GHZ. К тому же у камеры в Первом Сонике есть врожденный баг. Он заключается в том, что если Соник зайдет ниже своей высоты за нижнюю границу камеры, игра посчитает что он свалился в пропасть и убьет его. Чтобы исправить этот баг, идите к суброутине Boundary_Bottom и замените ее на это: Boundary_Bottom: move.w ($FFFFF726).w,d0 move.w ($FFFFF72E).w,d1 cmp.w d0,d1 ; screen still scrolling down? blt.s Boundary_Bottom_locret ; if so, don't kill Sonic cmpi.w #$501,($FFFFFE10).w ; is level SBZ2 ? bne.w KillSonic ; if not, kill Sonic cmpi.w #$2000,($FFFFD008).w bcs.w KillSonic clr.b ($FFFFFE30).w ; clear lamppost counter move.w #1,($FFFFFE02).w ; restart the level move.w #$103,($FFFFFE10).w ; set level to SBZ3 (LZ4) Boundary_Bottom_locret: rts Еще один заметный баг, который вскроется после исправления Spike Bug (смотрите гид «Как исправить Spike Bug») — отказ Соника прекращать Спин Дэш, после того как он накололся на иголках (Spikes). Для исправления бага, идите к лейбелу Hurt_ChkSpikes и вставьте прямо за ним строчку: move.b #0,$39(a0) ; clear Spin Dash flag Следующий баг, который предстоит исправить, заключается в том, что враг может спокойно повредить Соника, когда он совершает разгон на месте. Чтобы его исправить, идите к суброутине Touch_Enemy и перед строчкой «cmpi.b #2,$1C(a0) ; is Sonic rolling?» вставьте проверку на Спин Дэш: cmpi.b #$1F,$1C(a0) ; is Sonic Spin Dashing? beq.w loc_1AF40 ; if yes, branch Постройте РОМ и убедитесь, что эти баги исправлены. Вставка анимации Спин ДэшаЧтобы Спин Дэш смотрелся как надо, сделать ему нормальную анимацию, чем мы сейчас и займемся. Для начала, нужно портировать арт Спин Дэша из Sonic 2. Это я уже за Вас сделал, скачайте этот архив: http://vladikcomper.scanf.su/dump/download/spindash.rar. Достаньте из него spindash.bin и поместите этот файл в папку artunc. Теперь в исходном коде игры найдите лейбел Art_Sonic и после строчки «incbin artunc\sonic.bin» вставьте: incbin artunc\spindash.bin ; Spin Dash art Арт Соника, особенный, так как каждый раз динамически подгружается нужный спрайту набор тайлов. Новый арт требуется вписать в список динамически загружаемых тайлов для дайльнешей работы с ним. Откройте файл _inc/Sonic dynamic pattern load cues.asm и вставьте эти строки перед лейбелом SonPLC_Blank: dc.w SonPLC_SpinDash1-SonicDynPLC ;58 dc.w SonPLC_SpinDash2-SonicDynPLC ;59 dc.w SonPLC_SpinDash3-SonicDynPLC ;5A dc.w SonPLC_SpinDash4-SonicDynPLC ;5B dc.w SonPLC_SpinDash5-SonicDynPLC ;5C dc.w SonPLC_SpinDash6-SonicDynPLC ;5D Эти номерки справа от строк стоят для удобства. Они обозначают номер кадра, который полезно знать для создания покадровой анимации. Мы только что сделали ссылки на лейбелы, в которых содержится информация о подгружаемых тайлах, теперь нужно вставить сами лейбелы. Почти в самом конце файла, перед строкой «even» вставьте нужные лейбелы: SonPLC_SpinDash1: dc.b 1, $F5, $10 ; 01 F 510 SonPLC_SpinDash2: dc.b 1, $F5, $20 ; 01 F 520 SonPLC_SpinDash3: dc.b 1, $F5, $30 ; 01 F 530 SonPLC_SpinDash4: dc.b 1, $F5, $40 ; 01 F 540 SonPLC_SpinDash5: dc.b 1, $F5, $50 ; 01 F 550 SonPLC_SpinDash6: dc.b 1, $F5, $60 ; 01 F 560 В комментариях справа от строк дано более удобное представление информации. Данные о подгружаемых тайлах имеют формат NN(STTT), где N - количество следуемых за ним данных о тайлах STTT, S - количество погружаемых тайлов (от 1 до 16), T - номер тайла, с которого начинается подгрузка. Вся эта динамическая подгрузка нужна для экономии VRAM (видеопамяти). Ее всего 64 Кб, если загрузить туда весь Сониковский арт, а он весит 40 Кб, то не хватит места на остальное. Но вернемся к делу. Теперь нам нужно слепить спрайты из блоков 8х8, предоставленных в арте. Откройте файл _maps/Sonic.asm и над строчкой «byte_21292: dc.b 0» вставьте: dc.w byte_spdh1-Map_Sonic, byte_spdh2-Map_Sonic dc.w byte_spdh3-Map_Sonic, byte_spdh4-Map_Sonic dc.w byte_spdh5-Map_Sonic, byte_spdh6-Map_Sonic Теперь надо прописать вышеперечисленные лейбелы. Опять, прописывать будем почти в конце файла, перед строкой «even»: byte_spdh1: dc.b 1 ; Spin Dash 1 dc.b $F8, $F, 0, 0, $F0 byte_spdh2: dc.b 1 ; Spin Dash 2 dc.b $F8, $F, 0, 0, $F0 byte_spdh3: dc.b 1 ; Spin Dash 3 dc.b $F8, $F, 0, 0, $F0 byte_spdh4: dc.b 1 ; Spin Dash 4 dc.b $F8, $F, 0, 0, $F0 byte_spdh5: dc.b 1 ; Spin Dash 5 dc.b $F8, $F, 0, 0, $F0 byte_spdh6: dc.b 1 ; Spin Dash 6 dc.b $F8, $F, 0, 0, $F0 Спрайты готовы. То, что мы добавили, называется Спрайтовыми Маппингами (Sprite Mappings), об их устройстве вы можете посмотреть в справочниках. Последнее, что осталось сделать — скрепить все спрайты анимацией, которая будет воспроизводится. Откройте _anim/Sonic.asm и перед строкой «SonAni_Walk: dc.b $FF, 8, 9, $A, $B, 6, 7, $FF» вставьте: dc.w SonAni_SpinDash-SonicAniData ;1F Справа записан порядковый номер анимации для справки. Теперь перед строкой «even» в конце файла вставьте: SonAni_SpinDash: dc.b 0, $58, $59, $58, $5A, $58, $5B, $58, $5C, $58, $5D, $FF В этой строке перечислены по порядку номера кадров, включенные в анимацию. И нам осталось почти ничего, всего лишь отредактировать код Спин Дэша, чтобы он использовал новую анимацию. Идите к суброутине Sonic_SpinDash и замените «move.b #2,$1C(a0)» на: move.b #$1F,$1C(a0) ; changed from #2 Теперь идите к лейбелу loc_1AD48 и раскомментируйте строчку «move.w #$1F00,$1C(a0)» Постройте РОМ, все должно заработать. |
Как добавить Jump DashАвтор гида: vladikcomperВторая версия Я все-таки решился раскрыть свой код Jump Dash'а. Не бойтесь, вставить Jump Dash намного легче, чем Spin Dash. Идите к суброутине Obj01_MdJump2 и добавьте после «bsr.w Sonic_LevelBound» строчку «bsr.w Sonic_JumpDash», получится вот так: Obj01_MdJump2: ; XREF: Obj01_Modes bsr.w Sonic_JumpHeight bsr.w Sonic_ChgJumpDir bsr.w Sonic_LevelBound bsr.w Sonic_JumpDash ; ++ branch to JumpDash subroutine jsr ObjectFall btst #6,$22(a0) beq.s loc_12E5C subi.w #$28,$12(a0) Сделайте то же самое в суброутине Obj01_MdJump2 Теперь напишем саму суброутину Sonic_JumpDash. Я лично вставил ее после Sonic_JumpHeight. Для этого найдите строчку «; End of function Sonic_JumpHeight» — это конец суброутины. Сразу после нее вставьте новый код: ; --------------------------------------------------------------------------- ; Subroutine to do Jump Dash (code by Vladikcomper) ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Sonic_JumpDash: cmpi.b #2,$1C(a0) ; is Sonic rolling? bne.s Sonic_JD_Rts ; if no, branch btst #7,$22(a0) ; was Jump Dash flag set? bne.s Sonic_JD_Rts ; if yes, branch move.b ($FFFFF603).w,d0 andi.b #$70,d0 ; is A,B or C pressed? beq.w Sonic_JD_Rts ; if no, branch bset #7,$22(a0) ; set Jump Dash flag move.w #$BC,d0 ; play JD sound jsr (PlaySound_Special).l ; move.w #$900,$10(a0) ; set Jump Dash speed move.w #0,$12(a0) ; clear Y-velocity btst #6,$22(a0) ; is Sonic underwater? beq.s Sonic_JD_ChkShoes sub.w #$300,$10(a0) ; set speed to $600 Sonic_JD_ChkShoes: tst.b ($FFFFFE2E).w ; does Sonic have speed shoes? beq.s Sonic_JD_ChkOrientation ; if not, branch add.w #$200,$10(a0) ; set speed to $B00 or $500 if underwater Sonic_JD_ChkOrientation: btst #0,$22(a0) beq.s Sonic_JD_Rts neg.w $10(a0) Sonic_JD_Rts: rts ; End of function Sonic_JumpDash Постройте РОМ, опробуйте новую способность. Новый Jump Dash срабатывает только один раз, после чего отказывается работать. Дело в том, что он использует флаг, который устанавлиется после рывка, чтобы предотвратить повторный Jump Dash в воздухе. Флаг этот располагается по адресу $22(a0), в восьмом бите. Если бит равен единице, то повторно Jump Dash не произойдет. Но флаг нужно сбрасывать при приземлении, чтобы дать Сонику возможность снова совершать Jump Dash. Для исправления бага, идите к лейбелу Sonic_ResetOnFloor и добавьте прямо после него строчку: bclr #7,$22(a0)
Отлично! Теперь у вас есть полноценный Jump Dash. Однако осталась одна небольшая недоработка, связанная с работой движка Первого Соника: в игре есть скоростной барьер (Speed Cap), который сильно уменьшает скорость Jump Dash'а. Для того, чтобы это исправить, опробуйте гид «Как убрать Speed Cap» |
Как исправить Spike BugАвтор гида: FraGag, Перевод на русский язык: vladikcomperОригинальный гид: «SCHG How-to:Change Spike behavior in Sonic 1» Spike Bug в первом Сонике заключается в том, что игра не проверяет соника на неуязвимость после повреждения (когда спрайт Соника моргает). Из-за этого после попадания на спики, Вас ждет верная смерть (если соника, конечно, не отбросит с их поля), тогда соник потеряет защитную сферу (shield), кольца, а потом жизнь. В последующих играх серии этот баг был исправлен. Убрать Spike Bug очень просто. Откройте исходный код игры и найдите в нем суброутину Obj36_Hurt. Вот начало ее первоначального кода: Obj36_Hurt: ; XREF: Obj36_SideWays; Obj36_Upright tst.b ($FFFFFE2D).w ; is Sonic invincible? bne.s Obj36_Display ; if yes, branch move.l a0,-(sp) ... Чтобы исправить баг, нам нужно добавить всего две строчки: Obj36_Hurt: ; XREF: Obj36_SideWays; Obj36_Upright tst.b ($FFFFFE2D).w ; is Sonic invincible? bne.s Obj36_Display ; if yes, branch tst.w ($FFFFD030).w ; +++ is Sonic invulnerable? bne.s Obj36_Display ; +++ if yes, branch move.l a0,-(sp) ... Теперь когда Соник касается спик, игра будет проверять, не уязвим ли Соник после повреждения (когда спрайт Соника моргает). |
S1 Hacking StudioТекст скоро будет. |
Как исправить Walk-Jump BugАвтор гида: Cinossu, Перевод на русский язык: vladikcomperОригинальный гид: «SCHG How-to:Fix the Walk-Jump Bug in Sonic 1» В первом Сонике есть замечательный баг, когда Соник прыгает с анимацией ходьбы. Он часто происходит в MZ и LZ и из-за него мы теряем наши драгоценные кольца. Но вы можете остановить это безобразие. Для этого нам придется несколько подправить главного виновника бага — суброутину SolidObject. Найдите лейбел loc_FB92, который к ней относится. Его код будет выглядеть как-то так: loc_FB92: btst #5,$22(a0) beq.s loc_FBAC move.w #1,$1C(a1) ; use walking animation loc_FBA0: bclr #5,$22(a0) Замените код выше на это: loc_FB92: btst #5,$22(a0) beq.s loc_FBAC cmp.b #2,$1C(a1) ; check if in jumping/rolling animation beq.s loc_FBA0 move.w #1,$1C(a1) ; use walking animation loc_FBA0: bclr #5,$22(a0) Вот и все! Баг исправлен. Скомпилируйте ваш РОМ и убедитесь в этом. |
Как добавить Water SkimmingАвтор гида: vladikcomperWater Skimming — это способность Соника прыгать по поверхности воды, будучи свернутым в клубочек, если он развил достаточную скорость. Такая приятная способность была добавлена в восьмибитный Sonic 2, в Sonic 3 была похожая способность, но она заключалась в беге по воде. А добавить такую способность довольно легко. Найдите суброутину Obj_MdJump2, она должа выглядеть примерно так: Obj01_MdJump2: ; XREF: Obj01_Modes bsr.w Sonic_JumpHeight bsr.w Sonic_ChgJumpDir bsr.w Sonic_LevelBound jsr ObjectFall btst #6,$22(a0) beq.s loc_12EA6 subi.w #$28,$12(a0) Дополните ее следующим кодом: move.w $10(a0),d0 cmp.w #0,d0 bgt.s Obj01_MdJump2_Abs neg.w d0 Obj01_MdJump2_Abs: cmp.w #$250,d0 ; if Sonic speed less than $250? blt.s loc_12EA6 ; if yes, branch move.w $C(a0),d0 sub.w ($FFFFF646).w,d0 cmp.w #$F,d0 bgt.s loc_12EA6 subi.w #$90,$12(a0) ; jump out of water Теперь скомпилируйте РОМ и посмотрите новую способность в действии. Самое удачное место для прыжков по воде — это огромная открытая местность в LZ1, почти в самом начале уровня. С помощью прыжков по поверхности воды вы сможете добраться до противоположной стороны и пройти уровень коротким путем. Раньше это сделать было крайне трудно. Только чтобы заставить соника прыгать по воде, надо, разогнавшись, свернуться в клубочек у самого обрыва перед водной поверхностью. Обычным прыжком в воду вы ничего не добьетесь. |
Как добавить Homing AttackАвтор гида: vladikcomperХоминг Аттака — это, пожалуй, самая классная вещь в Сониковских играх. Sonic Megamix в свое время совершил настоящий прорыв, включив в игру эту аттаку. После чего многие хотели вставить Хомминг в свои хаки, но из-за трудности задачи и сложности ее реализации, многим это не удавалось. Мне после долгой работы удалось написать этот код, и я готов делиться им с сильными хаками. * Не, вы этот код так просто не получите! * С тех пор, как некоторые умельцы смогли откопать этот гид, я решил перенести его в более безопасное место. Для кого же тогда этот гид и почему он скрыт? Этот гид для людей, хорошо разбирающихся в АСМе и знающих истинную цену подобным вещам. Для тех, кто вложил услия в то, чтобы сделать свою Homing Attack'у или показал себя сильным в другом аспекте АСМ-хакинга. |
Как добавить Homing Attack — 2Автор гида: vladikcomperХоминг Аттака, исходный код которой дан в первой части гида, содержит немалое количество багов, которые необходимо исправить. Вторая часть в основном посвящена исправлению багов в Хоминг Аттаке. * Не, вы этот код так просто не получите! * С тех пор, как некоторые умельцы смогли откопать этот гид, я решил перенести его в более безопасное место. Для кого же тогда этот гид и почему он скрыт? Этот гид для людей, хорошо разбирающихся в АСМе и знающих истинную цену подобным вещам. Для тех, кто вложил услия в то, чтобы сделать свою Homing Attack'у или показал себя сильным в другом аспекте АСМ-хакинга. |
Как добавить Jump DashАвтор гида: vladikcomperJump Dash — способность Соника совершать мнгновенный рывок вперед, находясь в воздухе в свернутом в клубок состоянии. В этом гиде я расскажу о том, как добавить полноценный Jump Dash с исправлением всех багов. Обратите внимание на то, что код в гиде написан под дизасембл Второго Соника от Xenowhirl, 2007 года. В исходном коде игры найдите суброутину Obj01_MdJump и добавьте над «bsr.w Sonic_JumpAngle» строчку: bsr.w Sonic_JumpDash ;++ branch to Jump Dash subroutine Теперь нужно вставить саму суброутину, отвечающую за Jump Dash. Я лично вставил ее после функции Sonic_Jump. Найдите конец функции, строчку «; End of function Sonic_Jump» и после нее вставьте новую суброутину: ; --------------------------------------------------------------------------- ; Subroutine to do Jump Dash (code by Vladikcomper) ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Sonic_JumpDash: cmpi.b #2,anim(a0) ; is Sonic rolling? bne.s Sonic_JumpDash_Rts ; if no, branch btst #7,status(a0) ; was Jump Dash flag set? bne.s Sonic_JumpDash_Rts ; if yes, branch move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 ; is A,B or C pressed? beq.w Sonic_JumpDash_Rts ; if no, branch move.w #$BC,d0 ; play sound jsr (PlaySound).l bset #7,status(a0) ; set Jump Dash flag move.w #$900,x_vel(a0) ; set Jump Dash speed move.w #0,y_vel(a0) ; clear Y-velocity btst #0,status(a0) ; is sonic facing left? beq.s Sonic_JumpDash_Rts ; if no, branch neg.w x_vel(a0) ; negate X-velocity Sonic_JumpDash_Rts: rts ; End of function Sonic_JumpDash Постройте РОМ, опробуйте новую способность. Новый Jump Dash срабатывает только один раз, после чего отказывается работать. Дело в том, что он использует флаг, который устанавлиется после рывка, чтобы предотвратить повторный Jump Dash в воздухе. Флаг этот располагается по адрессу status(a0), в восьмом бите. Если бит равен единице, то повторно Jump Dash не произойдет. Но флаг нужно сбрасывать при приземлении, чтобы дать Сонику возможность снова совершать Jump Dash. Для исправления бага, идите к лейбелу Sonic_ResetOnFloor и добавьте прямо после него строчку: bclr #7,status(a0)
Теперь при приземлении флаг Jump Dash будет очищаться и Соник снова сможет совершать Jump Dash. Постройте РОМ, если все сделано правильно, Jump Dash примет нормальный вид. Однако останется еще один баг: при приземлении на объекты (мостик, платформ и пр.) игра не будет сбрасывать флаг Jump Dash, так как при призимлении на них суброутина Sonic_ResetOnFloor не запускается. Когда Соник становится на объект, четвертый бит байта status(a0) устанавливается как единица. Нужно добавить на это проверку. Идите к суброутине Obj01_Control и над строчкой «tst.b (Control_Locked).w ; are controls locked?» вставьте этот кусок кода: + btst #3,status(a0) ; is Sonic standing on an object? beq.s + ; if no, branch bclr #7,status(a0) ; clear Jump Dash flag Последний баг, который необходимо исправить, это баг со скоростью Jump Dash. Попробуйте совершить Jump Dash зажав кнопку Влево или Вправо, а затем без зажатия этих кнопок. Скорость рывка будет разной. Это из-за скоростного барьера в воздухе (Air Speed Cap), чтобы убрать его, воспользуйтесь гидом «Как убрать Air Speed Cap» Постройте РОМ. Теперь у Вас есть полноценный безглючный Jump Dash. |
Как исправить звук «SEGA»Автор гида: Puto, Перевод на русский язык: vladikcomperОригинальный гид: «SCHG How-to:Fix the SEGA Sound» После редактирования исходного кода Первого Соника, звук «SEGA» становится настоящей головной болью из-за посторонних шумов. Чтобы исправить это и вернуть нормальное звучание звуку, найдите лейбел Sound_E1, он выглядит так: Sound_E1: ; XREF: Sound_ExIndex move.b #$88,($A01FFF).l move.w #0,($A11100).l ; start the Z80 move.w #$11,d1 loc_71FC0: move.w #-1,d0 loc_71FC4: nop dbf d0,loc_71FC4 dbf d1,loc_71FC0 addq.w #4,sp rts Замените его на это: Sound_E1: lea (SegaPCM).l,a2 move.l #$6978,d3 move.b #$2A,($A04000).l PlayPCM_Loop: move.b (a2)+,($A04001).l move.w #$14,d0 dbf d0,* sub.l #1,d3 beq.s return_PlayPCM lea ($FFFFF604).w,a0 lea ($A10003).l,a1 jsr (Joypad_Read).w btst #7,($FFFFF604).w ; Check for Start button bne.s return_PlayPCM ; If start is pressed bra.s PlayPCM_Loop ; Otherwise, continue playing return_PlayPCM: addq.w #4,sp rts |
Как исправить баг деформации EHZАвтор гида: qiuu, Перевод на русский язык: vladikcomperОригинальный гид: «SCHG How-to:Fix the EHZ Deformation bug» В Sonic 2 есть небольшой баг в EHZ, когда два нижних слоя неподвижны, потому что на них не действует код деформации. Чтобы исправить это, нам потребуется исправить часть кода в роутине SwScrl_EHZ. Найдите лейбел SwScrl_RippleData, который является частью этой роутины. Прямо над ним вы увидите вот такой кусок кода: move.w #($B4)/12-1,d1 ; $B4 bytes - move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ swap d3 add.l d0,d3 add.l d0,d3 add.l d0,d3 swap d3 dbf d1,- rts Замените его на это: move.w #($B4)/12-1,d1 ; $B4 bytes - move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ move.w d4,(a1)+ move.w d3,(a1)+ swap d3 add.l d0,d3 add.l d0,d3 add.l d0,d3 swap d3 dbf d1,- rts Как видите, здесь мы лишь дважды добавили в середину кода эти строки: move.w d4,(a1)+ move.w d3,(a1)+ Они зайдествовали два нижних слоя при деформации, баг успешно исправлен. |
Как убрать Air Speed CapАвтор гида: vladikcomperОсновано на гиде: «SCHG How-to:Remove the Speed Cap» Speed Cap, или скоростной барьер, строго ограничивает скорость передвижения персонажа, чтобы она никогда не превышала максимально положенную и, естественно, не дает персонажу разгоняться дальше. В Соник 2 скоростной барьер был убран при передвижении по земле — и это серьезно улучшило геймплей: теперь, когда Соник/Тейлз ускорились вертикальной красной пружиной, их скорость не урезается, как то было в Соник 1, они мчатся на скорости, которую не смогли бы достигнуть самостоятельно, сохраняя ее и не разгоняясь. Однако, создатели Соник 2 забыли убрать Speed Cap в воздухе, поэтому в Соник 2 присутствует Air Speed Cap, который портит весь скоростной драйв, если разогнавшийся Соник вдруг оторвался от земли. Чтобы исправить это упущение разработчиков, идите к суброутине Sonic_ChgJumpDir, и над строчкой move.w d1,d0 ; limit speed in air going left, even if Sonic was already going faster (speed limit/cap) добавьте: add.w d5,d0 ; remove this frame's acceleration change cmp.w d1,d0 ; compare speed with top speed ble.s + ; if speed was already greater than the maximum, branch Чуть ниже найдите команду move.w d6,d0 ; limit speed in air going right, even if Sonic was already going faster (speed limit/cap) и прямо перед ней, добавьте следующий код: sub.w d5,d0 ; remove this frame's acceleration change cmp.w d1,d0 ; compare speed with top speed bge.s + ; if speed was already greater than the maximum, branch Отлично, теперь у Соника Speed Cap убран, так что он свободно может сохранять высокие скорости в воздухе. Теперь нужно убрать Air Speed Cap у Тейлза, для этого идите к суброутине Tails_ChgJumpDir и проделайте те же самые изменения. |
Как убрать Speed CapАвтор гида: vladikcomperОсновано на гиде: «SCHG How-to:Remove the Speed Cap» Speed Cap, или скоростной барьер, механизм который жестко ограничивает скорость Соника до заданного лимита при попытке разогнаться. Этот жесткий барьер особенно ощущается, когда Соник ускорился с помощью вертикальной пружны — стоит вам нажать кнопку Влево/вправо, Соник попытается ускорится, и Speed Cap тут же сработает, сурово урезав скорость ежика. Начиная с Соник 2, этот эффект был исправлен, и вместо урезания скорости код просто не позволял Сонику разгонятся дальше, а достигнутая скорость сохранялась. Удаление скоростного барьера на землеВначале позаботимся об удалении Speed Cap'а при перемещении на земле, это самый важный и заметный момент в исправлении неприятного эффекта барьера. Идите к суброутине Sonic_MoveLeft. В ней, над лейбелом loc_130A6 вы увидите команду: move.w d1,d0 Прямо перед ней добавьте эти строки: add.w d5,d0 cmp.w d1,d0 ble.s loc_130A6 Скоростной барьер при движении влево теперь убран! Теперь перейдите к суброутине Sonic_MoveRight и над лейбелом loc_1310C найдите команду: move.w d6,d0 Прямо перед ней добавьте эти строки: add.w d5,d0 cmp.w d6,d0 bge.s loc_1310C Теперь скоростной барьер на земле полностью убран. Удаление скоростного барьера в воздухеДействие скоростного барьера в воздухе почти незаметно, пока в вашем хаке не появился Jump Dash, который задает Сонику скорости, которых он при обычном беге достигнуть не может. Ограничение в воздухе впервые было исправлено только в Соник 3. Метод удаления барьера точно такой же. Идите к суброутине Sonic_ChgJumpDir и перед командой «move.w d1,d0» добавьте: add.w d5,d0 ; +++ remove this frame's acceleration change cmp.w d1,d0 ; +++ compare speed with top speed ble.s loc_13278 ; +++ if speed was already greater than the maximum, branch Опуститесь чуть ниже, пока не увидите команду «move.w d6,d0». Прямо над ней, добавьте: sub.w d5,d0 ; +++ remove this frame's acceleration change cmp.w d1,d0 ; +++ compare speed with top speed bge.s Obj01_JumpMove ; +++ if speed was already greater than the maximum, branch Вот так в конечном счете будет выглядет суброутина после внесения изменений: Sonic_ChgJumpDir: ; XREF: Obj01_MdJump; Obj01_MdJump2 move.w ($FFFFF760).w,d6 move.w ($FFFFF762).w,d5 asl.w #1,d5 btst #4,$22(a0) bne.s Obj01_ResetScr2 move.w $10(a0),d0 btst #2,($FFFFF602).w; is left being pressed? beq.s loc_13278; if not, branch bset #0,$22(a0) sub.w d5,d0 move.w d6,d1 neg.w d1 cmp.w d1,d0 bgt.s loc_13278 add.w d5,d0 ; +++ remove this frame's acceleration change cmp.w d1,d0 ; +++ compare speed with top speed ble.s loc_13278 ; +++ if speed was already greater than the maximum, branch move.w d1,d0 loc_13278: btst #3,($FFFFF602).w; is right being pressed? beq.s Obj01_JumpMove; if not, branch bclr #0,$22(a0) add.w d5,d0 cmp.w d6,d0 blt.s Obj01_JumpMove sub.w d5,d0 ; +++ remove this frame's acceleration change cmp.w d1,d0 ; +++ compare speed with top speed bge.s Obj01_JumpMove ; +++ if speed was already greater than the maximum, branch move.w d6,d0 |
Как исправить баг с загрузкой Pattern Load CuesАвтор гида: FraGag, Перевод на русский язык и модификация: vladikcomperОригинальный гид: «SCHG How-to:Fix a race condition with Pattern Load Cues» В Соник 1 и Соник 2 на первом этапе загрузки Pattern Load Cues, или сокращенно PLC, в редких случаях может произойти баг, приводящий к падению игры. Баг можно заметить в Соник 1 на уровнях LZ1 и LZ2: если вода находится на определенном уровне относительно начала экрана, игра зависает про попытке загрузить арт для экрана 'SONIC HAS PASSED' (см. видео) В Соник 2 создать условия для возникновения бага почти невозможно. В Соник 3 и Соник и Наклз баг был исправлен как показано ниже. Испраление багаШаги, расказанные ниже, действуют как для кода Соник 1 (Hivebrain's 2005 disassembly / S1HS2 disassembly), так и для Соник 2 (Xenowhirl's Disassembly). Код, с которыми вы будете работать, в обоих играх абсолютно одинаков, будут только несколько визуальных отличий между дизасемблами. Представленный ниже код взять из Соник 1, дизасембл S1HS2. Найдите процедуру "RunPLC_RAM", вы увидите следующий код: ; --------------------------------------------------------------------------- ; Subroutine to use graphics in PLC queue ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| RunPLC_RAM: ; XREF: Pal_FadeTo tst.l ($FFFFF680).w ; is PLC queue empty? beq.s RunPLC_Rts ; if yes, branch tst.w ($FFFFF6F8).w ; is decompression in progress? bne.s RunPLC_Rts ; if yes, branch movea.l ($FFFFF680).w,a0 ; load PLC queue lea (loc_1502).l,a3 lea ($FFFFAA00).w,a1 move.w (a0)+,d2 bpl.s loc_160E adda.w #$A,a3 loc_160E: andi.w #$7FFF,d2 move.w d2,($FFFFF6F8).w bsr.w NemDec4 move.b (a0)+,d5 asl.w #8,d5 move.b (a0)+,d5 moveq #$10,d6 moveq #0,d0 move.l a0,($FFFFF680).w move.l a3,($FFFFF6E0).w move.l d0,($FFFFF6E4).w move.l d0,($FFFFF6E8).w move.l d0,($FFFFF6EC).w move.l d5,($FFFFF6F0).w move.l d6,($FFFFF6F4).w RunPLC_Rts: rts ; End of function RunPLC_RAM Переместите строчку move.w d2,($FFFFF6F8).w на новое место, прямо за этой строкой: move.l d6,($FFFFF6F4).w Причины багаPattern Load Cues - это механизм, разделяющий загрузку сжатых алгоритмом Nemesis тайлов на несколько кадров, чтобы игра могла синхронно (не зависая) работать наряду декомпрессором тайлов, так как этот процесс отнимает очень много времени. В начале PLC запроса, некоторые тайлы разжимаются на месте, затем процедура настраивает значения в памяти, чтобы другая часть декомпрессора смогла продолжить декомпрессию тайлов. Тайлы разжимаются небольшими порциями во время вертикального прерывания (VBlank), этот процесс растягивается на несколько кадров, и благодаря этому остальные части игры также могут функционировать. Баг происходит из-за того, что одно из значений ($FFF6F8, флаг PLC) задается до декомпресии первых тайлов, в то время как остальные задаются после. И если вертикальные или горизонатальные прерывания (VBlank, HBlank) случаются во время начальной декомпресии, процедуры в коде VBlank'а (sub_1642 и sub_165E) попытаются "подхватить" (продолжить) декомпрессию, потому что флаг $FFF6F8 активен, однако остальные значения в этот момент не инициализированы. В частности, адрес $FFF6E0, который ссылается одну из процедур декомпрессора, будет ссылаться на неверное место. Это и проводит к падению игры с ошибкой ILLEGAL INSTRUCTION. Чтобы исправить проблему, флаг $FFF6F8 должен быть задан после того, как все значения инициализированы. |
Как исправить баги при смене уровнейАвтор гида: vladikcomperВ Соник 1, при смене уровня, можно заметить несколько неприятных багов, когда экран затухает. 1) При переходе в новую зону, деформация задника (BG) может сбиться. Это заметно при переходе от GHZ3 к MZ1, сравните два скриншота, показывающих состояние до затухания экрана и во время него: |
||||
Это может быть незаметно в других зонах — все зависит от того, как деформация задника работает в следующем уровне. 2) Если конечная позиция в текущем уровне совпадает с позицией босса в следующем, загрузится босс и заиграет музыка босса, но тут же остановится, так все звуки затухают вместе с экраном. Этот баг не встречался в оригинальной игре, так как акты 2 были короче актов 3. Однако, если вы продлите уровни, вы можете довольно легко столкнуться с этим багом. 3) При смене зон, циклы палитр для новой зоны сработают как только начнется затухание. Из-за этого некоторые цвета на экране могут испортиться. Это можно заметить при переходе MZ3—SYZ1. Все эти баги происходят из-за того, что условие рестарта уровня расположено в неподходящем месте. Когда объект "SONIC HAS PASSED" переключает уровень, многие процедуры, такие как процедуры деформации задника, циклов палитр, успевают сработать до того, как сработает условие рестарта уровня. Поскольку номер уровня к этому моменту уже обновился, все эти процедуры будут деформировать задник, загружать объекты и циклы палитр для следующего уровня, что и выливается в баги, описанные выше. Найдите лейбел Level_MainLoop, вы увидите следующий код: Level_MainLoop: bsr.w PauseGame move.b #8,($FFFFF62A).w bsr.w DelayProgram addq.w #1,($FFFFFE04).w ; add 1 to level timer bsr.w MoveSonicInDemo bsr.w LZWaterEffects jsr ObjectsLoad tst.w ($FFFFFE08).w bne.s loc_3B10 cmpi.b #6,($FFFFD024).w ; is Sonic dying? bcc.s loc_3B14 ; if yes, branch loc_3B10: bsr.w DeformBgLayer loc_3B14: jsr BuildSprites jsr ObjPosLoad bsr.w PalCycle_Load bsr.w RunPLC_RAM bsr.w OscillateNumDo bsr.w ChangeRingFrame bsr.w SignpostArtLoad tst.w ($FFFFFE02).w ; is the level set to restart? bne.w Level ; if yes, branch cmpi.b #8,($FFFFF600).w beq.s Level_ChkDemo ; if screen mode is 08 (demo), branch cmpi.b #$C,($FFFFF600).w beq.w Level_MainLoop ; if screen mode is $0C (level), branch rts ; quit А вот и наш бранч на рестарт уровня: tst.w ($FFFFFE02).w ; is the level set to restart? bne.w Level ; if yes, branch Он переходит к процедуре Level для затухания экрана и перезагрузки уровня. Все вроде бы хорошо, но посмотрите сколько ненужных процедур работает после смены номера уровня (во время работы ObjectsLoad, так как переключается объектом) и до того, как встречается этот переход. Для исправления бага, переместите условие рестарта на несколько строк вышел, прямо за строкой: jsr ObjectsLoad
В результате код примет вид: Level_MainLoop: bsr.w PauseGame move.b #8,($FFFFF62A).w bsr.w DelayProgram addq.w #1,($FFFFFE04).w ; add 1 to level timer bsr.w MoveSonicInDemo bsr.w LZWaterEffects jsr ObjectsLoad tst.w ($FFFFFE02).w ; is the level set to restart? bne.w Level ; if yes, branch tst.w ($FFFFFE08).w bne.s loc_3B10 cmpi.b #6,($FFFFD024).w ; is Sonic dying? bcc.s loc_3B14 ; if yes, branch loc_3B10: bsr.w DeformBgLayer loc_3B14: jsr BuildSprites jsr ObjPosLoad bsr.w PalCycle_Load bsr.w RunPLC_RAM bsr.w OscillateNumDo bsr.w ChangeRingFrame bsr.w SignpostArtLoad cmpi.b #8,($FFFFF600).w beq.s Level_ChkDemo ; if screen mode is 08 (demo), branch cmpi.b #$C,($FFFFF600).w beq.w Level_MainLoop ; if screen mode is $0C (level), branch rts ; quit Вот и все! Серия багов исправлена одним действием. |
Как исправить баг со сдвигом очереди Pattern Load CuesАвтор гида: vladikcomperPattern Load Cues, или сокращенно PLC, система для подгрузки сжатого в Nemesis арта. PLC загружает арт в начале уровней, а также в некоторые моменты игры (например, подгрузка арта сигнпоста или боссов в конце уровней). Эта система устроена довольно сложно, и в Соник 1 и 2 в ней присутсвуют баги, один из которых описан в гиде «Как исправить баг с загрузкой Pattern Load Cues». Еще один баг связан с механизмом сдвига PLC очереди. ВступлениеУ системы PLC есть буффер, или правильнее сказать, очередь (queue, тип структуры данных), которая занимает $60 байт в памяти. В очередь записываются и поочередно исполняются команды на загрузку арта, каждая команда занимает 6 байт: первые 4 байта - исходный оффсет загружаемого арта в РОМе, оставшиеся 2 - оффсет VRAM, куда следует загрузить арт. Посмотреть как выглядят эти команды можно в файле _inc\Pattern Load Cues.asm в Соник 1. Очередь PLC рассчитана на $10 (16) команд. Выполнение команд в очереди начинается с ее начала, как только команда выполнена, вся очередь сдвигается: первая команда замещается второй, вторая - третьей, и так далее. Говоря другими словами, команды в очереди сдвигаются. Однако, из-за ошибке в коде, когда буффер заполнен всеми $10 командами, последняя команда не очищается при сдвиге, что приводит к тому, что вся очередь заполняется остатками последней команды и игра попадает в бесконечный цикл, обрабатывая одну и ту же команду снова и снова. С этим можно столкнуться в Соник 1, если добавить в PLC_GHZ хотя бы две новые команды, очередь заполнится до максимума, и игра зависнет на Title Cards, хотя количество команд в очереди будет в рамках допустимых значений. Причина багаДавайте рассмотрим код сдвига очереди, чтобы понять суть программной ошибки. В качестве примера, я приведу код из Sonic 2 Xenowhirl's Disassembly, так как он лучше раскоментирован, чем код из Соник 1. Заметьте, что сам код и в Соник 1, и в Соник 2, абсолютно одинаков. ; =========================================================================== ; pop one request off the buffer so that the next one can be filled ; loc_177A: ProcessDPLC_Pop: lea (Plc_Buffer).w,a0 moveq #$15,d0 - move.l 6(a0),(a0)+ dbf d0,- rts Во-первых, этот код перемещает $16*4 = $58 байт, что даже не соответствует целому числу команд (не делится на 6). Он должен был перемещать $5A байт, т.е. $F команд. Поскольку код перемещает только $58 байт, недокопируются последние 2 байта последней ($10-ой команды), т.е. оффсет VRAM. Таким образом, оффсет этой команды собьется и останется с прошлой команды. Во-вторых, последняя команда в очереди не очищается, что ведет к ее бесконечному копированию (или, "размазыванию" по очереди). В конечном счете, вся очередь будет заполнена остатками последней команды и игра будет бескончено обрабатывать одну и ту же команду. Исправление багаSonic 1 (Hivenbrain's Disassembly)Идите к loc_16DC и замените весь код на это: loc_16DC: ; XREF: sub_165E lea ($FFFFF680).w,a0 lea 6(a0),a1 moveq #$E,d0 ; do $F cues loc_16E2: ; XREF: sub_165E move.l (a1)+,(a0)+ move.w (a1)+,(a0)+ dbf d0,loc_16E2 moveq #0,d0 move.l d0,(a0)+ ; clear the last cue to avoid overcopying it move.w d0,(a0)+ ; rts ; End of function sub_165E Sonic 2 (Xenowhirls's Disassembly)Идите к ProcessDPLC_Pop и замените весь код на это: ProcessDPLC_Pop: lea (Plc_Buffer).w,a0 lea 6(a0),a1 moveq #$E,d0 ; do $F cues - move.l (a1)+,(a0)+ move.w (a1)+,(a0)+ dbf d0,- moveq #0,d0 move.l d0,(a0)+ ; clear the last cue to avoid overcopying it move.w d0,(a0)+ ; rts Новая версия кода правильно сдвинет все команды в очереди и предотвратит ее засорение. Баг исправлен! Однако не забывайте про переполнение очереди, это исправление исправит проблему, когда очередь заполнена до $10 команд, но на $11 команд она уже не рассчитана. |
© 2004-2018, Vladikcomper E-Mail: [email protected] |