Прокачиваем карманную приставкуva1en0k (fromxa@va1en0k.net) Хакер, номер #114, стр. 114-088-1 Программерские повороты в жизни твоей PlayStation: PortablePrince of Persia, Gods of War... эх, что может быть интереснее, чем ежедневное рубилово на мощной карманной приставке? Для геймера – ничего. А вот хакер, скорее, заинтересуется, как воспользоваться незаурядными возможностями ультрапортативной PSP в своих черных (и не очень) целях. От игр к программированиюКогда у меня появилась PSP, я был изрядно разочарован. Действительно, зачем мне игровая платформа, игры к которой стоят под тысячу рублей каждая, а никакого иного толку от нее нет? И это при том, что игры рассчитаны на несколько часов или дней, а шедевров вроде Final Fantasy VII: Crisis Core или Patapon'a мало, да и достать их (легально) в нашей стране трудновато. Но, к счастью, я оказался не прав: пираты давно разобрались с этой жуткой проблемой, и теперь каждый может попробовать игру перед покупкой, проведя лишь однажды нехитрый процесс перепрошивки. После того, как на флешке приставки побывали десятки гигабайт игр, разочарование постигло меня вновь – играть надоело, хотелось большего. Wi-Fi, неплохой процессор, большой экран и полноценная ОС на очень портативном устройстве, весьма экономично использующем батарейку, – неужели это все лишь для игр? Возможно, так думали наивные разработчики приставки, но уж никак не хакеры, научившие GCC компилировать программы под соньку. И научили они, надо сказать, неплохо. Мы получаем полноценный С/С++ без грязных хаков и недоделок, с более-менее документированным API и – ух ты! – возможностью использовать некоторые известные библиотеки, вроде SDL. Это, конечно, не IDE от Sony, продающаяся за какие-то нереальные деньги. Большой подробной справки по каждой функции тут нет, но использовать можно. Тем более, жизнь облегчает то, что PSP – она такая одна и не надо делать всякий раз поправки на производителя и модель, как это бывает при программировании на j2me. С другой стороны, поскольку никаких виртуальных машин нет, есть шанс накосячить, сломав к чертовой матери всю приставку или отдельные ее части. Риск уменьшает то, что тут, как и во многих других «взрослых» ОС, обычные программы запускаются в пользовательском режиме, не столь опасном, как режим ядра. ПриступаемУчти, программирование для PSP – неизученные и опасные дебри. Одно дело программировать для компьютера, где все давно изучено и знакомо, или для телефонов, где виртуальная машина не дает тебе сойти с дороги, а другое – для странного MIPS-процессора, до которого прежде добирались лишь самые опасные маньяки с паяльником. Высадку в эти джунгли стоит начать с перепрошивки – она достаточно подробно описана, например, на http://pspfaqs.ru/. Теперь, когда приставка готова к высадке homebrew (так кличут программы, написанные пользователями для приставок), нужно заготовить войска. Плацдармом для компиляции будет служить Cygwin c пакетами Devel и Web/wget – он у тебя уже установлен, так? Понадобится PSPToolchain – набор необходимых для компиляции программ. Скачав с http://ps2dev.org/psp/Tools/Toolchain/ или забрав с диска, его надо будет разархивировать в %cygwin_install_folder%/home/%windows_username% (если такой папки нет и тебе страшно ее создавать – запусти сигвин и она появится). Открой шелл сигвина и перейди в директорию с тулчайном (cd psptoolchain), после чего запусти ./toolchain-sudo.sh. Он скачает и установит еще несколько нужных программ. Это займет немалое время. После установки открой файл %cygwin_install_folder%/cygwin.bat и замени его содержимое на: @echo off C: chdir C:\cygwin\bin set path=%path%;C:/cygwin/usr/local/pspdev/bin set PSPSDK=C:/cygwin/usr/local/pspdev bash --login -i Так мы устанавливаем переменные среды перед запуском Cygwin-a – чтобы легче было компилировать (не забудь поменять пути в файле, если ты ставил сигвин не в C:\cygwin\). Все! С настройкой, похоже, покончено и можно писать программу. Перезапусти сигвин, чтобы он узнал о переменных, и создай папку projects (mkdir projects). Перейди в нее (cd projects), создай папку helloworld (ха-ха, а ты чего хотел? с этого все всегда начинается) и перенесись туда. Теперь нужно открыть какой-нибудь текстовый редактор (или даже IDE), создать в нем файл и сохранить его в нашу папку helloworld под именем main.cpp. Лично я советую Notepad++, но ты можешь использовать что угодно – хоть Visual Studio, хоть блокнот. Файл должен содержать вот такой код: Самый первый код #include <pspkernel.h> #include <pspdebug.h> PSP_MODULE_INFO("Hello World", 0, 1, 1); int ExitCallback(int Arg1, int Arg2, void *Common) { sceKernelExitGame(); return 0; } int CallbackThread(SceSize Args, void *Argp) { int CallbackId; CallbackId = sceKernelCreateCallback ("Exit Callback", ExitCallback, NULL); sceKernelRegisterExitCallback(CallbackId); sceKernelSleepThreadCB(); return 0; } int SetupCallbacks(void) { int ThreadId = 0; ThreadId = sceKernelCreateThread ("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0); if (ThreadId >= 0) { sceKernelStartThread(ThreadId, 0, 0); } return ThreadId; } int main(int argc, char ** argv) { pspDebugScreenInit(); SetupCallbacks(); while(1) { pspDebugScreenPrintf("Hello ][AKEP!\n"); sceDisplayWaitVblankStart(); } sceKernelSleepThread(); return 0; } Компилируют (ой, собирают) программу в GCC обычно с помощью Makefile. Создаем и его – в той же папке: Первоначальный Makefile TARGET = hello OBJS = main.o CFLAGS = -O2 -G0 -Wall CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti ASFLAGS = $(CFLAGS) EXTRA_TARGETS = EBOOT.PBP PSP_EBOOT_TITLE = Hello World PSPSDK=$(shell psp-config --pspsdk-path) include $(PSPSDK)/lib/build.mak Вернись в сигвиновский шелл. Перейди в папку helloworld, если ты вдруг оказался не в ней, и, затаив дыхание, набери make. Внезапно в папке появится файл с милым названием EBOOT.PBP. Подключи приставку к компьютеру и создай на карточке папку :\PSP\GAME150\HelloWorld\, после чего скопируй полученный файл туда. Запустив программу, ты увидишь на экране сотни заветных строчек! Или не увидишь, тогда придется посидеть и поразмышлять, что, собственно, произошло и как теперь быть. Затем можно и разобраться с кодом. В заголовке содержатся два необходимых инклюда, а далее идет небольшой заголовок на машинном коде. Как ни странно, pspDebugScreenInit() подготавливает экран. Приставка Debug значит, что мы переводим экран в режим отладки (текстовый режим). В функции SetupCallbacks() мы создаем поток (CallbackThread()), в котором при помощи sceKernelCreateCallback() создается каллбэк (ExitCallback()). После чего через sceKernelRegisterExitCallback() регистрируем его как каллбэк выхода из приложения (если такого каллбэка не будет, то из приложения выйти без ребута и прочей неприятной ерунды не получится). Сам каллбэк вызывается при нажатии на кнопку HOME и выборе «ДА» – в ответ на вопрос, не хотим ли мы выйти? Теперь можно переходить к содержательной части программы – выводу на экран строки «Hello ][AKEP!» посредством pspDebugScreenPrintf. Как и следует из названия, эта функция наследует аргументы родного, сишного printf – код поведет себя, как и должен: pspDebugScreenPrintf(" %i ", 666). Кириллицу, кстати, функция очень не любит. Загадочная sceDisplayWaitVblankStart() синхронизирует экран, заодно не давая программе зависать – если убрать, то приложение может перестать реагировать на кнопку HOME или зависнуть при завершении программы. Жмем на кнопкиСамый гениальный геймдевелопер вряд ли разработает игру, не получающую команд от пользователя, а посему нам придется как-то получать доступ к состоянию кнопок и джойстика. За это отвечает одна-единственная волшебная функция sceCtrlReadBufferPositive(&pad, 1). Здесь pad – заранее определенная тобой переменная типа SceCtrlData. Функция (описанная в файле pspctrl.h – не забудь пополнить список инклудов!) заполняет эту структуру значениями. Они, в свою очередь, зависят от текущего состояния контроллера, устанавливающегося при помощи функции sceCtrlSetSamplingMode. Всего состояний два: PSP_CTRL_MODE_ANALOG – привычное нам состояние и PSP_CTRL_MODE_DIGITAL (выставлено по умолчанию) – без джойстика. Таким образом, если мы хотим использовать в программе джойстик, нам надо будет вызвать sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG). В структуре SceCtrlData (после заполнения вышеуказанной функции) будут определены четыре значения: TimeStamp – количество фреймов от начала программы; Buttons – битовая маска кнопок; Lx, Ly – координаты джойстика, меняющиеся от 0 до 255. Стандартное положение джойстика – примерно 128:128, однако из-за чувствительности отпущенный джойстик вполне может оказаться в координатах вроде 112:139 – всегда стоит делать на это поправку. Положение каждой кнопки определяется при помощи (pad.Buttons & button_id), где button_id – одна из констант: PSP_CTRL_SELECT – кнопка SELECT PSP_CTRL_START – кнопка START PSP_CTRL_UP – стрелка вверх PSP_CTRL_RIGHT – стрелка вправо PSP_CTRL_DOWN – стрелка вниз PSP_CTRL_LEFT – стрелка влево PSP_CTRL_LTRIGGER – левый шифт PSP_CTRL_RTRIGGER – правый шифт PSP_CTRL_TRIANGLE – треугольник PSP_CTRL_CIRCLE – круг PSP_CTRL_CROSS – крестик PSP_CTRL_SQUARE – квадратик Например, if (pad.Buttons & PSP_CTRL_CIRCLE) pspDebugScreenPrintf("O") выполнится лишь в случае, если в момент последнего вызова sceCtrlReadBufferPositive был нажат кружок. Что ж, этих знаний вполне хватает, чтобы написать новую программу: Main() второго «хилловорлда» int main(int argc, char ** argv) { pspDebugScreenInit(); SetupCallbacks(); sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); SceCtrlData pad; while(1) { sceCtrlReadBufferPositive(&pad, 1); if (pad.Buttons & PSP_CTRL_CIRCLE) { if (pad.Buttons & PSP_CTRL_LTRIGGER) pspDebugScreenPrintf("OOOOOOOOOO"); else pspDebugScreenPrintf("oooooooooo"); } if (pad.Buttons & PSP_CTRL_CROSS) { if (pad.Buttons & PSP_CTRL_RTRIGGER) pspDebugScreenPrintf("XXXXXXXXXX"); else pspDebugScreenPrintf("xxxxxxxxxx"); } pspDebugScreenPrintf("\nJOYSTICK: %i, %i; FRAME: %i\n", pad.Lx, pad.Ly, pad.TimeStamp); sceDisplayWaitVblankStart(); } sceKernelSleepThread(); return 0; } Достаточно простая программа. Она все время выводит номер текущего фрейма и координаты положения джойстика, а также, если нажат нолик или крестик, выведет буквы X или О (большие или маленькие, в зависимости от нажатых шифтов). Не проблема переделать код так, чтобы выводилось больше информации, да и если приложить немного усилий – можно сделать, что угодно – от крестиков-ноликов до солидной rogue-like игрушки (ну, почти «что угодно»). Но простой белый текст на черном фоне выглядит не очень солидно. Чтобы в твоих супермегакрестиках-ноликах был анимированный красочный аски-арт, не хватает одного – цветов. Они задаются при помощи 32-битного представления RGB, и для перевода используется вот такой макрос: #define RGB(r,g,b) ((u32)((byte)(r)|((byte)(g) << 8)|((byte)(b) << 16))) Так, соответственно, будет представлен зеленый: RGB(0, 255, 0). Цвет фона и текста задаются при помощи pspDebugScreenSetBackColor(u32 color) и pspDebugScreenSetTextColor(u32 color): pspDebugScreenSetBackColor(RGB(255, 0, 0)) – красный. Если ты программировал для компьютерного текстового режима, то помнишь, что после установки цвета фон будет меняться только у свеженапечатанных символов. Также и в этом случае. Поэтому для смены фона экрана придется пробежаться по всему экрану в цикле (обойти 32 строки и 68 столбцов). Чтобы поставить символ в произвольную точку экрана, можно воспользоваться функцией pspDebugScreenPutChar(int x, int y, u32 color, u8 ch), где x и y – координаты, color – цвет символа, а ch – сам символ. Например, pspDebugScreenPutChar(99, 120, RGB(0,0,255), 'Y') установит синюю Y в точке с координатами 99 и 120. Узнать, в какую точку ты попал после таких прыжков, помогут pspDebugScreenGetX и pspDebugScreenGetY. Ну а pspDebugScreenClear очистит экран (кстати, вызов этой функции, согласно документации, возвращает цвет фона в исходное значение – черное). Итак, ты с видом энтузиаста начал писать программу, и понял, что без обращения по имени-отчеству игра теряет свою суператмосферу. Как быть? Программировать самому систему ввода? Слава компьютерным богам, нет. В SDK входят функции для вызова экранной клавиатуры и вызывать ее неожиданно просто. Сначала надо подключить заголовочный файл <pspdebugkb.h> и добавить в Makefile строку, отвечающую за подключение дополнительных библиотек: LIBS = -lpspdebugkb После этого достаточно объявить строку и передать ее функции pspDebugKbInit. По завершении ввода строка будет изменена: char str[20] = "X][X"; pspDebugKbInit(str); pspDebugScreenPrintf("%s", str); // выводим! Так можно облегчить ввод строк, не отвлекаясь на унылый кодинг интерфейса. Конечно, про юзабилити тут лучше и не заикаться, но для text mode – очень даже ничего. Кстати, есть другая реализация клавиатуры, используемая в порте на PSP rogue-like игрушке ToME. Если зажать в игре треугольник, на экране появится список кнопок, среди которых стрелочками можно выбрать букву. Правый шифт – аналог компьютерного шифта, левый – Ctrl. После выбора можно отпустить треугольник, и игра отреагирует на букву. В рогаликах так удобнее, поскольку у каждой буквы в сочетании с каждой из управляющих кнопок (ctrl и shift) есть свое собственное действие. Скриншоты и отладкаДля удобной отладки при помощи самого обычного gdb приставку, разумеется, придется подключить к компьютеру. Единственный «легальный» способ соединения ПК и PSP – через USB и специальный пункт в меню настройки, но его невозможно использовать одновременно с игрой. На помощь приходит PSPLINK – он не просто предоставит нам типичный шелл с возможностями отлаживать программы, делать скриншоты экрана или просто бродить по директориям, но и позволит провести связь через Wi-Fi, что избавит от необходимости запутываться в проводах. Ясное дело, для этого нам понадобится точка доступа. Кстати, у меня не получилось приконнектиться через PSPLINK к запароленному Wi-Fi, поэтому придется на время снять защиту (это не так уж и страшно, – за несколько часов, на которые я убираю пароль, еще никто не успел насладиться безлимитным интернетом за мой счет. В крайнем случае можно настроить фильтр клиентов по MAC-адресу). Итак, после того, как настроил точку доступа, зайди в настройки сети приставки и научи ее соединяться со своей сетью без ввода пароля. И вернись за клавиатуру: открой Cygwin и слей PSPLINK из репозитория: svn co svn://svn.ps2dev.org/psp/trunk/psplink Потом перейди в директорию psplink (cd psplink), собери его (make release) и зайди с ним в директорию (в моем случае – C:\cygwin\home\admin\psplink\) через свой файл-менеджер. В папке release находятся три подпапки с программами для приставки – я выбрал v1.5. Если она не запустится, попробуй две другие (v1.0 и v1.5_nocorrupt). Скопируй содержимое (две папки, psplink и psplink%) на приставку, в каталог :/PSP/GAME150/ на PSP. После чего открой файл :/PSP/GAME150/psplink/psplink.ini и перенастрой (буду давать лишь те строчки, значения в которых надо заменить): usbmass=0 sioshell=0 kprintf=0 wifi=1 wifishell=1 Не забудь раскомментировать (убрать #) пять последних строк, озаглавленных «load the modules for networking». Теперь можешь отключать USB-соединение и скачивать PuTTy или PuTTyTel (если у тебя его еще нет) – с http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html, а затем запускать PSPLINK. Подожди несколько секунд, и, когда на экране появится «connection state 4 of 4», запускай PuTTy. В качестве IP-адреса укажи появившийся на экране; порт: 10000, тип соединения: Telnet. Сохрани сессию, чтобы не пришлось всякий раз вводить заново, и подключайся. Появится консоль. Первую команду у меня она не распознавала из-за странных глюков, но начиная со второй, все шло успешно. Все запускаемые программы идут на приставке параллельно с работой в консоли с компа. То есть, запустив программу, консоль не закроется и не остановится, и ты сможешь дампить память и делать скрины. Команды тут вполне знакомые (полный их список, кстати, ты найдешь в psplink_manual.pdf в папке с psplink-ом):
Программы запускаются так: ./EBOOT.pbp (предварительно нужно сменить директорию на ту, где находится исполняемый файл). Прямо по ходу выполнения можно делать дамп памяти на экран (memdump), выводить разную информацию о потоках и каллбеках и т.п., но удобнее использовать отладчик – о том, как его использовать, рассказано в мануале. В заключениеНа этом все. Если тема тебе понравится (а мы всегда ждем твоих отзывов посредством всех известных науке средств связи, включая интернет, телеграф и отправку нарочным), то в следующих статьях я расскажу о выводе на экран простейшей графики, картинок, работе со спрайтами. А также остановлюсь подробнее на архитектуре процессора и отладке (которая, кстати, на достаточно высоком уровне) и на работе с Wi-Fi. PSP не такая простая и тупая система, как порой кажется. И, возможно, если зловещая корпорация Sony пересмотрит свою политику по отношению к инди-девелоперам, программирование для приставок может оказаться весьма прибыльным занятием. В качестве «домашнего задания» попробуй сделать крестики-нолики. Не думаю, что это будет жутко сложным занятием, зато достаточно интересно в плане реального опыта. DVDНа компакт-диске лежат исходные коды программ, написанных мной для этой статьи, а также инструменты, упомянутые в статье. WWWИнтересную информацию и полезные форумы (на английском языке) можно найти на сайтах http://ps2dev.org/psp/ и http://www.psp-programming.com/. INFOПрочти psplink_manual.pdf для более подробной информации о возможностях PSPLINK. |