mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 01:53:56 -06:00
feat: index arcade sets by bios parent
This commit is contained in:
1 parent
dc337c893c
commit
249b189ec9
17 files changed
+1045
No files matched your search
@@ -0,0 +1 @@
|
||||
{"bios":{"astrocde":["280zzap","adssm","amazmaze","artiduel","artiduelp1","artiduelp2","artiduelp3","astrobat","ballypin","baseball","biorhy","blckjack","bowling","brckyard","bstdroid","buylove","candyman","checkers","claim","colbook","conan","conanp","cosmicrd","cosmicrd35","cosmicrd36","cosmicrda","cosmicrdb","dealdemo","dogpatch","eightday","fawn","fawna","feelfine","football","galactic","galacticp","golddig","golddiga","golddigb","grandprx","hardday","icbmatk","icbmatkp","letitbe","life","lttrmtch","matk","mazeman","mazemanp","mlangman","mscandy","muncher","musicmak","musicmakp","musicmakp2","nodie","pacmaze","paperbck","pirate","rainbow2","seadevil","seawolf","sheloves","sloshed","sneaky","soccer","soccera","soccera2","soccera3","soccera4","soccerp","solarcnq","solarcnqp","solarcnqp28","solarcnqp39","songs","songsa","songsu","spacefrt","spacefrtp","spaceinv","spaceinva","spaceinvf","spaceinvj","spaceinvp","spaceinvt","spedmath","sslope","starbttl","stweek","tests","treasure","treasurep","vidstory","war","wizard","yesternv","yesterv","ziggyjx","ziggymo"],"atarisy1":["indytemp","indytemp2","indytemp3","indytemp4","indytempc","indytempd","marble","marble2","marble3","marble4","marble5","peterpak","reliefs1","roadblst","roadblst1","roadblst2","roadblst3","roadblstc","roadblstc1","roadblstcg","roadblstg","roadblstg1","roadblstg2","roadblstgu","roadblstj","roadblstp","roadrunn","roadrunn1","roadrunn2"],"bubsys":["bs_gradius","bs_gwarrior","bs_rf2","bs_twinbee"],"cchip":["bonzeadv","bonzeadvo","bonzeadvp","bonzeadvp2","bonzeadvu","jigkmgri","jigkmgria","jumping","jumpinga","jumpingi","megablst","megablstj","megablstu","opwolf","opwolfa","opwolfb","opwolfj","opwolfjsc","opwolfu","rbisland","rbislandb","rbislande","rbislando","rbislands","superman","supermanj","supermanp","supermans","supermanu","volfied","volfiedj","volfiedjo","volfiedo","volfiedu","volfieduo"],"channelf":["alieninv","backgamm","builtin","casinop","clrorgan","democrt1","democrt2","dodgeit","dragrace","drawpkr","dsrtfox","hangman","lightsout","magicnum","maze","mazea","memory","mthquiz1","mthquiz2","muehle","multicrt","multicrto","ordtvlng","pfootbll","pinbacvbyred","pinball","pinballa","ratmal","robotwar","robotwarp","schach","slotmchn","sonrsrch","spacewar","spitfire","spitfirep","tctactoe","vblckjck","werbetxt","whizball","wrdlf"],"coleco":["1942sgm","1on1","2010","2010p","421","advisland","ae","aerial","alcazar","alpharoid","alphazoo","amazing","amzsnake","antarct","antarcta","antarctj","antarctk","antarctp","antiair","apshai","aquatack","aquatacka","arkadion","armagedn","arnodash","artduel","ascend","asteroids","astrododge","astroinv","awass","aztecchal","bankbuild","bankpanic","barbarricade","barnbuster","battlot","bbears","bcquest","bcquest2","bcquest2ca","bcquesta","bcquestca","bdash","bdashp","beachead","beamridr","bejeweled","bigcatch","blackonyx","blockrun","bloktris","bofhoth","bokosukawars","bombking","bomblast","bomblast2","boomingboy","boothill","bootskell","bosconian","bountybob","boxxle","brainstr","breakout","breakout23j","breakout23r","buckrogsgm","bugsbots","bullmighty","bustabobble","bustabobble2","bustinout0","bustinout1","bustinout2","bustinout3","cabbage","cabbagep1","cabbagep2","cabbagepk","cabbshow","cacorm","camknights","camknightsb","campaign","canamini","caosbgns","carfight","castelo","castle","castleex","castlek","castlet","catsos","caverns","cavfight","cavit","ccharlie","ccredux","ccrisis","chackpop","challenger","chesschal","chprowres","chucknor","cicehockey","cix","clock","coco","coldblood","coltennis","comicbakery","comicbakeryo","comspace","cotn","crackpots","cracky","crazychickyjr","crazychickyjr2","cso","csok","csot","cure","cvjoytest","cye","dacman","dacman2","dacmana","dambust","dambustca","dangrtower","datrolls","dbreakout","ddash","ddash2","deathrace","decathln","deepdngadv","deflektor","demoattackna","demonattack","desolator","destruct","destructsce","digdugsgm","dkarcade","dkjrarcade","dkongarcade","dkongjrsgm","dkongsgm","dlairsgm","dncfntsy","donuts","drgnfire","drol","drolk","drolt","drseuss","eaclassics","easterbunny","ed2083","eggerland","elecquart","elevaction","elevactionsgm","enerquiz","evolutio","explodingfoot","eyebrawls","f2ffootball","f2fhockey","f2fsoccer","facemakr","fallguy","fantasy","fathom","ffactory","ffreddy","finaltst","fireman","flapbrd","flipslip","flora","flux","fortune","fracfevr","frantic","frogfeast","frogger2","frontlinsgm","frostbite","fruitpanic","fuzzywomp","gamepack1","gamepack2","gamester81","gamewatch","gblaster","gerbils","ggarden","ghost","ghostbst","ghostbst1","ghostbsts","ghostbstse","ghostrap","gnz","golgo13","golgo13c","golgo13sc","goonies","gooniesa","gooniesb","gooniesc","gooniesoc","gooniesttc","gpvic20","gpworld","gpworlda","gpworldt","guardic","guardica","gulkave","gulkavea","gulkavek","gunfright","guntus","gustbust","hamsterww","hamurabi","hardhatmack","hazzard","hchumy","heist","helifire","heroesar","holeone","hopman","hrally","huojiche","icemuncher","iceLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"airlbios":["alpilot","alpilotj"],"awbios":["anmlbskt","anmlbskta","basschal","basschalo","blokpong","claychal","demofist","dirtypig","dolphin","fotns","ftspeed","ggisuka","ggx15","kofnw","kofnwj","kofxi","kov7sprt","maxspeed","mslug6","ngbc","ngbcj","rangrmsn","rumblef","rumblef2","rumblefp","rumblf2p","salmankt","samsptk","sprtshot","sushibar","vfurlong","waidrive","xtrmhnt2","xtrmhunt"],"f355bios":["f355twin","f355twinp","f355twn2"],"hod2bios":["hotd2","hotd2e","hotd2o","hotd2p"],"naomi":["18wheelr","18wheelro","18wheelrt","18wheels","18wheelu","alienfnt","alienfnta","anpanman2","anpanman2a","asndynmt","asndynmto","ausfache","azumanga","bdrdown","capsnk","capsnka","capsnkb","cfield","chocomk","cleoftp","confmiss","crackndj","crakndj2","crzytaxi","csmash","csmasho","cspike","cvs2","cvs2mf","cvsgd","deathcox","deathcoxj","deathcoxo","derbyo2k","derbyoc","derbyoc2","derbyocw","doa2","doa2a","doa2m","dragntr","dragntr2","dragntr3","dragntra","drbyocwa","drbyocwb","drbyocwc","drbyocwt","dybb99","dybbnao","dygolf","dygolfp","ggx","ggxx","ggxxac","ggxxrl","ggxxrlo","ggxxsla","gram2000","gundmct","gundmgd","gundmxgd","gunsur2","gunsur2j","gwing2","hmgeo","hopper","ikaruga","illvelo","inunoos","jambo","jingystm","karous","keyboard","kick4csh","kurucham","lupinsho","lupinshoo","luptype","mamonoro","manicpnc","marstv","mazan","mazanj","mazanu","mbaa","mbaao","meltyb","meltybld","meltyblo","meltybo","mj1","moeru","mok","monkeyba","mushi2eo","mushi2k3","mushi2k4","mushi2k5","mushi2k61","mushi2k62","mushik2e","mushik2k","mushik4e","mushik4t","mushikc","mushike","mushikeo","mushikep","mushikk","mvsc2","mvsc2u","ninjaslt","ninjaslta","ninjasltj","ninjasltu","ntvmys","oinori","otrigger","pjustic","pjustica","pokasuka","pstone","pstone2","pstone2b","psyvar2","puyoda","puyofev","puyofevj","puyofevp","qmegamis","quizqgd","radirgy","radirgyn","radirgyo","rhytngk","ringout","ringouto","samba","samba2k","sambaa","sambap","senko","senkoo","senkosp","sfz3ugd","sgtetris","shaktam","shaktamb","shaktmsp","shangril","shikgam2","shootopl","shootpl","shootplm","shootplmp","shors2k1","shors2k2","shors2k2l","shors2k2m","shors2k2s","shorse","shorsel","shorsem","shorsep","shorsepb","shorsepl","shorsepm","shorsepr","shorseps","sl2007","slasho","slashout","smarinef","smlg99","spawn","spkrbtl","sprtjam","ss2005","ss2005o","sstrkfgt","sstrkfgta","starseek","suchie3","takoron","tduno","tduno2","tetkiwam","tokyobus","totd","totdo","toukon4","toyfight","trgheart","trghearto","trizeal","undefeat","usagiym","vathlete","virnba","virnbao","virnbap","virnbapa","vonot","vs2_2k","vs2_2ko","vtenis2c","vtennis","vtennis2","vtennisg","wccf116","wccf1dup","wccf212e","wccf234j","wccf310j","wccf322e","wccf331e","wccf331j","wccf341j","wccf400j","wccf420e","wldkicks","wldkicksj","wldkickspj","wldkickspw","wldkicksu","wrungp","wrungpo","wsbbgd","wwfroyal","zerogu2","zombrvn","zombrvne","zombrvno","zunou"],"naomi2":["beachspi","clubk2k3","clubk2kp","clubk2kpa","clubkcyc","clubkcyco","clubkprz","clubkpzb","clubkpzbp","clubkrt","clubkrta","clubkrtc","clubkrto","inidv3ca","inidv3cy","initd","initdexp","initdexpo","initdo","initdv2e","initdv2j","initdv2ja","initdv2jo","initdv3e","initdv3j","initdv3jb","kingrt66","kingrt66p","soulsurf","vf4","vf4b","vf4cart","vf4evo","vf4evoa","vf4evob","vf4evoct","vf4o","vf4tuned","vf4tuneda","vf4tunedd","vstrik3c","vstrik3co","wldrider"],"segasp":["bingogal","bingogals","brickppl","btlracer","dinoki25","dinoki4","dinokich","dinoking","dinokior","galilfac","huhimage","isshoni","kingyo","kingyoch","loveber3","loveber3cn","lovebero","lovebery","magicpop","manpuku","monomedal","ochaken","puyomedal","shateki","tetgiano","tetgiant","unomedal","westdrmg"]},"chd":["azumanga","bdrdown","beachspi","cfield","chocomk","cleoftp","clubkcyc","clubkcyco","confmiss","cvs2","cvs2mf","cvsgd","dinoki25","dinoki4","dinokich","dinokior","dragntr","dragntr2","dragntr3","dragntra","dygolf","ggxx","ggxxac","ggxxrl","ggxxrlo","ggxxsla","gundmgd","gundmxgd","huhimage","ikaruga","inidv3ca","inidv3cy","initd","initdexp","initdexpo","initdo","initdv2e","initdv2j","initdv2ja","initdv2jo","initdv3e","initdv3j","initdv3jb","jingystm","karous","keyboard","kingyoch","kurucham","loveber3","loveber3cn","lupinsho","lupinshoo","luptype","meltyb","meltybld","meltyblo","meltybo","mj1","moeru","mok","monkeyba","monomedal","psyvar2","puyofev","puyofevj","quizqgd","radirgy","radirgyo","senko","senkoo","senkosp","sfz3ugd","shaktam","shaktamb","shaktmsp","shikgam2","slashout","spkrbtl","sprtjam","ss2005","ss2005o","starseek","takoron","tetgiano","tetkiwam","trgheart","trghearto","trizeal","undefeat","usagiym","vathlete","vf4","vf4b","vf4evo","vf4evoa","vf4evob","vf4o","vf4tuned","vf4tuneda","vf4tunedd","vtennis2","vtennisg","wccf116","wccf1dup","wccf212e","wccf234j","wccf310j","wccf322e","wccf331e","wccf331j","wccf341j","wccf400j","wccf420e","wsbbgd"],"core_version":"v2.7-90-ge88b0ce","counts":{"bios_roots_used":7,"bios_sets":12,"chd_games":114,"games":349,"games_witLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"3dobios":["orbatak"],"airlbios":["alpilot"],"aleck64":["11beat","doncdoon","hipai","hipai2","kurufev","mayjin3","mtetrisc","srmvs","srmvsa","starsldr","twrshaft","vivdolls"],"alg3do":["md23do","sht3do"],"alg_bios":["aplatoon","crimep2","crimep2_11","crimepat","crimepat_10","crimepat_12","crimepat_14","fastdraw","fastdraw_130","gallgall","gallgall_21","lastbh","lastbh_006","maddog","maddog2","maddog2_100","maddog2_110","maddog2_202","maddog_202","maddoga","marvice","marvice100hz","spacepir","spacepir_14","tierras100hz","wsjr","wsjr_15","zortonbr","zortonbr_100"],"allied":["circa33","disco79","erosone","foathens","heartspd","hoedown","royclark","starshot","suprpick","takefive","thndbolt"],"ar_bios":["ar_airh","ar_airh2","ar_argh","ar_blast","ar_bowl","ar_dart","ar_dart2","ar_dlta","ar_fast","ar_fasta","ar_ldrb","ar_ldrba","ar_ldrbb","ar_ninj","ar_ninj2","ar_pm","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"aristmk5":["adonis","adonisa","adonisce","adonisu","alchemst","baddog","blackpnt","blackpntu","bootsctn","bootsctnu","bootsctnua","bootsctnub","bootsctnuc","bootsctnud","bparty","bpartya","bpartyb","bumblbug","bumblbugq","bumblbugu","bumblbugua","bumblbugub","buttdeli","canrose","cashcat","cashcata","cashcatnz","cashcham","cashchama","cashchamnz","cashchamu","cashcra5","cashcra5a","chariotc","chariotcpe","chariotcu","checkma5","chickna5","chickna5q","chickna5u","chickna5ua","chickna5v","coralrc2","cuckoo","cuckoou","diamdest","diamdove","dimtouch","dmdfever","dolphntr","dolphntra","dolphntrb","dolphntrce","dolphntrcea","dolphntrceb","dolphntrq","dolphntru","dolphntrua","dreamwv","drgneye","dstbloom","dstblooma","dynajack","dynajacku","eforsta5","eforsta5u","eforsta5ua","eforstce","eforstcea","eldorda5","fastfort","fortellr","fortfvr","gamblra5","geisha","geishanz","genmagi","glizrdce","gnomeatw","goldenra","goldpyr","goldpyru","goldpyrua","honeypot","honktonk","hrtthrob","incasun","incasunnz","incasunsp","incasunu","incasunua","incasunv","indrema5","indrema5q","indrema5v","jkrpkra5","jumpbean","jumpjoey","jumpjoeyu","jungjuic","kgalah","kgalaha","kgalahce","kgbirda5","kingsran","koalamnt","kookabuk","kyhaton","locoloot","locoloota","locolootnz","locolootq","locolootu","lonewolf","luckyclo","magimask","magimaska","magimaskb","magimaskc","magtcha5","magtcha5a","mammothm","marmagic","marmagicu","marmagicua","mgarden","mgardena","minemine","minemineu","minemineua","minemineub","monmouse","monmousea","montree","mountmon","mountmona","mountmonce","mountmonu","mountmonua","multidrw","mystgard","one4all","orchidms","orchidmsa","orchidmsnz","oscara5","oscara5a","pantmag","pantmaga","partygrs","partygrsa","partygrsb","peaflut","peafluta","pengpay","pengpaya","pengpayb","pengpayc","pengpayce","pengpaycea","pengpayd","pengpaydx","pengpayu","pengpayua","pengpayub","pengpuck","penpir","penpir2","penpira","petshop","petshopa","phantpay","phantpyc","przfight","qcash","qnile","qnilea","qnileb","qnilebr","qnilec","qnilece","qnilecea","qnileceb","qniled","qnilefl","qnilemax","qnilenl","qnilenz","qnilepe","qnileq","qnileu","qnileua","qtbird","rainwarr","rainwarrce","reelpwr","reelrock","reelrockq","retrsam","retrsama","retrsamb","rushrst","sbuk2","sbuk2a","sbuk3","sbuk3a","sldeluxe","slvrwolf","snowcat","sumospin","swhr2","swhr2a","swhr2q","swhr2u","swhr2v","thndh","thndha","thor","topbana","toutango","toutangonl","tritreat","trojhors","trpdlght","trpdlghtu","trstrove","unicornd","unicornda","unicorndnz","unicorndu","venicea5","wafrica","wamazon","wamazona","wamazonv","wcougar","wcougaru","wcougarua","wcougarub","wcougaruc","wcougarud","wcoyote","wikwin","wildbill","wizways","wldangel","wldangelu","wnpost","wthing","wthinga","wtigerc","yukongl5"],"aristmk6":["100lions","100lionsa","15lions","50lionrr","50lions","50lionsa","50lionsu","5drag","5draga","5dragce","5dragnz","5dragsp","5koi","5koia","adonisa6","aduskcm","afterhrs","alchemy","amzqueen","amztempl","antcleo","antcleoa","apocof","arabnga6","arabnga6a","arabnga6u","arish","arwincm","asnprinc","atkgld","beavbuk","betzoo","bigben","bigbena","bigbenb","bigbenq","bigheart","bigred","bigred2","bigreda","blkbrum","blkmust","bluetigr","bmbugs","bobdolly","brazil","brazila","bucaneer","bucaneera","bucaneeru","buckstop","buffalo","buffaloa","buffalou","bushrngr","cactusco","callwld","calyking","canaryrc","centrion","cinhell","cmtonig","corrida","cryspri","cryspria","cryspriu","csd","csda","csdce","csdjc","csdq","csdsp","csret","dagenta6","dbldolph","dbldolphq","dbldolphu","deepfrz","deepfrzu","deltabel","deltaqn","di4ever","diamhrts","dimeye","dimeyece","dimeyeu","dinosa6","doltra6","doltra6nz","drgnlord","drgnmgic","drmcatch","drmcatcha","drmred","dstgold","eaglrock","eastland","efce","eggsrccm","enchantr","encspell","eott","estrike","firednc","firednca","flamolce","flamoljc","followst","fortath","fortking","frogwld","gambjack","gbmoney","geishaa6","geishaa6a","geishaa6b","geishajc","gldgong","gldgongq","gldgongu","gldnflc","goape","gogreen"Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"3dobios":["orbatak"],"airlbios":["alpilot"],"aleck64":["11beat","doncdoon","hipai","hipai2","kurufev","mayjin3","mtetrisc","srmvs","srmvsa","starsldr","twrshaft","vivdolls"],"alg3do":["md23do","sht3do"],"alg_bios":["aplatoon","crimep2","crimep2_11","crimepat","crimepat_10","crimepat_12","crimepat_14","fastdraw","fastdraw_130","gallgall","gallgall_21","lastbh","lastbh_006","maddog","maddog2","maddog2_100","maddog2_110","maddog2_202","maddog_202","maddoga","marvice","marvice100hz","spacepir","spacepir_14","tierras100hz","wsjr","wsjr_15","zortonbr","zortonbr_100"],"allied":["circa33","disco79","erosone","foathens","heartspd","hoedown","royclark","starshot","suprpick","takefive","thndbolt"],"ar_bios":["ar_airh","ar_airh2","ar_argh","ar_blast","ar_bowl","ar_dart","ar_dart2","ar_dlta","ar_fast","ar_fasta","ar_ldrb","ar_ldrba","ar_ldrbb","ar_ninj","ar_ninj2","ar_pm","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"aristmk5":["adonis","adonisa","adonisce","adonisu","alchemst","baddog","blackpnt","bootsctn","bootsctnu","bootsctnua","bootsctnub","bparty","bpartya","bumblbug","bumblbugql","bumblbugu","bumblbugua","buttdeli","canrose","cashcat","cashcata","cashcatnz","cashcham","cashchama","cashchamnz","cashchamu","cashcra5","cashcra5a","chariotc","chariotcv","checkma5","chickna5","chickna5ql","chickna5u","chickna5ua","chickna5v","coralrc2","cuckoo","cuckoou","diamdest","diamdove","dimtouch","dmdfever","dolphntr","dolphntra","dolphntrb","dolphntrce","dolphntrcea","dolphntrceb","dolphntrql","dolphntru","dreamwv","drgneye","dstbloom","dstblooma","dynajack","dynajacku","eforsta5","eforsta5ce","eforsta5cea","eforsta5u","eforsta5ua","eldorda5","fastfort","fortellr","fortfvr","gambler","geisha","genmagi","glizrdce","gnomeatw","goldenra","goldpyr","goldpyra","goldpyrb","hnktonku","honeypot","incasun","incasunnz","incasunsp","incasunu","incasunua","incasunv","indrema5","indrema5ql","jumpbean","jumpjoey","jungjuic","kgalah","kgalaha","kgalahce","kgbirda5","koalamnt","kookabuk","kyhatonu","locoloot","locoloota","locolootnz","locolootu","lonewolf","luckyclo","magimask","magimaska","magimaskb","magtcha5","magtcha5a","mammothm","marmagic","marmagicu","marmagicua","mgarden","minemine","minemineu","minemineua","monmouse","monmousea","montree","mountmon","mountmona","mountmonce","mountmonu","mountmonua","multidrw","mystgard","one4all","orchidms","orchidmsa","orchidmsnz","oscara5","oscara5a","pantmag","pantmaga","partygrs","partygrsa","partygrsb","peaflut","peafluta","pengpay","pengpaya","pengpayb","pengpayc","pengpayce","pengpayd","pengpayu","pengpayua","pengpayub","pengpuck","penpir","penpir2","penpira","petshop","petshopa","phantpay","phantpyc","przfight","qcash","qnile","qnilea","qnileb","qnilebr","qnilec","qnilece","qnilecea","qnileceb","qniled","qnilefl","qnilemax","qnilenl","qnilenz","qnileql","qnileu","qnileua","qnilev","qtbird","rainwarr","rainwarrce","reelpwr","reelrock","reelrockql","retrsam","retrsama","retrsamb","rushrst","sbuk2","sbuk2a","sbuk3","sbuk3a","sldeluxe","slvrwolf","snowcat","sumospin","swhr2","swhr2a","swhr2u","swhr2v","thndh","thndha","thor","topbana","toutango","toutangonl","tritreat","trojhors","trpdlght","trpdlghtu","trstrove","unicornd","unicornda","unicorndnz","unicorndu","venicea5","wafricau","wamazon","wamazona","wamazonv","wcougar","wcougaru","wcougarua","wcougarub","wcougaruc","wcoyote","wikwin","wildbill","wizways","wldangel","wnpost","wthing","wthinga","wtiger","yukongl5"],"aristmk6":["100lions","100lionsa","15lions","50lionrr","50lions","50lionsa","50lionsu","5drag","5draga","5dragce","5dragnz","5dragsp","5koi","5koia","adonisa6","aduskcm","afterhrs","alchemy","amzqueen","amztempl","antcleo","antcleoa","apocof","arabnga6","arabnga6a","arabnga6u","arish","arwincm","asnprinc","atkgld","beavbuk","betzoo","bigben","bigbena","bigbenb","bigbenq","bigheart","bigred","bigred2","bigreda","blkbrum","blkmust","bluetigr","bmbugs","bobdolly","brazil","brazila","bucaneer","bucaneera","bucaneeru","buckstop","buffalo","buffaloa","buffalou","bushrngr","cactusco","callwld","calyking","canaryrc","centrion","cinhell","cmtonig","corrida","cryspri","cryspria","cryspriu","csd","csda","csdce","csdjc","csdq","csdsp","csret","dblagent","dbldolph","dbldolphq","deepfrz","deepfrzu","deltabel","deltaqn","di4ever","diamhrts","dimeye","dimeyece","dimeyeu","dinosa6","doltra6","doltra6nz","drgnlord","drgnmgic","drmcatch","drmcatcha","drmred","dstgold","eaglrock","eastland","efce","eggsrccm","enchantr","encspell","eott","estrike","firednc","firednca","flamolce","flamoljc","followst","fortath","fortking","frogwld","gambjack","gbmoney","geishaa6","geishaa6a","geishaa6b","geishajc","gldgong","gldgongq","gldgongu","gldnflc","goape","gogreen","gogreena","goldarch","goldinc","goldincu","goldpya6","goldpym","goldsam","grizzly","gwnn","heartgld","heartglda","heartveg","highnoon","hlntroy","hlntroya","hlntroyb","hlntroyc","hlntroyu","holdrma6","holdrma6a","househrt","househrta","incachf","indgldcm","indprnLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"3dobios":["orbatak"],"airlbios":["alpilot"],"aleck64":["11beat","doncdoon","hipai","hipai2","kurufev","mayjin3","mtetrisc","srmvs","srmvsa","starsldr","twrshaft","vivdolls"],"alg3do":["md23do","sht3do"],"alg_bios":["aplatoon","crimep2","crimep2_11","crimepat","crimepat_10","crimepat_12","crimepat_14","fastdraw","fastdraw_130","gallgall","gallgall_21","lastbh","lastbh_006","maddog","maddog2","maddog2_100","maddog2_110","maddog2_202","maddog_202","maddoga","marvice","marvice100hz","spacepir","spacepir_14","tierras100hz","wsjr","wsjr_15","zortonbr","zortonbr_100"],"allied":["circa33","disco79","erosone","foathens","heartspd","hoedown","royclark","starshot","suprpick","takefive","thndbolt"],"ar_bios":["ar_airh","ar_airh2","ar_argh","ar_blast","ar_bowl","ar_dart","ar_dart2","ar_dlta","ar_fast","ar_fasta","ar_ldrb","ar_ldrba","ar_ldrbb","ar_ninj","ar_ninj2","ar_pm","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"aristmk5":["adonis","adonisa","adonisce","adonisu","alchemst","baddog","blackpnt","blackpntu","bootsctn","bootsctnu","bootsctnua","bootsctnub","bootsctnuc","bootsctnud","bparty","bpartya","bpartyb","bumblbug","bumblbugq","bumblbugu","bumblbugua","bumblbugub","buttdeli","canrose","cashcat","cashcata","cashcatnz","cashcham","cashchama","cashchamnz","cashchamu","cashcra5","cashcra5a","chariotc","chariotcpe","chariotcu","checkma5","chickna5","chickna5q","chickna5u","chickna5ua","chickna5v","coralrc2","cuckoo","cuckoou","diamdest","diamdove","dimtouch","dmdfever","dolphntr","dolphntra","dolphntrb","dolphntrce","dolphntrcea","dolphntrceb","dolphntrq","dolphntru","dolphntrua","dreamwv","drgneye","dstbloom","dstblooma","dynajack","dynajacku","eforsta5","eforsta5u","eforsta5ua","eforstce","eforstcea","eldorda5","fastfort","fortellr","fortfvr","gamblra5","geisha","geishanz","genmagi","glizrdce","gnomeatw","goldenra","goldpyr","goldpyru","goldpyrua","honeypot","honktonk","hrtthrob","incasun","incasunnz","incasunsp","incasunu","incasunua","incasunv","indrema5","indrema5q","indrema5v","jkrpkra5","jumpbean","jumpjoey","jumpjoeyu","jungjuic","kgalah","kgalaha","kgalahce","kgbirda5","kingsran","koalamnt","kookabuk","kyhaton","locoloot","locoloota","locolootnz","locolootq","locolootu","lonewolf","luckyclo","magimask","magimaska","magimaskb","magimaskc","magtcha5","magtcha5a","mammothm","marmagic","marmagicu","marmagicua","mgarden","mgardena","minemine","minemineu","minemineua","minemineub","monmouse","monmousea","montree","mountmon","mountmona","mountmonce","mountmonu","mountmonua","multidrw","mystgard","one4all","orchidms","orchidmsa","orchidmsnz","oscara5","oscara5a","pantmag","pantmaga","partygrs","partygrsa","partygrsb","peaflut","peafluta","pengpay","pengpaya","pengpayb","pengpayc","pengpayce","pengpaycea","pengpayd","pengpaydx","pengpayu","pengpayua","pengpayub","pengpuck","penpir","penpir2","penpira","petshop","petshopa","phantpay","phantpyc","przfight","qcash","qnile","qnilea","qnileb","qnilebr","qnilec","qnilece","qnilecea","qnileceb","qniled","qnilefl","qnilemax","qnilenl","qnilenz","qnilepe","qnileq","qnileu","qnileua","qtbird","rainwarr","rainwarrce","reelpwr","reelrock","reelrockq","retrsam","retrsama","retrsamb","rushrst","sbuk2","sbuk2a","sbuk3","sbuk3a","sldeluxe","slvrwolf","snowcat","sumospin","swhr2","swhr2a","swhr2q","swhr2u","swhr2v","thndh","thndha","thor","topbana","toutango","toutangonl","tritreat","trojhors","trpdlght","trpdlghtu","trstrove","unicornd","unicornda","unicorndnz","unicorndu","venicea5","wafrica","wamazon","wamazona","wamazonv","wcougar","wcougaru","wcougarua","wcougarub","wcougaruc","wcougarud","wcoyote","wikwin","wildbill","wizways","wldangel","wldangelu","wnpost","wthing","wthinga","wtigerc","yukongl5"],"aristmk6":["100lions","100lionsa","15lions","50lionrr","50lions","50lionsa","50lionsu","5drag","5draga","5dragce","5dragnz","5dragsp","5koi","5koia","adonisa6","aduskcm","afterhrs","alchemy","amzqueen","amztempl","antcleo","antcleoa","apocof","arabnga6","arabnga6a","arabnga6u","arish","arwincm","asnprinc","atkgld","beavbuk","betzoo","bigben","bigbena","bigbenb","bigbenq","bigheart","bigred","bigred2","bigreda","blkbrum","blkmust","bluetigr","bmbugs","bobdolly","brazil","brazila","bucaneer","bucaneera","bucaneeru","buckstop","buffalo","buffaloa","buffalou","bushrngr","cactusco","callwld","calyking","canaryrc","centrion","cinhell","cmtonig","corrida","cryspri","cryspria","cryspriu","csd","csda","csdce","csdjc","csdq","csdsp","csret","dagenta6","dbldolph","dbldolphq","dbldolphu","deepfrz","deepfrzu","deltabel","deltaqn","di4ever","diamhrts","dimeye","dimeyece","dimeyeu","dinosa6","doltra6","doltra6nz","drgnlord","drgnmgic","drmcatch","drmcatcha","drmred","dstgold","eaglrock","eastland","efce","eggsrccm","enchantr","encspell","eott","estrike","firednc","firednca","flamolce","flamoljc","followst","fortath","fortking","frogwld","gambjack","gbmoney","geishaa6","geishaa6a","geishaa6b","geishajc","gldgong","gldgongq","gldgongu","gldnflc","goape","gogreen"Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"neogeo":["2020bb","2020bbh","3countb","alpham2","androdun","aodk","aof","aof2","aof3","bakatono","bjourney","blazstar","breakers","breakrev","bstars","bstars2","burningf","burningh","crsword","cyberlip","doubledr","eightman","fatfursp","fatfury1","fatfury2","fatfury3","fbfrenzy","fightfev","flipshot","galaxyfg","goalx3","gowcaizr","gpilots","gururin","irrmaze","janshin","joyjoy","kabukikl","karnovr","kizuna","kof94","kof95","kof96","kof97","kof98","kotm","kotm2","lastblad","lastbld2","lbowling","legendos","lresort","magdrop2","magdrop3","maglord","maglordh","mahretsu","marukodq","miexchng","minasan","mosyougi","mslug","mslug2","mutnat","nam1975","ncombat","ncommand","neobombe","neocup98","neodrift","neomrdo","ninjamas","overtop","panicbom","pbobble","pgoal","popbounc","pspikes2","pulstar","puzbobb","puzzldpr","puzzledp","quizdai2","quizdais","quizkof","ragnagrd","rbff1","rbff2","rbffspec","ridhero","roboarmy","samsho","samsho2","samsho3","samsho4","savagere","sdodgeb","sengokh","sengoku","sengoku2","shocktr2","shocktro","socbrawl","sonicwi2","sonicwi3","spinmast","ssideki","ssideki2","ssideki3","ssideki4","stakwin","stakwin2","strhoop","superspy","tophuntr","tpgolf","trally","turfmast","twinspri","tws96","viewpoin","wakuwak7","wh1","wh2","wh2j","whp","wjammers","zedblade"]},"chd":[],"core_version":"0.37b5","counts":{"bios_roots_used":1,"bios_sets":1,"chd_games":0,"games":2240,"games_with_bios":128,"games_with_devices":0,"missing_parents":0,"sets":2241},"devices":{},"missing_parents":{},"profile":"mame2000","schema":1,"source":{"commit":"181b42e5da0d2f0c7a2e59244f23633ed594de2e","files":{"metadata/MAME 0.37b5 XML.dat":"3ff1773c4982e21d2301b14708e8257bdefec0db"},"repo":"https://github.com/libretro/mame2000-libretro"}}
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"acpsx":["jdredd","jdreddb","nbajamex"],"cpzn1":["glpracr","jgakuen","rvschool","sfex","sfexj","sfexp","sfexpj","starglad","tgmj","ts2","ts2j"],"cpzn2":["kikaioh","sfex2","sfex2p","sg2j","shiryu2","strider2","stridr2a","techromn"],"cvs":["8ball","8ball1","cosmos","darkwar","dazzler","goldbug","heartatk","herbiedk","hero","herodk","herodku","hunchbak","hunchbkd","hunchbks","huncholy","logger","outline","radarzn1","radarznt","radarzon","sbdk","spacefrt","superbik","wallst"],"decocass":["castfant","cbdash","cbnj","cbtime","cburnrb2","cburnrub","cdiscon1","cexplore","cfghtice","cflyball","cgraplop","cgraplp2","chwy","clapapa","clapapa2","clocknch","cluckypo","cmissnx","cnights2","cnightst","cppicf","cppicf2","cprobowl","cprogolf","cprosocc","cptennis","cscrtry","cscrtry2","csuperas","csweetht","cterrani","ctisland","ctislnd2","ctislnd3","ctornado","ctsttape","czeroize"],"konamigx":["daiskiss","dragoonj","fantjour","ggreats2","gokuparo","le2","le2u","opengolf","puzldama","racinfrc","rungun2","rushhero","salmndr2","sexyparo","slamdnk2","soccerss","tbyahhoo","tkmmpzdm","tokkae","vsnetscr","winspike"],"megaplay":["mp_gaxe2","mp_sonic"],"megatech":["mt_aftrb","mt_astro","mt_bbros","mt_beast","mt_eswat","mt_fshrk","mt_gaxe","mt_gaxe2","mt_ggolf","mt_gng","mt_gsocr","mt_kcham","mt_lastb","mt_mwalk","mt_mystd","mt_parlg","mt_revsh","mt_shar2","mt_shnbi","mt_smgp","mt_sonic","mt_stbld","mt_tetri","mt_tfor2","mt_tgolf","mt_tlbba","mt_wcsoc"],"neogeo":["2020bb","2020bbh","3countb","alpham2","androdun","aodk","aof","aof2","aof3","bakatono","bangbead","bjourney","blazstar","breakers","breakrev","bstars","bstars2","burningf","burningh","crsword","ctomaday","cyberlip","doubledr","eightman","fatfursa","fatfursp","fatfury1","fatfury2","fatfury3","fbfrenzy","fightfev","fightfva","flipshot","galaxyfg","ganryu","garou","garouo","garoup","goalx3","gowcaizr","gpilots","gururin","irrmaze","janshin","joyjoy","kabukikl","karnovr","kizuna","kof2000","kof2000n","kof94","kof95","kof95a","kof96","kof96h","kof97","kof97a","kof98","kof98n","kof99","kof99a","kof99e","kof99n","kof99p","kotm","kotm2","kotmh","lastblad","lastbld2","lastblda","lbowling","legendos","lresort","magdrop2","magdrop3","maglord","maglordh","mahretsu","marukodq","miexchng","minasan","mosyougi","mslug","mslug2","mslug3","mslug3n","mslugx","mutnat","nam1975","ncombat","ncommand","neobombe","neocup98","neodrift","neomrdo","ninjamas","nitd","overtop","panicbom","pbobbl2n","pbobblen","pbobblna","pgoal","popbounc","preisle2","pspikes2","pulstar","puzzldpr","puzzledp","quizdai2","quizdais","quizkof","ragnagrd","rbff1","rbff2","rbff2a","rbffspec","ridhero","ridheroh","roboarmy","s1945p","samsho","samsho2","samsho3","samsho4","savagere","sdodgeb","sengokh","sengoku","sengoku2","shocktr2","shocktra","shocktro","socbrawl","sonicwi2","sonicwi3","spinmast","ssideki","ssideki2","ssideki3","ssideki4","stakwin","stakwin2","strhoop","superspy","tophuntr","tpgolf","trally","turfmast","twinspri","tws96","viewpoin","wakuwak7","wh1","wh1h","wh2","wh2j","whp","wjammers","zedblade"],"nss":["nss_actr","nss_adam","nss_aten","nss_con3","nss_fzer","nss_lwep","nss_ncaa","nss_rob3","nss_skin","nss_smw","nss_ssoc","nss_sten"],"pgm":["dragwld2","kov","kov115","kovplus","kovsh","orlegend","orlegndc","orlegnde","photoy2k"],"playch10":["pc_1942","pc_bball","pc_bfght","pc_bstar","pc_cntra","pc_cshwk","pc_cvnia","pc_dbldr","pc_ddrgn","pc_drmro","pc_duckh","pc_ebike","pc_ftqst","pc_gntlt","pc_golf","pc_goons","pc_grdue","pc_grdus","pc_hgaly","pc_kngfu","pc_mario","pc_miket","pc_mman3","pc_moglf","pc_mtoid","pc_ngai2","pc_ngai3","pc_ngaid","pc_pinbt","pc_pwbld","pc_pwrst","pc_radr2","pc_radrc","pc_rcpam","pc_rkats","pc_rnatk","pc_rrngr","pc_rygar","pc_sjetm","pc_smb","pc_smb2","pc_smb3","pc_suprc","pc_tbowl","pc_tenis","pc_tkfld","pc_tmnt","pc_tmnt2","pc_trjan","pc_vball","pc_wcup","pc_wgnmn","pc_ynoid"],"skns":["cyvern","galpani4","galpanis","galpans2","gutsn","jjparad2","jjparads","panicstr","puzloopj","puzloopu","puzzloop","ryouran","sarukani","sengekij","sengekis","senknow","teljan","vblokbrk"],"stvbios":["astrass","bakubaku","batmanfr","colmns97","cotton2","cottonbm","danchih","decathlt","diehard","dnmtdeka","ejihon","elandore","ffreveng","fhboxers","findlove","finlarch","gaxeduel","grdforce","groovef","hanagumi","introdon","kiwames","maruchan","mausuke","myfairld","othellos","pblbeach","prikura","puyosun","rsgun","sandor","sassisu","seabass","shanhigw","shienryu","sleague","sokyugrt","sss","suikoenb","thunt","twcup98","vfkids","vfremix","vmahjong","winterht","znpwfv"],"taitofx1":["beastrzb","ftimpcta","gdarius","gdarius2","mgcldate","mgcldtea","psyforce","raystorm","sfchamp"],"tps":["doapp","glprac2j","glprac2l","glpracr2","tondemo"]},"chd":["a51mxr3k","area51","area51mx","biofreak","blitz","blitz2k","blitz99","bm1stmix","bm2ndmix","bm2ndmxa","bm4thmix","bmcompmx","bmcorerm","bmdct","calspeed","carnevil","cryptklr","hmcompm2","hmcompmx","jdredd","jdreddb","kinst","kinst2","mace","maxf_102","mLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"acpsx":["nbajamex"],"coh1000a":["jdredd","jdreddb"],"coh1000c":["glpracr","glpracrj","sfex","sfexa","sfexj","sfexp","sfexpj","sfexpj1","sfexpu1","sfexu","starglad","stargladj","ts2","ts2j","ts2ja","ts2u","ts2ua"],"coh1000t":["ftimpact","ftimpactj","ftimpactu","ftimpcta","gdarius","gdarius2","gdariusj","gdariusu","mgcldate","mgcldtex","psyforce","psyforcej","psyforcex","raystorm","raystormj","raystormo","raystormu","sfchamp","sfchampj","sfchampo","sfchampu"],"coh1002m":["1on1gov","brvblade","brvbladea","brvbladej","brvbladeu","cbaj","cbajbl","doapp","doappk","flamegun","flamegunj","glpracr2","glpracr2j","glpracr2l","glpracr3","glpracr3j","lpadv","mfjump","shngmtkb","tblkkuzu","tondemo","twcupmil"],"coh3002c":["jgakuen","jgakuen1","kikaioh","plsmaswd","plsmaswda","rvschool","rvschoola","rvschoolu","sfex2","sfex2a","sfex2h","sfex2j","sfex2p","sfex2pa","sfex2ph","sfex2pj","sfex2pu","sfex2u","sfex2u1","shiryu2","stargld2","strider2","strider2a","strider2u","techromn","techromna","techromnu","tgmj"],"cvs":["8ball","8ball1","cosmos","darkwar","dazzler","diggerc","goldbug","heartatk","herbiedk","hero","herodk","herodku","hunchbak","hunchbkd","hunchbks","huncholy","logger","outline","radarzn1","radarznt","radarzon","sbdk","spacefrt","superbik","wallst"],"decocass":["cadanglr","castfant","cbdash","cbnj","cbtime","cburnrb2","cburnrub","cdiscon1","cexplore","cfghtice","cfishing","cflyball","cgraplop","cgraplp2","chwy","clapapa","clapapa2","clocknch","cluckypo","cmanhat","cmissnx","cnights2","cnightst","coozumou","cppicf","cppicf2","cprobowl","cprogolf","cprosocc","cptennis","cscrtry","cscrtry2","csdtenis","cskater","csuperas","csweetht","cterrani","ctisland","ctislnd2","ctislnd3","ctornado","ctsttape","czeroize"],"konamigx":["daiskiss","dragoona","dragoonj","fantjour","ggreats2","gokuparo","le2","le2u","opengolf","puzldama","racinfrc","rungun2","rushhero","salmndr2","sexyparo","sexyparoa","slamdnk2","soccerss","tbyahhoo","tkmmpzdm","tokkae","vsnetscr","winspike"],"megaplay":["mp_addams","mp_bio","mp_col3","mp_gaxe2","mp_gslam","mp_gunhe","mp_mazin","mp_shnb3","mp_soni2","mp_sonic","mp_sor2","mp_twc"],"megatech":["mt_aftrb","mt_astro","mt_bbros","mt_beast","mt_eswat","mt_fshrk","mt_gaxe","mt_gaxe2","mt_ggolf","mt_gng","mt_gsocr","mt_kcham","mt_lastb","mt_mwalk","mt_mystd","mt_parlg","mt_revsh","mt_shar2","mt_shnbi","mt_smgp","mt_sonic","mt_stbld","mt_tetri","mt_tfor2","mt_tgolf","mt_tlbba","mt_wcsoc"],"namcoc76":["danceyes","danceyesj","danceyesu","dunkmnia","dunkmniajc","myangel3","pocketrc","primglex","souledge","souledgea","souledgeja","souledgeua","souledgeuc","starswep","starswepj","tekken","tekken2","tekken2a","tekken2b","tekken2jb","tekken2jc","tekken2ua","tekken2ub","tekken2ud","tekkenac","tekkenb","tekkenjb","xevi3dg","xevi3dga","xevi3dgj"],"neogeo":["2020bb","2020bbh","3countb","alpham2","androdun","aodk","aof","aof2","aof3","b2b","bakatono","bangbead","bjourney","blazstar","breakers","breakrev","bstars","bstars2","burningf","burningh","captainbarrel","crswd2bl","crsword","ct2k3sp","cthd2003","ctomaday","cyberlip","cyborgforce","doubled1","doubled1a","doubled1b","doubled1c","doubled1d","doubled1e","doubledr","eightman","fatfursa","fatfursp","fatfury1","fatfury2","fatfury3","fbfrenzy","fightfev","fightfva","flipshot","galaxyfg","ganryu","garou","garouo","garoup","ghostlop","gladmort","goalx3","gowcaizr","gpilots","gururin","hypernoid","ironclad","irrmaze","janshin","joyjoy","kabukikl","karnovr","kf10thep","kf2k2mp","kf2k2mp2","kf2k2pla","kf2k2pls","kf2k3bl","kf2k3bla","kf2k3pl","kf2k3upl","kf2k5uni","kizuna","knightsch","kof10th","kof2000","kof2000n","kof2001","kof2001h","kof2002","kof2003","kof2003d","kof2k4se","kof94","kof95","kof95a","kof96","kof96h","kof97","kof97a","kof97pls","kof98","kof98k","kof98n","kof99","kof99a","kof99e","kof99n","kof99p","kog","kotm","kotm2","kotmh","lastblad","lastbld2","lastblda","lasthope","lbowling","legendos","lresort","magdrop2","magdrop3","maglord","maglordh","mahretsu","marukodq","matrim","miexchng","minasan","mosyougi","mslug","mslug2","mslug2t","mslug3","mslug3n","mslug4","mslug5","mslugx","mutnat","nam1975","ncombat","ncommand","neobombe","neocup98","neodrift","neomrdo","ninjamas","nitd","overtop","panicbom","pbobbl2n","pbobblen","pbobblna","pgoal","pnyaa","popbounc","preisle2","pspikes2","pulstar","puzzldpr","puzzledp","quizdai2","quizdais","quizkof","ragnagrd","rbff1","rbff2","rbff2a","rbffspec","ridhero","ridheroh","roboarmy","rotd","s1945p","samsh5sp","samsho","samsho2","samsho3","samsho4","samsho5","savagere","sdodgeb","sengokh","sengoku","sengoku2","sengoku3","shocktr2","shocktra","shocktro","socbrawl","sonicwi2","sonicwi3","spinmast","ssideki","ssideki2","ssideki3","ssideki4","stakwin","stakwin2","strhoop","superspy","svc","svcboot","svcplus","svcplusa","svcsplus","teot","tophuntr","tpgolf","trally","turfmast","twinspri","tws96","viewpoin","wakuwak7","wh1","wh1h","wh2","wh2j","whp","wjammers","xeno","zedblade","zintrckb","zupapa"],"nss":["nss_actr","nss_Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"acpsx":["jdredd","jdreddb","nbajamex"],"airlbios":["alpilota","alpiltdx"],"aleck64":["11beat","doncdoon","hipai","kurufev","mayjin3","mtetrisc","srmvs","starsldr","twrshaft","vivdolls"],"alg_bios":["aplatoon","crimep2","crimep211","crimepat","fastdraw","gallgall","lastbh","maddog","maddog2","maddog21","maddog22","maddoga","spacepir","wsjr","wsjr15","zortonbr"],"ar_bios":["ar_airh","ar_airh2","ar_bowl","ar_dart","ar_dart2","ar_fast","ar_ldrb","ar_ldrba","ar_ninj","ar_ninj2","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"atarisy1":["indytemp","indytemp2","indytemp3","indytemp4","indytempc","indytempd","marble","marble2","marble3","marble4","marble5","peterpak","roadblst","roadblst1","roadblst2","roadblst3","roadblstc","roadblstc1","roadblstcg","roadblstg","roadblstg1","roadblstg2","roadrunn","roadrunn1","roadrunn2"],"atluspsx":["hvnsgate"],"atpsx":["primrag2"],"awbios":["demofist","dolphin","fotns","ggisuka","kofnw","kofnwj","kov7sprt","ngbc","rangrmsn","rumblef","salmankt","sprtshot","vfurlong","xtrmhnt2","xtrmhunt"],"bctvidbs":["adders","crmaze","crmaze2","crmaze2a","crmaze2d","crmaze3","crmaze3a","crmaze3d","crmazea","crmazed","eyesdown","eyesdownd","mating","matingd","quidgrid","quidgrid2","quidgrid2d","quidgridd","skiltrek","strikeit","strikeit2","strikeit2d","strikeitd","timemchn","turnover"],"cd32":["cndypuzl","haremchl","lasstixx","lsrquiz","lsrquiz2","mgnumber","mgprem11"],"cdi":["quizard","quizrd22","quizrd32","quizrr41"],"chihiro":["ghostsqu","gundamos","hotd3","mj2","mj3","ollie","outr2","scg06nt","vcop3","wangmid","wangmid2"],"cpzn1":["glpracr","sfex","sfexa","sfexj","sfexp","sfexpj","sfexpu1","sfexu","starglad","stargladj","ts2","ts2j"],"cpzn2":["jgakuen","kikaioh","plsmaswd","plsmaswda","rvschool","rvschoola","sfex2","sfex2a","sfex2h","sfex2j","sfex2p","sfex2pa","sfex2pj","shiryu2","stargld2","strider2","strider2a","techromn","techromnu","tgmj"],"crysbios":["crysking","evosocc","topbladv"],"decocass":["castfant","cbdash","cbnj","cbtime","cburnrub","cburnrub2","cdiscon1","cexplore","cfghtice","cflyball","cgraplop","cgraplop2","chwy","clapapa","clapapa2","clocknch","cluckypo","cmissnx","cnightst","cnightst2","cppicf","cppicf2","cprobowl","cprogolf","cprosocc","cptennis","cscrtry","cscrtry2","csuperas","csweetht","cterrani","ctisland","ctisland2","ctisland3","ctornado","ctsttape","czeroize"],"f355bios":["f355","f355twin","f355twn2"],"galgbios":["galgame2"],"gq863":["bmiidx","bmiidx3","bmiidx4","bmiidx6","bmiidx7","bmiidx8","bmiidxc","bmiidxc2","bmiidxca"],"hikaru":["airtrix","pharrier","podrace"],"hng64":["bbust2","buriki","fatfurwa","roadedge","sams64","sams64_2","xrally"],"hod2bios":["hotd2","hotd2o"],"konamigv":["btchamp","hyperath","kdeadeye","nagano98","pbball96","simpbowl","susume","tokimosh","tokimosp","weddingr"],"konamigx":["daiskiss","dragoona","dragoonj","fantjour","fantjoura","ggreats2","gokuparo","le2","le2j","le2u","opengolf","opengolf2","puzldama","racinfrc","racinfrcu","rungun2","rushhero","salmndr2","salmndr2a","sexyparo","slamdnk2","soccerss","soccerssa","soccerssj","soccerssja","tbyahhoo","tkmmpzdm","tokkae","vsnetscr","vsnetscra","vsnetscreb","vsnetscrj","vsnetscru","winspike","winspikej"],"kviper":["boxingm","code1d","code1db","gticlub2","gticlub2ea","jpark3","mfightc","mfightcc","mocapb","mocapbj","mocapglf","p911","p9112","p911e","p911j","p911kc","p911uc","popn9","ppp2nd","sogeki","sscopex","thrild2","thrild2a","thrild2c","tsurugi","tsurugij","wcombat","wcombatj","wcombatk","xtrial"],"macsbios":["cultname","kisekaeh","kisekaem","yujan","yuka"],"maxaflex":["mf_achas","mf_bdash","mf_brist","mf_flip"],"megaplay":["mp_bio","mp_col3","mp_gaxe2","mp_gslam","mp_mazin","mp_shnb3","mp_soni2","mp_sonic","mp_sor2","mp_twc"],"megatech":["mt_aftrb","mt_arrow","mt_astrm","mt_astro","mt_asyn","mt_bbros","mt_beast","mt_cols","mt_crack","mt_eswat","mt_fshrk","mt_fwrld","mt_gaxe","mt_gaxe2","mt_gfoot","mt_ggolf","mt_gng","mt_gsocr","mt_kcham","mt_lastb","mt_mlh","mt_mwalk","mt_mystd","mt_orun","mt_parlg","mt_revsh","mt_shado","mt_shang","mt_shar2","mt_shnbi","mt_smgp","mt_soni2","mt_sonia","mt_sonic","mt_spman","mt_srage","mt_srbb","mt_stbld","mt_stf","mt_tetri","mt_tfor2","mt_tgolf","mt_tlbba","mt_tout","mt_wcsoc","mt_wwar"],"naomi":["18wheelr","capsnk","capsnka","crackndj","crzytaxi","csmash","csmasho","cspike","deathcox","derbyoc","derbyoc2","derbyocw","doa2","doa2m","dybb99","dybbnao","ggram2","ggx","gram2000","gunsur2","gwing2","hmgeo","jambo","kick4csh","mvsc2","otrigger","pjustic","pstone","pstone2","qmegamis","samba","sgtetris","shangril","shootopl","shootpl","slasho","smarinef","smlg99","spawn","sstrkfgt","suchie3","totd","toyfight","virnba","virnbao","vs2_2k","vtenis2c","vtennis","wwfroyal","zombrvn"],"naomi2":["beachspi","clubkrte","initd","initdexp","initdv2j","initdv3j","vf4","vf4b","vf4c","vf4cart","vf4evo","vf4evoa","vf4evoct","vf4tuned","vf4tuneda","vf4tunedd","vstrik3","vstrik3c","wldrider"],"naomigd":["azumanga","bdrdown","cfield","chocomLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"3dobios":["orbatak"],"acpsx":["jdredd","jdreddb","nbajamex"],"airlbios":["alpiltdx"],"aleck64":["11beat","doncdoon","hipai","kurufev","mayjin3","mtetrisc","srmvs","starsldr","twrshaft","vivdolls"],"alg_bios":["aplatoon","crimep2","crimep2_11","crimepat","crimepat_12","crimepat_14","fastdraw","fastdraw_130","gallgall","gallgall_21","lastbh","lastbh_006","maddog","maddog2","maddog2_100","maddog2_110","maddog2_202","maddog_202","maddoga","spacepir","spacepir_14","wsjr","wsjr_15","zortonbr"],"allied":["circa33","disco79","erosone","foathens","heartspd","hoedown","royclark","starshot","suprpick","takefive","thndbolt"],"ar_bios":["ar_airh","ar_airh2","ar_argh","ar_blast","ar_bowl","ar_dart","ar_dart2","ar_dlta","ar_fast","ar_fasta","ar_ldrb","ar_ldrba","ar_ldrbb","ar_ninj","ar_ninj2","ar_pm","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"aristmk5":["goldprmd","magicmsk"],"aristmk6":["50lions","50lionsa","antcleo","choysun","crystals","indianmm","thaiprin","whalecsh","wheregld","wildways"],"atarisy1":["indytemp","indytemp2","indytemp3","indytemp4","indytempc","indytempd","marble","marble2","marble3","marble4","marble5","peterpak","roadblst","roadblst1","roadblst2","roadblst3","roadblstc","roadblstc1","roadblstcg","roadblstg","roadblstg1","roadblstg2","roadrunn","roadrunn1","roadrunn2"],"atluspsx":["hvnsgate"],"atpsx":["primrag2"],"awbios":["anmlbskt","basschal","claychal","demofist","dirtypig","dolphin","fotns","ftspeed","ggisuka","ggx15","kofnw","kofnwj","kofxi","kov7sprt","maxspeed","mslug6","ngbc","rangrmsn","rumblef","rumblef2","salmankt","samsptk","sprtshot","vfurlong","xtrmhnt2","xtrmhunt"],"cdibios":["quizard","quizrd12","quizrd17","quizrd18","quizrd22","quizrd23","quizrd34","quizrr40","quizrr41","quizrr42"],"chihiro":["ccfboxa","crtaxihr","ghostsqo","ghostsqu","gundamos","hotd3","mj2","mj2c","mj2f","mj3","mj3d","ollie","outr2","outr2st","scg06nt","vcop3","wangmd2b","wangmid","wangmid2"],"cpzn1":["glpracr","glpracrj","sfex","sfexa","sfexj","sfexp","sfexpj","sfexpj1","sfexpu1","sfexu","starglad","stargladj","ts2","ts2a","ts2j"],"cpzn2":["jgakuen","kikaioh","plsmaswd","plsmaswda","rvschool","rvschoola","rvschoolu","sfex2","sfex2a","sfex2h","sfex2j","sfex2p","sfex2pa","sfex2ph","sfex2pj","shiryu2","stargld2","strider2","strider2a","techromn","techromnu","tgmj"],"crysbios":["crysking","evosocc","topbladv"],"cubo":["cndypuzl","haremchl","lasstixx","lsrquiz","lsrquiz2","mgnumber","mgprem11","odeontw2"],"decocass":["cadanglr","castfant","cbdash","cbnj","cbtime","cburnrub","cburnrub2","cdiscon1","cdsteljn","cexplore","cfghtice","cfishing","cflyball","cgraplop","cgraplop2","chamburger","chwy","clapapa","clapapa2","clocknch","cluckypo","cmanhat","cmissnx","cnightst","cnightst2","coozumou","cppicf","cppicf2","cprobowl","cprogolf","cprogolf18","cprogolfj","cpsoccer","cpsoccerj","cptennis","cscrtry","cscrtry2","csdtenis","cskater","csuperas","csweetht","cterrani","ctisland","ctisland2","ctisland3","ctornado","ctsttape","czeroize"],"f355bios":["f355twin","f355twn2"],"f355dlx":["f355"],"galgbios":["galgame2"],"gp_110":["blvelvet","camlight","chucklck","foxylady","real","rio"],"gq863":["bmiidx","bmiidx2","bmiidx3","bmiidx3a","bmiidx4","bmiidx5","bmiidx6","bmiidx6a","bmiidx7","bmiidx8","bmiidxa","bmiidxc","bmiidxc2","bmiidxca","bmiidxs"],"gts1":["astannie","buckrgrs","charlies","cleoptra","closeenc","countdwn","dragon","geniep","hexagone","hulk","jokrpokr","pinpool","roldisco","sinbad","sinbadn","solaride","sys1test","torch","totem"],"gts1s":["astannie","buckrgrs","geniep","hexagone","hulk","roldisco","torch","totem"],"hikaru":["airtrix","braveff","pharrier","podrace","sgnascar"],"hng64":["bbust2","buriki","fatfurwa","roadedge","sams64","sams64_2","xrally"],"hod2bios":["hotd2","hotd2o","hotd2p"],"isgsm":["shinfz","tetrbx"],"iteagle":["carnking","gtfore02","gtfore02o","gtfore04","gtfore05"],"konamigv":["btchamp","hyperath","kdeadeye","lacrazyc","nagano98","naganoj","powyak96","simpbowl","susume","tmosh","tmoshs","tmoshsp","tmoshspa","weddingr"],"konamigx":["crzcross","daiskiss","dragoona","dragoonj","fantjour","fantjoura","ggreats2","gokuparo","le2","le2j","le2u","opengolf","opengolf2","puzldama","racinfrc","racinfrcu","rungun2","rushhero","salmndr2","salmndr2a","sexyparo","sexyparoa","slamdnk2","soccerss","soccerssa","soccerssj","soccerssja","tbyahhoo","tkmmpzdm","tokkae","vsnetscr","vsnetscra","vsnetscreb","vsnetscrj","vsnetscru","winspike","winspikej"],"kviper":["boxingm","code1d","code1db","gticlub2","gticlub2ea","jpark3","mfightc","mfightcc","mocapb","mocapbj","mocapglf","p911","p9112","p911e","p911j","p911kc","p911uc","popn9","ppp2nd","sogeki","sscopex","thrild2","thrild2a","thrild2c","tsurugi","tsurugij","wcombat","wcombatj","wcombatk","xtrial"],"lindbios":["2spicy","abclimax","ghostsev","hotd4","hotdex","hummerxt","initiad4","initiad4c","lbvbiosu","letsgoju","outr2sdx","primevah","psmash3","rambo","segartv","vf5","vtennis3"],"macsbios":["cultname","kisekaeh","kisekaem","yujan","yuLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"bios":{"3dobios":["orbatak"],"acpsx":["jdredd","jdreddb","nbajamex"],"airlbios":["alpiltdx"],"aleck64":["11beat","doncdoon","hipai","kurufev","mayjin3","mtetrisc","srmvs","starsldr","twrshaft","vivdolls"],"alg_bios":["aplatoon","crimep2","crimep2_11","crimepat","crimepat_12","crimepat_14","fastdraw","fastdraw_130","gallgall","gallgall_21","lastbh","lastbh_006","maddog","maddog2","maddog2_100","maddog2_110","maddog2_202","maddog_202","maddoga","spacepir","spacepir_14","wsjr","wsjr_15","zortonbr"],"allied":["circa33","disco79","erosone","foathens","heartspd","hoedown","royclark","starshot","suprpick","takefive","thndbolt"],"ar_bios":["ar_airh","ar_airh2","ar_argh","ar_blast","ar_bowl","ar_dart","ar_dart2","ar_dlta","ar_fast","ar_fasta","ar_ldrb","ar_ldrba","ar_ldrbb","ar_ninj","ar_ninj2","ar_pm","ar_rdwr","ar_sdwr","ar_sdwr2","ar_socc","ar_spot","ar_sprg","ar_xeon"],"aristmk5":["adonise","adonishl","badbog","blackpnt","bootscot","bootsctn","bumblbug","bumbugs","butdeli","cashcham","cashchm","cashcroa","chickena","chkmatar","chkrun","coralr2v","cshcat","cuckoo","cuckooa","dmddove","dolphtre","dolptra","drawpka","dstblom","dyjack","eldora","enchfore","fortela","genmagi","gnomatw","goldphm","goldprmd","goldpyra","goldra","hrttrhb","incanz","incsun","kgalah","koalamnt","kookabk","locoloot","loconz","lonwolf","magicmsk","magicmska","marmagic","mgarden","mgctouc","minemine","monmous","moutmon","mtreenz","mystgrd","orchidms","oscarar","pantmag","partygrs","peaflut","pengpay","pengpays","penpay20","penpaya","petshopa","prtygras","przfigha","qncsh","qonilea","qonileb","qotna","retrsam","retsam20","retsam5","rwarhl","sbucks3","sumospin","supbuk2","swheart2","thgamblr","topbana","tretrva","triptrea","trjhrs","trpdlght","unicornd","wildbill","wldamz1","wldamz3","wldcoug","wldcougr","wldthg"],"aristmk6":["100lions","50lionr","50lions","50lionsa","5draga","5dragons","5koipp","adonis6","adustcm","aftrhrs","alchemya","amzqnar","amzteml","antcleo","apocof","arbngts","arbnigar","arish","arwincm","asnprinc","atkgld","beavbuk","bigbenpj","bigheart","bigredjp","birredar","blkbrum","blkmust","bluetigr","bmbgs","bobdolu1","brazilar","bucanar","buckstop","buffalar","buffaloa","bushrngr","cactusco","callwlar","calyking","canary2","choysun","cinhell","cmtonig","corrida","crspru","cryspri","crystals","csd","csdce","csret","davedrag","dblagus","deepfrz","deltqn","di4ever","dinoaris","dltbel","dmneye2","dmneyeus","doubdolp","drfz6us","drgnlrd","drgnmgic","drmred","eaglrock","eaglst1","eaglst2","eastland","efcashex","eggsrich","encspela","ent6r","eott","firedan","flamolus","flamolusa","flamoly","flamoly2","followst","fortath","foxred","frogwld","gambjack","gbmoney","geishaar","gldgong","gldincas","gldnflc","goapear","gogreen","goldarjp","goldinc","goldpym","goldsam","grizzly","gwnn","heartgar","heartveg","highnoar","hlntry","hlsunmon","holdrma","houhear","incachf","indce","inddrma","indgcm","indianmm","indll","indprncs","indsc","indsrp","jailbrar","jefffox","jewelunc","kingnep","kingnile","kingnilea","koalmnt","kohinoor","lemrol","lgfish","lineking","lordleo","lougcm","lovestrk","luck88","luigball","mchilli","metestrm","mgforst","mgmerm","milkin","mnytree","moonwalt","mrwoo","mskitty","mystarr","mysteycm","mysteyec","mystmerm","nerdwild","opsesam","orbeaut","outbackj","outbackja","owlcap","pandpays","pariligh","parilius","pcbydem","pcfav","pcfava","pcmostw","pcnewz","pcockmag","pelipete","pelpetus","pennyar","penpayus","pharpor","phxfant","piratar","pompeiar","pompus2","prideaf","prophar","pwsp1","qnile","qnilecj","qnilese","qsheeba","raprichs","rdtripus","redbarar","rubmgc","rumbrel","ruprup","sbuk5","scatmg2","sealdeal","shamnmg","sharmnm","shorthng","silgolar","silkrdus","skydncra","smtmar","spgcarn","stardrft","stardrfu","starqst","statsymb","sunmon2","sunqndol","swh2ld","swhr2us","tahitim","thaiprar","thaiprin","thndrhrt","tigresar","tikitalk","tiktoru","timwolfu","topshtar","torgods","torpdice","trkinus","turtrea","venetian","venicus","werewild","wgorilla","whalecsh","wheregld","whlcshus","whlcshusa","whtwtr","wikwin2","wildways","wingolus","witgold","witgoldd","wldafus","wldcatsa","wldgoos","wldpan2","wldpntus","wldstal2","wtepee","wwaratah","wwaysar","wwaysll","zorrojp","zorrous"],"atarisy1":["indytemp","indytemp2","indytemp3","indytemp4","indytempc","indytempd","marble","marble2","marble3","marble4","marble5","peterpak","roadblst","roadblst1","roadblst2","roadblst3","roadblstc","roadblstc1","roadblstcg","roadblstg","roadblstg1","roadblstg2","roadblstgu","roadrunn","roadrunn1","roadrunn2"],"atluspsx":["hvnsgate"],"atpsx":["primrag2"],"awbios":["anmlbskt","basschal","basschalo","claychal","demofist","dirtypig","dolphin","fotns","ftspeed","ggisuka","ggx15","kofnw","kofnwj","kofxi","kov7sprt","maxspeed","mslug6","ngbc","ngbcj","rangrmsn","rumblef","rumblef2","rumblefp","rumblf2p","salmankt","samsptk","sprtshot","sushibar","vfurlong","xtrmhnt2","xtrmhunt"],"cdibios":["quizard","quizard2","quizard2_22","quizard3","quizard3_32","quizLine truncated
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://abdess.github.io/retrobios/api/v1/schemas/arcade-parents.schema.json",
|
||||
"title": "RetroBIOS arcade parent index of one core version",
|
||||
"description": "games/arcade/<profile>.json, written by python -m scripts.scraper.arcade_parent_importer from the DAT the profile pins. A game set needs every BIOS set it is listed under (the romof chain read to its end) and, for the MAME family, every device set with ROMs it is listed under.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "profile", "core_version", "source", "bios", "devices", "chd", "missing_parents", "counts"],
|
||||
"$defs": {
|
||||
"set_name": { "type": "string", "minLength": 1, "pattern": "^[^\\s/\\\\]+$" },
|
||||
"set_list": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/set_name" },
|
||||
"uniqueItems": true
|
||||
},
|
||||
"sets_by_archive": {
|
||||
"type": "object",
|
||||
"propertyNames": { "$ref": "#/$defs/set_name" },
|
||||
"additionalProperties": { "$ref": "#/$defs/set_list", "minItems": 1 }
|
||||
},
|
||||
"sha1": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
||||
"count": { "type": "integer", "minimum": 0 }
|
||||
},
|
||||
"properties": {
|
||||
"schema": { "const": 1 },
|
||||
"profile": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_.-]*$" },
|
||||
"core_version": { "type": ["string", "null"] },
|
||||
"source": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"description": "A MAME -listxml release asset.",
|
||||
"additionalProperties": false,
|
||||
"required": ["release", "url", "sha256"],
|
||||
"properties": {
|
||||
"release": { "type": "string", "minLength": 1 },
|
||||
"url": { "type": "string", "pattern": "^https://" },
|
||||
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Files of a repository at a commit, each with its git blob sha.",
|
||||
"additionalProperties": false,
|
||||
"required": ["repo", "commit", "files"],
|
||||
"properties": {
|
||||
"repo": { "type": "string", "pattern": "^https://" },
|
||||
"commit": { "$ref": "#/$defs/sha1" },
|
||||
"files": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": { "$ref": "#/$defs/sha1" }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"bios": {
|
||||
"$ref": "#/$defs/sets_by_archive",
|
||||
"description": "BIOS set -> the game sets whose romof chain reaches it."
|
||||
},
|
||||
"devices": {
|
||||
"$ref": "#/$defs/sets_by_archive",
|
||||
"description": "Device set with ROMs -> the game sets that instantiate it."
|
||||
},
|
||||
"chd": {
|
||||
"$ref": "#/$defs/set_list",
|
||||
"description": "Game sets that also need a CHD, which is game content and is not installed."
|
||||
},
|
||||
"missing_parents": {
|
||||
"type": "object",
|
||||
"description": "Game set -> the romof target the DAT does not carry.",
|
||||
"propertyNames": { "$ref": "#/$defs/set_name" },
|
||||
"additionalProperties": { "$ref": "#/$defs/set_name" }
|
||||
},
|
||||
"counts": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["sets", "games", "games_with_bios", "bios_sets", "bios_roots_used", "games_with_devices", "chd_games", "missing_parents"],
|
||||
"properties": {
|
||||
"sets": { "$ref": "#/$defs/count" },
|
||||
"games": { "$ref": "#/$defs/count" },
|
||||
"games_with_bios": { "$ref": "#/$defs/count" },
|
||||
"bios_sets": { "$ref": "#/$defs/count" },
|
||||
"bios_roots_used": { "$ref": "#/$defs/count" },
|
||||
"games_with_devices": { "$ref": "#/$defs/count" },
|
||||
"chd_games": { "$ref": "#/$defs/count" },
|
||||
"missing_parents": { "$ref": "#/$defs/count" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,586 @@
|
||||
"""Import, per arcade core version, which BIOS set each game set loads.
|
||||
|
||||
A game set names its parent through ``romof``: a clone reads its parent game,
|
||||
and the chain ends at a BIOS set (``neogeo``, ``naomi``, ``pgm``). The archive
|
||||
the installer has to provide for a game on the card is every BIOS set on that
|
||||
chain, plus, for the MAME family, the device sets with ROMs the machine
|
||||
instantiates (``device_ref``), which a split or merged romset reads from their
|
||||
own archive. The DAT that answers is the one of the core version that runs
|
||||
the game, read at the revision its profile pins:
|
||||
|
||||
python -m scripts.scraper.arcade_parent_importer --all
|
||||
python -m scripts.scraper.arcade_parent_importer --profile mame2003_plus
|
||||
python -m scripts.scraper.arcade_parent_importer --all --offline
|
||||
|
||||
A BIOS set is ``isbios="yes"`` (MAME 0.139 and later, FBNeo) or, in the
|
||||
MAME 0.78 lineage (mame2003, mame2003-plus), which has no such attribute, a
|
||||
set marked ``runnable="no"``. flycast keeps its NAOMI and Atomiswave table in
|
||||
code: ``Games[].bios`` names the BIOS, ``naomi`` when it is null
|
||||
(core/hw/naomi/naomi_cart.cpp:195-197), and ``BIOS[].filename`` the archive
|
||||
when it differs from the name (naomi_cart.cpp:75).
|
||||
|
||||
Output: games/arcade/<profile>.json. CHD games are listed, not resolved: a
|
||||
CHD is game content. A ``romof`` pointing outside the DAT is listed with its
|
||||
target, never guessed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.parse
|
||||
import zipfile
|
||||
from collections.abc import Callable, Iterator
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from ..common import load_emulator_profiles
|
||||
from .romset_dat_importer import (
|
||||
LISTXML_CHUNK,
|
||||
MAME_LISTXML_URL,
|
||||
BlobMismatchError,
|
||||
_api_json,
|
||||
_download,
|
||||
_listxml_opener,
|
||||
_machine_roms,
|
||||
fetch_pack,
|
||||
git_blob_sha,
|
||||
)
|
||||
|
||||
SCHEMA_VERSION = 1
|
||||
DEFAULT_OUTPUT = Path("games") / "arcade"
|
||||
DEFAULT_CACHE = Path(".cache") / "dats"
|
||||
|
||||
# How each profile's DAT is found. "listxml": the -listxml release asset of
|
||||
# the MAME version core_version names. "fbneo": every DAT of libretro/FBNeo's
|
||||
# dats/ at source_commit. "repo": files of the profile's source repository at
|
||||
# source_commit (a ZIP member when the file is an archive). "flycast": the
|
||||
# BIOS and Games tables of naomi_roms.cpp at source_commit.
|
||||
PROFILES: dict[str, dict] = {
|
||||
"mame": {"kind": "listxml", "marker": "isbios"},
|
||||
"mame0258": {"kind": "listxml", "marker": "isbios"},
|
||||
"mame0278": {"kind": "listxml", "marker": "isbios"},
|
||||
"fbneo": {"kind": "fbneo", "marker": "isbios"},
|
||||
"mame2016": {
|
||||
"kind": "repo",
|
||||
"marker": "isbios",
|
||||
"paths": ["metadata/MAME 0.174 Arcade XML DAT.zip", "metadata/MAME 0.174 Home XML DAT.zip"],
|
||||
},
|
||||
"mame2015": {"kind": "repo", "marker": "isbios", "paths": ["metadata/mame2014-xml.zip"]},
|
||||
"mame2010": {"kind": "repo", "marker": "isbios", "paths": ["metadata/mame2010.xml"]},
|
||||
"mame2003_plus": {"kind": "repo", "marker": "runnable", "paths": ["metadata/mame2003-plus.xml"]},
|
||||
"mame2003": {"kind": "repo", "marker": "runnable", "paths": ["metadata/mame2003.xml"]},
|
||||
"mame2000": {"kind": "repo", "marker": "isbios", "paths": ["metadata/MAME 0.37b5 XML.dat"]},
|
||||
"flycast": {"kind": "flycast", "paths": ["core/hw/naomi/naomi_roms.cpp"]},
|
||||
}
|
||||
|
||||
# About 95 % of what the pinned DATs gave on 2026-11-03: a build that
|
||||
# resolves fewer games to a BIOS set has read a DAT of another shape.
|
||||
FLOORS: dict[str, int] = {
|
||||
"mame": 2600,
|
||||
"mame0258": 2490,
|
||||
"mame0278": 2580,
|
||||
"fbneo": 9800,
|
||||
"mame2016": 1840,
|
||||
"mame2015": 1470,
|
||||
"mame2010": 1060,
|
||||
"mame2003_plus": 650,
|
||||
"mame2003": 420,
|
||||
"mame2000": 120,
|
||||
"flycast": 330,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class SetEntry:
|
||||
"""What the index needs from one machine or game element."""
|
||||
|
||||
name: str
|
||||
romof: str | None = None
|
||||
isbios: bool = False
|
||||
isdevice: bool = False
|
||||
runnable: bool = True
|
||||
has_roms: bool = False
|
||||
disks: bool = False
|
||||
devices: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
_ELEMENT = re.compile(rb"<(machine|game)\b([^>]*)>")
|
||||
_ATTR = re.compile(rb'([\w:-]+)="([^"]*)"')
|
||||
_DEVICE_REF = re.compile(rb'<device_ref\b[^>]*\bname="([^"]+)"')
|
||||
_DISK = re.compile(rb"<disk\b([^>]*)>")
|
||||
# Longer than any start tag the DATs write, shorter than any element.
|
||||
_TAG_TAIL = 4096
|
||||
|
||||
|
||||
def _attrs(raw: bytes) -> dict[str, str]:
|
||||
return {k.decode(): v.decode() for k, v in _ATTR.findall(raw)}
|
||||
|
||||
|
||||
def iter_sets(stream: io.BufferedIOBase) -> Iterator[SetEntry]:
|
||||
"""Every machine or game element of a DAT or -listxml, streamed.
|
||||
|
||||
Attributes come in any order (mame2003 writes ``<game runnable="no"
|
||||
name="neogeo">``), and the document is never held whole: the window
|
||||
keeps only the element being closed.
|
||||
"""
|
||||
buffer = b""
|
||||
while True:
|
||||
chunk = stream.read(LISTXML_CHUNK)
|
||||
buffer += chunk
|
||||
position = 0
|
||||
# Where the next read has to resume: the start of an element still
|
||||
# open, else a tail long enough to hold a start tag cut in two.
|
||||
keep_from = max(0, len(buffer) - _TAG_TAIL)
|
||||
while True:
|
||||
match = _ELEMENT.search(buffer, position)
|
||||
if not match:
|
||||
break
|
||||
closing = b"</" + match.group(1) + b">"
|
||||
end = buffer.find(closing, match.end())
|
||||
if end == -1:
|
||||
keep_from = match.start()
|
||||
break
|
||||
attrs = _attrs(match.group(2))
|
||||
body = buffer[match.end() : end]
|
||||
disks = [_attrs(d) for d in _DISK.findall(body)]
|
||||
yield SetEntry(
|
||||
name=attrs.get("name", ""),
|
||||
romof=attrs.get("romof") or None,
|
||||
isbios=attrs.get("isbios") == "yes",
|
||||
isdevice=attrs.get("isdevice") == "yes",
|
||||
runnable=attrs.get("runnable", "yes") != "no",
|
||||
has_roms=bool(_machine_roms(body)),
|
||||
disks=any(d.get("status") != "nodump" for d in disks),
|
||||
devices=[d.decode() for d in _DEVICE_REF.findall(body)],
|
||||
)
|
||||
position = end + len(closing)
|
||||
keep_from = max(position, len(buffer) - _TAG_TAIL)
|
||||
if not chunk:
|
||||
return
|
||||
buffer = buffer[keep_from:]
|
||||
|
||||
|
||||
def is_bios(entry: SetEntry, marker: str) -> bool:
|
||||
if entry.isbios:
|
||||
return True
|
||||
return marker == "runnable" and not entry.runnable and not entry.isdevice
|
||||
|
||||
|
||||
def build_index(sets: dict[str, SetEntry], marker: str) -> dict:
|
||||
"""BIOS sets per game, device sets per game, CHD games, broken chains."""
|
||||
bios_names = {n for n, e in sets.items() if is_bios(e, marker)}
|
||||
device_roms = {n for n, e in sets.items() if e.isdevice and e.has_roms}
|
||||
games = {
|
||||
n: e for n, e in sets.items()
|
||||
if n not in bios_names and not e.isdevice and e.runnable and (e.has_roms or e.disks)
|
||||
}
|
||||
bios: dict[str, set[str]] = {}
|
||||
devices: dict[str, set[str]] = {}
|
||||
missing: dict[str, str] = {}
|
||||
for name, entry in games.items():
|
||||
seen = {name}
|
||||
current = entry.romof
|
||||
while current and current not in seen:
|
||||
seen.add(current)
|
||||
parent = sets.get(current)
|
||||
if parent is None:
|
||||
missing[name] = current
|
||||
break
|
||||
if current in bios_names:
|
||||
bios.setdefault(current, set()).add(name)
|
||||
current = parent.romof
|
||||
for device in set(entry.devices) & device_roms:
|
||||
devices.setdefault(device, set()).add(name)
|
||||
with_bios = {g for members in bios.values() for g in members}
|
||||
return {
|
||||
"bios": {k: sorted(v) for k, v in sorted(bios.items())},
|
||||
"devices": {k: sorted(v) for k, v in sorted(devices.items())},
|
||||
"chd": sorted(n for n, e in games.items() if e.disks),
|
||||
"missing_parents": dict(sorted(missing.items())),
|
||||
"counts": {
|
||||
"sets": len(sets),
|
||||
"games": len(games),
|
||||
"games_with_bios": len(with_bios),
|
||||
"bios_sets": len(bios_names),
|
||||
"bios_roots_used": len(bios),
|
||||
"games_with_devices": len({g for v in devices.values() for g in v}),
|
||||
"chd_games": sum(1 for e in games.values() if e.disks),
|
||||
"missing_parents": len(missing),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# --- C table reader (flycast naomi_roms.cpp) ---------------------------------
|
||||
|
||||
_TOKEN = re.compile(r'"(?:\\.|[^"\\])*"|[{},]|[^\s{},"]+')
|
||||
|
||||
|
||||
def _strip_comments(text: str) -> str:
|
||||
"""C source with comments removed, string literals kept intact."""
|
||||
out: list[str] = []
|
||||
i, n = 0, len(text)
|
||||
while i < n:
|
||||
c = text[i]
|
||||
if c == '"':
|
||||
j = i + 1
|
||||
while j < n and text[j] != '"':
|
||||
j += 2 if text[j] == "\\" else 1
|
||||
out.append(text[i : j + 1])
|
||||
i = j + 1
|
||||
elif text.startswith("//", i):
|
||||
i = text.find("\n", i)
|
||||
i = n if i == -1 else i
|
||||
elif text.startswith("/*", i):
|
||||
i = text.find("*/", i + 2)
|
||||
i = n if i == -1 else i + 2
|
||||
else:
|
||||
out.append(c)
|
||||
i += 1
|
||||
return "".join(out)
|
||||
|
||||
|
||||
def _initializer(text: str, array: str) -> list:
|
||||
"""The brace initializer of ``const <type> <array>[] = {...}`` as nested
|
||||
lists of tokens, each element split on its top-level commas."""
|
||||
match = re.search(rf"\b{re.escape(array)}\s*\[\s*\]\s*=\s*\{{", text)
|
||||
if not match:
|
||||
raise ValueError(f"no initializer for {array}[]")
|
||||
tokens = _TOKEN.findall(text[match.end() - 1 :])
|
||||
stack: list[list] = []
|
||||
item: list = []
|
||||
for token in tokens:
|
||||
if token == "{":
|
||||
stack.append(item)
|
||||
item = []
|
||||
stack.append([])
|
||||
elif token == "}":
|
||||
elements = stack.pop()
|
||||
if item:
|
||||
elements.append(item if len(item) > 1 else item[0])
|
||||
item = stack.pop()
|
||||
item.append(elements)
|
||||
if not stack:
|
||||
return item[0]
|
||||
elif token == ",":
|
||||
elements = stack[-1]
|
||||
if item:
|
||||
elements.append(item if len(item) > 1 else item[0])
|
||||
item = []
|
||||
else:
|
||||
item.append(token)
|
||||
raise ValueError(f"unterminated initializer for {array}[]")
|
||||
|
||||
|
||||
def _string(token: object) -> str | None:
|
||||
if isinstance(token, str) and token.startswith('"'):
|
||||
return token[1:-1]
|
||||
return None
|
||||
|
||||
|
||||
def flycast_index(source: str) -> dict:
|
||||
"""BIOS archive per NAOMI/Atomiswave game from naomi_roms.cpp."""
|
||||
text = _strip_comments(source)
|
||||
archive_of: dict[str, str] = {}
|
||||
for element in _initializer(text, "BIOS"):
|
||||
if not isinstance(element, list) or not element:
|
||||
continue
|
||||
name = _string(element[0])
|
||||
if name is None:
|
||||
continue
|
||||
# BIOS_t: name, blobs[], filename (naomi_roms.h:53-66).
|
||||
filename = _string(element[2]) if len(element) > 2 else None
|
||||
archive_of[name] = filename or name
|
||||
bios: dict[str, set[str]] = {}
|
||||
chd: list[str] = []
|
||||
unknown: dict[str, str] = {}
|
||||
games: set[str] = set()
|
||||
for element in _initializer(text, "Games"):
|
||||
if not isinstance(element, list) or not element:
|
||||
continue
|
||||
name = _string(element[0])
|
||||
if name is None:
|
||||
continue
|
||||
# shors2k2 is declared twice at e88b0ced: a name counts once.
|
||||
games.add(name)
|
||||
# Game: name, parent_name, description, size, key, bios, cart_type,
|
||||
# rotation, blobs[], gdrom_name, ... (naomi_roms.h:71-94).
|
||||
named = _string(element[5]) if len(element) > 5 else None
|
||||
if named is not None and named not in archive_of:
|
||||
unknown[name] = named
|
||||
continue
|
||||
bios.setdefault(archive_of[named or "naomi"], set()).add(name)
|
||||
if len(element) > 9 and _string(element[9]):
|
||||
chd.append(name)
|
||||
return {
|
||||
"bios": {k: sorted(v) for k, v in sorted(bios.items())},
|
||||
"devices": {},
|
||||
"chd": sorted(chd),
|
||||
"missing_parents": dict(sorted(unknown.items())),
|
||||
"counts": {
|
||||
"sets": len(games) + len(archive_of),
|
||||
"games": len(games),
|
||||
"games_with_bios": len({g for v in bios.values() for g in v}),
|
||||
"bios_sets": len(archive_of),
|
||||
"bios_roots_used": len(bios),
|
||||
"games_with_devices": 0,
|
||||
"chd_games": len(chd),
|
||||
"missing_parents": len(unknown),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# --- Fetching ----------------------------------------------------------------
|
||||
|
||||
|
||||
def _repo_slug(url: str) -> str:
|
||||
parts = [p for p in urllib.parse.urlsplit(url).path.split("/") if p]
|
||||
if len(parts) < 2:
|
||||
raise ValueError(f"not a repository URL: {url}")
|
||||
return f"{parts[0]}/{parts[1].removesuffix('.git')}"
|
||||
|
||||
|
||||
def fetch_repo_file(slug: str, commit: str, path: str, cache_dir: Path) -> tuple[Path, str]:
|
||||
"""One file of a GitHub repository at a commit, checked against the blob
|
||||
sha the repository lists for it, and that sha."""
|
||||
folder, _, name = path.rpartition("/")
|
||||
listing = _api_json(
|
||||
f"https://api.github.com/repos/{slug}/contents/{urllib.parse.quote(folder)}?ref={commit}"
|
||||
)
|
||||
item = next(
|
||||
(i for i in listing if isinstance(i, dict) and i.get("name") == name), None
|
||||
) if isinstance(listing, list) else None
|
||||
if item is None:
|
||||
raise FileNotFoundError(f"{slug}@{commit[:8]} has no {path}")
|
||||
destination = cache_dir / "repo" / slug / commit / path
|
||||
if destination.is_file() and git_blob_sha(destination.read_bytes()) != item["sha"]:
|
||||
destination.unlink()
|
||||
url = (
|
||||
f"https://raw.githubusercontent.com/{slug}/{commit}/"
|
||||
f"{urllib.parse.quote(path)}"
|
||||
)
|
||||
_download(url, destination)
|
||||
if git_blob_sha(destination.read_bytes()) != item["sha"]:
|
||||
destination.unlink()
|
||||
raise BlobMismatchError(f"{url} is not blob {item['sha']}")
|
||||
return destination, item["sha"]
|
||||
|
||||
|
||||
def _cached_repo_file(slug: str, commit: str, path: str, cache_dir: Path) -> tuple[Path, str]:
|
||||
"""The cached copy, offline. Its blob sha is recomputed from the bytes."""
|
||||
destination = cache_dir / "repo" / slug / commit / path
|
||||
if not destination.is_file():
|
||||
raise FileNotFoundError(f"{slug}@{commit[:8]}:{path} is not in {cache_dir}")
|
||||
return destination, git_blob_sha(destination.read_bytes())
|
||||
|
||||
|
||||
def _xml_streams(path: Path) -> Iterator[Callable[[], io.BufferedIOBase]]:
|
||||
"""Openers for the XML documents a file holds: itself, or each XML or
|
||||
DAT member of a ZIP."""
|
||||
if path.suffix.lower() != ".zip":
|
||||
yield lambda: path.open("rb")
|
||||
return
|
||||
with zipfile.ZipFile(path) as archive:
|
||||
names = sorted(
|
||||
n for n in archive.namelist() if n.lower().endswith((".xml", ".dat"))
|
||||
)
|
||||
for member in names:
|
||||
def opener(member: str = member) -> io.BufferedIOBase:
|
||||
archive = zipfile.ZipFile(path)
|
||||
stream = archive.open(member)
|
||||
stream.close_archive = archive # keep the archive alive
|
||||
return stream
|
||||
|
||||
yield opener
|
||||
|
||||
|
||||
def read_sets(openers: list[Callable[[], io.BufferedIOBase]]) -> dict[str, SetEntry]:
|
||||
"""Every set of several documents. A set read twice keeps its first
|
||||
reading: FBNeo's Arcade and Neogeo DATs both carry the neogeo BIOS."""
|
||||
sets: dict[str, SetEntry] = {}
|
||||
for opener in openers:
|
||||
with opener() as stream:
|
||||
for entry in iter_sets(stream):
|
||||
if entry.name:
|
||||
sets.setdefault(entry.name, entry)
|
||||
return sets
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
for block in iter(lambda: handle.read(1 << 20), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def import_profile(
|
||||
profile: str, data: dict, cache_dir: Path, offline: bool = False
|
||||
) -> dict:
|
||||
"""The index document for one profile, from the DAT its pin names."""
|
||||
spec = PROFILES[profile]
|
||||
commit = str(data.get("source_commit") or "")
|
||||
source_url = str(data.get("source") or "")
|
||||
kind = spec["kind"]
|
||||
if kind == "listxml":
|
||||
tag = "mame" + str(data["core_version"]).replace(".", "").zfill(4)
|
||||
if offline:
|
||||
pack = cache_dir / f"{tag}lx.zip"
|
||||
if not pack.is_file():
|
||||
raise FileNotFoundError(f"{pack} is not cached")
|
||||
else:
|
||||
pack, _ = fetch_pack("mame", tag, cache_dir)
|
||||
source = {
|
||||
"release": f"mamedev/mame {tag}",
|
||||
"url": MAME_LISTXML_URL.format(tag=tag),
|
||||
"sha256": _sha256(pack),
|
||||
}
|
||||
index = build_index(read_sets([_listxml_opener(pack)]), spec["marker"])
|
||||
elif kind == "fbneo":
|
||||
if offline:
|
||||
folder = cache_dir / "fbneo-dats"
|
||||
blobs = {p.name: git_blob_sha(p.read_bytes()) for p in sorted(folder.glob("*.dat"))}
|
||||
if not blobs:
|
||||
raise FileNotFoundError(f"{folder} is not cached")
|
||||
else:
|
||||
folder, blobs = fetch_pack("fbneo", commit, cache_dir)
|
||||
source = {
|
||||
"repo": source_url,
|
||||
"commit": commit,
|
||||
"files": {f"dats/{name}": sha for name, sha in sorted(blobs.items())},
|
||||
}
|
||||
openers = [
|
||||
(lambda p=folder / name: p.open("rb")) for name in sorted(blobs)
|
||||
]
|
||||
index = build_index(read_sets(openers), spec["marker"])
|
||||
else:
|
||||
slug = _repo_slug(source_url)
|
||||
files: dict[str, str] = {}
|
||||
paths: list[Path] = []
|
||||
for path in spec["paths"]:
|
||||
local, sha = (
|
||||
_cached_repo_file(slug, commit, path, cache_dir)
|
||||
if offline
|
||||
else fetch_repo_file(slug, commit, path, cache_dir)
|
||||
)
|
||||
files[path] = sha
|
||||
paths.append(local)
|
||||
source = {"repo": source_url, "commit": commit, "files": files}
|
||||
if kind == "flycast":
|
||||
index = flycast_index(paths[0].read_text(encoding="utf-8"))
|
||||
else:
|
||||
openers = [opener for p in paths for opener in _xml_streams(p)]
|
||||
index = build_index(read_sets(openers), spec["marker"])
|
||||
return {
|
||||
"schema": SCHEMA_VERSION,
|
||||
"profile": profile,
|
||||
"core_version": data.get("core_version"),
|
||||
"source": source,
|
||||
**index,
|
||||
}
|
||||
|
||||
|
||||
# --- Checks against the collection -------------------------------------------
|
||||
|
||||
|
||||
def declared_archives(profile: dict) -> set[str]:
|
||||
"""Archive names a profile declares, lowercased: file names, aliases and
|
||||
the archive a member lives in."""
|
||||
names: set[str] = set()
|
||||
for entry in profile.get("files") or []:
|
||||
for value in [entry.get("name"), entry.get("archive"), *(entry.get("aliases") or [])]:
|
||||
if isinstance(value, str) and value:
|
||||
names.add(value.rsplit("/", 1)[-1].lower())
|
||||
return names
|
||||
|
||||
|
||||
def collection_names(database: Path) -> set[str]:
|
||||
if not database.is_file():
|
||||
return set()
|
||||
with database.open(encoding="utf-8") as handle:
|
||||
db = json.load(handle)
|
||||
return {n.lower() for n in db.get("indexes", {}).get("by_name", {})}
|
||||
|
||||
|
||||
def bios_roots(doc: dict) -> list[str]:
|
||||
return sorted(doc["bios"])
|
||||
|
||||
|
||||
def dump(doc: dict) -> str:
|
||||
return json.dumps(doc, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + "\n"
|
||||
|
||||
|
||||
def write_if_changed(path: Path, text: str) -> bool:
|
||||
if path.is_file() and path.read_text(encoding="utf-8") == text:
|
||||
return False
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
scratch = path.with_suffix(path.suffix + ".part")
|
||||
scratch.write_text(text, encoding="utf-8")
|
||||
scratch.replace(path)
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False, description=__doc__.splitlines()[0])
|
||||
which = parser.add_mutually_exclusive_group(required=True)
|
||||
which.add_argument("--profile", action="append", choices=sorted(PROFILES))
|
||||
which.add_argument("--all", action="store_true")
|
||||
parser.add_argument("--emulators-dir", default="emulators")
|
||||
parser.add_argument("--database", default="database.json")
|
||||
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT)
|
||||
parser.add_argument("--cache-dir", type=Path, default=DEFAULT_CACHE)
|
||||
parser.add_argument("--offline", action="store_true", help="read the download cache only")
|
||||
parser.add_argument("--dry-run", action="store_true", help="print coverage, write nothing")
|
||||
args = parser.parse_args()
|
||||
|
||||
profiles = load_emulator_profiles(args.emulators_dir, skip_aliases=False)
|
||||
names = collection_names(Path(args.database))
|
||||
failed = False
|
||||
for profile in sorted(PROFILES) if args.all else args.profile:
|
||||
if profile not in profiles:
|
||||
print(f"FAIL {profile}: no emulators/{profile}.yml", file=sys.stderr)
|
||||
failed = True
|
||||
continue
|
||||
try:
|
||||
doc = import_profile(profile, profiles[profile], args.cache_dir, args.offline)
|
||||
except (OSError, ValueError, KeyError) as exc:
|
||||
print(f"FAIL {profile}: {exc}", file=sys.stderr)
|
||||
failed = True
|
||||
continue
|
||||
counts = doc["counts"]
|
||||
roots = bios_roots(doc)
|
||||
declared = declared_archives(profiles[profile])
|
||||
undeclared = [r for r in roots if f"{r}.zip" not in declared]
|
||||
absent = [r for r in roots if names and f"{r}.zip" not in names]
|
||||
print(
|
||||
f"{profile}: {counts['games']} games, {counts['games_with_bios']} with a BIOS set, "
|
||||
f"{counts['bios_roots_used']} BIOS sets used, "
|
||||
f"{counts['games_with_devices']} with device sets, {counts['chd_games']} CHD, "
|
||||
f"{counts['missing_parents']} parents absent from the DAT"
|
||||
)
|
||||
if absent:
|
||||
print(f" BIOS sets absent from the collection: {', '.join(absent)}")
|
||||
if undeclared:
|
||||
print(f" BIOS sets the profile does not declare: {', '.join(undeclared)}")
|
||||
floor = FLOORS.get(profile, 0)
|
||||
if counts["games_with_bios"] < floor:
|
||||
print(
|
||||
f"FAIL {profile}: {counts['games_with_bios']} games with a BIOS set, "
|
||||
f"floor {floor}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
failed = True
|
||||
continue
|
||||
if not args.dry_run:
|
||||
target = args.output_dir / f"{profile}.json"
|
||||
if write_if_changed(target, dump(doc)):
|
||||
print(f" wrote {target}")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE mame [
|
||||
<!ELEMENT mame (machine+)>
|
||||
]>
|
||||
<mame build="0.000 (fixture)">
|
||||
<machine name="neogeo" sourcefile="neogeo/neogeo.cpp" isbios="yes">
|
||||
<description>Neo-Geo</description>
|
||||
<rom name="sp-s2.sp1" size="131072" crc="9036d879" sha1="4f5ed7105b7128794654ce82b51723e16e389543"/>
|
||||
<device_ref name="ym2610"/>
|
||||
</machine>
|
||||
<machine name="naomi" sourcefile="sega/naomi.cpp" isbios="yes">
|
||||
<description>Naomi</description>
|
||||
<rom name="epr-21576h.ic27" size="2097152" crc="d4895685"/>
|
||||
</machine>
|
||||
<machine name="naomigd" sourcefile="sega/naomi.cpp" isbios="yes" romof="naomi">
|
||||
<description>Naomi GD-ROM</description>
|
||||
<rom name="epr-21576h.ic27" merge="epr-21576h.ic27" size="2097152" crc="d4895685"/>
|
||||
</machine>
|
||||
<machine name="mslug" sourcefile="neogeo/neogeo.cpp" romof="neogeo">
|
||||
<description>Metal Slug</description>
|
||||
<rom name="201-p1.p1" size="2097152" crc="08d8daa5"/>
|
||||
<device_ref name="ym2610"/>
|
||||
<device_ref name="m68705p3"/>
|
||||
</machine>
|
||||
<machine name="mslugh" sourcefile="neogeo/neogeo.cpp" cloneof="mslug" romof="mslug">
|
||||
<description>Metal Slug (clone)</description>
|
||||
<rom name="201-p1h.p1" size="2097152" crc="11111111"/>
|
||||
<device_ref name="ym2610"/>
|
||||
</machine>
|
||||
<machine name="ikaruga" sourcefile="sega/naomi.cpp" romof="naomigd">
|
||||
<description>Ikaruga</description>
|
||||
<rom name="317-0323-jpn.pic" size="7665" crc="22222222"/>
|
||||
<disk name="gdl-0010" sha1="0000000000000000000000000000000000000000"/>
|
||||
</machine>
|
||||
<machine name="orphan" sourcefile="misc/x.cpp" romof="gonebios">
|
||||
<description>Orphan</description>
|
||||
<rom name="o.bin" size="16" crc="33333333"/>
|
||||
</machine>
|
||||
<machine name="pacman" sourcefile="namco/pacman.cpp">
|
||||
<description>Pac-Man</description>
|
||||
<rom name="pacman.6e" size="4096" crc="c1e6ab10"/>
|
||||
<device_ref name="ym2610"/>
|
||||
</machine>
|
||||
<machine name="nodumponly" sourcefile="misc/x.cpp">
|
||||
<description>Nothing dumped</description>
|
||||
<rom name="u1" size="16" status="nodump"/>
|
||||
<disk name="d" status="nodump"/>
|
||||
</machine>
|
||||
<machine name="ym2610" sourcefile="sound/ym2610.cpp" isdevice="yes" runnable="no">
|
||||
<description>YM2610</description>
|
||||
</machine>
|
||||
<machine name="m68705p3" sourcefile="cpu/m6805.cpp" isdevice="yes" runnable="no">
|
||||
<description>M68705P3</description>
|
||||
<rom name="m68705p3.bin" size="2048" crc="44444444"/>
|
||||
</machine>
|
||||
</mame>
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Fixture in the shape of flycast core/hw/naomi/naomi_roms.cpp
|
||||
const BIOS_t BIOS[] =
|
||||
{
|
||||
{
|
||||
"naomi",
|
||||
{
|
||||
{ 1, "epr-21576h.ic27", 0x000000, 0x200000, 0xd4895685 },
|
||||
}
|
||||
},
|
||||
{
|
||||
"naomi-jp-d",
|
||||
{
|
||||
{ 0, "epr-21576d.ic27", 0x000000, 0x200000, 0x3b2afa7b },
|
||||
},
|
||||
"naomi"
|
||||
},
|
||||
{
|
||||
"awbios",
|
||||
{
|
||||
/* "commented", { 9, "x" } */
|
||||
{ 1, "bios0.ic23", 0x000000, 0x020000, 0x719b2b0b },
|
||||
}
|
||||
},
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
const Game Games[] =
|
||||
{
|
||||
// "not a game", nullptr,
|
||||
{
|
||||
"gram2000",
|
||||
nullptr,
|
||||
"Giant Gram 2000",
|
||||
0x0b000000,
|
||||
0x7f805c3f,
|
||||
nullptr,
|
||||
M1,
|
||||
ROT0,
|
||||
{
|
||||
{ "epr-23377.ic11", 0x0000000, 0x0400000, 0x4ca3149c },
|
||||
},
|
||||
nullptr,
|
||||
&giant_gram_inputs,
|
||||
},
|
||||
{
|
||||
"kofnw",
|
||||
nullptr,
|
||||
"The King of Fighters Neowave",
|
||||
0x08000000,
|
||||
0,
|
||||
"awbios",
|
||||
AW,
|
||||
ROT0,
|
||||
{
|
||||
{ "ax2201p01.ic12", 0x0000000, 0x0800000, 0xb1fff0c8 },
|
||||
},
|
||||
},
|
||||
{
|
||||
"jpgame",
|
||||
"gram2000",
|
||||
"Japan only, a brace } in a string",
|
||||
0x08000000,
|
||||
0,
|
||||
"naomi-jp-d",
|
||||
M1,
|
||||
ROT0,
|
||||
{
|
||||
{ "x.ic11", 0x0000000, 0x0800000, 0x00000001 },
|
||||
},
|
||||
},
|
||||
{
|
||||
"ikaruga",
|
||||
nullptr,
|
||||
"Ikaruga",
|
||||
0x8000000,
|
||||
0,
|
||||
nullptr,
|
||||
GD,
|
||||
ROT270,
|
||||
{
|
||||
{ "317-0323-jpn.pic", 0, 0x4000 },
|
||||
},
|
||||
"ikaruga",
|
||||
},
|
||||
{
|
||||
"bad",
|
||||
nullptr,
|
||||
"Unknown BIOS name",
|
||||
0x8000000,
|
||||
0,
|
||||
"nosuchbios",
|
||||
M1,
|
||||
ROT0,
|
||||
{
|
||||
{ "b.ic11", 0, 0x4000 },
|
||||
},
|
||||
},
|
||||
{ nullptr }
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE mame [
|
||||
<!ELEMENT mame (game+)>
|
||||
]>
|
||||
<mame build="0.78 (fixture)">
|
||||
<game name="kof98" sourcefile="neogeo.c" romof="neogeo">
|
||||
<description>KOF 98</description>
|
||||
<rom name="242-p1.bin" size="2097152" crc="61ac868a"/>
|
||||
</game>
|
||||
<game runnable="no" name="neogeo">
|
||||
<description>Neo-Geo</description>
|
||||
<rom name="neo-geo.rom" size="131072" crc="9036d879"/>
|
||||
</game>
|
||||
<game runnable="no" name="psarc95x">
|
||||
<description>A root no game names</description>
|
||||
<rom name="x.bin" size="16" crc="55555555"/>
|
||||
</game>
|
||||
<game name="brvblade" sourcefile="zn.c" romof="psarc95">
|
||||
<description>Brave Blade</description>
|
||||
<rom name="b.bin" size="16" crc="66666666"/>
|
||||
</game>
|
||||
</mame>
|
||||
@@ -0,0 +1,179 @@
|
||||
"""Arcade parent index: game set to BIOS sets and device sets, per core."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
TMP_ROOT = ROOT / "tmp" / "tests"
|
||||
TMP_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
FIXTURES = ROOT / "tests" / "fixtures" / "arcade_parents"
|
||||
COMMITTED = ROOT / "games" / "arcade"
|
||||
|
||||
from scripts.scraper import arcade_parent_importer as api # noqa: E402
|
||||
|
||||
try:
|
||||
from jsonschema import Draft202012Validator
|
||||
except ImportError: # validate_schemas.py is the only tool that needs it
|
||||
Draft202012Validator = None
|
||||
|
||||
COMMIT = "a" * 40
|
||||
|
||||
|
||||
def _sets(name: str, chunk: int | None = None) -> dict:
|
||||
data = (FIXTURES / name).read_bytes()
|
||||
saved = api.LISTXML_CHUNK
|
||||
if chunk:
|
||||
api.LISTXML_CHUNK = chunk
|
||||
try:
|
||||
return api.read_sets([lambda: io.BytesIO(data)])
|
||||
finally:
|
||||
api.LISTXML_CHUNK = saved
|
||||
|
||||
|
||||
class ListxmlIndex(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.index = api.build_index(_sets("listxml.xml"), "isbios")
|
||||
|
||||
def test_clone_reaches_bios_through_its_parent(self) -> None:
|
||||
self.assertEqual(self.index["bios"]["neogeo"], ["mslug", "mslugh"])
|
||||
|
||||
def test_every_bios_on_the_chain_is_listed(self) -> None:
|
||||
self.assertIn("ikaruga", self.index["bios"]["naomigd"])
|
||||
self.assertIn("ikaruga", self.index["bios"]["naomi"])
|
||||
|
||||
def test_device_with_roms_only(self) -> None:
|
||||
self.assertEqual(self.index["devices"], {"m68705p3": ["mslug"]})
|
||||
|
||||
def test_chd_and_missing_parent(self) -> None:
|
||||
self.assertEqual(self.index["chd"], ["ikaruga"])
|
||||
self.assertEqual(self.index["missing_parents"], {"orphan": "gonebios"})
|
||||
|
||||
def test_sets_without_a_dump_are_not_games(self) -> None:
|
||||
counts = self.index["counts"]
|
||||
# mslug, mslugh, ikaruga, orphan, pacman
|
||||
self.assertEqual(counts["games"], 5)
|
||||
self.assertEqual(counts["games_with_bios"], 3)
|
||||
self.assertEqual(counts["bios_sets"], 3)
|
||||
|
||||
def test_small_window_reads_the_same(self) -> None:
|
||||
small = api.build_index(_sets("listxml.xml", chunk=64), "isbios")
|
||||
self.assertEqual(api.dump(small), api.dump(self.index))
|
||||
|
||||
|
||||
class RunnableMarker(unittest.TestCase):
|
||||
def test_runnable_no_marks_the_bios(self) -> None:
|
||||
index = api.build_index(_sets("runnable_no.dat"), "runnable")
|
||||
self.assertEqual(index["bios"], {"neogeo": ["kof98"]})
|
||||
self.assertEqual(index["missing_parents"], {"brvblade": "psarc95"})
|
||||
self.assertEqual(index["counts"]["bios_sets"], 2)
|
||||
|
||||
def test_isbios_marker_ignores_runnable(self) -> None:
|
||||
index = api.build_index(_sets("runnable_no.dat"), "isbios")
|
||||
self.assertEqual(index["bios"], {})
|
||||
|
||||
|
||||
class FlycastTable(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.index = api.flycast_index((FIXTURES / "naomi_roms.cpp").read_text())
|
||||
|
||||
def test_null_bios_is_naomi_and_filename_names_the_archive(self) -> None:
|
||||
self.assertEqual(self.index["bios"]["naomi"], ["gram2000", "ikaruga", "jpgame"])
|
||||
self.assertEqual(self.index["bios"]["awbios"], ["kofnw"])
|
||||
self.assertNotIn("naomi-jp-d", self.index["bios"])
|
||||
|
||||
def test_gdrom_game_needs_a_chd(self) -> None:
|
||||
self.assertEqual(self.index["chd"], ["ikaruga"])
|
||||
|
||||
def test_unknown_bios_is_reported(self) -> None:
|
||||
self.assertEqual(self.index["missing_parents"], {"bad": "nosuchbios"})
|
||||
|
||||
|
||||
class OfflineImport(unittest.TestCase):
|
||||
"""import_profile reads the cache only, and two runs write the same bytes."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.temp = tempfile.TemporaryDirectory(dir=TMP_ROOT)
|
||||
self.cache = Path(self.temp.name)
|
||||
dat = self.cache / "repo" / "libretro" / "mame2003-libretro" / COMMIT / "metadata"
|
||||
dat.mkdir(parents=True)
|
||||
(dat / "mame2003.xml").write_bytes((FIXTURES / "runnable_no.dat").read_bytes())
|
||||
with zipfile.ZipFile(self.cache / "mame0000lx.zip", "w") as archive:
|
||||
archive.write(FIXTURES / "listxml.xml", "mame0000.xml")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.temp.cleanup()
|
||||
|
||||
def test_repo_profile_is_deterministic(self) -> None:
|
||||
profile = {
|
||||
"source": "https://github.com/libretro/mame2003-libretro",
|
||||
"source_commit": COMMIT,
|
||||
"core_version": "0.78",
|
||||
}
|
||||
first = api.dump(api.import_profile("mame2003", profile, self.cache, offline=True))
|
||||
second = api.dump(api.import_profile("mame2003", profile, self.cache, offline=True))
|
||||
self.assertEqual(first, second)
|
||||
doc = json.loads(first)
|
||||
self.assertEqual(doc["bios"], {"neogeo": ["kof98"]})
|
||||
self.assertEqual(list(doc["source"]["files"]), ["metadata/mame2003.xml"])
|
||||
|
||||
def test_listxml_release_from_the_cache(self) -> None:
|
||||
doc = api.import_profile("mame", {"core_version": "0.000"}, self.cache, offline=True)
|
||||
self.assertEqual(doc["source"]["release"], "mamedev/mame mame0000")
|
||||
self.assertEqual(doc["devices"], {"m68705p3": ["mslug"]})
|
||||
|
||||
@unittest.skipIf(Draft202012Validator is None, "jsonschema not installed")
|
||||
def test_fixture_output_matches_the_schema(self) -> None:
|
||||
profile = {
|
||||
"source": "https://github.com/libretro/mame2003-libretro",
|
||||
"source_commit": COMMIT,
|
||||
"core_version": "0.78",
|
||||
}
|
||||
doc = api.import_profile("mame2003", profile, self.cache, offline=True)
|
||||
schema = json.loads((ROOT / "schemas" / "arcade-parents.schema.json").read_text())
|
||||
self.assertEqual(list(Draft202012Validator(schema).iter_errors(doc)), [])
|
||||
|
||||
|
||||
class DeclaredArchives(unittest.TestCase):
|
||||
def test_names_aliases_and_archives(self) -> None:
|
||||
profile = {"files": [
|
||||
{"name": "neogeo.zip"},
|
||||
{"name": "sp-s2.sp1", "archive": "neogeo.zip"},
|
||||
{"name": "pgm.zip", "aliases": ["PGMBIOS.zip"]},
|
||||
]}
|
||||
self.assertEqual(api.declared_archives(profile), {"neogeo.zip", "sp-s2.sp1", "pgm.zip", "pgmbios.zip"})
|
||||
|
||||
|
||||
class CommittedIndex(unittest.TestCase):
|
||||
def test_every_profile_has_its_file_above_the_floor(self) -> None:
|
||||
for profile in api.PROFILES:
|
||||
with self.subTest(profile=profile):
|
||||
path = COMMITTED / f"{profile}.json"
|
||||
self.assertTrue(path.is_file(), path)
|
||||
doc = json.loads(path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(doc["profile"], profile)
|
||||
self.assertGreaterEqual(doc["counts"]["games_with_bios"], api.FLOORS[profile])
|
||||
|
||||
def test_committed_files_are_written_canonically(self) -> None:
|
||||
for path in sorted(COMMITTED.glob("*.json")):
|
||||
with self.subTest(path=path.name):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
self.assertEqual(api.dump(json.loads(text)), text)
|
||||
|
||||
@unittest.skipIf(Draft202012Validator is None, "jsonschema not installed")
|
||||
def test_committed_files_match_the_schema(self) -> None:
|
||||
schema = json.loads((ROOT / "schemas" / "arcade-parents.schema.json").read_text())
|
||||
validator = Draft202012Validator(schema)
|
||||
for path in sorted(COMMITTED.glob("*.json")):
|
||||
with self.subTest(path=path.name):
|
||||
errors = list(validator.iter_errors(json.loads(path.read_text(encoding="utf-8"))))
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user