Ну я и говорю что тут о Спайро речь идет а не о обсуждениях личной жизни - Кто ты? - Я - восьмое чудо света. - Почему? - Потому, что я на половину дракон и на половину грифон - А как это? - Не задавай мне лишних вопросов! - Почему? - * взглянул пылающим взглядом*.
Стоило отойти на пару-тройку лет, как уже ни в чем не могу разобраться. Я прям нубяра какой-то. Вчера перед тем, как лечь спать, решил запустить вторую часть Спайро. Выяснилось, что у меня проблемы с драйверами. Переустановил. Всё запустилось, немного поиграл. Вот тут-то я и решил зайти в эту тему, а так же отдельно почитать сообщения Алексея. Как вы наверное догадались, ночь я провёл без сна.
Я как думал. За кучу времени, с того момента, как я перестал следить за переводом, Алексей выпустит такую программу. Запускаешь, а там одна кнопка: «Перевести». Тыкаешь, и все переведено, да так, что Нора Галь позавидует. Ан нет, все оказалось гораздо запутаннее, чем я думал. Все взорвало мне мозг, а я окончательно убедился, что гуманитарий.
Итак, вот что я хотел сделать. У меня есть версия Спиро 2, в которую я (возможно) играл в детстве. Мне захотелось извлечь от туда текст. Желательно, одним файлом. Я скачал MegaTextRip.rar Но я в нём не разобрался. Я раз 5 прочитал, что делают эти программы, как Алексей их создавал, чем вдохновлялся. Но как приспособить их для Спуро 2? В каком-то батнике вроде заменил номера субфайлов, но текст все равно не извлекался. Можно какой-нибудь кратенький алгоритм, как извлечь тексты?
Второй вопрос. Что там с золотым шрифтом? Вроде же он взломан, да? Но хоть кто-нибудь попробовал его изменить? Хотя бы чуть-чуть? Ну там превратить букву N в И, и встроить обратно в игру.
Еще на счет SpyroWorld Viewer. Программа просто шедевральна и прекрасна. Пока у меня не работал эмулятор, просто летал по уровням. Вот только есть одно но. Это горячие клавиши. Их столько. СТОЛЬКО. Каждый раз я запускаю справку, пытаюсь их запомнить, возвращаюсь, понимаю, что опять забыл. У меня такой вопрос, а нельзя ли сделать в этой программе какое-нибудь меню? А из горячих клавиш оставить только полёт, да выбор уровня. А все прочие настройки: детализация текстур, угол наклона камеры, отображение информации, направляющих ит. д. вынести в отдельное меню. Тогда можно было бы вообще отказаться от справки.
imageshack, который и так у aleksusklim'a не грузился, умер. Или близко к тому. Поэтому оказались утерянными Табличка от MrModez и Текстура объявления "WANTED" из Dino Mines от MrModez. Бекапы у кого остались? И вообще полезны ли они? Или можно спокойно про них забыть. Если я правильно понял, табличка — это тихий ужас, с громадной надписью ПИРИВОД ОТ СПУРОРУЛМС В 2012 ГАДУ. А вот табличку Wanted жалко, но правда я ее не видел.
Так, а еще. Вот названия уровней, которые содержатся в EXE. Где они используются? Если это описывалось, просто киньте ссылку на сообщение, а-то я не нашёл.
PS. Покидайте сохранения от любых частей. Только нормальные сохранения, не хакнутые.
Сообщение отредактировал Serlutin - Вторник, 21.06.2016, 20:08
А никак. По крайней мере, я простого способа не знаю.
У меня есть скрипт для извлечения текста... Правда, он довольно-таки недоделан. Я могу извлечь текст, если вы мне пришлёте файл WAD.WAD с диска. Как вариант, могу выложить здесь весь комплект скриптов и попытаться объяcнить, как ими пользоваться.
ЦитатаSerlutin ()
Что там с золотым шрифтом? Вроде же он взломан, да?
Нет. Вернее, взломан, но только для Спайро 1. (да-да, с тех пор ничего особо не поменялось)
ЦитатаSerlutin ()
Табличка от MrModez
У меня точно где-то есть. Насчёт "WANTED" не знаю.
ЦитатаSerlutin ()
Вот названия уровней, которые содержатся в EXE. Где они используются?
По-моему, в Атласе, на экранах загрузки и в золотых буквах.
Сообщение отредактировал steeldragon - Вторник, 21.06.2016, 20:36
Я могу извлечь текст, если вы мне пришлёте файл WAD.WAD с диска.
Не, тут именно челлендж сделать это самому. В принципе, наверное это можно и старыми средствами сделать. Ведь когда-то же я текст вытаскивал. А так там в ничего особенного, он даже гораздо хуже перевода от Вектора.
Цитатаsteeldragon ()
По-моему, в Атласе, на экранах загрузки и в золотых буквах.
Ох, перевод будет явно мучительным.
Цитата
У меня точно где-то есть. Насчёт "WANTED" не знаю.
Я манал вообще всё. Я столько всего хочу, и почему-то ничего не могу. Я почти же закончил свой супер крутой плеер STR и XA, экспортер и ваще кул анализатор, но меня случайно отвлекла совершенно сторонняя идея насчёт кое-какого сравнения кое чего, а там реальный математический алгоритм придумывать надо, меня четырёхмерные массивы уже не первый день изводят. Фак.
Вот я смотрю на весь наш проект перевода, и вижу: ВСЁ ТАК ЗАПУЩЕНО!.. Можно было всё. SWV v2.0 & Castle Skatepark 2, экспортировать текст, импортировать текст, перепаковывать уровни, вытащить или изменить коллизионную модель, пересобрать STR, начать озвучку, утвердить фразы… Да что же со мной такое, я так хочу всем этим заняться!
А насчёт текста из Spyro2 – бред. Как-то же я его вытаскивал. И успешно, даже почти из японской версии. Хм, а иногда хорошо взять свою программу, писанную годы назад, и сделать новую версию. На старте долго, зажигание медленное. Зато потом такой бёрст! Теперь-то багаж знаний куда больше, чем на первом недорелизе. Но финал один. Я придумываю новые и новые фишки, без которых ну не знаю. Кому нужен direct-плеер XA с iso/mdf, в котором нет возможности создать плейлист?..
P.S. Все картинки у меня есть. У меня есть всё… but nobody came
А насчёт текста из Spyro2 – бред. Как-то же я его вытаскивал.
Так-так-так, экспорт-импорт текста на мне, я уже почти переделал весь свой комплекс скриптов. И да, я не говорил, что текст из Spyro 2 вообще нельзя вытащить, я говорил о том, что готовой программы для этого нет. Не думаете же ли вы, что все недавние сборки текста я вручную переписывал?...
Цитатаaleksusklim ()
я так хочу всем этим заняться!
Кстати говоря, шрифт Атласа до сих пор не взломан.
Сообщение отредактировал steeldragon - Вторник, 21.06.2016, 23:48
Ну допустим, я взломал текстуру шрифта атласа. Это было быстро!
Субфайл 179. Смещение 29008 (dec), в память грузится по 801B2718 (hex), неожиданно далеко. http://klimaleksus.narod.ru/Files/GH/atlas_01.png Это чистейшие тайлы, то есть координаты текстуры. Там весь атлас ими вырисовывается (если тыкать по соседям, можно хоть генеральный редизайн всего интерфейса замутить). Структуры 8 байт. Первый же тайл – координаты маленькой буквы «a». По-моему, там сразу всё. Координаты текстурной страницы и левого верхнего угла в ней; высота и ширина, а также возможно – координаты палитры. Скорее всего, в «чистом» виде (то есть непосредственно в голом VRAM), и весьма вероятно, что по своей сути используемых переменных и даже их порядка – всё точно так же, как в координатах текстур для модели земли.
А это ещё не конец. Это только рисовательная часть, а там есть и вычислительная. Она считает расстояние между буквами, причём берёт его не из самой текстуры, а из отдельного списка в том же субфайле. Смещение 28092 (dec), в памяти там где надо – 8007B16C (hex) http://klimaleksus.narod.ru/Files/GH/atlas_02.png Это просто однобайтовая ширина каждого символа в пикселях, начиная с той же маленькой «a». Кажется, отрицательные значения понимаются правильно, а то что там снизу F* – это кажется символоподобные знаки, типа квадрат-кружок. А после выделенного текста – следует что-то что характеризует, какую фразу нужно написать напротив каждого яйца.
Что делать будем? http://klimaleksus.narod.ru/Files/GH/atlas_179.png Там это… по-моему, там не так много места, как кажется. А хотя нет: http://klimaleksus.narod.ru/Files/GH/atlas_raw.png – серьёзно!? Весь правый угол свободен? Так заберём, чё. Надо хотя бы попробовать. Если нет – место ещё есть внизу в основной области, пониже яичек. И-и! В палитрах. Вон там сверху. Эту дырку можно взять, часть алфавита поместится.
Но я думаю, если попробовать-таки захватить пространство справа – мы сможем щедро каждую букву в большое и равномерное пространство поставить, так размечать проще будет. А реальную ширину букв во второй список аккуратно вставлять. Мо-ожно даже некую прослойку сделать к font_table, чтобы её интерфейсом редактировать. Но тут нет вертикального отступа символов – всё настраивается нужным сдвигом координат текстуры.
*UPD*
Хм, так проблема же не только в этом.
Таблицы соответствия то нет! Ну зато сама функция рисования единственна и хоть и большая, но аккуратная. Думаю, туда можно хоть в русский алфавит сместить и перенаправить на полное тайловое соответствие нашего собственного сопоставления текстур символов, и их кодов.
Но! Во-первых, имена миров и уровней берутся централизованно – из EXE – и вне атласа должны обрабатываться нашей же таблицей соответствия обычного шрифта. То есть их придётся синхронизировать. Что делать с потенциальной диакритикой – вообще без понятия. Лучше в именах миров её не использовать. А в описаниях яиц если что, можно будет новые варианты тайлов с чем угодно нахимичить.
Теперь во-вторых. Имена миров ещё и рисуются на порталах. Золотым шрифтом… Там должна быть некая таблица соответствия, хотя бы на основании того, что символ точка в названии «Волшеб.Болото» (условно говоря, там на самом деле на транслитной латинице) пиратской версии Spyro2 вызывала всем известное зависание при подходе к порталу. То есть за точкой не была закреплена ни одна живая модель.
Что-то мне захотелось анимированные 3D модели ломануть… but nobody came
Сообщение отредактировал aleksusklim - Четверг, 23.06.2016, 17:45
…Фу, вот я запарился, она кривая и багнутая, в неё столько всего понапихано, что должна уже лопнуть давно, а в этой куче утилит для конверсии уже чёрт ногу поломал! Озёра нерабочего кода, неиспользуемого кода, неотлаженного кода, необновлённого кода…
Пока я собрал только для Spyro3. Ага, а больше всего меня удручило то, что список объектов нужно извлекать и для каждого подуровня тоже, а не только для самого уровня (но увидев то, что я там опять когда-то что-то поменял, и почему-то код извлечения объектов оказался в проге для парсинга цельного уровня, я пришёл к выводу, что если теперь и менять что-то – то всё и накорню), хотя один фиг по-моему там углы направления объектов уже с каких-то пор стали неверны.
Основное нововведение – коллизионная модель. Надо бы там ж тоже анонсировать, но это же не «новая версия», а «стыдно показать».
Выбор коллизионной модели происходит по Ctrl+Shift, как одного из базовых типов. Она рисуется с нормалями и освещением, которое отключается по Alt+Space, как и смешивание текстур на графической модели; освещение нужно отключать при выборе Ctrl+Space вида сверху.
Цвета – мои. Выбрал более-менее логичные: • жёлтый – отсутствие типа; • зелёный, голубой, синий и фиолетовый – разный звук при ходьбе и беге. • красный – лава; • все остальные «нечистые» цвета – дополнительные метки действий, такие как порталы, двери и разрушаемые объекты; • серый – это когда на дополнительные типы цветов не хватило; • прозрачный тёмно-синий – вода (на самом деле в воде ещё столько же типов, сколько везде, но они абсолютно нелогичные и бесполезные, видимо, мусор); • другой прозрачный цвет – конечное (или начальное…) состояние анимированного кусочка модели; противоположное состояние рисуется непрозрачно, где-то неподалёку; • тёмный оттенок любого из цветов – непонятный нелогичный флаг, разбросанный словно случайно, но тем не менее иногда образующий красивые симметричные сочетания, и в общем, я оставил его…
На самом деле далеко не во всех уровнях цвета играют свою роль. Иногда они перепутаны ролями, но чаще – вообще разбросаны в беспорядке. Но если же где-то они расположены неожиданно, но логично и аккуратно – скорее всего в игре тоже так, стоит проверить.
Ну вот, это всё. …а, нет, не всё. Ещё добавлена фича для Super Bonus Round, чтобы небо в подуровне скейтпарвка бралось правильное. Однако другая, более серьёзная проблема с HQ-текстурками, не решена. Повышение качества размазыванием краёв тоже не допилено.
О, ещё есть «режим спайро». Нажмите M, и камера из свободной станет нацеленной. Управление тоже немного меняется: Shift и Ctrl теперь регулируют расстояние до цели. Метка, кстати, размером примерно со Спайро…
По Ctrl+Alt+Shift включается отображение LOD-низкокачественной модели поверх (вернее, подниз) любой активной. Хотя это ж для Spyro1/2 было нужно.
Так, насчёт объектов. Эх, там же ещё когда-то была возможность даже редактировать объекты (и чуть ли не в сам эмулятор непосредственно передавать), но я тут попробовал её включить, и она стала плеваться исключениями, так что я пока закомментировал её останки. Короче, жмёте F9 и вбираете текстовый документ с номером домашнего мира. Цвета меток не означают ничего, просто примерно отделяют объекты разных типов
А ещё для Greatest Hits таки есть режим стыкования с эмулятором, чтобы тырить оттуда координаты всех объектов, Спайро и камеры, а также передавать камеру туда! Работает через пропатчивание памяти эмулятора (поэтому нужно, чтобы его имя.exe и начальный адрес памяти игры были вписаны в «emul.ini», без этого будет работать только на моём любимом Epsxe 1.7.2; найти адрес начала памяти можно, наверное, через RamtopFinder) Нужно запустить игру, а в SWV нажать F11. Всё свернутся в окно и начнёт действовать. Enter – перехватить управление камерой в окне. Ещё раз F11 – сворачивание в маленький квадратик в углу экрана (чтобы поверх эмулятора юзать, из тех плагинов, что не умирают от такого), Shift+F11 – наоборот на весь экран развернуть; Esc – возврат в обычный режим. Для передачи камеры в игру, нужно нажать Ctrl+Enter, чтобы пропатчить память. Это «сломает» игровую камеру (возможно, камеру катсценок тоже) и удалит графическую составляющую меню паузы (чтобы летать камерой на паузе, но Спаркс всё равно будет мешаться), но чтобы патч вступил в силу, возможно, потребуется сохраниться и загрузиться в эмуляторе. Повторный Ctrl+Enter отменит патч, и всё вернёт как было.
Все игровые объекты на момент активации подключения к эмулятору будут скачаны из игры (тут уже углы поворота верные, зато типизации нет…), а чтобы поддерживать их обновление в реальном времени, нажмите F9. Цвета шапок могут быть белыми, серыми или чёрными, в зависимости от состояния объекта – жив, или мёртв/деактивирован (что там какое уже не помню). Режим нацеленной на Спайро камеры и тут работает, и более того – действительно перемещает дракончика.
Интереснее всего наблюдать в совмещении с эмулятором именно коллизионную модель. К слову, это позволило мне в скейтпарке Sunny Villa законным способом перепрыгнуть через ограничительную невидимую стену и поездить по зелёным холмам; но это совсем другая история, ради которой (и всего похожего на неё), я, вероятно, создам тут отдельную тему.
Ещё кнопочка «B» вызывает 3D-фотографирование мира (зажатие Ctrl/Shift/Alt и их комбинаций, возможно, изменит расстояние стереопары), я ещё хотел же анаглиф сделать, но пока сам же очки-то раздобыть не могу. А ещё я подумывал о расставке ключевых точек для камеры, интерполяции меж них, поворот по кватернионам, движения по лагранжу, сохранение всего этого в набор скриншотов для удобного видео монтажа, и всё это опционально в 3D (или вот так 2x2, или таки анаглив), чтобы потом смонтировать идеальный клипец о мирах Spyro2/3, и наложить на него музычку (всё же, не скажу какую, по крайней мере до тех пор, пока не признаю задумку безнадёжно мёртвой), чтобы у меня был ещё один клип SMV…
Но блин, ещё раз, там говнокода полный .gmk, и я скорее всё это заново на каком-нибудь другом трёхмерном движке перепишу, чем буду вновь и вновь копаться в этих дебрях. Нет, всё не потому что я такой плохой программист; ну поэтому тоже, но основная проблема, как мне кажется, что при проектировании SWV я не понимал масштабы этого инструмента. Чёрт побери, на нем должно быть возможно просматривать ВСЕ миры, ВСЕХ игр! Для каждой игры нужно лишь несколько программ и скриптов, вытаскивающих модели. А основная программа должна быть именно там, во что я и пытался её превратить – в вьювер. Которому без разницы, что за модель и откуда. А всякие фишки типа координат объектов должны как-то сверху, как плагины накладываться. И да, кнопки управления тоже должны быть адекватными, а то у меня что ни новая команда – так if not … then exit на все предыдущие.
Да и мои утилитки SpyroWorld_* уже в который раз хочется заново переписать. Чтобы там, во-первых, все хранимые данные имели одинаковую структуру (да хотя бы, чтобы ВСЕГДА начинались именно с поля «размера общих данных», а не вразнобой), во-вторых, была адекватная и понятная спецификация парсаемого формата, а в третьих – унифицированный вывод и аргументы запуска – чтобы в кой-то веке все проги принимали на вход «номер Spyro», это даже удобно – -1 для Spyro1, 0 для Spyro2, а положительные – для Spyro3, причём это же число покажет номер подуровня, когда он нужен; это же логично, не ведь нет, некоторые проги могут сами его вычислить, а некоторые нет, и из-за этого всё так неунифицированно получилось. О, и номер субфайла или сам субфайл нужно бы стандартизировано принимать (например 0 будет означать, что на вход предан уже извлечённый субфайл, а положительное число – что это WAD.WAD, в котором нужно навигироваться куда надо). Да и вообще проги должны уметь сами находить нужные бинарные участки, а у меня сейчас сначала одна отдельная прога всё для них извлекает…
Не, я для Spyro1/2 конечно без вопросов отображение коллизионной модели сделаю (там всё взломано и работает, нужно лишь уладить формальности в общении между конверторами), но вот когда мы доберёмся до анимированных живых моделей… Дело даже не в том, что их поддержку в эту кучу бессистемного кода добавить будет непросто, а скорее в том, что Game Maker (по крайней мере 8.0, может на -studio попробовать перейти) вряд ли адекватно потянет столько анимации, у него ж опять всяко отваливаться начнёт.
P.S. Ах, да. Перетаскиваете извлечённый субфайл 98, 100, 102 … 170 на \bat\beta_spyro3.bat, можно сразу кучкой. Да чтоб оно всё не работало, блин.
ЦитатаRiptoM@XFly
Давайте я Спайро озвучу пожалуйста.
Yay!.. Вы только это… не уходите никуда, ладно?.. but nobody came
Программа для быстрого запуска сохранёнок эмулятора Epsxe.
Установка: 1) Поместите файлы «SavestateRunner2V0.exe» и «SavestateRunner.bin» в папку с эмулятором. 2) Запустите «SavestateRunner2V0.exe». Возможно, откроется окно эмулятора (если настойки по умолчанию сработали), его можно закрыть. 3) Появится файл «SavestateRunner2V0.ini», откройте его Блокнотом. 4) Если нужно, измените значение «EPSXE_EXE=ePSXe.exe» на имя исполняемого файла вашей копии эмулятора – например, на «EPSXE_EXE=ePSXe200.exe». 5) Эмулятор должен быть версии 1.7.0. Если это не так, то вам нужно как-то выяснить адрес в его памяти, по которому можно достать идентификатор запущенной игры, и внести его в строку «GAME_ID_PTR=12650304» – например для Epsxe 2.0.0 этот адрес равен «GAME_ID_PTR=23256000». 6) Если вас интересует только какая-то конкретная игра – впишите путь к её образу в строку «DEFAULT_IMAGE=SavestateRunner.bin», например «DEFAULT_IMAGE=D:\DATA\Spyro-Year-Of-The-Dragon gh.iso»
Использование: 1) Снимите дубликат с любой своей сохранёнки (они копятся в подпапке «sstates\»), и смените её расширение на «.SavestateRunner». Например, файл «SCUS_944.67.002» можно переименовать в «SCUS_944.67.000.SavestateRunner». Файл сохранёнки получит иконку. 2) Его можно запустить! Тогда, откроется настроенный эмулятор, в который будет загружена эта сохранёнка, как открытая на образе по умолчанию (изначально «пустой»). Если эта сохранёнка от другой игры – скорее всего, рано или поздно всё сломается. 3) Чтобы запустить сохранёнку под образом конкретной игры – перетащите ваш образ, например .iso, на неё саму – на файл .SavestateRunner (он будет вести себя как «программа», позволяя перетаскивать на себя), и эмулятор загрузит данную сохранёнку с этой игрой. Если сохранёнка действительно от этой игры, то всё должно работать идеально. 4) Также, можно перетаскивать сохранёнки на «SavestateRunner2V0.exe». Либо изменить в настройках опцию «DEFAULT_IMAGE», чтобы задать образ по умолчанию для всех сохранёнок, открытых двойным щелчком, а не перетаскиванием образа на них. Технически, для самой программы: первым аргументом передаётся сохранёнка, а вторым – образ, если есть.
Ограничения: 1) Когда сохранёнка будет запущена – чтобы вручную производить сохранение и загрузку, а также совершать другие действия горячими клавишами Epsxe – вам необходимо один раз нажать F3. Например, вы можете делать свои сохранения после загрузки изначальной – и для этого вы попытаетесь нажимать F2, но окошко с картинкой не станет появляться, пока кнопка F3 не будет нажата хотя бы однажды (первый раз «загрузка savastate» не выполнится). 2) Сворачивание эмулятора через Escape, или любая другая попытка поставить игру на паузу – закроет эмулятор. Чтобы не потерять свой прогресс, сохранитесь в какой-нибудь слот вручную через F1, чтобы потом загрузиться через F3. Но не сохраняйтесь в первый слот, потому что он будет перезаписан при следующем же открытии исходной сохранёнки (которая, кстати, не будет перезаписана ни при каких обстоятельствах), а примените F2 и выберите другой. Если вы желаете продолжить работать с сохранёнкой в эмуляторе так, чтобы иметь возможность сворачиваться в меню, то просто запустите эмулятор обычным способом, выберите открытие того образа, через который была загружена данная сохранёнка (для образа по умолчанию, выберите «SavestateRunner.bin») и нажмите F3 сразу после инициализации эмулятора. but nobody came
Две разные копипасты описания формата, как хранятся тексты Spyro3. Сделаны в разное время для разных людей. Что-то актуально, что-то может быть уже нет. Публикую сюда.
Во-от. Так что пока в теории мой план более надёжен. Итак ещё раз, подробнее.
(далее под «4байта» я буду понимать 32-бит «word» / int)
Берём лист 88-ых объектов. Каждое первое 4байта объекта – локальный указатель на 12 байт (три раза по 4байта) доп-данных. Если последние 4байта не равны «FF000000» – отбросить объект. Далее пойти вниз, по списку указателей на «локальные указатели». Если какой-то из них показывает на 4байта после 12 байт доп-данных, отметить объект как хороший (ещё можно на следующие 4 байта тоже ждать указатель, ещё не знаю, даст ли эта дополнительная строгость отсечение какого-нить мусора). Затем выкинуть все «нехорошие» объекты. Потом читать для каждого всё, что за доп-данными, запоминая первый и последний указатели на строки. Предположение в том, что это всё ЦЕЛИКОМ текст – какие-то реплики. Важно начало и максимальный размер этой области.
Хотя все реплики сами по себе нам тоже понадобятся. Для каждого объекта нужно знать следующее: • номер WAD_SUB. • порядковый индекс объекта (позиция в 88-списке). • его 12 байт (ну 8 на деле) доп-данных, и всё что это может нам дать. • общее количество его фраз, включая все пустышки. • имя объекта Теперь для каждой его фразы: • указатель на то, где расположен непосредственный локальный указатель на эту строку. • индекс в листе указателей, который отвечает за указатель из предыдущего пункта. • значение первого байта текста, либо ноль – если это первая фраза = само имя объекта. • столько байт в начале строки, предшествующие настоящему тексту (всё это придётся куда-то запоминать, потому что этот кусочек должен остаться привязан к переведённой строке, где бы она не оказалась). • вопрос, нужно ли запоминать [номер] реплики по нашему кривополученному списку соответствия озвучкам? Пока я технического смысла в этом не вижу. С другой стороны, номер позволяет легко идентифицировать каждую строку. Однако как я помню, его очень трудно получить. А ещё, если наше техническое дело-таки дойдёт до озвучики, нам понадобится не только ребилдинг STR (можно считать что уже solved), но и какое-то сопоставление всех озвучек всем текстам, и очень желательно, чтобы оно было полностью автоматическим. Вот чувствую, что там уже номер реплики понадобится как ни крути…
Так вот. Я думал что-то вроде по одному .txt документу на подуровень, скажем, следующей структуры:
12) Zoe - text 1 - text 2 - - text 3 45) Hunter - do it? - yes - no 62) - go off - come on in - 116) Zoe - hint
То есть тут по порядку появления перечисляются говорящие объекты, в формате «номер-скобка-имя» (игнорим пробелы здесь и везде), за которым по строкам перечислены все его реплики в том порядке, в каком следуют указатели на них, в формате «дефис-текст», причём если реплика пустая, то будет просто дефис (ну нам ведь не понадобятся нигде тексты состоящие из одних пробелов или пустых строк?). Все прочие переносы или вхождения, не удовлетворяющие формату – игнорируются. Такой файл вполне себе будет пригоден для ручного редактирования.
Но это не единственный «output» алгоритма, потому что всё то, что я сказал, что нужно знать и запомнить – придётся где-то хранить, и очень желательно, чтобы не здесь. Потому что индексы, байты и указатели – критичная штука, которую мы менять не собираемся, и она не должна быть редактируемой вот так просто. Поэтому придётся в каком-то ещё формате хранить всю кучу данных, единожды извлечённый из, что важно, оригинального WAD.WAD.
Далее тексты будут вставляться, как я сказал, алгоритмом рюкзака, причём сразу для всей игры для всех подуровней. Считываются все строки текущего уровня, а потом перебором разбрасываются по тем имеющимся областям, где когда-то был оригинальный текст.
Ну алгоритм примерно такой (работает математически на цифрах, а не на самих данных): рекурсивная функция, которая пытается разместить текущую строку в доступное свободное место (строки заранее отсортированы по убыванию длины) – если поместилась, то рекурсия на следующую строку; если нет – пытаться положить текущую строку в следующее свободное место, и так далее; если всё равно не удалось – выйти из рекурсии выше, чтобы породившая рекурсия отвергла эту ветку и пыталась далее. А ещё нужно запоминать самый глубокий провал, чтобы в случае, если разместить всё не получилось – выкинуть эту строку (в EXE), а алгоритм повторить без неё.
Итак, если всё вернулось удачно – раскидываем строки как рюкзак приказал. Причём здесь очень аккуратно нужно будет с указателями работать, чтобы всё плотненько и корректно запихать. Если же не всё гладко – ломаем указатель на неудобные строчки в оконечном листе, каждую из них копируем в очередное (ну начиная с какого-то) место в EXE, естественно, запоминая смещение после неё, для копирования других таких. Вычисляем абсолютный указатель на неё в EXE, и вписываем его вместо локального в сам объект.
Ну вроде как всё верно. В итоге, предположим, мы ни в чём себе не отказывали, и реплики у нас дай бог. Тогда некоторые из них улетят в EXE, но при этом же освободят своё место другим строкам! А рюкзак гарантирует, что оно будет использовано максимально. То есть по факту место в EXE, мне кажется, не так уж быстро будет кончаться. И туда всё поместится. Вообще все, что мы захотим.
С именами и того прикольней, можно таких как Зоя и Хантер отдельной бригадой в EXE заточить, и они априори отдадут дополнительные байты тексту. Плюс та фишка с FF000000-пустыми репликами. Их тоже для верности можно в EXE кидать, чё б нет, всего 4 байта.
Но к сожалению, это не всё. Я умолчал о перекодировании нашего текста по таблице соответствия, а также о чистке текста. Чистка – например замена тире на дефисы (физически символ), три точки подряд на многоточье, двойные пробелы на один пробел. Этот надо как-то формализовать, и желательно не в коде программы, чтобы менять можно было.
Я подумал о чём-то вроде списка замены. Этот как бы таблица с двумя столбцами, обрабатываемая построчно; в каждой ячейке – подстрока (может быть более одного символа). В целевой строке ищется первая подстрока: если не найдена – идём дальше по списку. Как только что-то совпало – делаем замену этого содержимым второго столбца, и сбрасываем всю обработку в начало (так куда надёжнее, хотя наверное можно что-то оптимизировать). А ещё надо как-то из потенциального зацикливания выходить…
Только пока у меня нет идей, как эффективно (для использования и изменения) хранить эту таблицу. Тут уже .ini структура A=B, C=D не прокатит, потому что сами «B» и «D» – бинарные (особенно для Spyro3), которые могут содержать символ переноса строки или ещё что похуже.
Считывает файлы в таком формате (все числа целые положительные): «количество участков памяти», «список размеров всех участков»; «количество размещаемых строк», «список размеров строк (включая терминирующий ноль каждой)». Если обработка успешна – пишет в выходной файл для каждой строки (в том же порядке) номер участка, куда её рекомендуется всунуть (участки нумеруются с нуля); либо = «-1», вместо индекса, если данную строку лучше вытащить в EXE. Входной файл передаётся либо в первом аргументе, либо считывается прямо со входного потока; выходной файл – либо во втором аргументе, либо просто пишет на консоль.
bagpack_test.bat – запуск на файл, in.txt – пример. В архиве две версии бинарника, сделанные на разных компиляторах.
Программу можно прикрутить как есть к будущей системе внедрения строк; можно и переписать заново. И кстати, я не уверен в полной правильности его работы, я плохо проверял результаты. На вид работает…
Начнём с первого файла, «Wad_112-4.wad». (Если что, это суб-субфайл)
По смещению 48 Нужно считать указатель. Это – относительный указатель, он показывает, на сколько байт от текущей позиции (от самого себя) нужно сдвинуться вперёд (или назад, но такого не бывает). Эффективно, если на самом деле СЧИТЫВАТЬ указатель – то текущая позиция уже будет на 4 байта впереди, поэтому от указателя надо отнять 4. Если же файл уже в памяти (а это значительно быстрее, чем по 4 байта с диска читать…) – то просто инкремент настоящего указателя на его же значение. Таким образом, если там число «4» – то оно показывает прямо на следующее же число.
Так вот! По смещению 48 – относительный указатель. Он показывает на следующий относительный указатель. Я это называю «прыжок». Совершить прыжок – значит считать текущее значение и продвинуться на него вперёд.
После двенадцатого прыжка курсор оказывается на относительном указателе, который по совместительству показывает размер области с объектами. Это – общая выделенная под них память, но заранее инициализированы не все. Количество реально существующих объектов записано в следующем же целом числе – в данном случае, это «5» (на скриншоте – не закрашено, перед жёлтой областью).
Каждый объект – это структура по 88 байт, они следуют прямо друг за другом. То есть дальше можно уже считывать первый объект.
Сейчас в каждой такой структуре нас интересует только первое целое число – это абсолютный указатель на дополнительные данные для этого объекта. Он всегда ненулевой.
Абсолютный указатель означает, что отсчёт ведётся от начала виртуального файла; но не всегда физически от самого бинарного файла; на в данной ситуации, поскольку это уже извлечённый суб-субфайл уровня – указатели прямо фактически от его начала.
Теперь нужно перейти по каждому из них по очереди, чтобы посмотреть на дополнительные данные объекта:
– я покрасил дополнительные данные в цвет того объекта, которому они принадлежат. Мы почему-то считаем, что эти данные всегда занимают не менее 12 байт, так что для простоты можно считать, что они всегда 12, просто располагаются не подряд.
Ещё раз, на эти данные я попал, перейдя по абсолютному указателю в первом целом числе структуры каждого объекта.
Из всех объектов меня интересуют только те, у которых в дополнительных данных третье число, то есть байты с 9 по 12 – равно «255», причём как четырёхбайтное целое, эффективно FF000000. Иными словами, байт номер 9 равен 255, а байты 10, 11, 12 – нулю.
Из всех пяти объектов только один удовлетворяем этому условию, а все остальные – не содержат текста, и должны игнорироваться. Посмотрим на саму структуру его, выше:
– здесь выделен именно первый абсолютный указатель на дополнительные данные, являющийся по факту файловым смещением, на которое нужно перейти.
Далее, раз мы знаем, что дополнительные данные оканчиваются на FF000000, то последующие абсолютные указатели за дополнительными данными – это указатели на строки.
Первый – самый простой, это имя объекта. Простой указатель на начало строки, а все строки начинаются со смещения, которое кратно четырём. А строки оканчиваются нулевым байтом. Это означает, что строки, следующие вплотную друг за другом – на самом деле могут быть разделены более чем одним нулевым байтом. Строка дополняется столькими нулями, пока смещение следующего за ней символа не станет кратно четырём. Например если в строке всего один символ – то после него будут 3 нуля, чтобы суммарная длина делилась на 4 без остатка. Для строки из двух символов – два нуля; для трёх символов – всего один настоящий ноль. А вот если длина уже кратная четырём – то в файле будут выделены все четыре нулевых байта, чтобы строки не слиплись.
Зная это, получаем имя объекта – строка по первому абсолютному указателю, до первого нулевого байта. Но не совсем: лучше засчитать в пользу строки и все дополнительные нулевые байты по описанному расчету, чтобы точно получить смещение, где должно ожидаться начало следующей строки.
– на скриншоте после синих данных сверху – раскрашены указатели на строки. Соответствующие им тексты закрашены тем же цветом ниже. (А розовое в самом низу – дополнительные данные последнего объекта, в котором нет текста.)
Но лишь имя объекта является простой строкой! Все его реплики – управляющие строки. Каждая управляющая строка начинается с байта, в котором записано общее количество управляющих байт в начале этой строки. Его нужно эффективно пропустить, причём первый байт сам по себе считается управляющим. Таким образом, если на самом деле управляющих байт нет, то первый байт будет равен единице, а сразу после него – сама строка. Однако обычно управляющих байт там может быть от 2 до 7 и более. Причём они считаются входящими в саму строку при расчете количества дополнительных нуле на конце.
Если первый управляющий байт строки равен 255, то это означает, что строка «пустая». Собственно, байт = 0 тоже формально сработает (нет управляющих, значит это и есть начало строки, но ноль её же и завершает). Пустые строки в объектах допустимы, и часто встречаются между репликами. Но они забирают по 4 байта на свою длину, и следующая строка начнётся после этого «255» (опять же как int).
Мы будем считывать подряд указатели на строки, до тех пор, пока последующий вдруг не перестанет показывать ровно на то место, где ожидается начало следующей строки. Если не совпало – значит строки кончились, и можно идти искать следующий объект.
На скриншоте: красная простая строка – имя объекта. Далее две непустые управляющие строки – желтая и зелёная, хотя в обеих управляющий байт всего один. За ними – ещё аж шесть пустых строк, отмечены тусклыми цветами. Правда, последняя не совсем понятно почему, но начинается с «0100…» – управляющий байт есть, но первым же символом строки является нулевой байт. Думаю, это можно считать эквивалентом пустой «FF00…» строки тоже (хотя может потом придётся поменять).
Последний указатель – число «4» – очевидно не показывает на последующую память, а значит – больше строчек в объекте нет.
Что должен сделать алгоритм? Получить всё место, отведённое под все строки объекта – прямо всё-всё, большим куском. Однако, все управляющие последовательности в начале каждой управляющей строки нужно сохранить отдельно, чтобы не потерять.
Далее, зная индекс исходной строки, и поданного на вход программы текста перевода – разместить в памяти переведённые строки, не забыв дописать в их начала правильные управляющие последовательности.
Строки могут не начинаться со смещения, кратного четырём. То есть разделять их можно всего лишь одним нулевым байтом, получая таким образом дополнительное пространство. Более того, все пустые строки (которые выводить не нужно, и в переводе их не будет) всех объектов – можно схлопнуть в единые 4 байта, разместив их где-нибудь. То есть все указатели можно поменять так, чтобы они указывали на одно и то же число 255, эффективно получая ещё по четыре символа с каждой пустой строки.
И разумеется, программа должна корректно поменять все указатели на строки, чтобы они попадали в начала их управляющих последовательностей. На выходе получаем сохранённый файл, в котором и строки перезаписаны, и указатели изменены.
Бонус! «Wad_104.wad» Как парсить субфайлы уровней, а не суб-субфайлы подуровней. На самом деле всё просто, нужно только в алгоритме сделать поддержку того, что абсолютные указатели отсчитываются не от фактического начала файла. А ещё проще – загрузить в память нужный косок файла, и пусть алгоритм работает как обычно, если он конечно всё читает сразу из своей памяти как из массива данных.
Нужно перейти по смещению 24 – это указатель на первый суб-субфайл подуровня. Его размер – в следующих четырех байтах, и весь субфайл можно сразу считать в память; но можно и игнорировать размер. В самом суб-субфайле – вести себя как в предыдущем алгоритме: перейти к смещению 48, сделать 12 прыжков… Но все абсолютные указатели будут именно относительно начала суб-субфайла в памяти.
Указатель на начало следующего субфайла – на 16 байт дальше, чем указатель на данный (цветные на скриншоте). По факту, это смещения 24, 40, 56, 72. Остановиться нужно на том, указатель по которому нулевой.
А в этом файле текста уже гораздо больше, вот например один из объектов:
Выделены 12 дополнительных байт, за которыми следуют указатели на имя и тексты; В трёх видимых текстах количество управляющих байт равно 4, 4, и 6. but nobody came
Файл «0» – это некорректная попытка, я там что-то нарушил. Но получилось прикольно. Там FreezeCheat, так что нужно нажать и отжать паузу. С Бентли не говорить, а то вылетает; надо сначала отойти от него. Файл «1» – это уже как надо. Я расширил ту зону, что следует после списка объектов. То есть я укоротил список до, хм… 3+10 штук, и всё былое пространство – внёс как в «дополнительные данные», которые просто не будут использоваться, но ни на что не влияют. Как видно, игра не вылетела. Она работает стабильно. Разве что, при создании нового объекта – если под него «нет места» – то он тупо не создаётся. Из чего следует, что мы, в принципе, вполне можем взять себе несколько штучек… (учитывая что в оригинале там сотни!) Каждый объект даёт нам +88 байт на строки или ещё что-либо. Например, потом надо попробовать вытащить в «последний объект» те строки, что предназначены для меню Help. Того гляди, и в EXE даже не придётся ничего писать.
Конечно, нельзя забирать слишком много объектов. Знаете, что будет, если места не хватит на Спаркса!? …да ничего страшного. Просто Спаркс не появится, и Спайро останется с одной жизнью. Ну, я к тому, что мы не можем знать точно, сколько пустого буфера под объекты реально необходимо уровню. Пожалуй, следует написать программу, высчитывающую количество живых объектов в реальном времени, тем более что я по-моему даже знаю, где у них там бит alive…
А вот преимущественно для актёров: (текст вложен в архив)
Установка: 1) Распакуйте архив куда-нибудь на жёсткий диск. (Для будущей стабильности убедитесь, что в пути нет всяких специальных символов.) 2) Запустите «install_settings.reg» и подтвердите изменение реестра. (Если не получается, то попробуйте открыть командную строку cmd.exe и написать туда «reg import [пробел]» и перетащить этот файл в консоль, выполнить).
Настройка: 1) Запустить «ePSXe.exe» 2) Выбрать пункт меню «Config > Game Pad > Port1 > Pad1». Здесь можно переназначить кнопки управления. 3) В пункте меню «Config > Video» можно выбрать плагин DirextX или OpenGL. Нажатие Configure – расширенные настройки плагина, самое важное – разрешение экрана вверху. Также есть Soft-плагин, настраиваемый кнопкой ниже. (Если ни один из предыдущих двух плагинов почему-то не работает, попробуйте переименовать «gpu.dat» в «gpu.dll» в папке «plugins\», и потом найдите Soft-плагин в верхнем списке.)
Запуск: 1) Пункт меню «File > Run ISO». 2) Выбрать нужный .iso или .bin файл. 3) Для паузы нажмите Esc. (Для продолжения – пункт «Run > Continue»). Чтобы изменить настройки, эмулятор придётся перезапустить.
Управление эмулятором: 1) Клавиша F1 – сохранение текущего состояния в активный слот. Изначально активен слот номер 1 (имена файлов .000). 2) Клавиша F2 – изменение активного слота с 1 по 5 и по кругу. Картинка предпросмотра будет показываться в верхнем правом углу экрана. 3) Клавиша F3 – загрузка состояния из активного слота. Эмулятор хранит отдельные пять слотов на каждую игру (они привязываются не к файлу-образу, а как бы к самой игре). 4) Клавиша F4 – включение и отключение ускоренного воспроизведения игры, соответствующая иконка отображается справа. 5) Клавиша F7 – переключение на Soft плагини обратно. В настройках по умолчанию, D3D и GL плагины развёрнуты на весь экран, а Soft работает в окне, поэтому F7 эффективно переводит игру в оконный режим; тем не менее, возврат в главное окно всё равно через Escape. 6) Клавиша F8 – снять скриншот экрана. Снимки будут копиться в папке «snap\». 7) По умолчанию графические опции настроены на цифры 1-6. Клавиша 1 активирует панель управления плагином (строка сверху), 2 – включает подсказки. Далее цифры 5-6 меняют текущий активный пункт, а 3-4 – включают или выключают его. Например, первый же пункт управляет ускорением и замедлением игры. 8) Другие клавиши Fx, и цифры 7,8,9,0 – лучше не нажимать.
Контроллер первого игрока по умолчанию: (изменяется в меню) 1) Стрелки вверх-вниз-вправо-влево – стрелки на клавиатуре; 2) Крест – RCtrl, Круг – Num0, Квадрат – RShift, Треугольник – End; 3) Старт – Space, Селект – LCtrl. 4) L1 – Delete, R1 – PageDown, L2 – Insert, R2 – PageUp.
Основные действия кнопок в игре Spyro2/3: 1) Крест – прыжок. Крест в прыжке – полёт. 2) Круг – атака огнём или снарядом. 3) Квадрат – бег и атака рогами, также под водой. 4) Треугольник – зажать на земле – осмотреться/прицелиться; в прыжке – вертикальный удар вниз; в полёте – взмах вверх с приземлением. 5) Старт – пауза, Селект – сразу атлас. 6) L2, R2 – вращение камеры; L1/R1 – центрирование камеры. 7) Техника дальних перелётов: квадрат для разгона; на самом краю – с квадрата удерживать крест и кнопку вперёд, отпустить квадрат; в верхней точке прыжка – быстро отпустить и зажать крест ещё раз, уже можно просто рулить; перед приземлением – в конце держать вперёд и нажать треугольник в самый точный момент, не слишком рано, но и не вплотную к целевой площадке. 8) Большие враги убиваются кнопкой круг, маленькие – кругом или квадратом, металлические – только квадратом; Иногда нужно ударить сверху треугольником. 9) Текущее здоровье показывает стрекоза: жёлтый цвет – 3 жизни, синий – 2, зелёный – 1, отсутствие стрекозы – 0. Здоровье пополняется при уничтожении мелких животных, из которых вылетают бабочки. but nobody came
На самом деле, формат почти как для PGG, но это список, а не плоскость. В начале идёт заголовок (не уверен, что та есть что-то полезное для нас, мы ж не собирается параметры картинок менять), потом непосредственно палитра на 256 цветов, и прямо за ней – графические данные, 8 бит на пиксель. За ними, _возможно_, сколько-то нулей, а потом – очередная картинка.
Этапы работы такие: 1) Вытащить первую картинку в .bmp файл. 2) Вытащить любую картинку по номеру – в цикле .bat запусков эффективно получаем все картинки! 3) Замена – считываем 256-цветный .bmp файл, и заменяем им нужную картинку по номеру.
Следующая картинка начинается с некоторого смещения, я так понимаю, число 18432 (dec) – константа, и можно резать по ней – если продолжать добавлять к смещению это число, то все картинки по-очереди пролистываются (то есть, смещение к заголовку каждой = номер*18432 байт). http://klimaleksus.narod.ru/Files/REPEAT/atl_4.png
Вообще, цвета палитры из двух байт получаются вот так. Если два байта считать как одно 16-битное число D, то каналы из него считаются алгоритмом: W:=D and 65535; // только два байта R:= W and 31; // красный W:= W shr 5; // эти пять бит G:= W and 31; // зелёный W:= W shr 5; // ещё пять бит B:= W and 31; // синий W:= W shr 5; // тут останется последний бит – флаг прозрачности.
То есть младшие 5 бит – красный канал, далее зелёный, потом синий, и самый старший 16-й бит – обычно нулевой, но на полупрозрачных текстурах типа воды – он единичный.
Считывать цвета можно как угодно извращаясь… ri:=c and 255; gi:=(c shr 8) and 255; bi:=(c shr 16)and 255; – тоже работает.
Можно и обратный побитовый разбор зафигачить, if (r and 1) >0 then b1:=(b1 or (1 shl 0)); if (r and 2) >0 then b1:=(b1 or (1 shl 1)); if (r and 4) >0 then b1:=(b1 or (1 shl 2)); … , фу-фу-фу.
Дальше – у нас есть интенсивность каждого канала как число от 0 до 31. Домножаем и округляем так, чтобы получилось от 0 до 255. Тут уже дробная арифметика, по-видимому, но у меня в PGG почему-то всё забито константными массивами:
Интенсивности трёх каналов рассчитываются отдельно, соединяются в RGB трёхбайтное значение, и это уже можно писать в .bmp заголовок палитры. Саму картинку можно либо хардкорно собирать как файлик (помня, что в .bmp строки идут снизу-вверх), а можно и через какой-нить canvas и SaveToFile…
Теперь немного о том, как считывать бинарные файлы на Delphi. Консольная программа, допустим, начинается так:
program sample; {$APPTYPE CONSOLE} uses Classes, SysUtils, Math;
– в «Classes» лежит TFileStream, очень полезная штука. Остальные две библиотеки – так, на всякий случай, обычно могут понадобиться.
Дальше я обычно всегда объявляю себе два стрима – входной и выходной. Ну и имена файлов можно сразу приметить:
var stream, save:TFileStream; infile,outfile:string;
Поскольку потоки плюются исключениями, их обязательно стоит завернуть в try-except. На самом деле даже в два (один с finally), но поскольку эта утилита «на один раз» – прогнал и закрылась – в неё можно даже попять не освобождать… Так что мне хватит просто что программа выведет в консоль ошибку и закроется.
begin stream:=nil; save:=nil;
if ParamCount<1 then begin Writeln('Usage: prog.exe "infile.wad" ["outfile.bmp"]'); exit; end; infile:=ParamStr(1); if ParamCount>1 then outfile:=ParamStr(2) else outfile:=infile+'.bmp';
try stream:=TFileStream.Create(infile,fmOpenRead or fmShareDenyNone); save:=TFileStream.Create(outfile,fmCreate);
Кстати, файлы можно действительно честно считывать потоком: нужны четыре байта – взял четыре байта… А можно сперва забрать в память большой кусок, и там ещё обрабатывать его примерно как массив. Это быстрее. Но в данном случае, поскольку тут просто список изображений – вполне пойдёт и прямое чтение.
Ещё можно объявить себе TMemoryStream, и кусками копировать в него из файлового потока, затем перемещать курсок к началу и считывать уже из него. Это тоже очень быстро, но можно на границе запутаться. (Я так делал только когда считывал raw-сектора ISO образа. Я читаю по секторам, но это медленно; поэтому я сперва гружу _много_ секторов в мемори-стрим, а по секторам вытаскиваю уже из него. Наличие чёткого размера сектора позволяет легко заполнить память новой пачкой секторов сразу, как только закончилась текущая).
У стрима только одна неочевидная проблемка: ReadBuffer, например, принимает (var Buffer; Count: Longint); – здесь «var Buffer» – непонятно что. Это как бы буфер. Но что это? Массив? Переменная?
Все параметры со словом var или const – обрабатываются компилятором по-особому, и на самом деле там передаётся указатель на то, что было постлано. То есть, что бы мы не передали – придёт указатель на него, поэтому не важно, что это такое было. Но ясно, что результат обрабатывается просто как байтовый массив.
Так вот, как же передать туда указатель? Для этого я касутю его к PChar, и разыменовываю, типа ReadBuffer(PChar(s)^,4).
А ещё, в Delphi можно эффективно использовать собственные динамические массивы и строки как хранилище в памяти. Например: UniqueString(s); SetLength(s,512); stream.ReadBuffer(PChar(s)^,512);
Правила такие (описываю, потому что сам когда-то долго разбирался, почему всё именно ТАК работает):
string (AnsiString) – строка. Но не простая, а волшебная. Сама по себе она – непонятно, что. Но её можно индексировать с единицы (а динамические массива типа «array of byte» – с нуля). Но её нельзя записать в поток просто так. Чтобы получить _указатель_ на сами двоичные данные строки – нужно прикастовать её к PChar (PAnsiChar). Вот PChar – это как «char*», всё вроде просто. Но при этом следует понимать, что переменная «pc:PChar» – это не строка, это просто указатель. В неё нельзя писать, её нельзя читать, её нельзя ничего присваивать – потому что он ещё никуда не указывает.
Но приведение от string к PChar происходит волшебным образом, Delphi многое делает за кулисами. Так вот, после pc:=s (или «pc:=PAnsiChar(s)», где pc:PAnsiChar;s:AnsiSting) – присвоит к pc указатель на фактические строковые данные. Плюс, она будет ещё и нуль-терминированная. (Причём даже если в исходной строке нули были в середине – память будет доступна вся). Правда, если исходная строка пуста – есть риск получить nil в результате.
А как я сказал, разыменовывание pc^ – даёт именно то, что желают получит всякие там «var buf». И PChar можно индексировать, с нуля.
Теперь обратное преобразование! string(pc), или «s:=AnsiString(pc)». Дельфи возьмсёт, и СКОПИРУЕТ нуль-терминированную строку, на которую указывал PChar, и создаст на её основе свою настоящую string. Её изменение уже не приведёт к изменению исходных данных.
И по PChar(s) можно _писать_. Главное, убедиться, что либо string объявлен только что, либо была вызвана UniqueString. Ну и что размера всегда хватит, это через SetLength.
Кстати, для успешного вызова WinAPI всё это тоже необходимо помнить. Если требуется указатель – нужно давать указатель. Если Delphi в интерфейсе предоставляет «var …», а у нас указатель – то его нужно разыменовать. Если же у нас есть сама «штука» (например, там «var result:Cardinal», а у нас есть r:Cardinal) – то извращаться не надо, просто передаём «r». (В противовес, если там «lpResult:Pointer», то вызвать надо как «@r»).
И строки string никогда никуда не передаются. Ни в потоки, ни в WinAPI. Там на входе всегда ждут PChar. Зато с PChar невозможно делать никаких адекватных строковых операций. Конкатенация, замена… (Если начать юзать всякие StrCopy, то это уже язык Си получится, со всеми вытекающими проблемами типа выхода за границы памяти) Однако, вполне допустимо save.Write(PChar('result='+IntToStr(res)+'!'),9);, ну это учитывая что res от 0 до 9, более общий случай, конечно, записать в строку и потом в качестве размера указать Length(s). Кстати, длинна на строках считается быстрее, чем на PChar'ах.
С динамическими массивами всё так же, как со строками. Если не ясно, что перед нами – массив или указатель на него – я хитрю и делаю «@a[0]» – тогда это точно указатель на первый элемент.
Ещё с PAnsiChar можно выполнять арифметику, скажем pc+4 – это сдвиг вперёд на 4 байта. Но если у нас Pointer (p:Pointer), то такое не сканает. Можно сделать Ptr(Integer(p)+4)… Если каст к PChar делается не строки, а простого указателя – то магии не происходит, и всё работает как ожидается.
Если любые мои программы использовать на юникод-версиях Delphi, то наверное будет куча проблем… Придётся все string заменить на AnsiString, а PChar – на PAnsiChar. И компилировать нужно под 32-битную систему. Спайро 32-битный… but nobody came
Консольная, все настройки берёт из .ini файла. По умолчанию настроена на Epsxe 1.7.x и Spyro3 GH. При первом запуске создаёт .ini Настройки: process – имя.exe или PID целевого процесса эмулятора, ramtop – десятичное число, адрес начала RAM игры в памяти эмулятора, objptr – десятичное число, адрес в памяти ИГРЫ ячейки, в которой хранится указатель на список объектов. Если process оставить пустым – то обрабатываться будет последний запущенный процесс (вроде бы).
Итак, после запуска она подключается к эмулятору и выводит в одну строку консоли информацию об объектах. Строка обновляется в реальном времени (у вас же не суженное окошко консоли, да?..)
Если произошла ошибка (допустим, эмулятор вылетел) – то строка будет перенесена, а программа продолжит пытаться открыть его, и если сможет – то восстановит процесс мониторинга на следующей строке; первый символ – крутящаяся палочка, показывает что всё работает.
Выводимые числа (например, «I: 209 S:325 A:190 M:211») обозначают следующее: I: (initial) – исходное количество объектов при старте уровня; S: (size) – общий размер доступной под объекты памяти, в кол-ве объектов; A: (active) – количество объектов, кажущихся «живыми» и активными; M: (maximal) – количество ненулевых элементов массива – все, что было затронуты.
Программа не хранит историю (да и вообще достаточно глупа, и не делает НИЧЕГО кроме подсчёта этих чисел – ни название уровня вам не выдаст, ни на ошибки не проверяет). То есть все эти значения – считаются каждый раз вновь и вновь. Поэтому программе не важно, где вы там в уровне оказались – в начале или с сохранёнки. И в уровне ли вообще…
Таким образом, за показаниями программы нужно следить вручную. Например, число М может и уменьшаться, наверное. А нас интересует его максимальнейшее же значение. (Я не программировал «максимум», потому что его придётся сбрасывать вручную, ведь во время перелётов между уровнями цифры прыгают только так.)
Возможно, программку можно будет настроить на Spyro2. А может и на 1… Позже гляну, какие настройки для этого нужны.
Так вот! Как я уже успел заметить – во многих уровнях объекты взяты «с запасом». Причём с достаточно большим. Не менее 20 уж точно.
Но, нужен эксперимент. Кто-то должен попробовать по-всякому поиграть в уровни, и снимать показания со счётчика. Интересуют числа – максимальное на вид A, и среднее М после долгой игры.
Нужно шпарить дерзко! Ломать бочки пачками (ведь каждый осколок – объект…), пускать больше снарядов. Рассыпать что-то. Короче пытаться заставить игру вылететь.
И потом собрать список значений – разницу между S и M. И думаю, мы сможем забрать от неё процентов 10-25 для наших нужд под тексты.
Исправления: • Теперь .ini файл на самом деле считывается. Также программа принимает опциональный аргумент – имя .ini файла, который требуется считать. По факту, на неё можно перетаскивать .ini файлы. • .ini файл не затирается и не воссоздаётся. При неверных настройках программа выводит ошибку. • Программа больше не падает. Добавлено больше проверок и убрано динамическое выделение памяти. • На экране не будут появляться одинаковые строчки, если список объектов был временно недоступен (листание страниц в атласе). По факту, при ошибке программа теперь сначала ждёт, когда всё станет хорошо, и проверяет результаты: если данные те же, то строка не переносится. • Если максимальное количество объектов вдруг становится меньше – программа переносит строку с символом «!». То есть следить за максимумом будет проще, ведь он не затирается. • Если изменяется начальное или доступное количество объектов, которое константно для уровня – значит был переход в другой уровень, и программа перенесёт строку с символом «?», опять же не затирая показания. • При ошибках в памяти (если игра не на уровне, например) на экран могут выводиться «!» или «?» даже на отдельных строках, я этот момент не смог чётко проработать. • Файлов настроек для чего-то отличного от Spyro3 GH пока ещё нет… but nobody came
Сообщение отредактировал aleksusklim - Воскресенье, 06.11.2016, 12:24