238 lines
6.5 KiB
Python
Executable file
238 lines
6.5 KiB
Python
Executable file
import dblayer
|
|
from dblayer import db, cur
|
|
import subprocess, shlex
|
|
import os
|
|
from zipfile import ZipFile
|
|
from threading import Thread
|
|
from ringbuffer import SplitRingBuffer
|
|
import time
|
|
|
|
class EmulatorHandlerThread(Thread):
|
|
def __init__(self, game, proc):
|
|
Thread.__init__(self)
|
|
self.buffer = SplitRingBuffer(1024*1024, 512*1024)
|
|
self.game = game
|
|
self.proc = proc
|
|
|
|
|
|
def run(self):
|
|
while True:
|
|
data = self.proc.stdout.read(4096)
|
|
self.proc.poll()
|
|
if not data and self.proc.returncode != None:
|
|
break
|
|
self.buffer.write(data)
|
|
time.sleep(0.4)
|
|
|
|
|
|
emulator_table = {}
|
|
gamesystem_table = {}
|
|
playlist_list = []
|
|
playlist_table = {}
|
|
|
|
class GamesystemInfo(object):
|
|
def __init__(self, id, name, fileext):
|
|
self.id = id
|
|
self.name = name
|
|
self.fileexts = fileext.split(';')
|
|
self.emu_id = None
|
|
|
|
class EmulatorInfo(object):
|
|
def __init__(self, id, name, path, options):
|
|
self.id = id
|
|
self.name = name
|
|
self.path = path
|
|
self.options = options
|
|
self.system_id = None
|
|
|
|
def start_game(self, game):
|
|
args = shlex.split(self.options)
|
|
emu = subprocess.Popen([self.path] + args + [game.path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
return emu
|
|
|
|
class GameInfo(object):
|
|
def __init__(self, id, name, path, emu, syst):
|
|
self.id = id
|
|
self.name = name
|
|
self.path = path
|
|
self.emu_id = emu
|
|
self.system_id = syst
|
|
|
|
class AllGames_Playlist(object):
|
|
def __init__(self):
|
|
self.id = None
|
|
self.name = '(All Games)'
|
|
|
|
def lookup(self):
|
|
pass
|
|
|
|
def gamelist(self):
|
|
cur.execute("select game.id, game.name, game.path, game.emu_id, game.gamesystem_id from game order by game.name")
|
|
|
|
gl = []
|
|
|
|
for r in cur:
|
|
i, n, p, e, s = r
|
|
gl.append(GameInfo(i, n, p, e, s))
|
|
|
|
return gl
|
|
|
|
class Playlist(object):
|
|
def __init__(self, id=None, name=None):
|
|
if id == None and name == None:
|
|
raise ValueError, "Either name or id must be specified"
|
|
self.id = id
|
|
self.name = name
|
|
self.gamelist_cache = None
|
|
|
|
def lookup(self):
|
|
if self.name != None:
|
|
cur.execute("select id from playlist where name = ?", [self.name])
|
|
else:
|
|
cur.execute("select name from playlist where id = ?", [self.id])
|
|
|
|
rs = cur.fetchall()
|
|
if len(rs) > 1:
|
|
raise ValueError, "Non unique name for playlist"
|
|
else:
|
|
raise ValueError, "Playlist not found"
|
|
|
|
if self.name == None:
|
|
self.name = rs[0][0]
|
|
else:
|
|
self.id = rs[0][0]
|
|
|
|
def gamelist(self):
|
|
if self.gamelist_cache != None:
|
|
return self.gamelist_cache
|
|
|
|
cur.execute("select game.id, game.name, game.path, game.emu_id, game.gamesystem_id from game, playlist_game where playlist_game.game_id = game.id and playlist_game.playlist_id = ? order by game.name", [self.id])
|
|
|
|
gl = []
|
|
|
|
for r in cur:
|
|
i, n, p, e, s = r
|
|
gl.append(GameInfo(i, n, p, e, s))
|
|
|
|
# enabling this could cause huge memory usage, but make things faster
|
|
#self.gamelist_cache = gl
|
|
return gl
|
|
|
|
def load_playlists():
|
|
global playlist_list
|
|
dblayer.connect()
|
|
cur.execute("select id, name from playlist order by name")
|
|
pl = [AllGames_Playlist()]
|
|
playlist_table[-1] = pl[0]
|
|
for r in cur:
|
|
id, name = r
|
|
p = Playlist(id, name)
|
|
pl.append(p)
|
|
playlist_table[id] = p
|
|
|
|
playlist_list = pl
|
|
|
|
def load_emulators():
|
|
dblayer.connect()
|
|
cur.execute("select id, name, path, options from emulator")
|
|
for r in cur:
|
|
id, name, path, options = r
|
|
ei = EmulatorInfo(id, name, path, options)
|
|
emulator_table[id] = ei
|
|
|
|
cur.execute("select id, name, fileext from gamesystem")
|
|
for r in cur:
|
|
id, name, fileext = r
|
|
gsi = GamesystemInfo(id, name, fileext)
|
|
gamesystem_table[id] = gsi
|
|
|
|
cur.execute("select gamesystem_id, emulator_id from gamesystem_emulator")
|
|
for r in cur:
|
|
gsid, eid = r
|
|
gamesystem_table[gsid].emu_id = eid
|
|
emulator_table[eid].system_id = gsid
|
|
|
|
def find_emu(emu):
|
|
if not os.environ.has_key('PATH'):
|
|
paths = ['.']
|
|
else:
|
|
paths = os.environ['PATH'].split(os.pathsep) + ['.']
|
|
|
|
for path in paths:
|
|
if os.path.exists(os.path.join(path, emu)):
|
|
return os.path.abspath(os.path.join(path, emu))
|
|
return None
|
|
|
|
def initialize_emulator_row(emulator, gamesystem):
|
|
cur.execute("insert into emulator (name, path, options) values (?, ?, ?)", [emulator.name, emulator.path, emulator.options])
|
|
emulator.id = cur.lastrowid
|
|
cur.execute("insert into gamesystem (name, fileext) values (?, ?)", [gamesystem.name, ';'.join(gamesystem.fileexts)])
|
|
gamesystem.id = cur.lastrowid
|
|
cur.execute("insert into gamesystem_emulator (gamesystem_id, emulator_id) values (?, ?)", [gamesystem.id, emulator.id])
|
|
|
|
def initialize_emulator_table():
|
|
emulist = [(('SNES','.SMC'),'snes9x','snes9x-sdl','snes9x-x11','snes9x-gtk','zsnes'),(('NES','.NES'),'fceu'),(('Genesis','.BIN'),'dgen'),(('MAME','**MAME**'),'xmame')]
|
|
for emutype in emulist:
|
|
system, fileext = emutype[0]
|
|
for emu in emutype[1:]:
|
|
emupath = find_emu(emu)
|
|
if emupath:
|
|
initialize_emulator_row(EmulatorInfo(None, emu, emupath, ''), GamesystemInfo(None, system, fileext))
|
|
|
|
def scan_zip(path):
|
|
zf = ZipFile(path, "r")
|
|
zfl = zf.namelist()
|
|
zf.close()
|
|
if len(zfl) > 1:
|
|
return "**MAME**"
|
|
elif zfl:
|
|
return os.path.splitext(zfl[0])[1].upper()
|
|
else:
|
|
return None
|
|
|
|
def autoload_data(path, playlist, system=None, emulator=None):
|
|
fileext_map = {}
|
|
if system == None:
|
|
for gsi in gamesystem_table.values():
|
|
for fe in gsi.fileexts:
|
|
fileext_map[fe.upper()] = gsi
|
|
print fileext_map
|
|
for base, dirs, files in os.walk(path):
|
|
for file in files:
|
|
fn, fe = os.path.splitext(file)
|
|
feu = fe.upper()
|
|
|
|
if feu == '.ZIP':
|
|
feu = scan_zip(os.path.join(path, base, file))
|
|
if not feu:
|
|
continue
|
|
autosystem = None
|
|
if feu in fileext_map:
|
|
autosystem = fileext_map[feu]
|
|
if system != None:
|
|
autosystem = system
|
|
if autosystem != None:
|
|
"We have a valid game, we know what system it's for, and we will use that system's default emulator"
|
|
emuid = None
|
|
if emulator != None:
|
|
emuid = emulator.id
|
|
sysid = None
|
|
if autosystem != None:
|
|
sysid = autosystem.id
|
|
cur.execute("insert into game (name, path, emu_id, gamesystem_id) values (?, ?, ?, ?)", [fn, os.path.join(path, base, file), emuid, sysid])
|
|
gameid = cur.lastrowid
|
|
cur.execute("insert into playlist_game (game_id, playlist_id) values (?, ?)", [gameid, playlist.id])
|
|
|
|
|
|
def get_game(idnum):
|
|
cur.execute("select game.id, game.name, game.path, game.emu_id, game.gamesystem_id from game where game.id = ? order by game.name", [idnum])
|
|
|
|
rs = cur.fetchall()
|
|
if len(rs) > 1:
|
|
raise ValueError, "Non unique id for game, multiple games matched"
|
|
elif len(rs) == 0:
|
|
raise ValueError, "Game not found"
|
|
i, n, p, e, s = rs[0]
|
|
gi = GameInfo(i, n, p, e, s)
|
|
|
|
return gi
|