|
本帖最后由 william 于 2025-9-24 07:14 编辑
romdata的加载过程,分为fbneo本体实现方式和fbneo_libretro方式两种,前者支持nebula格式和fbneo格式的romdata,后者仅支持fhbeo格式的romdata
加载romdata文件后,逐行读取文件,如果遇到 System ,则认为是Nebula 格式的romdata ,将标志位 nDatMode置为1,以记忆文件格式的类型,后续根据此标志位进行差异化处理,Nebula格式的romdata第一行有效文字必然是System开始,这是个很重要的信息,实际含义是基板类型,基板类型不同,子系统和类型的宏定义不同,如果没有这个基板类型信息,后续内容就不能有效处理。
nebula格式为五列数据并且有段标记来描述是哪个子系统,fbneo格式是四列数据并且有子系统和类型的常量定义(详细的子系统和常量定义参见本人另外一贴ROMDATA的类型和子系统详解 - 模拟FAQ - 肖琪模拟游戏站 - Powered by Discuz! (xqemu.cn)
举例:同一个romdata,nebula格式如下:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
System: CPS1
RomName: wofjryu
Game: 三国志2 降龙
Parent: wofj
[Program]
tk2j23c.bin,0,80000,0,0
tk2j22c.bin,80000,80000,0,0
[Graphics]
tk2_01.3a,0,80000,D9CB9BF,0
tk2_02.4a,2,80000,45227027,0
tk2_03.5a,4,80000,C5CA2460,0
tk2_04.6a,6,80000,E349551C,0
tk2_05.7a,200000,80000,E4A44D53,0
tk2_06.8a,200002,80000,58066BA8,0
tk2_07.9a,200004,80000,D706568E,0
tk2_08.10a,200006,80000,D4A19A02,0
[Z80]
tk2_qa.5k,0,20000,C9183A0D,0
[Samples]
tk2-q1.1k,0,80000,611268CF,0
tk2-q2.2k,80000,80000,20F55CA9,0
tk2-q3.3k,100000,80000,BFCF6F52,0
tk2-q4.4k,180000,80000,36642E88,0
[System]
DriverId:
DriverInit: 0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fbneo格式如下:
ZipName: wofjryu
DrvName: wofj
ExtraRom:
FullName: "三国志2 降龙"
//romname romsize crc type
tk2j23c.bin, 80000, 0x00000000, 2 | BRF_ESS | BRF_PRG
tk2j22c.bin, 80000, 0x00000000, 2 | BRF_ESS | BRF_PRG
tk2_qa.5k, 20000, 0xC9183A0D, 3 | BRF_ESS | BRF_PRG
tk2_01.3a, 80000, 0x0D9CB9BF, 4 | BRF_GRA
tk2_02.4a, 80000, 0x45227027, 4 | BRF_GRA
tk2_03.5a, 80000, 0xC5CA2460, 4 | BRF_GRA
tk2_04.6a, 80000, 0xE349551C, 4 | BRF_GRA
tk2_05.7a, 80000, 0xE4A44D53, 4 | BRF_GRA
tk2_06.8a, 80000, 0x58066BA8, 4 | BRF_GRA
tk2_07.9a, 80000, 0xD706568E, 4 | BRF_GRA
tk2_08.10a, 80000, 0xD4A19A02, 4 | BRF_GRA
tk2-q1.1k, 80000, 0x611268CF, 6 | BRF_SND
tk2-q2.2k, 80000, 0x20F55CA9, 6 | BRF_SND
tk2-q3.3k, 80000, 0xBFCF6F52, 6 | BRF_SND
tk2-q4.4k, 80000, 0x36642E88, 6 | BRF_SND
解析过程略过,那么解析完romdata数据,做了什么事才让romdata工作起来的?解析的目的是构造和原版游戏一样的数据结构并且包含的是自己做的数据(即自定义romdata)的内容,BurnRomInfo,有个这个数组,就需要去找替换对象,就是fbneo里的Drvname ,nebula里的Parent .
在本例中,替换的是wofj,让我们看wofj的原版定义:
static struct BurnRomInfo WofjRomDesc[] = {
{ "tk2j_23c.8f", 0x080000, 0x9b215a68, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP },
{ "tk2j_22c.7f", 0x080000, 0xb74b09ac, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP },
{ "tk2_01.3a", 0x080000, 0x0d9cb9bf, BRF_GRA | CPS1_TILES },
{ "tk2_02.4a", 0x080000, 0x45227027, BRF_GRA | CPS1_TILES },
{ "tk2_03.5a", 0x080000, 0xc5ca2460, BRF_GRA | CPS1_TILES },
{ "tk2_04.6a", 0x080000, 0xe349551c, BRF_GRA | CPS1_TILES },
{ "tk2_05.7a", 0x080000, 0xe4a44d53, BRF_GRA | CPS1_TILES },
{ "tk2_06.8a", 0x080000, 0x58066ba8, BRF_GRA | CPS1_TILES },
{ "tk2_07.9a", 0x080000, 0xd706568e, BRF_GRA | CPS1_TILES },
{ "tk2_08.10a", 0x080000, 0xd4a19a02, BRF_GRA | CPS1_TILES },
{ "tk2_qa.5k", 0x020000, 0xc9183a0d, BRF_PRG | CPS1_Z80_PROGRAM },
{ "tk2-q1.1k", 0x080000, 0x611268cf, BRF_SND | CPS1_QSOUND_SAMPLES },
{ "tk2-q2.2k", 0x080000, 0x20f55ca9, BRF_SND | CPS1_QSOUND_SAMPLES },
{ "tk2-q3.3k", 0x080000, 0xbfcf6f52, BRF_SND | CPS1_QSOUND_SAMPLES },
{ "tk2-q4.4k", 0x080000, 0x36642e88, BRF_SND | CPS1_QSOUND_SAMPLES },
那么是怎么实现替换原版rom定义,实现偷梁换柱的呢?程序中有个全局指针,指向要处理的游戏数据 pDataRomDesc ,其他程序都用这个指针指向的游戏数据工作,正常加载的时候,这个指针指向的是原版的游戏定义 BurnRomInfo WofjRomDesc[] ,在有romdata发现,并且加载romdata后,这个全局指针指向了romdata,这就要求romdata的构造格式要完全遵从程序的BurnRomInfo数据结构定义,否则后续其他程序是无法正常工作的,就是说,通过romdata改变了驱动层的数据定义,让后续程序都用romdata定义的数据工作,这提供了极大的灵活性和扩展能力,当然,定义错了,就无法工作,所以romdata必须是严谨的
详细的替换过程如下:
a.rom数据替换
LoadRomdata() 函数中,当解析完romdata文件,根据romdata文件中的数据构建完BurnRomInfo后,把这个结构赋值给了全局指针,实现了rom数据的完全替换
pDataRomDesc = (struct BurnRomInfo*)malloc((RDI.nDescCount + 1) * sizeof(BurnRomInfo)); //根据romdata的数据进行内存分配并且把分配的内存指针赋值给全局指针,其他程序都是根据这个全局指针来进行后续处理
b.驱动替换
// 1. 获取DrvName对应的驱动索引
RDI.nDriverId = BurnDrvGetIndex(RDI.szDrvName); // "wofj" -> 例如返回索引15
// 2. 验证原版驱动是否存在
if (RDI.nDriverId >= 0) { // 找到了原版wofj驱动
// 3. 将原版驱动的ZIP名称替换为改版ZIP名称
BurnDrvSetZipName(RDI.szZipName, RDI.nDriverId); // 让wofj驱动加载"wofjryu.zip"
}
然后我们看会发生什么?
1.crc验证程序,crc验证程序通过全局指针读的是romdata定义的文件和crc,所以crc必然是通过的,除非你算错了或者写错了romdata中的crc值,还有一种方法,直接把romdata的crc写成00000000,那么crc判定程序会通过
2.文件size判断,也是读取的romdata的值,这里就可以实现扩容,因为size是你写的
3.文件多少的判断,也是读取的romdata的值,可以增加文件并定义,但必须在对应的游戏zip里有这个文件,并且文件大小要和定义里一样,这都是你自己写的,所以可以保持一致。
4.加载过程,会用加载索引15(wofj)的加载代码去加载wofjryu,至于加载后是否能工作,就是zip里图形文件和程序文件是否能配合的问题,程序文件可能是68000(或者其他处理器)代码,其他程序文件可能是z80代码或者其他处理器(用处理器的汇编语言写逻辑,修改逻辑需要很大篇幅针对每个处理器写,这里略过),图形文件内进行一些自定义图形,也可以自定义音频文件,增加或者替换原来的音频文件,程序文件需要反汇编后修改指令,以实现一些自定义逻辑。
这就是romdata的完整工作机制。
这里留下了一些疑问,第四列的2,3,4,6 是什么?type是什么?干什么用的?
2,3,4,6和后面符号是各基板类型的子系统描述,比如程序文件,图形文件,音频文件等,程序会根据不同基板的不同文件类型,调用不同的驱动程序去处理这些文件,形成游戏画面,所以这个必须写对,因为篇幅较大,这里不展开,单独开贴详解各基板的子系统ROMDATA的类型和子系统详解 - 模拟FAQ - 肖琪模拟游戏站 - Powered by Discuz! (xqemu.cn)
|
|