ПОЙНТЕРЫ
v.1.0
Эти пояснения предназначены для всех, кто хочет КАЧЕСТВЕННО переводить игры для приставок
NES и SNES.
ВСТУПЛЕНИЕ
Первое, что хочу сказать,
это то, что дока эта еще относительно сырая и малоинформативная. НО, это только
первая версия, и я в последствие многое в ней я буду дополнять и менять. А для
скорейших и нужных вам изменений, присылайте мне ваши отзывы об этой доке:
чего-то не хватает, что-то не понятно, о чем-то мало сказано и хотелось бы
узнать побольше, или же я в чем-то ошибся и т.д. В общем, пишите!
Итак, для начала, многие
из вас, наверное, слышали о пойнтерах, многие даже знают, что это такое :-))),
но не все знают как ими пользоваться. А тем, кто не знает, могу сказать только
одно - они позволяют менять длину строки, и делать строку длиннее, чем она была
изначально, это может пригодиться, если по-русски перевести какую-то фразу не
возможно, так как не влазишь в отведенное под нее место(например, как бы вы
перевели фразу "If I sad no?",
сохранив такую же длину? :-)). Но я сразу хочу заметить для всех - пойнтеры НЕ
дают вам неограниченного места! Менять длину строки можно ТОЛЬКО(если в самом
РОМе ничего не менять) с условием, что вы настолько же укоротите какую-нибудь
другую строку, так как место под текст в РОМе ограничено уже имеющимся текстом.
Это правило действует НЕ всегда, но такие случаи скорее исключение, чем правило.
И еще, т.к. я разбирался с пойнтерами по доке " THE MADHACKER'S
GUIDE TO NES POINTERS ", то я многое
брал из нее, т.к. в ней достаточно понятно все изложено и я думаю ни к чему
изобретать велосипед и придумывать какое-нибудь другое объяснение, но это,
конечно, не значит, что я просто пишу здесь ее перевод, просто какие-то части
моего объяснения могут совпадать(и это не случайное совпадение) с этой докой.
ЧТО ВАМ ДЛЯ ЭТОГО ПОНАДОБИТСЯ
Hexposure v0.44b,
знание работы с 16-ричными редакторами и естественно шестнадцатеричной системой
исчисления, много свободного времени и главное ТЕРПЕНИЕ, ну и конечно
переводимый вами РОМ :-))).
ВСТУПЛЕНИЕ
Итак, что же такое
пойнтеры? -, спросят не знающие люди. Попросту говоря, это байт(вернее, два
байта), указывающий где в РОМе расположена какая-то конкретная строка. Для
знающих BASIC могу привести такой пример:
10 Print "Это игровой текст"
20 Goto 10
В рассмотренном примере команда Goto
10 и является пойнтером, то есть указателем(to
point - указывать) на то место где
расположен текст - в данном случае это текст команды
Print.
Естественно, что и команд Print и Goto
может быть и больше и сама программа посложнее, но, надеюсь, суть вы поняли. А
для людей, не знающих Бэйсик могу привести такой пример: вы, наверное, не раз
видели в книгах такую надпись <ст. 193, 2 обзац, 3 предложение.>, ну или что-то
наподобие того, так вот это тоже своего рода пойнтер. Сразу хочу сказать, что
пойнтеры для NES (далее
буду называть Денди - для меня это как-то <роднее> звучит :-))) и
SNES -
ДВУХБАЙТНЫЕ. Запомнить это очень важно. Итак сейчас важно усвоить -
Пойнтеры - это двухбайтные шестнадцатеричные значения, предназначенные для
указания расположения в РОМе какой-то конкретной строки или участка текста.
ВЫЧИСЛЕНИЕ И НАХОЖДЕНИЕ ПОЙНТЕРОВ
Итак, суть и назначение
пойнтеров мы поняли, теперь нам необходимо их найти. Для начала я расскажу, как
это делается в РОМах Денди, а на СНЕС остановлюсь немного позже. Хочу сразу
сказать, что систем у пойнтеров много, но суть этих систем всегда одна. И еще, я
сам о пойнтерах знаю без году неделю, поэтому я пишу здесь только основы, то, в
чем сам разобрался. По мере моего дальнейшего изучения пойнтеров и набирания
опыта, я буду дополнять этот док.
Так как для более
понятного объяснения необходим пример, то я возьму в качестве примера игру
Final Fantasy
1, английскую версию. Думаю, что если вы уже интересуетесь пойнтерами, то вам не
составит труда приготовить таблицу ;-)). Итак, надеюсь, вы все приготовили и
готовы продолжать читать. Для начала вы должны запомнить, что от всех
адресов надо отнимать 10h
байт.
Поясню, у КАЖДОГО РОМа в начале файла записан
так называемый хедер(header),
в нем содержится информация системного характера(меппер, размер
CHR и PRG и т.д.), он занимает 10 байт в 16-ричной системе исчисления(и далее все адреса и
значения я буду указывать именно в ней), а так как в настоящих картриджах такого
хедера нет, то все значения при вставке в сдампленный с картриджа РОМ хедера
сдвигаются. Эмуляторы при прочтении РОМа загружают его в оперативку уже без
хедера, поэтому этот сдвиг не вызывает никаких ошибок. Но при работе с РОМом
размер хедера ВСЕГДА надо учитывать и ВСЕГДА отнимать от любых адресов(например,
адрес $29F024 на самом деле является адресом $29F014).
Теперь, наконец-то, перейдем к практическому высчитыванию пойнтеров. Как я уже
говорил выше, существуют разные типы пойнтеров, но система одна. Для удобства
понимания, я разобью ее на две системы(хотя, технически система одна,
запомните!):
- Standart Header
- SetOff X000
Standart Header
Самая простая как для
высчета, так и для нахождения. К сожалению, она встречается в РОМах не так часто
как хотелось бы.
А сейчас внимание! Сейчас
я буду рассказывать основы пойнтеров, поэтому советую в этом месте ушами не
хлопать :))).
Итак, откройте в
Hexposure
РОМ Final Fantasy
1, и найдите фразу "Nothing
here."(если в лом искать, то она
находится по адресу $28210), эту фразу легко встретить в игре, поэтому мы ее и
возьмем. И запомните: пойнтер указывает на первую букву строки, то
есть, на следующий после байта <конец строки>, байт - это и есть место, на
которое указывает пойнтер. Итак, ваши дальнейшие действия состоят из следующих
шагов:
- Начальный адрес
$28210
- Отнять 10h:
28210 à
28200
- Убрать все цифры до
тысячных: 28200 à
8200
- Мысленно разделить
это число на две пары: 8200h
à
82 00
- Теперь поменять эти
пары местами: 82 00
à 00 82
- И также мысленно
соединить пары обратно: 00 82
à
0082
- И вот он, ваш
пойнтер: 0082
Небольшие пояснения,
насчет отнятия 10h
от адреса надеюсь все понятно, а вот насчет убирания чисел до тысячных, то это
сделано именно потому, что Денди использует двухбайтные пойнтеры, это значит,
что адреса $34F567
и $123F567
одинаковы, т.к. в обоих случаях пойнтером будет число 57F5(с
учетом хедера естественно). Отсюда вытекает очень важное правило: каждый
пойнтер действует только в пределах 10000h.
Это значит, что в примере с
FF1
пойнтер потенциально может указывать только на тот текст, который находится
ТОЛЬКО в между адресами $20010 и $3001F(ну,
или $20000 и $2FFFF,
если считать хедер). Поэтому вы сможете писать строку где угодно, но в указанных
пределах. И запомнить это очень важно! А почему пары меняются местами я точно не
знаю, но их ВСЕГДА надо менять местами, и это тоже надо запомнить.
Теперь самая сложная часть
- найти расположение этого пойнтера. Здесь понадобится эмулятор(я рекомендую
Nester,
т.к. он в данном случае очень удобен: можно ассоциировать его с файлами .nes
и запускать простым кликом по файлу, он запускается в окне и запускается
довольно быстро, к тому же загружать сейвы можно простым перетаскиванием иконки
файла сейва в окно эмулятора, да и по совместимости он в рядах лидеров). Для
того чтобы найти пойнтер вам возможно придется перебрать множество перепробовать
много разных адресов, т.к. значений 0082 в РОМе много, а пойнтер такой только
один. Итак, в Hex search
вводим значение 0082 и ищем. Итак, значение найдено, но это вовсе не значит, что
это и есть ваш пойнтер, попробуйте поменять это значение на какое-нибудь
другое(например, высчитайте пойнтер какой-нибудь другой строки и напишите его).
Затем сохраните изменения(думаю о резервном копировании мне напоминать ни к чему
:-)), запустите игру в эмуляторе и вызовите эту строку(просто находясь в городе
нажать <А>). Если надпись изменилась - поздравляю, вы нашли пойнтер, если же
нет, то закрывайте эмулятор, меняйте измененное вами значение обратно, и ищите
дальше, и через некоторое время вы ОБЯЗАТЕЛЬНО найдете ваш пойнтер!
И так во всех играх, но
только, если вы сделали все правильно, но не нашли пойнтер(т.е. искомая строка
так и не изменилась), то вы похоже, столкнулись с вышеупомянутой системой
SetOff X000.
SetOff X000
Уже сложнее, но
встречается гораздо чаще. В принципе это тоже самое, что и
Standart Header,
за исключением еще одного шага в при вычислении пойнтера(учтите, что в
FF1
используется система
Standart Header!!! Просто для удобства
восприятия, я оставил для примера адрес из нее, и в дальнейшем я, пожалуй, в
качестве примера и продолжу его использовать):
1.
Начальный адрес $28210
2.
Отнять 10h:
28210 à
28200
3.
Прибавить к тысячным значение Х000: (если, к примеру, Х=3)
28200 à
31200
4.
Убрать все цифры до тысячных: 31200
à
1200
5.
Мысленно разделить это число на две пары: 1200
à
12 00
6.
Теперь поменять эти пары местами: 12 00
à
00 12
7.
И также мысленно соединить пары обратно: 00 12
à
0012
8.
И вот он, ваш пойнтер: 0012
Как вы заметили, шаг то
добавился один, а число получилось другое. Все дело здесь в том, что значение
первой цифры второго байта пойнтера пойнтера по каким-то причинам изменено на
определенное количество шагов, то есть к ней прибавлено какое-то число. Для чего
это сделано я не знаю, но это порой сильно затрудняет поиск. Если вы не знаете
значение Х, то поиск надо вести следующим способом:
- Вычислите пойнтер
строки по системе
Standart Header
- В полученном значении
мысленно замените первую цифру второго байта на Х: т.е. если пойнтер 452A,
то представьте его как 45XA. Теперь в
Hex search,
ищите 45. Как вы понимаете, результатов будет очень много, но тут есть
маленькая зацепка: при нахождении 45, посмотрите на байт СПРАВА от него, если
в нем ВТОРАЯ цифра А, то это вполне вероятно и есть ваш пойнтер. Какой-то
конкретный пример из игры я привести пока не могу, но попытаюсь объяснить, что
называется <на пальцах> :))). Итак, скажем, ваш начальный адрес $453A1B,
отнимаем 10h,
убираем 45, меняем пары местами и получаем 0B3A.
Теперь в поиске ищем 0B,
и найдя его смотрим на байт справа, допустим мы нашли его и справа от него
байт 45, тогда это заведомо НЕ пойнтер, т.к. вторая цифра этого байта не А.
Продолжаем искать, отбрасывая заведомо неправильные варианты, пока наконец не
столкнемся с чем-то наподобие 0B6A,
это уже потенциальный пойнтер, т.к. вторая цифра А, поэтому его надо
проверить, напишите вместо 0B
какое-нибудь(любое) число, сохранитесь, и попробуйте запустить игру в
эмуляторе. Если строка, пойнтер которой вы хотели изменить, изменилась, то вы
нашли пойнтер, отнимайте от 6A
3A,
т.е. от шести отнимите три, это и будет ваш Х. То есть значение
SetOff
у этой игры будет SetOff
3000. Такая система работает в абсолютном большинстве случаев, но если уж и
она не поможет, то остается один вариант - искать и проверять ВСЕ байты 0B(в
ОЧЕНЬ редких случаях используется однобайтная система, если столкнетесь с ней,
советую забить на игру в которой она вам попалась, т.к. поиски и проверки, тем
более если игра весит несколько мегов, займет ОЧЕНЬ много времени).
ТАБЛИЦЫ ПОЙНТЕРОВ
Как вы знаете, текст в
играх чаще всего расположен блоками. В блоках строки идут одна за другой, и
разделены они каким-нибудь байтом, обозначающим конец строки. Так вот, пойнтеры
очень часто(особенно в таких играх как РПГ) расположены такими же блоками, даже
последовательность имеют ту же, что и строки в текстовом блоке. Если в
переводимом вами РОМе текст расположен блоками и вы уже нашли пойнтер какой-то
строки из этого блока, попробуйте высчитать пойнтер следующей строки, затем
найдите то место, где расположен уже найденный вами пойнтер предыдущей строки и
посмотрите на два байта справа от него, если они такие же как и ваш пойнтер, то
здесь и есть ваша таблица пойнтеров. Когда пойнтеры расположены таблицей менять
длины строк в текстовом блоке легко, т.к. изменив длину какой-то строки(и
высчитав при этом ее пойнтер), вы находите адрес, на котором теперь начинается
следующая строка, вычисляете ее новый пойнтер и просто вписываете новый пойнтер
за место того пойнтера, который был справа от пойнтера строки, длину которой вы
изменили.
Короче, чтобы не грузить
вас, приведу наглядный пример:
В
FF1
по адресу 28010(как вы уже, наверно, вычислили, это и есть адрес, по которому
расположен пойнтер строки "Nothing
here") идет такая строка:
0082 0982
5C82 BD82 D982 3083 5383 D183
А теперь идите на то
место, где написана строка "Nothing
here", и попробуйте высчитать пойнтеры
следующих за ней строк(не забывая, конечно, что следующая строка начинается
после байта <конец строки>, и про хедер, конечно же, тоже), и теперь сравните их
с указанной строкой. Видите? Все просто!!!
ПОЙНТЕРЫ
SNES
На самом деле система
пойнтеров у СНЕСа практически точно такая же как и у Денди, за исключением того,
что в отличии от Денди, размер хедера у РОМов СНЕСа равен 200h
байт. То есть вся разница в том, что отнимать надо не 10h,
а 200h.
Но здесь надо сделать важное замечание: у СНЕСа не всегда нужно отнимать
размер хедера. Иногда вообще ничего отнимать не надо, а иногда нужно
что-то отнять, но неизвестно что(в таком случае можно сказать, что вам не
повезло :-))).
СЛУЧАИ, КОГДА ПОЙНТЕРЫ БЕСПОЛЕЗНЫ
Вы, наверное, не раз замечали, что можно в
некоторых играх свободно менять длину строки просто поставив байт <конец строки>
и писать дальше следующую строку. Этот тип текста называется
SEQUENTIAL TEXT.
Тут вам пойнтеры не нужны, вы и сами можете
менять длину строки, но помните, что менять их чаще всего можно только в
пределах диалога, или сцены, или еще какого-то события, а на следующий диалог,
сцену, событие уже пойнтером указывается расположение первой строки.
Другая система, это
FIXED LENGTH.
Суть ее в том, что в программном коде указано,
не только расположение строки, но и ее длина. Это значит, что даже при всем
желании длину какого-то слова вам не увеличить(по крайней мере, не зная
ассемблер). Для текста эта система почти не используется, но довольно часто
встречается в менюшках, в частности, в серии
Final Fantasy.
Явным признаком этой системы является отсутствие байта <конец строки> между
словами. Заданная длина одинакова для всех строк в этой системе, и обычно бывает
равна 8, или 16 символам.
НЕКОТОРЫЕ РЕКОМЕНДАЦИИ И ЗАМЕЧАНИЯ
Если вы ищете строку из
таблицы, то попробуйте пару способов, которые действуют не всегда и не везде, но
если действуют то очень сокращают время поисков. Во-первых, помните, что если вы
ищете строку из блока, то вполне вероятно, что в РОМе есть таблица пойнтеров с
аналогичным строкам из блока расположением пойнтеров, в таком случае просто
вычислите пойнтер этой и следующей строки, и в поиске напишите их
вместе(например, если первый пойнтер 489C, а второй 529С, то просто введите 489C529C),
при этом ВЕЛИКА вероятность, что пойнтер вы найдете с первой же попытки. Этот
способ не работает с текстовыми блоками с диалогами из
FF5,
да и со многими другими играми тоже. Во-вторых, таблица пойнтеров довольно часто
находится близко, или вообще над текстовым блоком, проверьте, это также сможет
сэкономить время.
И еще, поищите программу
A Simple Pointer Table
Recalculator(по-моему, она была где-то
на Zophar`е),
она очень помогает, если надо пересчитывать всю таблицу пойнтеров(например, если
вставили текст), а заниматься этим в лом чугунный :-))). Программа эта помогает
не всегда, но иногда просто незаменима.
Прежде чем писать мне на
мыло, учтите, что:
Я не буду присылать или
принимать от вас РОМы(не касается это только участников группы <ШЕДЕВР>).
Я знаю, что вы хотите
знать про пойнтеры Сеги, я тоже хочу :-)).
Я не занимаюсь никакими
черными делами :-)))).
Не стоит мне писать всякую
чушь.
И не надо меня кучу раз
спрашивать когда выйдет новая версия дока - как только, так сразу :-))).
И еще, я не буду иметь
никаких претензий, если вы будете использовать этот док для некоммерческих
целей, но любое коммерческое использование без моего ведома, или ведома группы
запрещено!
Автор
KEN
Группа перевода игр <ШЕДЕВР> 2001
|