Python批量处理GoodGen压缩文件

爷青回

回想大四上学期在广告公司实习,这辈子头回挣到笔稍像样的收入,感觉终于能花自己的钱买游戏机了,真是心花怒放:) 当时在世嘉Mega Drive和任天堂Super Famicom之间犹豫了很久,最终因为超任要玩的痛快还要配磁碟机,花费太高,而跑到帝都景山后街一家电游店买了台世嘉MD兼容机。

一眨眼二十多年过去,某天从箱子里翻出这台主机,擦擦灰接上一台14吋小CRT电视,插进游戏卡,居然还能完美运作,感觉真是爷青回了!之后稍稍折腾了一下,把家里一个带脚轮的置物架改成怀旧游戏中心,最上面一层放小电视,下面两层分别是2002年买的Play Staion One和这台世嘉MD,时不时拉出来玩一会儿收藏的老游戏,也是个乐事。最近则加了个新设备:一个乌克兰团队开发出来给MD用的烧录卡——表面上看跟普通世嘉黑卡没什么区别,但顶部有个槽可以插TF卡。只要把你想玩的游戏ROM文件存入TF卡,放回烧录卡再插入世嘉主机,就可以玩几乎所有的MD主机游戏了!

而玩老游戏ROM的玩家一般都知道GoodTools,这是一个整理、压缩、打包、发布各种主机游戏ROM文件的工具软件、数据库和管理规则的组合。其中世嘉MD主机(美版名称为Genesis)游戏ROM的集合叫GoodGen,每个游戏对应一个压缩包文件,内含这个游戏所有版本的ROM文件。使用7z格式进行压缩,非常节省存储空间。玩家在电脑上用模拟器玩的话,软件一般能直接读取压缩包内容,不用解压缩。但对烧录卡用户来说,解压缩步骤不可避免。目前GoodGen最新版本是3.21,包含了1108个游戏,远超维基百科所统计的878部(授权发行)+30部(未授权)+40部(32X扩展)的数量,可说应有尽有。但包爸一看上千个文件,头都大了,更别说要从每个压缩文件中找到合适的ROM版本,手动弄恐怕俩钟头都不够,所以决定写个程序做自动处理。正好最近开始和包子一起学习Python编程语言,可以用这个需求来带动学习,一石二鸟。

功能需求

  1. 自动遍历所有压缩包文件,将ROM文件解压缩到指定目录下;
  2. 对每个压缩包内的所有ROM进行筛选,挑出一个版本解压缩。优先选经GoodTools验证为原版的ROM(文件名中带[!]字符);
  3. 如有多个原版ROM,则根据ROM的发行国家码(可以认为等同于语言版本)做进一步筛选,不同国家码的优先级可随意设置。


得益于GoodTools严格的管理规范,第2和第3条需求的实现可以很简单,只要对压缩包内不同ROM文件名中的特定字符进行比对就行了。于是,包爸在学习后写出了下面的代码。

代码

程序要引入py7zr库处理.7z压缩包文件,没有的话可以用pip3 install py7zr的命令安装。为便于娃理解和学习,尽量加了注释。

import os  
import py7zr

sourcePath="D:\\Your\\Archive\\Files\\Path\\"#压缩包文件所在路径  
targetPath="D:\\Target\\Path\\"#解压缩后rom保存路径  
countryPreference=["(C)", "(CH)", "(HK)", "(A)", "(W)", "(UE)", "(JUE)", "(JU)", "(U)", "(UK)", "(J)", "(E)"]#排在前面的优先

fileCount=0#压缩包文件总数  
extract=0#成功解压缩的rom数  
empty=0#压缩包内为空的文件数  
bad=0#非7z压缩包文件数  
romCount=0#所有rom文件总数  
flag=0

for path, dirList, fileList in os.walk(sourcePath):#遍历压缩包所在文件夹  
    pass

for fileName in fileList:#遍历所有压缩包文件  
    fileCount=fileCount+1
    fullPath=path+fileName
    if py7zr.is_7zfile(fullPath):
        zip=py7zr.SevenZipFile(fullPath, 'r')#打开某个压缩包文件
        roms=zip.getnames()#获取压缩包内各版本rom文件名
        if roms:
            isVerified=[v for v in roms if "[!]" in v]#找出文件名中带[!]的rom
            if isVerified:
                romCount=romCount+len(isVerified)
                for countryCode in countryPreference:#按国家码优先顺序进行循环
                    for romName in isVerified:#在rom文件名列表内循环
                        if romName.lower().find(countryCode.lower())!=-1:
                            extract=extract+1
                            flag=1
                            zip.extract(targetPath, romName)#解压缩ROM文件
                            print("Extracting: "+romName)
                            break#跳出rom文件名列表的循环
                    if flag&eqals;&eqals;1:
                        flag=0
                        break#跳出按国家码循环
        else:
            empty=empty+1
        zip.close()#关闭压缩包文件
    else:
        bad=bad+1

print("Total/Bad/Empty Archive Files: "+str(fileCount)+"/"+str(bad)+"/"+str(empty))  
print("Total/Extracted Roms: "+str(romCount)+"/"+str(extract))

以上代码中第4和第5行的变量分别为压缩包所在路径和解压缩路径,注意路径中的\需加\转义;第6行的变量使用了GoodTools规范中的国家码,这个列表内各元素的排列顺序决定了优先级,可以根据自己的需求进行调整;第25行使用GoodTools规定的[!]字符串来确定该ROM文件是否为经验证的原版ROM;第30行在比较国家码时都转为小写字符,以免因大小写原因漏判。

执行结果


执行效果不错,成功解压缩922个ROM,用时不到三分钟。因为烧录卡每个目录最多只能放512个文件,所以全部解压缩后还要再建2个以上的子目录,将ROM分散放进去,才能被烧录卡正确显示和读取。

白猴君的怀旧游戏中心

最后给大家看看白猴君家里的怀旧游戏中心,用了两台专业CRT监视器做显示器,分别连接SNK的Neo Geo主机和世嘉MD主机。

参考资料

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。