Перевод Spyro 3: Взлом и программы
|
|
aleksusklim | Сообщение # 1 Воскресенье, 26.06.2011, 14:19 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
Работа с текстовой частью перевода введётся на code.google.com/p/spyro3-rus, обсуждение перевода (а не взлома) В темеПеревод Spyro 3: Текст В этой теме происходит обсуждение перевода, а также создание софта для потрошения игры. Важные сообщения: Структура WAD-файлов Спец. символы и File Paint 1 | Извлечение и добавление текста | О звуке | В продолжение о звуке | Вариант перевода Buzz's Dungeon и Midday Gardens
Сообщение отредактировал aleksusklim - Среда, 29.04.2015, 19:51 |
|
| |
DrWho | Сообщение # 391 Воскресенье, 19.05.2013, 21:09 |
Мудрый Дракон
Почетный Житель
«1371»
Где: Не в городе Драконов
|
Цитата (nihonjin) Нет-нет, файлы(а вернее доступ к ним) у меня пропали ещё до этого. Нажав «войти» я смог создать новый сайт(или перевезти старый) и начать восстанавливать файлы, т.е. ни одну ссылку на форуме я не трогал. Ради безопасности лучше всего заливать в нескольких местах. А то вдруг удалят/переедет сайт - и потом все, потеряно выложенное.
Сообщение отредактировал DrWho - Воскресенье, 19.05.2013, 21:09 |
|
| |
aleksusklim | Сообщение # 392 Понедельник, 20.05.2013, 18:52 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
Забавно, перепутал ссылки…
Цитата (DrWho) А ничего, что это перевод от Вектора, не?
Какая разница? Главное что EXE не пропатченный и совпадает с тем, который патчили вплоть до байтов переведённого текста.
Цитата (DrWho) Я вроде бы приложил ссылку на YouTube, где есть обходной вариант этой проблемы. Сейчас ссылки копирну, те, что скопировал сюда steeldragon.
Ага, конечно. Но во-первых, это ссылка на .exe, в то время как выложен был другой файл, во-вторых доменное имя не соответствует самому файлообменнику (не официальная программка), а в-третьих я никогда не доверяю сторонним программам для закачки, да и вообще любым мини-качалкам. Никогда. Это банальное доверие к источнику. Могу даже продемонстрировать: http://klimaleksus2.ucoz.ru/Files/S/believe.exe (афигеть… Вот видите, как даже uCoz относится к прямым ссылкам на .exe! Но не бойтесь, я могу даже код показать, если что…)
Цитата (DrWho) Ну RipBin'ом я не пользовался, но - если принцип использования программы такой же, то, мне кажется, нет необходимости (для меня) в этой программе.
Всё равно рано или поздно понадобится.
Цитата (DrWho) Без понятия, в так глубоко я еще не умею копать. Если есть аккаунт на rutracker'e, то можно его спросить по этому поводу.
Да пусть хоть инструменты перечислит, там же явно что-то большее, чем WinHex. Дизассемблер хоть был?
Цитата (DrWho) Вообще, brill нашел способ обойти защиту, но - конвертировать представленный образ нельзя. Никак. Нужно его записать Алкоголем или через CloneCD. То есть если хочется представленный образ переконвертировать для запуска, к примеру, на эмуляторе PS2PSXe - фигушки. Тут уже надо фиксить Libcrypt.
Вот именно, следует снять защиту полностью, чтобы игра либо даже не читала субканал, либо принимала его отсутствие как успешную проверку на оригинальность диска.
Цитата (DrWho) Сами же и ответьте на этот вопрос. Не вижу смысла использовать GH'ный SYSTEM.CNF, если таким же образом можно его использовать и из обычной версии.
Да у меня только ДВЕ версии игры, угадайте какие…
Цитата (DrWho) Именно. Только весь прикол в том, что со вторым вариантом будут крестики вместо этого символа, когда Спайро сядет на транспорт. Второй вариант годится только для Миров животных. Первый же - для боссов.
Надо просто посмотреть код символа апострофа в оригинале, а не гадать на авось.
Цитата (DrWho) Черный экран? Интересно.
Нет же, просто зависание. Возможно повторение звуков или повреждение модельки дракона.
О, всё понятно. Кстати, спасибо, что обратили на это внимание.
Игра центрует надписи! Для этого она подсчитывает количество символов. Поэтому даже если наш перевод будет длиннее (без разницы как мы его засунем…), он всё равно отцентруется. Но а если короче оригинала? Что делать? Пираты (как и я хотел) просто заполняли конец пробелами. Однако пробел считается за символ! И центровка произойдёт неправильно, сколько бы вы там не отсчитывали пробелов перед текстом. Единственный разумный выход – всегда заливать нули после последней буквы каждой фразы.
Нужно мне наконец уже начать создавать новую программу для внедрения нашего текста…
Цитата (DrWho) Не спорю, но FireCross'овцы - еще те лентяи. Перерисовать шрифт хорошо могут, а вот качественно работу проделать - нет. Да и озвучка у них не очень (я имею в виду мужскую).
Помню-помню. Кстати, выложу. Вслед за Платиновской, которую залью уже на днях (для MrModez)
Цитата (DrWho) Кстати, объясните мне как правильно вшить этот патч в образ, чтобы он работал и на реальной консоли. Хочу на пс2 попробовать этот вихрь.
RipBin! Ставите патч, риппаете образ, вытаскиваете WAD, перепрожигаете через CdGenPS2, копируете .sectordata обратно и собираете BIN (который ISO)
Цитата (DrWho) Действительно, виснет. А не могли бы вы попробовать вылечить образ от этих зависаний?
Уфф.. никчёмное дело, с этим даже вы справитесь… Нужен только WinHex и MemGet.
|
|
| |
DrWho | Сообщение # 393 Понедельник, 20.05.2013, 20:52 |
Мудрый Дракон
Почетный Житель
«1371»
Где: Не в городе Драконов
|
Цитата (aleksusklim) Но а если короче оригинала? Что делать? Пираты (как и я хотел) просто заполняли конец пробелами. Необязательно, Вектор как раз центрировали надпись, ставя пробелы не в конец перед многоточиями, а просто стараясь пробелы с обеих сторон ставить. Тогда надпись будет в центре.
Цитата (aleksusklim) RipBin! Ставите патч, риппаете образ, вытаскиваете WAD, перепрожигаете через CdGenPS2, копируете .sectordata обратно и собираете BIN (который ISO) Ок, будем пробовать.
Цитата (aleksusklim) Уфф.. никчёмное дело, с этим даже вы справитесь… Нужен только WinHex и MemGet. Ну с этим я еще не знаю как справляться, просто если бы пираты опять налажали с апострофом, то тут да, можно было бы Хексом подправить и проверить. Просто эта версия уже на рутрекер и псхпланету залита, а хочется, чтобы этих 3х зависаний не было, чтобы можно было перезалить образ в обеих местах. Так хоть народ ничего не пишет о работоспособности образа, т.е. есть шанс поправить эти зависания, пока он не узнал.
Цитата (aleksusklim) И центровка произойдёт неправильно, сколько бы вы там не отсчитывали пробелов перед текстом. Единственный разумный выход – всегда заливать нули после последней буквы каждой фразы. Главное структуру не нарушить, а то я один раз так сделал у меня надписи съехали в игре.
Добавлено (20.05.2013, 19:27) --------------------------------------------- Цитата (aleksusklim) Да у меня только ДВЕ версии игры, угадайте какие… GH, FireCross. Угадал?
Цитата (aleksusklim) Надо просто посмотреть код символа апострофа в оригинале, а не гадать на авось. Само собой.
Добавлено (20.05.2013, 20:52) --------------------------------------------- Цитата (aleksusklim) Да пусть хоть инструменты перечислит, там же явно что-то большее, чем WinHex. Дизассемблер хоть был? Его ответ: Цитата (_brill_) Я уже кажись писал что меня не волнует spyro realms. Больше не беспокой меня этим вопросом. Лишних неприятностей я не хочу, потому по таким вопросам его не буду беспокоить. Хотите - тревожьте его сами.
Сообщение отредактировал DrWho - Вторник, 28.05.2013, 19:07 |
|
| |
misterio-3 | Сообщение # 394 Вторник, 28.05.2013, 19:00 |
Маленький Дракон
Житель Города
«215»
Где: Не в городе Драконов
|
А если при переносе все данные пропадут что тогда ты будешь делать????
да я со всем согласен
|
|
| |
DrWho | Сообщение # 395 Вторник, 04.06.2013, 01:11 |
Мудрый Дракон
Почетный Житель
«1371»
Где: Не в городе Драконов
|
Цитата (misterio-3) А если при переносе все данные пропадут Ничего при переносе не пропадет. Если неправильно что-то подкорректировать в образе, можно нарушить структуру, что может в лучшем случае привести к смещению текста или глюкам, в худшем - неработоспособностью игры. Надо всегда полученный результат стараться сохранять как новый.
Добавлено (31.05.2013, 15:23) --------------------------------------------- Кстати, немного отойду от темы, но все равно спрошу: а кто-нибудь пробовал что-нибудь изменить в Crash Team Racing? Я это спросил к тому, что там WAD нету, есть BIG. Что я хотел сделать: Подкорректировать шрифты от Вектора. Зачем? Они хуже, чем у остальных. Сам перевод хороший получился, а вот шрифт не задался. Хотел у Парадокса его взять, только я структуру не знаю.
Добавлено (04.06.2013, 01:11) --------------------------------------------- Занялся сейчас переносом текста от Вектора, с шрифтом пока что только проблемы. Цитата (steeldragon) Шрифт вместе с большинством игровой графики хранится в первом субфайле, его можно изменить с помощью PGG2 . Простой подменой шрифт не поменялся - видно надо PGG2 пробовать. А еще у меня почему-то треск издавался 3 секунды, после того, как появилась табличка с названием разработчиком. Видно что-то задел, тупо перенеся первый суб-файл. Либо со вторым накосячил. Кстати, во втором суб-файле оказывается тоже есть текст. Причем, если к примеру через хекс редактор просто поменять текст, то тогда игра работает. А вот если тупо подменить второй файл - игра отказывается работать и эмулятор выдаст ошибку. Интересно, чего такого во 2й файл запихали кроме текста, раз обычной подменой игра перестает работать?
Сообщение отредактировал DrWho - Вторник, 04.06.2013, 01:25 |
|
| |
steeldragon | Сообщение # 396 Четверг, 27.06.2013, 13:30 |
Старейшина Драконов
Редактор
«422»
Где: Не в городе Драконов
|
Цитата (DrWho) Простой подменой шрифт не поменялся - видно надо PGG2 пробовать. Цитата (DrWho) Интересно, чего такого во 2й файл запихали кроме текста, раз обычной подменой игра перестает работать? Во всех субфайлах хранится информация и помимо этой. (Окружение уровня, текстуры, и так далее. Подробнее: Энциклопедия - Структура субфайлов WAD).
Цитата (DrWho) Кстати, немного отойду от темы, но все равно спрошу: а кто-нибудь пробовал что-нибудь изменить в Crash Team Racing? Я это спросил к тому, что там WAD нету, есть BIG. Что я хотел сделать: Подкорректировать шрифты от Вектора. Шрифт где-то в начале файла. Прогони через конвертеры Any2pvv - Pvv2bmp, найди нужный кусок и замени (в DIB - тоже), потом - Bmp2pvv, Pvv2any. Эти конвертеры входят в комплект поставки обоих PGG. Инструкция отдельно (тут недалеко... относительно).
Добавлено (11.06.2013, 21:09) --------------------------------------------- Такс, набросал что-то вроде базового редактора переводов: Ссылка Пятьдесят процентов не работает, сорок процентов не готово, но уже что-то.
Также в процессе разработки у меня возник вопрос: Как сохранить файл обратно на сайт (конкретно - на вот этот самый Ucoz-овский)? Значит, страница отправляет запрос POST с информацией. А как научить сервер обрабатывать этот запрос (то есть принять данные и сохранить в положенное место)?
Добавлено (15.06.2013, 22:35) --------------------------------------------- https://dl.dropboxusercontent.com/u....-s3.rar Второй патч для меню. Заменяет лого Universal, табличку и шрифт. Для разнообразия, этот патч в формате PPF. Парадоксовский PPF-O-Matic в комплекте.
Добавлено (20.06.2013, 13:35) --------------------------------------------- И снова о 3Д моделях. В 001-ом субфайле всё же есть модель Спайро. Начинается почти сначала и заканчивается на смещении 14806. Конечно, для перевода эта модель ценности не имеет, но всё же... В конце модели хранятся данные о текстурах: 3 байта плюс разделитель. Над ними - 24 байта непонятно для чего. А дальше самое интересное. Над ними хранятся полигоны модели, по 19 байт (!) плюс разделитель. Идут от смещения 6188, если я прав, то их получается 295. Буду дальше расшифровывать...
Добавлено (27.06.2013, 13:30) --------------------------------------------- Второй день пишу конвертер моделей в OBJ. Пока что вот результат: https://dl.dropboxusercontent.com/u....2OBJ.py
Написана эта программа на Питоне 3.3*, соответственно, требует установленный Python: http://python.org/ftp/python/3.3.2/python-3.3.2.msi (установщик, 19 МБ)
Как пользоваться: - Запустить .py файл - Перетащить в окно нужную модель**, нажать Enter - Смотреть результат в папке программы, название файла 'output.obj'
Сейчас конвертер работает, мягко говоря, плохо, у моделей после конвертирования не хватает полигонов, но я исправлю это... когда разберусь, в чём тут проблема. EDIT:Об-нов-ле-но! Теперь всё работает нормально. Скриншот: https://dl.dropboxusercontent.com/u....del.jpg
*Небольшое объяснение: Python - язык интерпретируемый, т.е. программа является одновременно и исходным кодом. ** В формате Spyro 1.
Сообщение отредактировал steeldragon - Суббота, 29.06.2013, 17:02 |
|
| |
nihonjin | Сообщение # 397 Понедельник, 01.07.2013, 17:30 |
Дракон Подросток
Редактор
«298»
Где: Не в городе Драконов
|
Цитата (steeldragon) В 001-ом субфайле всё же есть модель Спайро. Цитата (steeldragon) Над ними хранятся полигоны модели .... их получается 295. А знаете, действительно, ведь первые субфайлы в Spyro2 и Spyro3 очень похожи на 022 и 095 из первой части(яп) в которых и содержатся модели. Так что, думаю, вы идёте в правильном направлении!
Цитата (steeldragon) Второй патч для меню. В своей теме я уже упоминал, что надо бы создать для Spyro3 в вики на ГуглКоде страницу, скажем "Process", где отражать прогресс проекта. Пока что я побаиваюсь это делать и поэтому представлю исхищрённый вариант предпросмотра: http://nihonjinryuu.narod.ru/himitsu/process-pre.htm Хм... видимо для Spyro3 такая страница менее актуальна, чем для 1? Так стоит ли её тогда создавать и если да то может нужно сделать картинки ссылками?
steeldragon, я пока не пойму, с теми наработками-программами, что вы уже составили можно только извлекать/редактировать модели или ещё и внедрять изменённые в игру? ~~~~~ Я сам нет-нет да поковыряюсь в 3й части. В этот раз я решил проверить одну теорию, касающуюся 181 и 182 субфайлов. Вот как они выглядят в PVV: http://nihonjinryuu.narod.ru/pvv/subfile181.png Не правда ли подозрительно, особенно, если учитывать ещё и то, что рядом расположен субфайл с обложкой атласа? Короче, чтобы разобраться я использовал TileMolester с его 'обширными' возможностями по просмотру графики. Покрутил настройки и получил, для 181: http://nihonjinryuu.narod.ru/tilemolester/atlas1.png http://nihonjinryuu.narod.ru/tilemolester/atlas2.png Для 182: http://nihonjinryuu.narod.ru/tilemolester/epiloge1.png http://nihonjinryuu.narod.ru/tilemolester/epiloge2.png Считайте это доказательством того, что 181 это атлас, а 182- эпилог. Осталось только починить палитру...
①Прогресс перевода и взлома японской версии Spyro 1 смотрите в «плавающем сообщении» и его дубле. ②Сводки по Spyro 2! http://nihonjinryuu.narod.ru/Spyro2/gaikatsu.html. ③Кратко о прогрессе перевода и взлома Spyro 3GH в «сообщении №258», а сборки перевода найдёте в «сообщении №512». ④Проект перевода в vk - «spyro3rus». ◯~Русская wiki по Спайро~. ◯~Японская история Спайро~.
|
|
| |
steeldragon | Сообщение # 398 Понедельник, 01.07.2013, 18:12 |
Старейшина Драконов
Редактор
«422»
Где: Не в городе Драконов
|
Цитата (nihonjin) steeldragon, я пока не пойму, с теми наработками-программами, что вы уже составили можно только извлекать/редактировать модели или ещё и внедрять изменённые в игру? Внедрять можно изменённые VertexEditor-ом, только вручную.
Цитата (nihonjin) А знаете, действительно, ведь первые субфайлы в Spyro2 и Spyro3 очень похожи на 022 и 095 из первой части(яп) в которых и содержатся модели. Так что, думаю, вы идёте в правильном направлении! Еще в 009 хранится модель Спайро из главного меню (да, их две). Эх, докопаться бы до моделей букв...
Цитата (nihonjin) Хм... видимо для Spyro3 такая страница менее актуальна, чем для 1? Вполне может быть.
Цитата (nihonjin) Так стоит ли её тогда создавать и если да то может нужно сделать картинки ссылками? Создавать стоит (наработок много, просто не вставлены в игру), картинки да, лучше ссылками.
Цитата (nihonjin) Считайте это доказательством того, что 181 это атлас, а 182- эпилог. Интересненько...
|
|
| |
aleksusklim | Сообщение # 399 Воскресенье, 07.07.2013, 18:57 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
Да! Я провёл огромнейший взлом игры, и теперь мы можем:
– Извлечь ВЕСЬ текст из игры разом; – Однозначно получить Numeration-сопоставление каждой строки конкретному STR-треку в автоматическом режиме; – Заменить звуковой трек любой реплики на любой другой существующий трек (например, большей длительности)
Качайте проги: http://klimaleksus2.ucoz.ru/Files/2/MegaTextRip.rar
Началось всё с того, что я решил найти наконец нормальный метод экстракции текста из уровней. Именно по «указателям» или ссылкам. А не просто парсить весь файл как неструктурированную свалку двоичного мусора.
Со времён Вихря я помню, как устроены четвёртые суб-субфайлы, а также оставшиеся подуровни (шестой, восьмой, десятый…) В самом конце – лист ссылок на экземпляры объектов. В каждом субфайле ссылки локальные, то есть дампить совсем не требуется, чтобы узнать, куда они ведут.
Итак, список ссылок, и все они указывают в этот же файл. Некоторые из них в «верхнюю» область, где виртуально размечаются блоки по 88 байт – полные начальные описания живых игровых объектов – их координаты, поворот, масштаб, анимация. Естественно тип и вид, некоторые дополнительные данные, которые уже зависят от самого рода объекта – разумно полагать, что персонаж с текстом должен значительно отличаться от простой бочки с алмазом.
Первые четыре из этих 88-ми байтов – прямой указатель. На некоторые дополнительные данные, которыми владеет этот экземпляр объекта. И вот их структура и тип уже конкретно сильно повязаны на роде объекта – тогда я сравнивал яйцо и Вихрь – последний хранил там свою толщину, высоту и позицию камеры, а яйцо описывало номер дракончика, который из него вылупляется. И данные эти занимают разные недетерминированный объём, который игра определяет, исходя из рода объекта. То есть его тип должен быть известен ещё по 88-ми основным байтам. И да, те прошлогодние исследования показали, что меня некий индекс в допустимых пределах можно превращать одни типы предметов в другие.
И всё бы оказалось очень просто, если бы абсолютно все ссылки из листа в конце вели строго на 88-ми байтовые блоки. Но ведь нет! Некоторые ведут не в верхушку, а в середину файла (где есть уже текст). Они тоже что-то показывают, но доподлинно лишь определено, что и там первые четыре байта – новый указатель на «что-то». И зачастую этими четырьмя байтами и оканчивается весь блок, ибо следующий из листа указывает на последующие же четыре байта, которые представляют собой ещё один указатель на «что-то другое».
Из этого следует, что игра различает 88 / ? / 4 блоки ещё до того, как перейдёт к ним. Либо по сличению адреса (блоки 88 располагаются значительно выше всего остального, причём «пустое пространство» в игре размечается на новые динамические 88-блоки…), либо каким-то другим флажком или списком, действующим не из листа (или есть закономерность, как зная тип всех предыдущий указателей, определить тип очередного). В любом случае, я пока не разгадал эту тайну, и «взлом» сводился к тому, чтобы научиться косвенно разделять указатели на истинно объектные «88» и всё остальное.
На самом деле меня волнуют не сами объекты, а лишь их дополнительные переменные, на которые указывают первые четыре байта (в файле, как я уже сказал, ссылки локальны, а уже в игре они заменяются на полные с «800хххххh»).
Основное наблюдение: все «говорящие» предметы – объекты 88. Ссылки ведут на область из восьми байт, после которых ВСЕГДА следует флаг «FF000000». А строго за ним располагается ссылка на ИМЯ этого персонажа.
Имя – просто текстовая строка с нулём на конце. И сама она идет немного ниже. После ссылки на имя расположенные четырёхбайтные ссылки на все его реплики. Чем последовательность ссылок заканчивается мне не совсем понятно, но можно проверять на нуль или на выход за границы файла, а также на неадекватность результирующей строки, но надо быть осторожным, ибо слишком суровое условие типа «char<32» может смести несколько экзотических строчек, но зато «char>127» работает всегда!
Кстати, строка «реплики» текста – совсем не такая простая, как имя персонажа. Выяснено наблюдениями: первый байт ВСЕГДА показывает, сколько начальных байт «лишних» включая его самого. Самый тривиальный случай: «01ssssss…», где «ss» – байты от строки. Возможны варианты вроде «022Assss…» или «032302ss…» – ну смысл ясен. Непонятно, чему максимально может равняться это число. Были и громадные «яяяя» типа «0901FFFFFFFFFFFFFFsssss…», так что рисково проверять «имя/реплика» через «char[0]<32». Фраза оканчивается нулём как обычная строка.
Что будет, если первый байт «00h» – не знаю, но ничего хорошего. Чтобы показать «пустую» строку используется «01000000h». Бывает и с флагом «FF000000h», это тоже надо отлавливать (а не прыгать чрез 254 символа вперёд…)
Я проводил эксперименты над байтами между первым (длина мусора) и начальным от строки – выяснил, что какая-то часть отвечает за позицию камеры при разговоре с персонажем на этой реплике. Можно было заставить её повернуться вообще в другую строну! Но большинство изменений игра не переживала, падая или зависая. Иногда портилась структура диалога, особенно вручение яиц или «(треугольник) TO TALK» – фразы путались, менялись местами или постоянно произносилась одна и та же.
Ну примерно понятно, там хранится поведение объекта на этой реплике, в частности, номер следующей и условия для её произношения. Если так вспомнить – различных действий может быть очень и очень много – от открытия порталов до появления мостов.
Также я проводил эксперименты над первыми восемью байтами «дополнительных переменных» объекта (после которых идёт «FF000000h» и ссылки на имя да на все реплики). Чего-то особо интересного и поддающегося простому изменению не выискал, но вот затирание первого байта (одного байта!) приводило к тому, что персонаж при разговоре произносил не свой текст из STR! То есть менялся его номер. Ну явно же одно байта мало, у нас ведь более 255 треков в Numeration! Значит, этот байт – индекс в каком-то списке, где треки распределяются уже более детально. Возможно, список должен был быть локален для уровня. Менялись сразу все его реплики, то есть этот байт отвечал за самого персонажа, а не за его текст. Где-то лежат списки, какие треки принадлежат каждому персонажу…
И тут я снова вспомнил про игровой пароль «квадрат-квадрат-круг-круг-квадрат-квадрат-круг-круг»! Который высвечивает диалоги уровня. Он разделяет их на «Type:» и «Msg:». Где фразы из одного «Type» произносит один персонаж. И да, наблюдения подтвердили, что первый байт дополнительных переменных – это и есть «Type». А все фразы персонажа располагаются именно в той последовательности, которую задаёт «Msg».
Меняя «тип», я мог заставить двух персонажей говорить одинаковые реплики, но не мог изменить сами треки по сути, то есть сопоставления «Type+Msg=?» у меня ещё не было.
Ударился в дизассемблирование. Надпись «Type: %d Msg: %d» лежит в SCUS. Нашёл её код, посмотрел где вызывается – лишь один раз в некой функции. Долгими замусориваниями я выяснил, что эта функция отвечает за «паузу» игры. Я посмотрел откуда она вызывается (а это была прикольная switch-select / case ассемблерная конструкция!), пробовал поменять вызов – дошло до того, что вместо меню паузы у меня отрисовывается весь VRAM, но игра на паузу-таки встаёт, и воспроизведение диалогов в чит-меню по-прежнему работает. Выходит, что «Type: %d Msg: %d» лишь рисует на экране тип и номер воспроизводимого трека, но совершенно не связано с его непосредственным воспроизведением.
Заодно проверил, действительно ли у них есть парсер «%d а-ля printf C++ стайл» – и да, оказывается в код игры встроен небольшой printf-щик, который заменяет %d на переданные аргументы. Вроде у него ещё «%08x» есть, я пока не исследовал подробно как он работает, но если удастся приручить эту функцию, то можно будет через неё выводить на экран содержимое интересующих нас регистров во время экспериментов с шифровкой-сжатием текста (только ещё предстоит найти «основной цикл» игры, где отрисовывается КАЖДЫЙ кадр); неплохо бы, а то я уж собирался свой конвертер из числа в строку на ассемблере писать…
Ладно, отвлеклись. Мне нужно было найти то место, откуда «Type: %d Msg: %d» берёт данные. Там постоянно происходила сверка с некой ячейкой, в которой хранился ноль. Другие ссылающиеся на эту ячейку тоже считывали этот ноль, а потом умножали (сдвигали) на большие степени. Я попробовал изменить на единицу или что повыше – все надписи на порталах, в меню, у персонажей, на летательных средствах и прочие – поменялись! Надписи из меню появились на порталах, названия сценок в меню, а некоторые и совсем пропали. Кажется, это какой-то адресный сдвиг. Но раз он нулевой, я мысленно подставил ноль в регистр, куда загружалось значение, и легко вычислил результирующий адрес (заодно и на экран его вывел….), куда обращалась «Type: %d Msg: %d» (по-идее сам ps2dis должен это знать, но из-за скрытого нуля он не мог вычислить выражение – засылающегося не считался).
По тому адресу располагались некоторые указатели – текущий и предыдущий – на данные, а все остальные – на функции. Я попробовал изменить текущий – и ура, «Type: %d Msg: %d» перестал видеть нормальные типы и номера треков. Но мало того, так ещё и все персонажи перестали устно говорить!
Супер. Перехожу по указателю и пытаюсь разгадать формат данных. А это было не сложно: первый байт – «Type», второй байт – «Msg», третий байт – номер XA канала в SPEECH.STR файле (которые у нас <32 в основных треках и >31 в Secret’STR, их ещё можно увидеть в «idx» файле, который создаёт jPSXdec, если открыть сохранённый индекс диска блокнотом), четвёртый байт игнорируется. Далее четыре байта – некая «длительность» трека, и ещё четыре байта – реальный адрес первого сектора данного трека в SPEECH.STR !
Это же то что доктор прописал! Через эту таблицу можно как узнать, так и изменить конкретный номер конкретного трека для конкретной фразы конкретного персонажа! http://klimaleksus2.ucoz.ru/Files/STARS/hack_0.png Итак ещё раз, 12 байт – номер персонажа, номер его реплики, канал XA, длительность в условных единицах и самое главное – LBA первый сектор трека в STR. По сути, нам нужен только он, но игра требует, чтобы номер XA канала совпадал с реальным.
LBA начала секторов конкретных треков тоже можно узнать в .idx сохранёнке jPSXdec, но придётся отнять «90000» от каждого номера – это LBA самого SPEECH.STR, а игра считает сектора по файлу, а не глобально по диску (разумно, если они захотят переместить файл на другой сектор, то придётся лишь где-то поменять смещение, а не в каждом уровне игры!)
Всё прямо-таки прекрасно, осталось узнать, где же лежит эта таблица треков на самом образе, а не в памяти. Нет, в четвёртом суб-субфайле её нет. Логично, ведь она едина на весь уровень, и можно прослушивать треки подуровней из основной локации, значит искать надо среди первых трёх субфайлов.
Ну в самом первом её точно нет, там ведь только звук и графика. Во втором тоже нет… Хорошо бы в третьем – будь я разработчиком, я бы поместил её в пустующую яичную зону! Но этой таблицы нет вообще в субфайле уровня…
Тогда может она в EXE? Нет, там бы это заняло кучу места, ведь на каждый уровень своя таблица, а она обычно не маленькая (кстати, окончанием служат «FFFF0000…», когда и Type и Msg равны 255)
Значит есть где-то субфайл, в котором располагаются таблицы сразу на все уровни – я провёл поиск по WAD, и ничего не нашёл… Да чего уж там, я много-много раз прошаривал весь ISO в надежде найти хоть кусочек это таблицы (вдруг из самого STR берётся?). Но не было ровным счетом ничего.
Значит, таблица генерируется динамически во время игры. Но чёрт, данные-то всё равно берутся откуда-то, не создаются ж из ничего! http://klimaleksus2.ucoz.ru/Files/STARS/hack_1.png Стал искать других ссылающихся на адрес (который указывает на Type=0 и Msg=0 – начало таблицы), но нашёл совсем немного – максимум лишь на чтение. Из-за того, что адресация прокачивается через «вечный ноль» и передаётся в функции, бедный дизассемблер не может обнаружить ссылку и не позволяет мне обойти по всем ссылающимся.
Тогда я стал копать соседние функции (несколько указателей, которые располагались после ссылки на таблицу). Сначала я по коду пытался понять, что они делают. Потом я просто попробовал найти их код – и хоп! Его снова нет…
Ни в SCUS ни в WAD. Ну не могут же ассемблерные функции появится сами собой!? Причём в каждом уровне это в общем случае ДРУГИЕ функции. Значит они откуда-то расшифровываются в память. Но простой поиск не давал ни одного хорошего совпадения.
Тогда я стал занулять вызовы функций или менять их местами. Некоторые вешали игру, но больше 80% проходили безболезненно. Я копал в SunriseSpring, и несколько первых функций отвечали за рождение яйца – если их нарушить, то оно либо не появлялось (от Хантера), либо не рождалось. Несколько следующих функций отвечали за спецэффекты. И вот это было интересно! Пыль, брызги, вспышки, звёзды, осколки, дымок – это такие небольшие спрайты, которые появляются и сразу исчезают. Так вот, после зануления функции они перестали исчезать! Они просто появлялись и оставались в 3D пространстве. Это было круто, никакими скриншотами не объяснить… После Зои, рождения яйца, разбивания банки или даже простого торможения остаются «спецэффекты». И они так и висят в воздухе застывшими как в желе! Можно даже выпускать пламя несколько раз – на концах остаются клубы дыма.
Мне сразу же стало любопытно, что будет, если ОЧЕНЬ много раз вызвать спецэффекты – заполонят ли они всё вокруг, или игра вылетит с нехваткой памяти. Оказалось всё гораздо умнее – когда спецэффектов становилось слишком много (причём независимо от типа – дым, звезда или искра), самые старые начинали исчезать. Но не сами по себе, а строго при появлении новых. Это неплохо раскрывает внутреннюю структуру их хранения. Скорее всего там определённый массив-очередь, и при добавлении новых смещается его индекс верхушки, при уничтожении – индексы высвобождаются. А если не удалять объекты, то индекс пройдёт кругом и начнёт затирать самые старые спецэффекты. Вернее даже не затирать, а лишь переопределять их.
Эффект не долговечен – до перезапуска уровня или выхода в другой, также имеет некоторые плохие побочные действия, но можно и как PEC код выложить…
Все оставшиеся последующие функции так или иначе были связаны с водой – плавание на поверхности, в глубине, поведение камеры в воде, скорость плавания, управление плаваньем, притяжение и столкновения – доходило до того, что прыгнув в ручей дракончик начинал «плавать» в его плоскости по воздуху не падая. И отменить было нельзя, прыжок тоже не работал. Была функция, отвечающая за нажатие треугольника.
Отдельного внимания заслуживает ещё одна функция, которая отвечала за всю живую природу вплоть до Спаркса. Стоило её отключить, как стрекоза замирала на месте и больше не следовала за дракончиком, но при этом оставалась живой и анимация воспроизводилось. Также не работали таблички, герои не разговаривали, надписи на порталах не возникали. Бочки оставались материальными, но не разбивались от огня; при бодании оставались на месте, но Спайро пробегал сквозь них. Овцы мирно щипали травку стоя на одном месте, из тоже нельзя было поразить. Камень, который надо разбить головой чтобы получить яйцо, больше головой не разбивался. Если же потом вернуть вызов функции на место, то все объекты снова оживали, Спаркс прилетал, а овцы разбегались.
Из всего этого я делаю заключение, что в эту область памяти помимо таблицы Type+Msg=STR выгружается ещё и сам код уровня! Конкретно поведение тех или иных объектов. Сами объекты отдельно, а их код здесь.
Поиск чего-либо по этим указателям по-прежнему ничего не давал, и я вдруг попробовал найти сами указатели. Мне казалось, что уж они-то точно должны на лету генерироваться…
Но я нашёл полное совпадение! И оно вело… на следующий субфайл! Все же помнят, что нумерация уровней идёт через один (98, 100, 102…). Так вот, соответствующий блок указателей хранится в самом начале следующего же субфайла (99,101,103…). Причём это начало полностью совпадает с тем, что находится в памяти. http://klimaleksus2.ucoz.ru/Files/STARS/hack_2.png И да, если компарить файл с памятью эмулятора, то «место, где должна быть таблица» оказывается в самом конце последующего субфайла. А при более детальном рассмотрении её даже видно! Однако данные совершено не те…
Это шифровка! Смотрите: http://klimaleksus2.ucoz.ru/Files/STARS/hack_3.png Ясно же, что за «00000000h» отвечает «CCC90880h». Я даже угадал способ шифрации. Сперва грешил на сложение и вычитание, но это оказался старый быстрый симметричный XOR ! все данные по 4 байта ксорились с неким ключом-паттерном, который записан… где бы вы думали? В заголовка файла! Начиная с четвёртого байта. А с двенадцатого байта идёт указатель на таблицу треков…
Мне нужна ссылка на начало шифрованной области. Её нигде не было, но исследовав несколько «следующих» субфайлов от разных уровней, я понял, что она мне и не требуется вовсе. Достаточно пропустить первые четыре байта, потом считать четырёхбайтный ключ, далее считывать по четыре байта и сверять с ключом – пока не совпадает – не трогать. Как только найдено полное совпадение (а это ВСЕГДА начало шифрованной области) – начиная со следующих же четырёх байт просто ксорить каждые считанные четыре байта с ключом. И так до конца файла!
Так мало того, ещё и зашифровывается обратно абсолютно так же – пропускаете декодированный файл через тот же механизм – он считает неизменный ключ и пойдёт его искать, а как найдёт (первое вхождение мы оставили нетронутым) – поксорит все нижеследующее с там же ключом, а в силу симметричности XOR, это восстановит оригинальные данные.
Ура! Теперь мы во-первых, знаем для чего нужные «следующие» субфайлы, а во-вторых, можем как считывать, так и менять сопоставление персонаж+фраза=озвучка. Остаётся открытым вопрос, зачем игра вообще что-то шифрует? Ей же гораздо проще вытащить неизменные данные, как начало этого же файла. Если же разработчики специально хотели скрыть эти данные от взломщиком/переводчиков, то могли бы придумать шифровку поизобретательнее чем банальный XOR, и ещё к примеру, закодировать сами указатели в заголовке, по которым я вычислил субфайл. Единственное, что я знаю, зачем применяют шифровку простым XOR – исключительно для блокирования автоматических попыток сличить данные между файлами. Например, чтобы встроить вирусный код в тело своей программы так, чтобы ни один антивирус его там не обнаружил. Почему XOR? Самый-самый быстрый. Почему не что-то помощнее? Нет смысла – машина и XOR не распознает, а от человека не спасёт и ничто другое…
Возвращаясь к сути полученных данных – для изменения номера STR на свой, нужно во-первых, найти Type персонажа, который его говорит, а также Msg номер реплики (всё это легче сделать в самой игре через тот же чит). Потом узнать начальный сектор целевого трека в SPEECH.STR, а также его XA канал. Остаётся загадочная «длительность» и единицы её измерения. Если поставить «0», то звука мы не услышим, а текст выведется на экран также быстро, как выводится текст от табличек. Если поставить большое число (стандартный разброс от «128» до «4512»), то текст будет выводиться по буковкам очень медленно, и продолжит делать это даже когда звук закончится. Что интересно – число никак не связано с длиной строки – одинаковая «длительность» заставит и длинную и короткую строки выводится на экран ровно нужное количество времени, в том смысле, что короткая строка будет выводиться дольше, а длинная наоборот, быстрее. А это означает, что игра ещё и вычисляет расчётную длину строки перед её выводом, умножает на нужный коэффициент и учитывает «длительность» из таблицы треков. А я-то думал, как все фразы на экране оканчиваются одновременно с голосом? Ну а для проекта перевода это означает, что НЕЛЬЗЯ в отличие от пиратов, завершать строки пробелами, даже если мы уместились в {Разницу} и наш текст оказался короче оригинального. Нужно оборвать строку нулём! Тогда её длина будет соответствовать скорости выведения на экран. А печатным строчкам в меню это хороший повод появиться ровно посередине экрана…
*Продолжение ниже*
Сообщение отредактировал aleksusklim - Среда, 10.07.2013, 01:40 |
|
| |
DrWho | Сообщение # 400 Воскресенье, 07.07.2013, 19:07 |
Мудрый Дракон
Почетный Житель
«1371»
Где: Не в городе Драконов
|
Продолжение ниже.
Сообщение отредактировал DrWho - Среда, 10.07.2013, 01:33 |
|
| |
aleksusklim | Сообщение # 401 Понедельник, 08.07.2013, 09:22 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
*Начало выше*
Вспомним, с чего мы начали, а именно – поиск и извлечение строк из данных для объектов. Замечательно, первый байт показывает «Type» героя, потом семь байт неизвестно что, затем «FF000000h», а далее сразу указатель на его имя, за ним – перечисление указателей на реплики.
Чтобы добраться до «Type» из нижнего списка, нужно дважды разыменовать указатели: из листа на 88, оттуда по четырём первым на «Type», и если через семь байт расположен «FF000000h», то скорее всего дальше следует имя и тексты. Однако «FF000000» встречается в объектных (4,6,8,10…) суб-субфайлах очень часто, и велика вероятность холостого срабатывания. Можно ещё и парсить строку по критериям вроде минимальной длины, допустимых символов, чтобы начиналась с заглавных букв.. но всё это уже почти перебор, а я ищу более строгий однозначных метод.
Оказалось, что после указателя на 88-байтовый объект с текстом, через несколько строк в листе обязательно будет указатель на 4 байта, которые ссылаются на ИМЯ этого персонажа! А потом и далее через четыре байта по всем его репликам. Получается, что текст это как бы тоже объект…
Легко запутаться. Суть в том, что на каждую фразу ровно один указатель, он находится выше неё. Рядом с ним есть указатели на имя и остальные фразы. До них идёт «FF000000h» а ещё левее – дополнительные переменные объекта. Основная ссылка в листе ведёт далеко вверх на 88. Оттуда по указателю – на «Type». Если отойти на 12 байт правее, то встретим указатель на имя, НО туда же ведёт одна из последующих ссылок в листе. Для этого нам даже не надо знать объект – разыменовываем первую – попадаем на 4 байта, разыменовываем их – вот и имя персонажа. А если также поступить с несколькими следующими строчками листа, то выйдем на все тексты. Получается, что можно либо найти объект и идти вправо в поисках текстов, либо найти тексты и щупать слева их объект…
Я объединил оба метода! Я храню массив «потенциальных» ссылок на имена. Если разыменовывая предполагаемый объект я обнаруживаю флаг «FF000000h», то помечаю «номером Type» место справа, где должна быть ссылка на имя. И если (и только если) это действительно ссылка на имя, то разыменовывая некоторый следующий объект я обязательно наткнусь на ячейку массива, в которой что-то записано (это номер Type потенциального объекта), а значит, смогу тут же считать и имя, и все реплики (даже не дожидаясь указателей на них, просто помечу как «использованные» на всякий случай), и при этом буду знать Type!
Всю эту систему я отлаживал довольно долго, пока она не стала работать ровно так, как должна – вытаскивать абсолютно все тексты с сохранением Numeration. Оказалось, что иногда ссылки на реплики ведут в пустые ячейки, но их нельзя игнорировать, ибо «Msg» повязан на порядковом номере, и если пустую строку выкинуть, то все последующие номера реплик этого персонажа будут сбиты. «Скачок» Msg наблюдается и в чит-меню в самой игре, так что несуществующие строки (коих например полно на табличках Шейлы начиная со второго мира– они определяют текст, который должен быть показан, если кенгуру «ещё» не спасена, что в принципе невозможно при легальном прохождении; мы хотели туда свой рекламный текст вставить… думаю, что теперь мы сможем вставить туда ещё и свою озвучку – да, кажется таблички тоже могут «говорить»…) я решил заменять на прочерки «-», как и «несуществующие имена» (тех же самых табличек например).
Осталось всего ничего – вытащить LBA начальные сектора всех треков из STR, чтобы сверять с ними содержимое из таблицы сопоставления «Type+Msg». Нет, я больше не хочу доверять ни jPSXdec, ни три часа рипать образ через RipBin. У меня уже есть утилитка для замены треков в STR, я перечитал её код, и написал такую прогу, которая сможет считать XA каналы и LBA начала секторов напрямую из ISO образа! Реально, мне нужно по одному байту с каждого сектора, зачем для этого извлекать весь SPEECH.STR ? Зато нужно знать как структуру формата образа (24/2048/280), так и размер + LBA самого STR файла в нём. Это уже эмулирование и WinHex… Зачем? Все нужные данные лежат в корневом секторе, это номер 22. Там таблица размещения файлов. Вообще документацию ISO9660 надо бы почитать, а не как я – посмотрел и взломал формат самого образа.
Короче, шняга такая. В 22-ом секторе находится описание корневой папки. Первые два байта – длина структуры. За ней следует очередная структура, где первые два байта снова показывает длину. Если длина равна нулю, то список закончился. Структуры описывают либо расположение файла, либо папки, либо текущий/родительский каталог. Четыре байта (после размера структуры) – LBA первый сектор объекта. Потом четыре байта с чем-то непонятным. За ними четыре байта – размер объекта. Ещё 18 байт непонятного назначения, и один байт – длина имени объекта. Следом за ним – само имя, терминированное нулём. После него идёт что-то ещё, но оно мне уже не нужно. «Объект» может быть либо файлом, либо папкой, либо сигнатурой каталога. У последней нулевое имя, так что её отличить легко (она показывает текущий LBA той папки, в которой вы находитесь). Файл от папки отличается чем-то внутренним, во что я не стал вникать, а решил просто сканировать имя – если оно заканчивается на «;1» (вернее просто предпоследний символ – точка с запятой), то это файл, а иначе – папка.
Находя папки нужно рекурсивно перемещаться в их сектора и снова считывать структуры. Поскольку записи каталогов всегда первые две (свой и корневой), то вместо них можно просто по желанию выводить путь текущей папки, а ещё возможно опционально отключать рекурсию или не выводить полные пути.
Правда я не тестил свою программу на образах с каталогами, которые занимают более одного сектора (2048 байт), скорее всего, она не сработает, но вряд ли для PlayStation есть диски, на которых будет ТАК много файлов в одной папке, и уж тем более, в Spyro.
Вроде и всё, но для полной картины я решил придумать способ, как вытащить названия миров из SCUS. Поскольку указатели глобальные, то надеется на них бессмысленно (я не хочу ни хардкорить, ни опционально задавать адрес размещения EXE в памяти игры, ибо у GH он один, а у обычной версии совсем другой). Взял навскидку адрес «4096» и незатейливыми свериваниями (пока четвёртый байт каждого квартета равен 00h или 80h – чистой воды указатели) продвигаюсь к «Pete the Mountain Goat», который пропускаю просто по известной длине, а уж потом считываю список разделённых нулям строк.
Такой же шаткий парсинг пришлось применить и для нахождения таблицы TypeMsg в раскодированном «следующем» субфайле (указатель я опять не мог использовать по тем же причинам) – шёл снизу вверх от Type=255 и Msg=255 пока не встречу Type=0 и Msg=0; и даже для считывания листа ссылок на объекты и строки в объектных суб-субфайлах – не просто снизу вверх игнорить пустоту: в некоторых уровнях там располагались громоздкие указатели на память, так что сначала искал «не ноль», потом что-то «меньше размера файла но больше нуля», а затем уже «ноль», разворачивался и шёл вниз.
Теория закончилась, начинается практика. Я ведь написал аж 10 программ на Delphi ! Некоторые общего назначения, другие чисто под Spyro. Одни могут применяться независимо, а некоторые лишь в связке с остальными.
Общее управление проходит через «.bat» сценарии, и как обычно нужно «перетаскивать».
Файл «Namespace.bat» определяет имена программ, сценариев и рабочих файлов, его вызывает почти каждый скрипт.
Кстати, способ извлечения файлов напрямую из образа тоже пригодился, и он работает не только на BIN/ISO, но и на MDF, надо лишь выставить «24/2048/376». А также, для пиратских образов нужно знать имена файлов «WAD.WAD», «SCUS_944.67» и «SPEECH.STR» (если есть).
Поэтому первый сценарий задаёт параметры для образа. Например для GH это Для моего пиратского сборника (он у меня в MDF)
Основную подготовку выполняет автоматический скрипт «Kernel.bat» Он извлекает таблицу размещения файлов с образа, потом пытается найти в ней «SPEECH.STR», и если это удаётся, то запускает программу, которая извлечёт из образа номера секторов для всех треков. Но этого мало, затем другая программа перестраивает список так, чтобы он соответствовал нашему Numaration (если XA канал ниже 32, то нумеровать с нуля, а если нет, то с тысячи) Далее в той же файловой таблице обнаруживаются SCUS и WAD, а потом извлекаются в рабочую папку (убедитесь, что на жёстком диске достаточно места!)
Остаётся три этапа – работа со SCUS, работа с WAD и сбор общего списка. Их выполняют разные скрипты.
«ScusWork.bat» извлекает из EXE названия всех уровней, а потом пропарсивает их так, чтобы номер «98» стал «098».
«AllLevelsWork.bat» работает с WAD куда комплексное. Он поочерёдно извлекает по два субфайла от 98 до 170 (171) и передаёт их для «OneLevelWork.bat». Тот сначала расшифровывает второй субфайл, потом вытаскивает из него таблицу треков, затем начинает поочередно извлекать из первого субфайла подуровни от основного четвёртого до «пока не перестанет извлекаться», ищет описанным способом все тексты подуровня и вызывает основную программу, которая сольёт их вместе с номерами Str в один большой документ (который каждый раз дополняется, а не перезаписывается).
Остаётся лишь финально пропарсить этот документ, чтобы вставить туда названия уровней и выдать читабельные версии. Сейчас на выходе есть один общий красивый документ с текстами в стиле Хранилища, один простой текстовый список, табличный формат для Numeration, файл для моей базы данных и простой список всех персонажей, разделённый по уровням, а глобальные герои, появляющиеся более чем в двух уровнях, вынесены в его начало.
Там же определяется длительность (для Numeration) каждого трека в STR исходя из количества XA секторов, занимаемых дорожкой. Сразу же пришла простая формула, связывающая размер XA с «длительностью», записанную с таблицу TypeMsg: это количество целы занятых секторов, умноженное на 32. Например, самые короткие реплики занимают пять секторов, значит «длительность» равна «(5-1)*32» = 128; Самая большая фраза Бианки имеет длительность 4512, значит «(4512/32)+1» = 142. Примерная реальная продолжительность трека в секундах вычисляется делением количества секторов на коэффициент «4,6875», погрешность составляет менее двух десятых секунды («с запасом») для первой реплики Бианки и чуть больше 0,7 для самых коротких треков.
*Окончание ниже*
Сообщение отредактировал aleksusklim - Среда, 10.07.2013, 01:34 |
|
| |
DrWho | Сообщение # 402 Среда, 10.07.2013, 01:33 |
Мудрый Дракон
Почетный Житель
«1371»
Где: Не в городе Драконов
|
Продолжение ниже.
Добавлено (10.07.2013, 01:31) --------------------------------------------- Ну и чтоб пост не пустовал, отпишусь: Цитата (steeldragon) Во всех субфайлах хранится информация и помимо этой. (Окружение уровня, текстуры, и так далее. Подробнее: Энциклопедия - Структура субфайлов WAD). А в котором можно шрифт извлечь и вставить в GH? Я хочу просто потестировать этот момент в атласе, меню пауза? субтитрах и катсценах. И чем, я пробовал тайловым (как-то там) штукой сделать - но там даже эксешника нет - а расписанный метод я не понял совсем.
Добавлено (10.07.2013, 01:33) --------------------------------------------- Ну и вопрос на засыпку: чем можно поковырять STR файл? Хочу попробовать озвучку перенести в STR от GH.
Сообщение отредактировал DrWho - Среда, 10.07.2013, 01:36 |
|
| |
aleksusklim | Сообщение # 403 Среда, 10.07.2013, 01:34 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
*Начало выше*
Ну что ж, осталось лишь описать интерфейсы всех своих программ и скриптов…
«FastExportWad.exe» – клон «FastWad», который может только извлекать субфайлы. Плюс, меньше пишет на консоль. Принимает путь к WAD, имя куда извлечь и номер субфайла.
«SpyroLabels.exe» – вытаскивает названия уровней из EXE. Принимает путь к SCUS, имя для сохранения текстового документа, номер последнего уровня и шаг нумерации. Поскольку располагаются они в обратном порядке, последние два аргумента будут «170 -2» Формат выходного файла: первая строка – номер уровня, следующая строка – полученное название. И так до конца файла (пустая строка).
«SpyroObject.exe» – основной алгоритм поиска объектов и их текста в субфайлах подуровней. Принимает извлечённый четвёртый (шестой, восьмой…) суб-субфайл, имя для выходного документа и максимальную длину строки, которую я определяю как 1024 символа. Формат выходного файла: строгий. 4 символа – разделитель «----» или число – длина строки (десятичный формат с начальными пробелами, не более «9999»). Затем 1 символ табулятора. Далее если это строка с разделителем «----», то три символа – десятичное число – «Type» индекс объекта (не более «255») и два символа переноса строки; если строка начиналась с числа-длины, то после табулятора идут столько (длина) символов – текстовая строка, оканчивающаяся двумя символами переноса строки. Если предыдущая строка была с разделителем и числом «Type», то текущая строка отражает имя объекта. Все следующие строки построены по принципу этой, но в них в корректной последовательности «Msg» указаны все реплики персонажа. До тех пор, пока вместо длины не появится очередной разделитель «----». Файл начинается с разделителя и заканчивается разделителем и табулятором.
«SpyroCodeCrypt.exe» – декодирует и шифрует обратно «следующий» субфайл уровня. Если передан один аргумент – имя субфайла – программа модифицирует его самого, если два аргумента, то первый считывается, а во второй сохраняется результат.
«SpyroCodeCrypt.exe» – Распечатывает таблицу «TypeMsg» из расшифрованного «следующего» субсубфайла. Принимает декодированный субфайл и имя документа для сохранения. Формат выходного файла: строгий, но читаемый. По строкам расписаны значения «Type», «Msg», «Канал XA», «длительность» и «LBA начальный сектор». Потом пустая строка. И так до тех пор, пока не выйдет «255/255/0/0/0» (всегда в конце). Каждая строка после нескольких табуляторов выносит «в центр» экрана те же самые данные, но уже с подписями. Программа также способна принимать свой же изменённый документ, чтобы модифицировать исходный декодированный субфайл. Для этого передайте ей любой третий аргумент (из второго данные пишутся в первый). Внимание, используются только значения из левого столбца в документе! И да, лучше ничего не добавлять и не забирать, а лишь изменять цифры. В конце всегда должен быть «255/255/0/0/0», иначе игре же хуже; а первым должен быть «0/0/…», иначе второй раз не откроется. В теории можно расширить список, но на практике я это не проверял, скорее всего общий размер фигурирует где-то в другом месте.
«CdSector.exe» – десять аргументов… Строит список файлов на образе диска. Первый – путь до образа, второй – имя для сохранения документа. Третий – начальный сектор с корневой папкой (обычно «22»), четвёртый-пятый-шестой – настройки «заголовок-тело-хвост» формата образа. Для ISO это «24», «2048», «280»; для MDF это «24», «2048», «376». Что обозначают эти числа я писал ещё в японской теме для RipBin. Здесь же более общий случай, поэтому «тело» обычно всегда будет «2048». Последние четыре аргумента – опции: «0» отключено, «1» (или что угодно) – включено. Первая – обрабатывать ли все каталоги рекурсивно? (Да – выведет и из всех дочерних, Нет – только из указанного сектора, если третий аргумент «22», то лишь из корневого). Вторая – Показывать ли найденные папки? (Да – обнаруженные папку будут выведены наравне с файлами, Нет – папки может и будут обработаны рекурсивно (предыдущая опция), но не выведутся в документ). Третья – выводить ли имя текущего каталога? (Да – в каждом каталоге перед списком окажется его имя и адрес, Нет – список будет содержать только найденные папки (если предыдущая опция) и файлы). Четвёртая – Отображать ли полные пути? (Да – перед именем каждого файла и папки будет выведен путь до него, начинающийся с «\», Нет – имена будут выводиться так, как они записаны в структуре каталога) Формат выходного файла: зависит от настроек, но принцип такой: первая строка – два числа и символ (через пробелы) : LBA первый сектор объекта, размер объекта в байтах и флаг: «P» для текущего каталога, «F» для найденного файла, «D» для найденной папки. Следующая строка – имя объекта. В конце списка пара чисел «сектор размер» будет «0 0». Рекурсия каталогов проходит только после полной распечатки текущего, то есть сначала перечисляется содержимое имеющейся папки, а за ней программа последовательно переходит по папкам сверху вниз. Файлы имеют в конце имени «;» и цифру, папки ничего, а информация о текущем каталоге оканчивается на «\».
«GetFromDisc.exe» – извлекает файл из образа диска напрямую. Аргументы: путь к образу, имя для сохранения, LBA первый сектор файла, размер файла в байтах, и троица настроек формата образа «24», «2048», «280» (или «24», «2048», «376» для MDF). По сути, извлекать файлы можно с любого типа образов, если они устроены одинаково с ISO/BIN/MDF, если рассмотреть неизвестный формат получше и подобрать эти числа. У программы нет функции засовывания файла обратно в образ, ибо ни к чему хорошему это не приведёт (сгорают контрольные суммы), зато её код до смешного простой.
«StrDump.exe» – распечатывает начала треков и их каналы из STR файла на образе. Аргументы один-в-один такие же, как у предыдущей программы (GetFromDisc). Формат выходного файла: список. Четыре числа в каждой строке: LBA первый сектор трека, его размер в секторах, номер XA канала, «Sector stride» – число, показывающее через сколько других треков перемешены нужные (jPSXdec кстати, это тоже запоминает в свой «idx»). Завершается флагом «0 0 0 0».
«SpeechList.exe» – Считывает выходной документ от предыдущей программы (StrDump) и строит на его основе наш Numeration список. Два аргумента (угадайте какие?) Формат выходного файла: тоже список из четырёх чисел: STR номер трека (в нашем смысле), LBA первый сектор, количество секторов трека, и его XA канал. Кончается ничем.
«TextMerge.exe» – сливает все необходимые данные в единый текстовый документ. Первый аргумент – выходной файл от «SpyroObject.exe»; второй аргумент – выходной файл от «SpyroStrInfo.exe»; третий – выходной файл от «SpeechList.exe»; четвёртый – документ для сохранения: если существует, то происходит дозапись, если нету – создаётся новый; пятый аргумент опциональный – номер уровня WAD (строка) : если передан, то сразу записывается в выходной файл со звёздочки («*098»), если не передан – допись просто происходит через несколько строк – это необходимо чтобы отличать уровни от подуровней – при обработке уровня надо передать его номер и он впишется, а подуровни просто потом допишутся через пары пустых строчек. Формат выходного файла: огромный список! Строки, начинающиеся со звёздочки – начала уровней., они отделяются пустыми строками с обеих сторон (первая строка по идее должна сразу начинаться со звёздочки). Очередная непустая строка – имя персонажа. За ним перечисляются реплики до тех пор, пока не встреться пустая строка; каждая реплика занимает две строчки: на первой четыре числа – наш номер STR, LBA первый сектор трека, размер трека в секторах, и «длительность» фразы из игры (номер канала XA я посчитал лишним и вообще ненужным); а на следующей строке уже сам текст реплики. После пустой строки снова ожидается имя персонажа и его реплики, но: если пустых строк две или более – это разделитель подуровней; если встретилась звёздочка – на этой же строке имя нового уровня. Если фраза не наёдена в STR и не может быть сопоставлена в номером для Numeration – вместо четырёх чисел выводится «0 0 0 0»
«FinalOutput.exe» – Последний монстр, принимает семь аргументов. Первый: имя выхода от предыдущей программы (TextMerge), ещё пять файлов являются выходными: нормальный, текстовый, табличный, список персонажей, код базы данных. Последний седьмой аргумент – текстовый документ из-под «SpyroLabels.exe» (дополнительно прошедший через «FixNames.bat») с названиями уровней, сопоставляемыми с их номерами. Форматы выходных файлов: Нормальный: разделитель уровней «~~~~~~~~» за ним после переноса номер WAD, двоеточие и после пробела название уровня; разделитель подуровней «~~» отдельным абзацем; имена персонажей в круглых скобках с двумя пробелами в начале, за ними строки, начинающиеся с номера STR в квадратных скобках с табулятором на конце, и на этой же строке сам текст реплики. Пустые строки отделяют персонажей с их текстом и разделители. Текстовый формат – простой список: номер STR, номер уровня WAD, имя персонажа, двоеточие с пробелом и текст реплики. Цифры выровнены пробелами, а после них вставлены табуляторы. Табличный – почти как текстовый, но везде стоят «||» для наших wiki-страниц. Под номером уровня WAD через «<br / >» ещё и вставлена приблизительная длительность трека в секундах (формулу я приводил выше). Список героев – всё просто: сначала «глобальные», потом через разделители «~~~ WAD ~~~» локальные (если есть). Формат базы данных пока описывать не буду, расскажу о нём когда про эту самую базу буду говорить.
Ну и все сценарии:
«FinalWork.cmd» – запускает «FinalOutput.exe» и сортирует текстовый и табличный вывод через «Sorting.bat».
«AllLevelsWork.bat» – принимает имя WAD-файла и делает кучу действий, основное – вызов «OneLevelWork.bat» в цикле.
«FixNames.bat» – принимает вывод от «SpyroLabels.exe» и добавляет двузначным цифрам начальный ноль, перезаписывает исходный файл.
«ISO_rip.bat» – принимает ISO образ GH и производит все действия над ним.
«Kernel.bat» – внутренне использование, принимает образ от «ISO_rip.bat».
«MDF_rip.bat» – клон «ISO_rip.bat», настроенный на ParaDoX образ MDF (и там нет SPEECH.STR). Можете заменить переменные в файле под себя.
«Namespace.bat» – вызывается всеми, закрепляет имена файлов, можете отредактировать его.
«OneLevelWork.bat» – принимает три аргумента: первый – субфайл уровня, второй – следующий субфайл, третий – номер основного уровня (ноль в начале для «98» добавляется внутри). Сливает текст из одного уровня куда следует.
«ParseLba.bat» – принимает вывод от «CdSector.exe» и имя интересующего файла (без «;1» на конце). Записывает в переменные %t_lba% и %t_size% сектор и размер если нашёл, и нули, если нет.
«ScusWork.bat» – принимает SCUS и вытаскивает оттуда названия уровней. Сам вызывает «FixNames.bat».
«Sorting.bat» – использует «sort», чтобы отсортировать переданный файл. Перезаписывает его.
Есть ещё несколько самостоятельных батников: (все они принимают один файл и выводят результат по тому же имени с новым добавленным расширением, просто наблюдайте появление нового файла рядом с исходным)
«CdSector_ISO.bat» – перетащите на него образ и он построит список файлов на нём. «CdSector_MDF.bat» – клон для MDF.
«Code_crypt.bat» – перетащите на него «следующий» субфайл, и он его раскодирует/закодирует.
«Get_levels.bat» – перетащите на него SCUS и он вытащит оттуда имена файлов.
«Get_SCUS.bat» – перетащите на него образ GH, и он вытащит оттуда SCUS. «Get_WAD.bat» – клон для WAD.
«Spyro_text.bat» – перетащите на него объектный субфайл, и он вытащит вам текст оттуда.
«Str_export.bat» – перетащите раскодированный «следующий» субфайл и получите TypeMsg таблицу треков. «Str_import.bat» – если изменили вывод предыдущего то можете всавить его обратно – перетаскивать надо по-прежнему за раскодированный «следующий» субфайл.
«StrDump_GH.bat» – перетащите на него образ GH, и он вытащит вам информацию о секторах.
Кстати, многие сценарии ломаются, если не передать какой-то параметр или сделать это неправильно. Никаких звёздочек не пишите, а том там есть команды удаления файлов, будет плохо если попадёт del /f /q * А все программы вообще не запустятся если не соблюдать количество аргументов. Всё что они пишут на консоль – название и статус: «OK !» – доработала до конца, «ERROR !» – возникла ошибка. Никакой справки по аргументам в них нет, лучше читайте вызовы из bat-фалов.
Последнее, что нужно сказать. Я понял, откуда у «панд» так много текста! Помните подуровень, где надо было на лодке защищать от носорогов целую кучу панд? Так разработчики тупо копирнули код одной из «говорящих» панд более десяти раз. При этом в игре текста нет, а в файле тонны его копий… (забавно, мои пираты его даже по-разному переводили!) В подуровне MoltenCrater где надо было прикрутить головы человечкам – всех их зовут «Rocky». Текстов нет, а имена есть. У всех трёх козлов одинаковое имя, текст и номера STR. Но имя меняется во время игры, они берётся из EXE. В MidnightMountain Бианка скопирована дважды, ибо она появляется как в начале уровня, так и возле ворот Колдуньи. И весь её текст тоже сдублирован дважды, со ссылкой на те же треки из STR (если найти где концы какой реплики, то в ненужный текст преспокойно влезут все данные для моего Вихря!) Такое встречается много где, например у Шерифа. А у Зои на табличках для Спаркса есть «Yes.» да «No.» и как ответа на вопросы и как отдельные реплики.
И вообще, надо новый Нумерейшн делать…
Вот весь вывод для Greatest Hits: http://klimaleksus2.ucoz.ru/Files/2/GhText.rar
А вот инвалидский без STR (GH’овские не подошли по LBA секторов) от Парадокса: http://klimaleksus2.ucoz.ru/Files/2/ParaDoX.rar
Ещё раз ссылка на мой архив: http://klimaleksus2.ucoz.ru/Files/2/MegaTextRip.rar
but nobody came
Сообщение отредактировал aleksusklim - Среда, 10.07.2013, 01:37 |
|
| |
steeldragon | Сообщение # 404 Среда, 10.07.2013, 10:42 |
Старейшина Драконов
Редактор
«422»
Где: Не в городе Драконов
|
Мощно. Надо будет посмотреть, особенно SpyroObject...
Цитата (aleksusklim) Ясно же, что за «00000000h» отвечает «CCC90880h». Я даже угадал способ шифрации. Сперва грешил на сложение и вычитание, но это оказался старый быстрый симметричный XOR ! все данные по 4 байта ксорились с неким ключом-паттерном, который записан… где бы вы думали? В заголовка файла! Цитата Остаётся открытым вопрос, зачем игра вообще что-то шифрует? Насколько я помню, именно таким (или почти таким) способом шифровки пользуется libcrypt: Цитата (consolecopyworld.com) The routine that performs the check on the disk uses the hardware registers of the CD-ROM(1F80180X) directly and it memorizes the temporary data in the scratchpad memory (1F800000-1F8003FF). It then calculates a 16bit number with a recursive algorithm. (Magic Word!) this magic word is then used as a parameter for the subroutines, the value is stored in some register of COP0 (coprocessor of system), leaving it in the low part of the BPC register until the program finishes. ... The second routine, that that checks the presence of the MagicWord in the BPC, is implemented in different ways in the various games that use it: some perform the check immediately (FF8 for example), others wait until a certain level (Spyro2)...
|
|
| |
aleksusklim | Сообщение # 405 Четверг, 11.07.2013, 16:51 |
фдулыгылдшь
Редактор
«1066»
Где: Не в городе Драконов
|
Цитата (steeldragon) Насколько я помню, именно таким (или почти таким) способом шифровки пользуется libcrypt
А можно полную ссылку на источник? По тексту я нашёл лишь: http://badtaste.free.fr/psx/outils/cours.html
А вообще да, это отголоски libcrypt.
Я провёл эксперименты над Платиной, причём у меня откуда-не-помню есть «.sub» файл, который если положить рядом с образом, то Epsxe эмулирует его как оригинальный диск, и в Cloud Spires не вылетает ошибка. Следовательно я имел возможность дампить память после ошибки защиты и без неё, чтобы найти отличия.
И да, во всём виноват XOR. Но не совсем тот, который расшифровывает «следующий» субфайл. Я поковырялся в коде и понял, что такое «ключ» для расшифровки. Оказывается это просто адрес, на котором нужно перестать ксорить! Система загружает «следующий» субфайл в память по статичному адресу, потом считывает ключ с четвёртого байта, потом прыгает вперёд ровно на «1000h» (4096 байт) и начинает производить XOR всех данных с ключом до тех пор, пока текущий адрес не станет больше или равен значению ключа. По сути наш ключ – это просто размер «следующего» субфайла плюс его адрес загрузки минус количество нулей на конце.
При правильной эмуляции с субканалом я сдампил память и сравнил её с файлом из WAD. Естественно, не совпадало. Тогда я расшифровал его своей программой (можно выпустить новую версию, раз теперь известно назначение ключа) и сравнил ещё раз: основная часть совпала, а небольшой участок остался шифрованным, но не так, как в остальном (WAD и дамп). С этими тремя я сравнил дамп при ошибке защиты, и в нём этот же участок выглядел опять неправильно. Через дизассемблер я нашёл функцию, которая им занимается, и там снова был XOR. Но сама функция находилась в этом же «следующем» субфайле, то есть была расшифрована первым XOR (который с ключом).
Выходит, что система защиты влияет на вторичный ключ, которым уже расшифрованный «следующий» субфайл расшифровывает небольшую часть себя. Откуда он берёт адрес, размер и ключ – не совсем понятно, но скорее всего, они просто вшиты в его тело (которое изначально шифровано первым XOR).
Второе наблюдение: если сохранить savestate при загрузке с субканалом, то потом даже если поменять образ на «нелегальный» и загрузить savestate, то больше ошибок не случится. Из чего я делаю вывод, что игра ОДИН раз сканирует легальность диска, а потом просто обращается к результатам проверки.
В описании libcrypt сказано, что хранит магическое слово игра чёрти где, и просто так в дампе его не будет. Но где-то в эмуляторе оно же есть! Плюс известно, как именно libcrypt сканирует диск – обращаясь к ячейкам-регистрам дисковода, их можно подсветить в ps2dis если загрузить ps1.map через «label definition file» в меню.
В итоге есть три способа обойти libcrypt на Платине: 1) Найти и уничтожить функцию, которая сканирует диск на легальность. Вместо неё написать код, который даст такой результат, будто прежняя функция подтверждает легальность любого диска. 2) Найти место, куда сохраняется магическое слово и само это слово (например, сравнивая память эмулятора в ArtMoney). Потом просто всунуть куда-нибудь функцию, которая положит магическое слово в нужную ячейку или регистр. Либо понять, откуда шифрованная функция в «следующих» субфайлах получает правильный или неправильный ключ, и сделать так, чтобы он всегда был правильным. 3) Полностью разрушить все XOR-шифрования, а в WAD поместить заранее расшифрованные (сдампенные) «следующие» субфайлы – игра будет пытаться их неправильно расшифровать, но мы занулим саму шифровку (поэтому независимо от правильности ключа, функция всё равно ничего не изменит), а поскольку файлы будут загружены уже расшифрованные нами, то всё будет «просто работать». Альтернатива – найти ключи-смещения-размеры для каждого уровня, которые использует вторичный XOR, и «перешифровать» части файлов так, чтобы неверный ключ наоборот становился верным и выдавал корректные данные (это легче чем кажется, XOR же симметричный!)
Результаты испытаний: 1) Найти первую функцию тестирования диска оказалось слишком сложно… Ссылок на аппаратные регистры дисковода очень много – я пытался занулять вызывающие их функции, но игра либо не загружалась совсем, либо наоборот работала абсолютно правильно (я пытался испортить алгоритм сканирования диска, чтобы на «легальном» образе происходила ошибка при входе в Cloud Spires) 2) Я искал через ArtMoney различия в памяти эмулятора при загрузках верного и неверного образов, а также после savestate на легальных и нелегальных запусках. То что нашёл не сработало… Пробовал сверять память, куда частенько идёт обращение в областях возле XOR, но полная её подмена на данные с «нелегального» запуска ничуть не помешали игре зайти в уровень корректно. 3) Предварительная расшифровка WAD может сработать, но есть две проблемы: нужно дампить и дизассемблировать КАЖДЫЙ уровень, что отнимет очень много времени… А во-вторых, ещё не факт что мы вот так просто найдём ВСЕ места, где игра обращается к системе защиты. Может она не только запуски уровней трассирует? (вылетание при касании воды, лавы или проигрышах (Buzz’s Dungeon) – это всё равно пока «следующий» субфайл, ибо там и описано поведение воды, камеры и спецэффектов. Но я имею в виду что-то совершенно другое, например появлении «Зои» понятно где…)
Что хочу сказать – Платину можно взломать. Но я пока не стану этим заниматься. Когда дойдём до перевода Платины – вернусь к взлому. Либо если кому интересно, можете сами попробовать снять libcrypt, или хотя бы проверить гипотезу о полном удалении ксора отовсюду. Насчёт GH – у нас всё в порядке. Даже если за первым ксором скрывается ещё один, то в обычном режиме он нормально расшифровывает критические данные, и игра всегда работает. Откуда берётся Зоя при загрузке подставной сохранёнки с карты памяти? Ещё не знаю, но во-первых я не уверен что это вообще связано с libcrypt, а во-вторых я не буду убирать Зою, мы же хотим в ней послание оставить!
but nobody came
Сообщение отредактировал aleksusklim - Четверг, 11.07.2013, 17:29 |
|
| |