类库大魔王
类库大魔王 懒惰,傲慢,以及无耐心

游戏驱动(译)

  终于把这篇也整了,原文在这里,同样,还是有些没懂起的。

游戏驱动
by Aaron Giles
  可能关于MAME怎样工作的最常见的问题是:我应该怎么编写驱动?为了理解怎么编写驱动,你需要理解驱动是怎样连接到MAME中去的,以及众多的驱动相关的难点是什么。这篇文章向你提供在从最顶层的,驱动需要些什么的概况。以后的文章会讲述这些以外的细节。
  开始,你得先看一下driver.c里的代码。实际上这个模块做的,只是生成了MAME支持的所有驱动的指针的列表。问题是每个驱动,需要选声明一下:
extern struct GameDriver driver_puckman;
extern struct GameDriver driver_puckmana; ...
  然后所有的驱动被声明了,我们要这样声明一个驱动的列表:
const struct GameDriver *drivers[] = {
&driver_puckman, &driver_puckmana, ..., 0 };
  使用C语言来做这事很烦人,因为它需要我们为每个驱动添加2个东西到driver.c,在顶部的一个声明和最底部的列表中的表项。实际上,在用一些好用的预处理魔法去简化这些事情前,我们已经这样做了好多年。如果你是一个C专家,你可能会指出来,也可能不会,没关系。重要的是,要把驱动添加到主列表中,你只要用特定的DRIVER宏这样声明一次就行了:
DRIVER( puckman ) DRIVER( puckmana ) ...
  这样把表项插入到列表中,和其它表项一起,你的驱动就已经可以被MAME通过主驱动列表正式引用了!当然,这也是说你实际上需要一个GameDriver对象,用在MAME其它的地方,用相同的名字,用于成功地连接到应用中。那应该怎样正确地定义一个游戏驱动?
  基本的GameDriver结构非常简单。它提供一个很基础的信息列表,关于这个驱动的,记录了绝大部分的描述性数据(游戏名,制造商,年份等)和指向其它用于描述游戏使用的硬件的结构的一些指针。比起你自己定义和使用GameDriver结构,你应该简单地使用宏来填充所有的细节内容。如果你看一下驱动目录中任何一个文件的底部,你会看到大量的这种宏用于描述驱动:
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
...

  使用宏的主要缺点是,对于每个参数的含义不够直观。甚至很多使用了相同的名字("pacman")。然而,这是你经常会遇到的。表项从左到右的含义是:
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第一个参数(1980)很明显,是MAME指定的这个游戏的年份。一般,我们会试图使用游戏自己显示的版权年份作为这个定义的年份。这可能不够完全精确,但它经常够接近,并且这个定义使我们至少是对游戏了解了。对于不显示版权的游戏,我们就要去收集相关的信息,去猜这个年份。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第二个参数(puckman/puckmana)是驱动自己的名字。这个名字要跟你在driver.c中使用DRIVER()宏时用的名字匹配。这里GAME()宏和driver.c中的DRIVER()宏都会在你指定的这个名字前添加前缀"driver_",是为了给系统中所有的游戏驱动都保持一个统一的命名机制。因此,在上面的例子中,会扩展成driver_puckman和driver_puckmana,这样就同前面讲的命名机制匹配了。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第三个参数(0/puckman)是父驱动的名字。MAME中,父驱动是一个游戏最近版本的驱动。这个参数总是可以为0来指定父驱动。游戏的所有其它版本都可以被认为是“克隆“,并且必须在这个域指定其父驱动的名字。上面这个例子中,puchman是父驱动,puchmana是克隆驱动。作为驱动名字,GAME宏会自动添加”driver_”宏在你指定的名字前面。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第四个参数(pacman)是机器驱动的名字,它描述了街机硬件。机器驱动值得讲一整篇文章,所以这里不会详细讨论。对于多游戏驱动共享同一机器驱动是很常见的,特别是对于在有一组相同硬件上面有很多种游戏的系统。机器驱动很有趣的一面是,不像游戏驱动是用GameDriver结果来表示,机器驱动不用结构而是在运行时通过宏生成的代码来创建。也就是说这第四个参数指向的不是一个结构,而是个“构造“函数。同样,根据维护规则,GAME宏会在名字前面添加”construct_”前缀。上面的例子中,宏会把这个参数扩展为指向函数construct_pacman,这个函数稍后会定义。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第五个参数(还是pacman)是输入端口定义的名字。输入端口会在其它地方描述,简要地说,它们描述了所有游戏中的输入(控制和DIP切换)是如何被映射的。像机器驱动,输入端口在运行时被创建,宏会添加”construct_ipt_”前缀在这个名字前。上面的例子中,宏就生成了指向函数construct_ipt_pacman的指针。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第六个参数(这里是0,但以数字表示其它的驱动)是驱动相关的初始化函数的名字。这个函数会试图做些解密操作,如果需要,可能会动态改变该游戏驱动的CPU的内存映射。这个会有用,是因为大多数游戏的硬件是通过机器驱动的定义来控制的,而这机器驱动会经常在多个游戏驱动中共享。但是实现中,甚至当游戏在相同的硬件上运行,都会有些细小的差别。初始化函数允许在虚拟硬件创建前就使驱动设置生效。宏会在你给的名字前添加”init_”前缀。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第七个参数(这里是ROT90)指定了游戏显示器的方向。所有游戏都实际是设计成TV形式的显示输出的。像Pac Man游戏,却是顺时针方向旋转90°。游戏中的所有图形在内部都经过旋转,所以它看起来旋转后就是右边是上边。类似,有很多游戏是设计成逆时针方向旋转90°。也有一些游戏是设计成从镜面反射的,所以所有的东西都是画在镜面中的镜像。这些转换通过这个方向参数描述。
  方向参数实际由3个bits组成,它们都是独立可控的。第一个bit (ORIENTATION_FLIP_X)指示显示器应该从左到右画。第二个bit (ORIENTATION_FLIP_Y)指示显示器应该从上到下画。第三个bit(ORIENTATION_SWAP_XY)指示显示器应该从左上角到右下角画对角线。如果你觉得这些东西很难懂,你会意识到如果把这些东西结合在一起,假设先是SWAP_XY,然后描述所有可能的旋转和镜像。例子顺时针90°旋转就是SWAP_XY与FLIP_X的结合。
多数时候,这个复杂性已经帮你屏蔽了,你可以使用预定义的宏ROT0, ROT90, ROT180, 和 ROT270来指示无旋转,顺时针90°旋转,180°旋转(翻转X和Y),和逆时针90°旋转。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第八个参数("Namco")是制造商的名字。很不幸,这并不像它看起来那么容易决定。很多时候游戏从一个制造商授权给另一个。例如Pac Man就是授权给Midway在美国发布。这样,制造商信息应该写成"[Namco] (Midway license)"。对于走私货就更麻烦点。一半的情况下,走私贩就只用一个或两个字母来表示名字,并且不是什么值得提及的合法的公司。多数时候,走私货用”bootleg”或”hack”为表示制造商名字。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  第九个参数("PuckMan (Japan set X)")是游戏的全名。这个名字应该代表它显示出来的名字,如果可能,应该接着圆括号里面是版本或区域信息。这里,区域(Japan)信息也被指定了,这表示跟其它众多版本有些细节上的区别。同很多街机游戏一样,实际版本号是从不指定的,所以我们就用自己的版本编号机制,而不管是否真的需要其中的细节区别。
GAME( 1980, puckman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  最后一个参数(GAME_SUPPORTS_SAVE)是用来描述驱动状态的一个或多个标识。如果没有标识,就简单地设为0。如果有多个标识,就用or来连结。当前使用的标识的含义如下:
GAME_NOT_WORKING —游戏没有完全正常工作;这可能是因为有些奇妙的bug使得游戏没有完全被模拟,因而不能正常从头玩到尾;
GAME_UNEMULATED_PROTECTION —游戏中某种形式的反盗版保护使得不能完全正常工作;这个标识几乎总是和GAME_NOT_WORKING一起使用。
GAME_WRONG_COLORS —色彩解码还没有完成;常因为是丢了一般用于色彩映射的色彩PROM。
GAME_IMPERFECT_COLORS —色彩解码已经全部完成了,但在有些情况下色彩显示有些小问题,比如没有阴影或者高亮。
GAME_IMPREFECT_GRAPHICS —图形显示中有些已知的问题;同样,这可能是一些很明显的问题(比如没有显示)或者很细小的问题(比如不正确的优先级或其它问题)
GAME_NO_COCKTAIL —游戏支持鸡尾酒模式,但可能是不知道如何使能这种模式,或者驱动作者太懒了,还不想在视频系统中支持它。
GAME_NO_SOUND —游戏没有声音;常是因为使用了未知的声音硬件。
GAME_IMPERFECT_SOUND —游戏的声音模拟有些不正确;可能是缺少一些声音芯片的模拟,或者少了一些过滤器,或者其它的声音的问题。
GAME_SUPPORTS_SAVE —驱动已经从代码级经过难,并已经通过测试支持保存状态;如果你从命令行使用-autosave选项,带了这个标识的游戏在你退出时就会自动保存,当下次你再运行时,就会自动从保存的状态中恢复,从那儿开始游戏。
NOT_A_DRIVER —这个游戏表项不是一个游戏,BIOS表项就可以像父驱动一样使一组游戏都共享这个核心BIOS。

感觉本文不错,不妨小额鼓励我一下!
支付宝扫一扫

支付宝扫一扫

微信扫一扫

微信扫一扫

如果你看不到评论框,说明Disqus被墙了。