Rambler's Top100
Свежие новости Наши проекты Документация Форум О нас
Ne sutor supra crepidam judicet.
Суди, сапожник, не выше сапога.
Апеллес
Архив новостей Готовые переводы Утилиты Гостевая Книга Уголок АЭСа

ПОЙНТЕРЫ 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), эту фразу легко встретить в игре, поэтому мы ее и возьмем. И запомните: пойнтер указывает на первую букву строки, то есть, на следующий после байта <конец строки>, байт - это и есть место, на которое указывает пойнтер. Итак, ваши дальнейшие действия состоят из следующих шагов:

  1. Начальный адрес $28210
  2. Отнять 10h: 28210 à 28200
  3. Убрать все цифры до тысячных: 28200 à 8200
  4. Мысленно разделить это число на две пары: 8200h à 82  00
  5. Теперь поменять эти пары местами: 82  00 à 00  82
  6. И также мысленно соединить пары обратно: 00  82 à 0082
  7. И вот он, ваш пойнтер: 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

Как вы заметили, шаг то добавился один, а число получилось другое. Все дело здесь в том, что значение первой цифры второго байта пойнтера пойнтера по каким-то причинам изменено на определенное количество шагов, то есть к ней прибавлено какое-то число. Для чего это сделано я не знаю, но это порой сильно затрудняет поиск. Если вы не знаете значение Х, то поиск надо вести следующим способом:

  1. Вычислите пойнтер строки по системе Standart Header
  2. В полученном значении мысленно замените первую цифру второго байта на Х: т.е. если пойнтер 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

Hosted by uCoz