Многие из счастливых обладателей PSP, читающих ][, наверняка не раз задумывались: вот прогаю я для компа, или взламываю всякие сайты - а вот клево было бы написать что-нибудь для своей приставки. Например, ММОРПГ - при наличии Wi-Fi, неплохих графических возможностей и удобного управления, созданного специально для игр, в этой нише у карманной игровой станции странная пустота. Или тулзу для взлома Wi-Fi сетей. Или брутер, который можно спокойно оставить на ночь - потому что он не шумит, не жрет много электричества и не кишит вирусами (кстати, тоже ведь идея!). Тем более, к PSP теперь можно подсоединить всяческие фотоаппараты и микрофоны - в общем, гуляй - не хочу.
Фото: uncle_lag
Как обычно зловещая корпорация не хочет, чтобы под их консоль штамповали игры, не отчисляя ей ни копейки - тем более, основной доход идет именно с процентов от продажи игр, а приставки частенько продаются по себестоимости, или даже в убыль. Поэтому абсолютно легально не получится не то что написать программу - да даже ее и запустить. Слава хакерам - нашлись умельцы, давно взломавшие зловещую прошивку приставки и убравшие из нее защиту (заодно врубив эмуляцию iso-образов игр, потому что UMD-диски дороговато стоят... но я вам этого не говорил ;)). Как получить такую версию прошивки расскажет pspfaqs.ru. Иди на этот сайт и перепрошивайся до
3.90 М33-3, и не забудь установить ядро 1.50 (если у тебя "толстая" версия консоли) - иначе то, что у нас получится, вполне может и не запуститься. Так что я буду считать, что ты это уже сделал, и останавливаться на этом не стану.
Также тебе понадобится карточка MemoryStick Duo. Идущая в комплекте должна вполне подойти, хотя я бы посоветовал купить хотя бы на 2 гигабайта - для программирования это очень много, но образы некоторых игр весят больше 1 Гб. Понадобится и шнур для соединения компа с приставкой (тоже есть в комплекте, а если уже потерял - что, ты не найдешь Usb-2-MiniUsb?). Еще из хардваре пригодится холодный, незамутненный разум хирурга, а то вскрытие (программное, корпус открывать не будем) может завершиться смертью или сумасшествием пациента.
Кстати, предупреждаю сразу: ни я, ни редакция, ни - тем более - Sony не дает никаких гарантий, что с приставкой все будет хорошо в результате наших анатомических исследований. Она вполне может брикнуться (так бывалые псп-геймеры называют таинственное превращение клевой приставки в черный, белый, розовый - нужное подчеркнуть кирпич), заглючить, убить твою карточку MemoryStick или, превратившись в НЛО, улететь на Луну оставлять там будоражащие кровь в жилах надписи.
Но ни у меня, ни у тех на ком я ставил свои противоестественные опыты, ничего такого не случилось. Самое страшное, что было - это зависание намертво, которое лечится обычным аварийным выключением: задержи рычажок выключения в позиции Power секунд на 6-7, пока зеленая лампочка не перестанет мигать. Теперь можешь включать.
В блокноте получается писать только на JavaScript'е, что для наших целей не особо подходит, поэтому придется скачать IDE и набор компиляторов. IDE я рекомендую Code::Blocks - именно для этой IDE я опишу процесс настройки, а набор компиляторов, под названием DevKitPSP. Я распаковал DevKitPSP в корень диска E:, так, что у меня теперь есть папка E:\devkitpsp\, а в ней папки bin, include и другие, и настройки буду указывать для этого пути - соответственно, если у тебя путь окажется другой, его придется везде
заменять. IDE можно установить куда угодно, после чего запусти его и настрой (буду давать сведения по настройке максимально сжато потому, что их много. Утешает то, что придется сделать это один раз):
1. Settings - Compiler and Debugger, Selected Compiler: GNU GCC, жмем Copy, называем копию DevKitPSP, выбираем ее в списке компиляторов.
2. В том же окне - вкладка Search Directories - Compiler должна содержать только:
E:\devkitPSP\include
E:\devkitPSP\psp\include
E:\devkitPSP\psp\sdk\include
Search Directories - Linker:
E:\devkitPSP\lib
E:\devkitPSP\psp\lib
E:\devkitPSP\psp\sdk\lib
3. Вкладка Toolchain Executables:
Compiler's installation directory: E:\devkitPSP
C-compiler: psp-gcc.exe
C++-compiler: psp-g++.exe
Linker for dynamic libs: psp-g++.exe
Linker for static libs: psp-ar.exe
Еще три поля там же оставляем пустыми, и жмем ОК.
4. Создаем проект: File-New-Project..., Projects, Empty. Project Title пусть будет PSPHelloWorld, указываем папку для сохранения проекта где-нибудь на компьютере; компилятор ставим DevKitPSP.
5. Cоздаем фаил File>New>File..., main.c — в мастере создания фаила выбираем язык Си и ставим галочки "Add to current project", "Debug" и "Release".
6. В созданный фаил копируем код из архива. Попозже разберемся, что он делает, а пока - донастроим проект и запустим его.
7. Project-Properties, Build targets. Снимаем галочку "Auto-generate filename extension", и правим в Output filename расширение "exe" на "elf". Выполняем тоже самое для конфигурации Release, жмем Ok.
8. Project-Build Options, Linker Options, Other linker options:
-lpspdebug -lpspdisplay -lpspge -lpspctrl -lpspsdk -lpsplibc -lpspnet_inet -lpspnet_apctl -lpspnet_resolver -lpsputility -lpspuser -lpspkernel
Повторяем для Release.
Ура! Можно жать на Build-Build и собирать проект, надеюсь - без ошибок. Так мы получим elf-файл с программой. Но эльфы "грабят корованы" в юниксах, а у нас странная ОС от PSP, готовая исполнять лишь файлы с милым русскому человеку названием EBOOT.PBP. Для выполнения сложной операции преобразования у меня есть волшебный bat-ник:
"E:\devkitPSP\bin\mksfo.exe" %1 %1.sfo
"E:\devkitPSP\bin\psp-fixup-imports.exe" %1.elf
"E:\devkitPSP\bin\psp-strip.exe" %1.elf -o stripped.elf
"E:\devkitPSP\bin\pack-pbp.exe" EBOOT.PBP %1.sfo NULL NULL NULL NULL NULL stripped.elf NULL
Разумеется, запускать его надо с аргументом-именем твоего elf-фаила (без расширения). Т.е., если у тебя получился HelloWorld.elf, то запускай батник как my_batnik.bat HelloWorld. Ну а если ты противник таких вещей - можешь вводить содержимое по строчке в консоль, заменяя %1 на имя того файла.
Теперь у нас есть долгожданный EBOOT, и его мы пихаем в папку /PSP/GAME150/HelloWorld/ нашей PSP-шки. После чего запускаем его, громогласно приветствуя мир. Поздравляю! Вы написали свою первую программу на PSP. Прежде чем закрывать IDE, советую сохранить проект как шаблон (File-Save Project as user-template...) - чтобы потом, при создании нового, не начинать все заново.
Как ты уже наверное понял, функция pspDebugScreenPrintf() выводит текст на экран. Она - полный аналог функции printf() и поддерживает ее форматную строку. Я начал описывать код с нее, так как это - первая функция, которой учат несчастных, изучающих Си. Вторая же изучаемая функция - обычно scanf(), но ее для PSP нет - потому что нет клавиатуры. Поэтому я просто расскажу, как получать состояние кнопок и джойстика.
За кнопки отвечает функция sceCtrlReadBufferPositive, описанная в фаиле pspctrl.h, так что добавляем в начало main.c строку:
#include <pspctrl.h>
В качестве параметров функция принимает указатель на структуру SceCtrlData, в которую она возвратит данные о кнопках, и количество буферов для чтения, так что вызывать мы ее будем так:
SceCtrlData pad;
sceCtrlReadBufferPositive(&pad, 1);
Вот описание возвращаемой структуры:
typedef struct SceCtrlData {
unsigned int TimeStamp; // читаемый фрейм
unsigned int Buttons; // битовая маска, задающая кнопки
unsigned char Lx;
unsigned char Ly; // положение джойстика
unsigned char Rsrv[6]; // зарезервировано
} SceCtrlData;
А вот - список кнопок, состояние которых появляется в битовой маске SceCtrlData::Buttons:
enum PspCtrlButtons
читается только в режиме ядра, т.е.
{
PSP_CTRL_SELECT = 0x000001,
PSP_CTRL_START = 0x000008,
PSP_CTRL_UP = 0x000010,
PSP_CTRL_RIGHT = 0x000020,
PSP_CTRL_DOWN = 0x000040,
PSP_CTRL_LEFT = 0x000080,
PSP_CTRL_LTRIGGER = 0x000100,
PSP_CTRL_RTRIGGER = 0x000200,
PSP_CTRL_TRIANGLE = 0x001000,
PSP_CTRL_CIRCLE = 0x002000,
PSP_CTRL_CROSS = 0x004000,
PSP_CTRL_SQUARE = 0x008000,
PSP_CTRL_HOME = 0x010000,
PSP_CTRL_HOLD = 0x020000,
PSP_CTRL_NOTE = 0x800000,
//
// не судьба нам узнать о нажатии на нее
PSP_CTRL_SCREEN = 0x400000, // только в режиме ядра
PSP_CTRL_VOLUP = 0x100000, // только в режиме ядра
PSP_CTRL_VOLDOWN = 0x200000, // только в режиме ядра
PSP_CTRL_WLAN_UP = 0x040000, // только в режиме ядра
PSP_CTRL_REMOTE = 0x080000, // только в режиме ядра
PSP_CTRL_DISC = 0x1000000, // только в режиме ядра
PSP_CTRL_MS = 0x2000000, // только в режиме ядра
};
Как использовать эти странные числа? А очень просто. Чтобы узнать, нажата ли, скажем, кнопка с кружочком, нам надо проверить (pad.Buttons & PSP_CTRL_CIRCLE). Т.е., например:
if (pad.Buttons & PSP_CTRL_CIRCLE)
pspDebugScreenPrintf("CIRCLE PRESSED");
А как использовать джойстик, можно догадаться и самому. Надо только знать, что координаты его положения меняются от 0 до 255 (значит, середина - в точке (128,128)), и не забывать, что он редко стоит прямо по центру (на отклонение в несколько единиц лучше не обращать внимания, позже ты увидишь, почему). Кстати, чтобы информация о положении джойстика приходила, необходимо добавить вот такие строки:
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
Все. Теперь мы знаем о кнопках все, что можем. И для примера можно написать несложную программу, которая будет показывать, что в данный момент нажато. Вот что получилось у меня (я, кстати, поленился и стал мониторить только кнопки с рисунками, т.е. крестик, кружок, треугольник и квадрат. Но не думаю, что у тебя возникнут какие-то проблемы с этим):
SceCtrlData pad;
int main(int argc, char ** argv)
{
pspDebugScreenInit();
// устанавливаем каллбеки
SetupCallbacks();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
// настраиваем на прием информации о положении стика
while(1) // главный цикл программы
{
pspDebugScreenClear(); // чистим экран
sceCtrlReadBufferPositive(&pad, 1);
// читаем содержимое буфера э-э клавиатуры
pspDebugScreenPrintf("TimeStamp %i | Pressed: ", pad.TimeStamp);
// выводим TimeStamp
if (pad.Buttons == 0) // ничего не нажато
{
pspDebugScreenPrintf("NONE ");
} else {
if (pad.Buttons & PSP_CTRL_TRIANGLE) pspDebugScreenPrintf("TRIANGLE ");
if (pad.Buttons & PSP_CTRL_CIRCLE) pspDebugScreenPrintf("CIRCLE ");
if (pad.Buttons & PSP_CTRL_CROSS) pspDebugScreenPrintf("CROSS ");
if (pad.Buttons & PSP_CTRL_SQUARE) pspDebugScreenPrintf("SQUARE ");
// какие кнопки нажаты?
}
pspDebugScreenPrintf("(%i) | Stick: %i:%i\n", pad.Buttons, pad.Lx, pad.Ly);
// выводим координаты стика
}
sceKernelSleepThread();
// поток засыпает
// если уснули все потоки, то единственное,
// что может случиться - это каллбек
// в этой программе эта строчка никогда не выполнится ;)
return 0;
}
Все остальные функции лучше оставить такими, какие есть. Полную версию этой программы с комментарием к каждой строчке можно найти в архиве.
Итак, мы можем теперь выводить что угодно и узнавать, как на это реагирует пользователь. Самое время портировать Moria или какую-нибудь змейку 🙂 Для полного текстового счастья не хватает, конечно же, цвета и возможности писать где угодно. Ради цвета я бы посоветовал использовать макрос, который я благополучно стырил откуда-то из хедеров к IDE:
typedef unsigned char byte;
определяем, потому что DevKitPSP понятия не имеет,
//
// что есть такой тип
#define RGB(r,g,b) ((u32)((byte)(r)|((byte)(g) << 8)|((byte)(b) << 16)))
Этот макрос выдает цвет, подходящий к любым функциям, связанным с цветом среди API PSP. Например, к функции установки фона для текста:
pspDebugScreenSetBackColor(RGB(0, 0, 255));
// ставим синий
или цвета самого текста:
pspDebugScreenSetTextColor(RGB(0, 255, 0));
// зеленый!
или даже установки символа в любую позицию:
pspDebugScreenPutChar(100, 100, RGB(255, 0, 0), 'A');
// ставим красный символ 'A' в точку с координатами (100, 100)
Кстати, координаты задаются в пикселях, а не в строках и столбцах символов.
Ну что ж, теперь ты можешь написать на экране PSP что угодно. Например, проклятие черными буквами на черном фоне своему врагу, или другу-готу. Или розовым по черному нарисовать ASCII-спанч боба. Или красным по розовому признаться в любви своей девушке (парню?). Или... а, о чем я. Решать тебе. Наслаждайся! А в следующей статье я постараюсь рассказать про картинки. Хотя бы немножко 😉
И на последок: советую подписаться на мой блог, http://psp.va1en0k.net/. Там я буду выкладывать много материала, полезного для PSP-программиста 😉