Initial commit
This commit is contained in:
406
mame.py
Normal file
406
mame.py
Normal file
@@ -0,0 +1,406 @@
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
import subprocess
|
||||
import zipfile
|
||||
from glob import glob
|
||||
|
||||
from lxml import etree
|
||||
from titlecase import titlecase
|
||||
|
||||
|
||||
class MameROM(object):
|
||||
''' One Mame ROM '''
|
||||
|
||||
def __init__(self):
|
||||
self.name = ''
|
||||
self.cloneof = None
|
||||
self.description = ''
|
||||
self.year = ''
|
||||
self.manufacturer = ''
|
||||
self.status = ''
|
||||
self.category = ''
|
||||
|
||||
def __str__(self):
|
||||
return self.description
|
||||
|
||||
def __repr__(self):
|
||||
return '%s("%s", "%s", "%s", "%s", "%s", "%s", "%s")' % (
|
||||
self.__class__.__name__, self.name, self.cloneof, self.description,
|
||||
self.year, self.manufacturer, self.status, self.category)
|
||||
|
||||
|
||||
class MameROMs(object):
|
||||
''' A Collection of MAME Roms '''
|
||||
|
||||
def __init__(self, data_dir, hide_mature, cfg):
|
||||
self.all_roms = []
|
||||
self.roms = []
|
||||
self.all_categories = []
|
||||
self.categories = []
|
||||
self.catdict = {}
|
||||
self.len = 0
|
||||
self.data_dir = data_dir
|
||||
self.hide_mature = hide_mature
|
||||
self.cfg = cfg
|
||||
self.emulator_dir = os.path.split(cfg['EXE'])[0]
|
||||
|
||||
self.parse()
|
||||
self.filter()
|
||||
|
||||
self.snapdir = os.path.join(self.emulator_dir, 'snap')
|
||||
if os.path.isfile(os.path.join(self.snapdir, 'snap.zip')):
|
||||
self.snapfile = zipfile.ZipFile(
|
||||
os.path.join(self.snapdir, "snap.zip"))
|
||||
else:
|
||||
self.snapfile = None
|
||||
self.snapdir = os.path.join(self.snapdir, 'snap')
|
||||
|
||||
self.marqueesdir = os.path.join(self.emulator_dir, 'marquees')
|
||||
if os.path.isfile(os.path.join(self.marqueesdir, 'marquees.zip')):
|
||||
self.marqueesfile = zipfile.ZipFile(
|
||||
os.path.join(self.marqueesdir, "marquees.zip"))
|
||||
else:
|
||||
self.marqueesfile = None
|
||||
self.marqueesdir = os.path.join(self.marqueesdir, 'marquees')
|
||||
|
||||
self.titlesdir = os.path.join(self.emulator_dir, 'titles')
|
||||
if os.path.isfile(os.path.join(self.titlesdir, 'titles.zip')):
|
||||
self.titlesfile = zipfile.ZipFile(
|
||||
os.path.join(self.titlesdir, "titles.zip"))
|
||||
else:
|
||||
self.titlesfile = None
|
||||
self.titlesdir = os.path.join(self.titlesdir, 'titles')
|
||||
|
||||
infofn = os.path.join(self.emulator_dir, 'mameinfo.dat')
|
||||
if not os.path.isfile(infofn):
|
||||
self.info = None
|
||||
else:
|
||||
with open(infofn, 'rt', encoding='latin1') as f:
|
||||
self.info = f.read()
|
||||
|
||||
historyfn = os.path.join(self.emulator_dir, 'history.dat')
|
||||
if not os.path.isfile(historyfn):
|
||||
self.history = None
|
||||
else:
|
||||
with open(historyfn, 'rt', encoding='utf-8') as f:
|
||||
self.history = f.read()
|
||||
|
||||
self.artwork_dirs = {}
|
||||
for item in cfg['ArtworkDirs']:
|
||||
directory = os.path.join(self.emulator_dir, item)
|
||||
if os.path.isdir(directory):
|
||||
if os.path.isfile(os.path.join(directory, '%s.zip' % item)):
|
||||
self.artwork_dirs[titlecase(item)] = {
|
||||
"dof": os.path.join(directory, '%s.zip' % item),
|
||||
'type': 'file'
|
||||
}
|
||||
else:
|
||||
self.artwork_dirs[titlecase(item)] = {
|
||||
"dof": directory,
|
||||
'type': 'dir'
|
||||
}
|
||||
|
||||
def __len__(self):
|
||||
return self.len
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.roms[item]
|
||||
|
||||
def filter(self):
|
||||
self.roms = []
|
||||
|
||||
for rom in self.all_roms:
|
||||
if rom.status in self.cfg['StatusFilter']:
|
||||
if self.cfg["Category"] == "All Games" or self.cfg["Category"] == rom.category:
|
||||
if self.cfg["ShowClones"] or rom.cloneof is None:
|
||||
if not self.hide_mature or '* Mature *' not in rom.category:
|
||||
self.roms.append(rom)
|
||||
|
||||
if self.cfg["Sort"] == "Name Asc":
|
||||
self.roms.sort(key=lambda x: x.description.lower(), reverse=False)
|
||||
elif self.cfg["Sort"] == "Name Dec":
|
||||
self.roms.sort(key=lambda x: x.description.lower(), reverse=True)
|
||||
elif self.cfg["Sort"] == "Year Asc":
|
||||
self.roms.sort(key=lambda x: x.year.lower(), reverse=False)
|
||||
elif self.cfg["Sort"] == "Year Dec":
|
||||
self.roms.sort(key=lambda x: x.year.lower(), reverse=True)
|
||||
|
||||
self.len = len(self.roms)
|
||||
|
||||
if self.hide_mature:
|
||||
self.categories = []
|
||||
for category in self.all_categories:
|
||||
if '* Mature *' not in category:
|
||||
self.categories.append(category)
|
||||
else:
|
||||
self.categories = self.all_categories
|
||||
|
||||
def parse(self):
|
||||
''' Parse xml '''
|
||||
|
||||
mame_version = self.get_mame_version()
|
||||
xmlfile = os.path.join(self.emulator_dir, 'mame.xml')
|
||||
datfile = os.path.join(self.data_dir, 'mame.dat')
|
||||
|
||||
if self.cfg['Version'] != mame_version:
|
||||
self.cfg['Version'] = mame_version
|
||||
|
||||
if os.path.isfile(xmlfile):
|
||||
os.unlink(xmlfile)
|
||||
|
||||
if os.path.isfile(datfile):
|
||||
with open(datfile, 'rb') as i:
|
||||
temp_mame_version = pickle.load(i)
|
||||
|
||||
if temp_mame_version == mame_version:
|
||||
self.all_categories = pickle.load(i)
|
||||
self.all_roms = pickle.load(i)
|
||||
return True
|
||||
else:
|
||||
os.unlink(datfile, 'mame.dat')
|
||||
|
||||
tempcat = {}
|
||||
catfile = open(os.path.join(self.emulator_dir, 'catver.ini'))
|
||||
|
||||
found = False
|
||||
for line in catfile:
|
||||
line = line.strip()
|
||||
if line == '[VerAdded]':
|
||||
break
|
||||
if line == '[Category]':
|
||||
found = True
|
||||
if found and line != '' and line[0] != ';' and line[0] != '[':
|
||||
# zwackery=Platform / Run Jump
|
||||
game, category = line.split('=')
|
||||
tempcat[game] = category
|
||||
if category not in self.all_categories:
|
||||
self.all_categories.append(category)
|
||||
self.catdict[category] = 0
|
||||
|
||||
self.all_categories.sort()
|
||||
self.all_categories.insert(0, 'All Games')
|
||||
self.all_categories.append('Unknown')
|
||||
self.catdict['All Games'] = 1
|
||||
self.catdict['Unknown'] = 0
|
||||
|
||||
if not os.path.isfile(xmlfile):
|
||||
try:
|
||||
with open(xmlfile, 'w') as out:
|
||||
retcode = subprocess.call(
|
||||
[self.cfg['EXE'], '-listxml'], stdout=out)
|
||||
if retcode != 0:
|
||||
try:
|
||||
os.unlink(xmlfile)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
except OSError:
|
||||
try:
|
||||
os.unlink(xmlfile)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
# pylint: disable=no-member
|
||||
|
||||
with open(os.path.join(self.data_dir, 'mame_exclude.txt'), 'rt') as f:
|
||||
exclude = f.readlines()
|
||||
exclude = [x.strip() for x in exclude]
|
||||
|
||||
tree = etree.parse(xmlfile)
|
||||
|
||||
rom = None
|
||||
for child in tree.getiterator():
|
||||
if child.tag == 'machine':
|
||||
if rom:
|
||||
if rom.category not in exclude:
|
||||
self.all_roms.append(rom)
|
||||
self.catdict[rom.category] += 1
|
||||
rom = None
|
||||
if 'runnable' in child.attrib and child.attrib['runnable'] == 'yes':
|
||||
rom = MameROM()
|
||||
rom.name = child.attrib['name']
|
||||
else:
|
||||
rom = None
|
||||
if rom and 'cloneof' in child.attrib:
|
||||
rom.cloneof = child.attrib['cloneof']
|
||||
if rom and rom.name in tempcat:
|
||||
rom.category = tempcat[rom.name]
|
||||
elif rom and rom.cloneof in tempcat:
|
||||
rom.category = tempcat[rom.cloneof]
|
||||
elif rom:
|
||||
rom.category = 'Unknown'
|
||||
elif rom and child.tag == 'description':
|
||||
rom.description = child.text
|
||||
elif rom and child.tag == 'year':
|
||||
rom.year = child.text
|
||||
elif rom and child.tag == 'manufacturer':
|
||||
rom.manufacturer = child.text
|
||||
elif rom and child.tag == 'driver':
|
||||
rom.status = child.attrib['status']
|
||||
|
||||
if rom:
|
||||
if rom.category not in exclude:
|
||||
self.all_roms.append(rom)
|
||||
|
||||
for cat in self.catdict:
|
||||
if self.catdict[cat] == 0:
|
||||
del self.all_categories[self.all_categories.index(cat)]
|
||||
|
||||
with open(datfile, 'wb') as output:
|
||||
pickle.dump(mame_version, output)
|
||||
pickle.dump(self.all_categories, output)
|
||||
pickle.dump(self.all_roms, output)
|
||||
|
||||
return True
|
||||
|
||||
def get_mame_version(self):
|
||||
mamerun = subprocess.run(
|
||||
[self.cfg['EXE'], '-?'], stdout=subprocess.PIPE)
|
||||
output = mamerun.stdout.decode('utf-8')
|
||||
output = output[output.find('v'):]
|
||||
output = output[:output.find(' ')]
|
||||
|
||||
return output
|
||||
|
||||
def get_info(self, rom):
|
||||
if not self.info:
|
||||
return
|
||||
|
||||
search = re.search("\\$info=%s" % rom.name, self.info)
|
||||
|
||||
if not search:
|
||||
search = re.search("\\$info=%s" % rom.cloneof, self.info)
|
||||
if not search:
|
||||
return None
|
||||
|
||||
start = search.start()
|
||||
|
||||
info = self.info[start:self.info.find('$end', start)].splitlines()
|
||||
|
||||
for i in range(len(info) - 1, -1, -1):
|
||||
if info[i].startswith('$'):
|
||||
del info[i]
|
||||
|
||||
while info[0].strip() == '':
|
||||
del info[0]
|
||||
|
||||
while info[len(info) - 1].strip() == '':
|
||||
del info[len(info) - 1]
|
||||
|
||||
return '\n'.join(info)
|
||||
|
||||
def get_history(self, rom):
|
||||
if not self.history:
|
||||
return
|
||||
|
||||
search = re.search("\\$info=%s" % rom.name, self.history)
|
||||
|
||||
if not search:
|
||||
search = re.search("\\$info=%s" % rom.cloneof, self.history)
|
||||
if not search:
|
||||
return None
|
||||
|
||||
start = search.start()
|
||||
|
||||
info = self.history[start:self.history.find('$end',
|
||||
start)].splitlines()
|
||||
|
||||
for i in range(len(info) - 1, -1, -1):
|
||||
if info[i].startswith('$'):
|
||||
del info[i]
|
||||
|
||||
while info[0].strip() == '':
|
||||
del info[0]
|
||||
|
||||
while info[len(info) - 1].strip() == '':
|
||||
del info[len(info) - 1]
|
||||
|
||||
return '\n'.join(info)
|
||||
|
||||
def get_snap(self, rom, type_of_image): # pylint : disable=R0912
|
||||
data = None
|
||||
image_file = None
|
||||
image_dir = None
|
||||
|
||||
if type_of_image == 'snap':
|
||||
image_dir = self.snapdir
|
||||
image_file = self.snapfile
|
||||
if type_of_image == 'title':
|
||||
image_dir = self.titlesdir
|
||||
image_file = self.titlesfile
|
||||
if type_of_image == 'marquee':
|
||||
image_dir = self.marqueesdir
|
||||
image_file = self.marqueesfile
|
||||
|
||||
if image_file:
|
||||
try:
|
||||
data = image_file.read(rom.name + ".png")
|
||||
except:
|
||||
try:
|
||||
data = image_file.read(rom.cloneof + '.png')
|
||||
except:
|
||||
data = None
|
||||
elif image_dir:
|
||||
fn = os.path.join(image_dir, rom.name + '.png')
|
||||
if os.path.isfile(fn):
|
||||
with open(fn, 'rb') as f:
|
||||
data = f.read()
|
||||
elif rom.cloneof:
|
||||
fn = os.path.join(image_dir, rom.cloneof + '.png')
|
||||
if os.path.isfile(fn):
|
||||
with open(fn, 'rb') as f:
|
||||
data = f.read()
|
||||
else:
|
||||
data = None
|
||||
else:
|
||||
data = None
|
||||
else:
|
||||
data = None
|
||||
|
||||
return data
|
||||
|
||||
def get_artwork(self, rom):
|
||||
result = []
|
||||
|
||||
for item in self.artwork_dirs:
|
||||
title = item
|
||||
dof = self.artwork_dirs[title]['dof']
|
||||
|
||||
if self.artwork_dirs[title]['type'] == 'dir':
|
||||
fn = None
|
||||
if glob('%s/**/%s.*' % (dof, rom.name), recursive=True) != []:
|
||||
fn = glob(
|
||||
'%s/**/%s.*' % (dof, rom.name), recursive=True)[0]
|
||||
elif glob(
|
||||
'%s/**/%s.*' %
|
||||
(dof, rom.cloneof), recursive=True) != []:
|
||||
fn = glob(
|
||||
'%s/**/%s.*' % (dof, rom.cloneof), recursive=True)[0]
|
||||
if fn:
|
||||
data = None
|
||||
if os.path.splitext(fn)[1].lower() in [
|
||||
'.png', '.jpg', '.jpeg'
|
||||
]:
|
||||
with open(fn, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
if data:
|
||||
result.append([title, data])
|
||||
else:
|
||||
data = None
|
||||
try:
|
||||
with zipfile.ZipFile(dof) as f:
|
||||
data = f.read('%s.png' % rom.name)
|
||||
except:
|
||||
try:
|
||||
with zipfile.ZipFile(dof) as f:
|
||||
data = f.read('%s.png' % rom.cloneof)
|
||||
except:
|
||||
data = None
|
||||
|
||||
if data:
|
||||
result.append([title, data])
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user