主题 : 【其他】一个PSP自制程序 BROWSE Demo
级别: 模拟之星
UID: 33131
精华: 0
发帖: 294
威望: 2 星
金钱: 3132 浮游币
贡献值: 29 点
好评度: 248 点
人气: 8 点
在线时间: 57(时)
注册时间: 2005-06-05
最后登录: 2021-05-29
楼主  发表于: 2009-02-07 13:50

【其他】一个PSP自制程序 BROWSE Demo

一个简单的程序 功能是实现指定目录的浏览。如果有兴趣而又不知道从何入手的可以搜索我之前的两篇文章,或者QQ空间http://user.qzone.qq.com/151243514

不知道是不是每个人都有个愉快的春节呢?

为了不让自己静下来,继续学习。

要看懂下面的内容,至少对C有要些了解。PSP程序源代码的结构和普通的C程序没有太大的区别。

首先介绍需要引入的头文件。
#include <pspkernel.h> //必须引入的头文件(已经包含了IO操作所需的头文件)
#include <pspdebug.h> //屏幕操作所需的头文件
#include <pspctrl.h> //按钮操作所需的头文件
#include <pspdisplay.h> //引入这个头文件是为了使用函数sceDisplayWaitVblankStart();它的作用在后面介绍


#include <string.h> //字符串操作所需的头文件

然后是:
PSP_MODULE_INFO("Test", 0, 1, 1);
第一个参数是程序的名字,但不是程序显示的名字,如果你看了上一篇文章,那么应该知道程序显示的名字是在make过程中通过mksfo指定的;第二个参数是程序的属性,为0是用户模式的程序,如果运行在内核模式换成0x1000;第三、四个参数为程序的版本号。

接着定义了一些全局变量
char selected[256][256], entry[256], upEntry[256]; //分别是:存储某目录下的条目全路径,本级目录,上级目录
int attr[256]; //存储某目录下的条目属性,区分是文件还是文件夹

为什么要这样定义呢?我试过将一个字符指针作为函数参数传递,结果在运行过程中我发现该指针指向的内存空间在我定义的其他数组内存空间中,导致一些不可预期的错误,在使用字符串的时候,最好是定义为全局变量,或者用malloc动态分配。

如果你想在程序中HOME键能起到相应的作用,那就必须加入下面的代码。
int exit_callback(int arg1, int arg2, void *common) {
sceKernelExitGame();
return 0;
}

int CallbackThread(SceSize args, void *argp) {
int cbid;

cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}

int SetupCallbacks() {
int thid = 0;

thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0) sceKernelStartThread(thid, 0, 0);
return thid;
}


因为SDK提供的函数都有可能因为某些原因而得不到预期的结果,所以最好再加一个处理异常的函数。该函数能在屏幕输出错误信息,并等待用户确认后安全的退出程序,避免PSP的死机。
void error(char *str, int code) {
SceCtrlData pad; //定义一个按钮的结构体变量

pspDebugScreenPrintf("\n%s: 0x%08X Press X to Exit.", str, code);//在屏幕输出信息,用法和printf类似。但在使用这个函数前,必须先初始化屏幕,这个动作在main中完成。
while (1) {
sceCtrlPeekBufferPositive(&pad, 1); //读取按键状态
if (pad.Buttons & PSP_CTRL_CROSS) break; //如果是按X则退出循环,其他信息请参考pspctrl.h
sceDisplayWaitVblankStart(); //如果这里不加入这个函数,相当于主线程得不到休息,那么HOME键所调用的退出线程也就不能分配到资源,导致死机。
}
sceKernelExitGame(); //退出程序
}


下面是根据本目录取得上级目录的函数
void getUpEntry() {
int len, i;

len = strlen(entry);
for (i = len; i > 0; i--)
if (entry == '/') break;
strcpy(upEntry, entry);
upEntry = '\0';
}

紧跟着的就是这个程序的重点函数了,实现浏览指定目录的内容。要得到目录信息,首先要得到一个已经打开的目录的描述符,用sceIoDopen获得。然后再用sceIoDread读取打开目录下的条目信息。
void browse() {
int fd, status, i;
SceIoDirent dir;
char otherEntry[256];

i = 0;
pspDebugScreenClear();
pspDebugScreenSetXY(0, 0);
getUpEntry();
pspDebugScreenPrintf("BROWSE Demo by Grey 090207 --> %s\n", entry);
fd = sceIoDopen(entry); //打开指定目录, entry在main中赋初值为ms0:,代表记忆棒
if (fd >= 0) { //小于0时打开失败
while(1) { //循环执行sceIoDread,每执行一次就得到该目录下条目的详细信息,直到返回0,退出循环,返回主函数
memset(&dir, 0, sizeof(SceIoDirent)); //SceIoDirent结构体用来存储条目的详细信息,但是在使用前最好将其所占内存区域初始化为0,否则可能发生不可预期的错误
status = sceIoDread(fd, &dir);
if (status < 0) error("sceIoDread", status);
if (status == 0) {
status = sceIoDclose(fd); //别忘了关闭打开的目录
if (status < 0) error("sceIoDclose", status);
return;
}
strcpy(otherEntry, entry);
strcat(otherEntry, "/");
strcat(otherEntry, dir.d_name); //dir.d_name为该条目的名称
//SceIoDirent的详细信息参考pspiofilemgr_dirent.h和pspiofilemgr_stat.h
strcpy(selected, otherEntry); //将条目的全路径存入selected中
if (strcmp(dir.d_name, ".") == 0) strcpy(selected[0], entry);
if (strcmp(dir.d_name, "..") == 0) strcpy(selected[1], upEntry);
if (dir.d_stat.st_attr == 0x10) { //dir.d_stat.st_attr代表条目的属性,用于区分文件和文件夹
pspDebugScreenPrintf("( dir) ");
attr = 0x10;
}
else {
pspDebugScreenPrintf("(file) ");
attr = 0x20;
}
pspDebugScreenPrintf("(%2d) %s\n", i++, dir.d_name);
}
status = sceIoDclose(fd);
if (status < 0) error("sceIoDclose", status);
}
else
error("sceIoDopen", fd);
}

然后是选择的函数,按上下键数字会增加或减少
int select() {
SceCtrlData pad;
int base, lastButtons;

base = 0;
sceCtrlPeekBufferPositive(&pad, 1);
lastButtons = pad.Buttons;
pspDebugScreenSetXY(0, 66);
pspDebugScreenPrintf("\nselect: 0 Press O to Confirm.");
while (1) {
sceCtrlPeekBufferPositive(&pad, 1);
pspDebugScreenSetXY(9, 66);
if (pad.Buttons != lastButtons) { //这样做的目的是按一下键base只增加或减少1
lastButtons = pad.Buttons;
if (pad.Buttons & PSP_CTRL_UP) pspDebugScreenPrintf("%2d", ++base);
if (pad.Buttons & PSP_CTRL_DOWN) pspDebugScreenPrintf("%2d", --base);
if (pad.Buttons & PSP_CTRL_CIRCLE) break;
}
sceDisplayWaitVblankStart();
}
return base;
}

最后是main函数
int main() {
int count;

pspDebugScreenInit(); //向屏幕输出信息首先要初始化屏幕
SetupCallbacks(); //创建了一个线程,并让其处于等待状态
strcpy(entry, "ms0:");
browse();
while (1) {
count = select();
strcpy(entry, selected[count]);
if (attr[count] == 0x10) browse(); //如果是目录则进入浏览
}
return 0;
}

附件里面带了源文件和Makefile,以及可以运行的EBOOT.PBP,将EBOOT.PBP放到PSP/GAME/BROWSE下即可运行。

欢迎转载,并注明出处。

Grey Deng <fujingdcn@yahoo.com.cn>
2009-2-7
附件: BROWSE.rar (35 K) 下载次数:1
happy new year!
YZB
级别: 超级版主

UID: 12451
精华: 6
发帖: 19055
威望: 173 星
金钱: 2962 浮游币
贡献值: 9881 点
好评度: 54745 点
人气: 4322 点
在线时间: 19078(时)
注册时间: 2004-10-02
最后登录: 2024-06-29
沙发  发表于: 2009-02-08 22:26

楼主你越来越牛起来了哈
本帖最近评分记录:
  • 抢到沙发 奖励人气: 1点(系统奖励)