前言
PSP,某种意义上,可以算是第一台在市场上有较高普及度的大屏便携掌上多媒体终端。在PSP可以运行自制软件之后,这个平台便有了各式各样的民间APP适配,《仙剑奇侠传》的跨平台实现SDLPal也曾是其中之一。
现在已经是2019年。在各位主力维护者和热心代码贡献者的努力之下,如今的SDLPal工程,和2010年前后的状态相比,已经有了相当大的变化,例如内部的文本系统已经完全迁移到Unicode标准(兼容原版游戏的BIG5和GBK编码资源)且能够方便地支持多语言翻译,音频系统强化到可以实时混合规格不同的音频流并输出,对平台GPU性能有较高要求的GLSL画面滤镜系统,等等。当然,还有很多修正使得游戏逻辑和原版更接近。
而PSP自制软件社区方面……2019年,不要说已经近有15年历史的PSP,其后继机种PSV也已经于上半年停产。PSP的自制软件社区的活跃度和当年的火热状态不能比。
那么在这个时候,将最新版的SDLPal再次适配给PSP平台,技术上是否可行呢?
可行性评估
现在的SDLPal使用的语言标准是gnu99和c++11。因此,当年使用的PSP自制软件工具链(使用gcc4.3.*)已经不能继续使用了。我们需要尽可能新的工具链。另一方面,SDLPal目前是以支持SDL2为主,GLSL画面滤镜系统也仅能搭配SDL2使用,但是现在依然兼容SDL1.2。
PSPdev仓库里[2]的工具链对应gcc4.9.3,可以认为是支持gnu99和c++11的。支持库也有对应的SDL1.2.15方案。看起来基本条件是可以满足的。
除错环境则比较麻烦。因为条件和经验都缺乏,一时也没有找到太好的教程,所以这里基本上只能用SDLPal的LOG系统来进行(但是实际上后面遇到的问题就和这个有关)。
测试环境方面,通用的PPSSPP模拟器虽然可以用于基本测试,但是毕竟模拟器和真机往往存在一些不能忽略的差异,后边的经历也确实证实了这一点。手边可供使用的真机测试设备其实是一台PSV。据猜测,PSV内部包含了一个真正的PSP CPU,可还以运行PSP的系统,所以在其上进行测试,结果应该比PPSSPP更有说服力。
开发环境的搭建
pspdev仓库里的说明[3],在Windows平台需要在cygwin下搭建开发环境。MSYS2实测不行。
这里实际上是从头编译了整个PSPdev工具链。说起来PSPdev的这套环境构建设计很友好,按照其说明进行必要软件安装(请参考cygwin的组件安装步骤。这次并没有安装libusb-dev,也通过了编译)、环境变量的设定、运行构建工具链的脚本即可,整个过程并没有什么遇到什么问题。过程很耗时,在4代i5移动版的笔记本上进行了数十分钟。值得注意的是,中途要确认信任仓库公钥信息,因此不要在开始运行构建工具链的脚本后就丢在那里不管。
- Ubuntu下的PSPdev工具链编译也是类似的步骤。请按照说明进行。
正常完成后,在cygwin的bash窗口输入psp-gcc -v回车,如果有以类似于“gcc 版本 4.9.3 (GCC)”为结尾的输出,说明工具链编译成功。
Hello World测试
参考《PSP Programming》教程的《Hello World》一节[3],我们编写一个PSP“Hello World”程序来对刚刚搭建好的PSPdev环境做测试。具体过程这里不详述了,请参考教程本身。
获得编译好的EBOOT.PSP后,我们在PSP记忆棒的“/PSP/GAME/”目录下新建一个HelloWorld子目录,将EBOOT.PSP拷贝到这个目录里。
- 对于PPSSPP模拟器来说,“memstick”目录就是记忆棒根目录。
运行“Hello World”程序,可以看到PSP屏幕上输出了“Hello World!”。OK!
编译旧版SDLPal PSP
很幸运,我们找到了@156692474 于2011年春天发布的源代码sdlpal-PSP-SRC-61376版[4]。
这一版稍微要在PSP自制软件初始化的部分做一点简单改动才能通过编译(注意要用make -f Makefile.psp启动编译)。参考Hello World程序,这些改动并不难。
编译获得EBOOT.PBP。测试,游戏基本正常运行。说明我们的SDL1.2.15环境也基本正常。
让新版SDLPal PSP跑起来
现在的SDLPal对多平台适配非常友好。我们按部就班地建立一个psp目录,在里边建立pal_config.h、pal_utils.c和Makefile文件,并作出相应适配即可。
pal_config.h中,请只开启必要基本功能。MP3和OGG不要启用,SDL_JOYSTICK需要保留。PAL_PREFIX请设定为""(而不是"./",非常重要)。
pal_utils.c的改动和补充,可以参考旧版SDLPal PSP的input_PSP.c以及其它的PSP自制软件。
Makefile可以参考旧版SDLPal PSP的Makefile.psp,以及SDLPal PSV版的Makefile[5]。
经过一番折腾,我们获得了全新的EBOOT.PBP!然而……
解决各种问题
解决路径问题
《PSP_Development》中的《PSP_Development》一节中,提到当前工作目录为“ms0:/PSP/GAME/APPLICATION”的时候,“ms0:/PSP/GAME/APPLICATION/FILES”的相对路径引用是“/FILES”[6]。
如果这个说法是准确的,那么,PAL_PREFIX“/”应该是指代当前工作目录。然而这似乎是错误的……
首先说明,这样设定,游戏确实跑起来了!那熟悉的群山飞鹤确实让人兴奋!然而……为什么没有声音?
经过多番检查,竟然是没有找到音乐文件MUS.MKF!直接解决方案是……把MUS.MKF放在记忆棒根目录……这说明“/”很可能不是EBOOT.PBP所在的当前工作目录,而是记忆棒根目录。那么,为什么其它资源文件可以读取呢?
实际上SDLPal中打开MUS.MKF的包装函数(和打开音效文件的操作)与打开必需资源文件的包装函数有些不同,没有在带路径打开失败后直接以文件名作为参数fopen的设计。而之前必需资源文件的打开,其实也是经历了带有路径打开失败 -> 直接以文件名作为参数fopen的过程。因此,音乐音效文件打不开,就默认为无声状态了。
那么,想要设定PAL_PREFIX为EBOOT.PBP所在的当前工作目录,设定为"",也就是留空,即可。注意,不是"./"!
只要有音乐,开始游戏和读档就会出错退出?
音乐是播放出来了,PPSSPP模拟器测试无误。可是在PSV真机的PSP模式测试,我们可以发现,片头过后,无论是开始游戏还是读档,都会让程序出错退出。如果无法加载MUS.MKF则游戏可以进行。这是为什么?
出错的位置,可以确定是在读取MAP.MKF和GOP.MKF的这一步。
我们有理由怀疑这是PSP内存不足导致的。最初的PSP 1000型号主机只有32MB内存,而应用程序只能够使用24MB(没错,所有的官方PSP游戏都必须可以只使用24MB内存运行,PSP 2000/3000多出来的内存不能是运行游戏必须的)!
如果真的是内存不足,那么困难就很大了。
为了确定是不是内存不足的问题,去掉程序内置的用不到的手柄图资源、缩减特定字型字库甚至Unifont基本字库都试过了。没什么改变。
那么让我们换一个角度。
打开MAP.MKF和GOP.MKF两个文件的操作,中间的流程,怎么插入LOG信息,都无法正常LOG下来(PPSSPP模拟器上并没有这个问题)。这就很麻烦了。
那么,不打开MUS.MKF,这里就完全正常了吗?
不!
如果不打开MUS.MKF,即之前提到的游戏可以无声进行的状态,打开MAP.MKF和GOP.MKF两个文件的操作,可以在打开第一个文件之后输出LOG,但是尝试打开第二个文件之后便没有LOG信息了。不仅如此,其它同时再打开两个文件的部分也有类似的现象(但是从游戏运行状态来看,尽管无法获得LOG信息,具体文件操作是成功的)。这是为什么呢?
SDLPal运行后,一直要保持打开8个必须资源文件。我们可以注意到,LOG信息输出到文件,也是需要打开文件的。这难道是说明,多打开两个文件之后,已经不能再打开新的文件了吗?
假如是这样,那么,一切就说得通了:
没有加载MUS.MKF时,游戏开始运行,程序首先同时打开8个文件,之后,打开第9个文件,用第10个句柄打开LOG文件写入后关闭,再打开第10个文件,这时不能再打开新的文件做LOG文件写入,直到第10个文件关闭,让第10个句柄可用,才能继续LOG。
加载MUS.MKF时,游戏开始运行,程序首先同时打开8个文件,之后,打开MUS.MKF,这就是第9个文件,此后只要再打开一个文件,用掉第10个句柄,就不能再打开新的文件做LOG文件写入,直到第10个文件关闭,让第10个句柄可用,才能继续LOG。在这个情况下,也只能再同时打开1个文件。因此MAP.MKF和GOP.MKF无法同时打开,也就会出错退出了!
系统能够同时打开的流确实是有限的,这个值的保证下限在stdio.h的FOPEN_MAX中指定。PSPdev SDK里,这个值是16,除去stdin、stdout和stderr之外还有13。而按照刚才的推测,我们可以使用的文件句柄量的上限是10。
后来,通过改造我们的Hello World程序做测试得知,之前的推测是正确的…… 我们确实只能在真机上同时打开10个文件。PPSSPP模拟器环境中这个上限要高太多,所以不会出错!
要解决这个问题……既然BGM的机制本来就是把单独的RIX格式曲目读取入内存播放,那还是把MUS.MKF完全缓冲到内存后关闭,空出这宝贵的句柄名额吧。毕竟文件只有几百KB而已。
BGM问题成功解决!
音效……
音效读取也有句柄数量限制问题。不同的是,音效文件不适合全部缓冲到内存了(DOS版仙剑的VOC.MKF有接近2MB,Windows版的sounds.mkf接近20MB!)。改造成随用随开,读取完关闭,即可……
操作很奇怪
虽然可以玩了,读档存档动画战斗都正常,但是这个操作……PSP摇杆可用,但是,四个方向键被错误地解释成了确定和取消之类的功能键。这就很别扭了。
经过折腾,可以确认,SDL自带的手柄机制在这里有问题。我们需要自己的一套按键处理。新版SDLPal的设计优势体现出来了,我们只需要用自定义按键处理拦截掉输入消息即可。
操作问题,完全解决!
柔情篇的视频动画可以播放吗?
可以播放。但是,目前还是得禁用。
播放视频动画需要切换图像模式到16位色(否则视频很难看的),之后再切回游戏本身所需的8位色。但是,PSP版SDL貌似有BUG,只要再次设定图像模式到8位色,就会出现花屏。这不是SDLPal层次的问题了,暂时搁置。
画面整体明暗淡入淡出
画面整体淡入淡出的调色板操作后需要显式调用一下VIDEO_UpdateScreen(NULL)函数,否则渐变效果不会显示。一共有三部分需要改:跳过群山飞鹤片头的快速明暗淡入淡出,通用画面整体明暗淡入淡出函数,战斗失败游戏结束的“胜败乃兵家常事……”画面整体变红效果。
性能表现
首先我们需要对PSP这2004年的333MHz的MIPS移动版CPU赞一个!它竟然能够基本顶住FM音乐用MAME OPL模拟核心在24858Hz频率下开立体声效果用中低质量重采样效果的运算负荷。要知道默认的最高音频效果可是连PSV都顶不住(是的,PSV也有SDLPal)。当然,想要玩得流畅,还是请用MAME OPL模拟核心单声道22050Hz模式。
画面嘛,PSP本身的分辨率就是480 x 272,当年这是很棒的,给320 x 200分辨率的仙剑用嘛,以默认的方式拉伸到全屏肯定不完美。以后可以考虑开启线性过滤。
读盘速度……仙剑本身就不是那种要苦等LOADING的游戏,进入地图前读盘时间稍长一些也完全是在可接受范围内。
加载翻译用的语言文件……没发现有什么问题。
总结
曾几何时,PSP几乎是随身多媒体终端的最佳选择。而SDLPal适配PSP,都算是可以在Fans圈子内引起热议的新闻了。
时过境迁,随身多媒体终端早已成为了大屏智能手机的天下。事实上,无论是iOS版还是安卓版SDLPal,其在主流手机上的综合表现均远超PSP版的效果。甚至对于手边的这台PSV来讲,使用PSV原生版SDLPal也是更好的选择。
但是,就像现在的很多怀旧玩家一次又一次地刷新老仙剑速通纪录一样,让群山飞鹤又一次在PSP的屏幕上浮现,让云谷鹤峰再一次从PSP的扬声器里响起,应该说,这本身就是一种情怀吧。
[1] https://github.com/pspdev/
[2] https://github.com/pspdev/psptoolchain
[3] https://en.wikibooks.org/wiki/PSP_Programming/General/Hello_World
[4] https://bbs.saraba1st.com/2b/thread-658006-1-1.html
[5] https://github.com/usineur/sdlpal
[6] https://en.wikibooks.org/wiki/PSP_Development/