Hi all.
Here is a first attempt for a modified game system :
- SNES roms are now parsed and the title/Country/Developper is displayed
- Everything is defined in the DIR_GAMES list
- Allows additionnal emulators by simply adding lines (example given with
the megadrive)
- Modified files :
- src/directory.py (for managing the add_args of the DIR_GAMES
structure)
- src/plugins/mediamenu.py (for parsing correctly the new DIR_GAMES)
- src/games/snesitem.py, mameitem.py, __init__.py
- New files :
- src/games/genericitem.py for managing 'GENERIC' types in DIR_GAMES
Here is the modified GAMES section of the freevo_config.py :
# ======================================================================
# Freevo games settings:
# ======================================================================
#
# MAME is an emulator for old arcade video games. It supports almost
# 2000 different games! The actual emulator is not included in Freevo,
# you'll need to download and install it separately. The main MAME
# website is at http://www.mame.net, but the version that is used here
# is at http://x.mame.net since the regular MAME is for Windows.
#
# SNES stands for Super Nintendo Entertainment System. Freevo relies
# on other programs that are not included in Freevo to play these games.
#
# Where the mame and snes files can be found.
#
# NEW GAMES SYSTEM :
# The DIR_GAMES structure is now build as follows :
# <NAME>, <FOLDER>, [<TYPE>, <COMMAND_PATH>, <COMMAND_ARGS>, <IMAGE_PATH>,
<FILE_SUFFIX_FOR_GENERIC>]
# where :
# - <TYPE> : Internal game types (MAME or SNES) or generic
one (GENERIC)
# - <COMMAND_PATH> : Emulator command
# - <COMMAND_ARGS> : Arguments for the emulator
# - <IMAGE_PATH> : Optionnal path to the pictures
# - <FILE_SUFFIX_FOR_GENERIC> : If the folder use the GENERIC
type, then you must specify here the file suffix used by the emulator
DIR_GAMES = [ ('MAME', '/home/media/games/xmame/roms', ('MAME',
'/usr/local/bin/xmame.SDL', '-fullscreen -modenumber 6',
'/home/media/games/xmame/shots', None) ),
('SUPER NINTENDO', '/home/media/games/snes/roms', ('SNES',
'/usr/local/bin/zsnes', '-m -r 3 -k 100 -cs -u', '', None )),
('MEGADRIVE', '/home/media/games/megadrive/roms', ('GENERIC',
'/usr/local/bin/generator-svgalib', '', '', ['smd', 'bin'] )) ]
#
# The list of filename suffixes that are used to match the files that
# are used for the Mame arcade emulator.
#
GAMES_NICE = -20 # Priority of the game process. 0 is unchanged,
# <0 is higher prio, >0 lower prio.
# prio <0 has no effect unless run as root.
This is my first attempt to code in Python, so i think there is certainly
some 'bad' code writing, so don't hesitate to post your remarks and
critics.
Regards, Sylvain.
#if 0 /*
# -----------------------------------------------------------------------
# __init__.py - interface between mediamenu and games
# -----------------------------------------------------------------------
# $Id: __init__.py,v 1.3 2003/04/24 19:56:08 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: __init__.py,v $
# Revision 1.3 2003/04/24 19:56:08 dischi
# comment cleanup for 1.3.2-pre4
#
# Revision 1.2 2003/04/21 18:17:51 dischi
# Moved the code from interface.py for video/audio/image/games to __init__.py
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import config
import util
import mame_cache
from mameitem import MameItem
from snesitem import SnesItem
from genericitem import GenericItem
def cwd(parent, files):
"""
return a list of items based on the files
"""
items = []
(type, cmd, args, imgpath, suffixlist) = parent.add_args[0]
if type == 'MAME':
print 'Type : %s' % type
mame_files = util.find_matches(files, [ 'zip' ] )
# This will only add real mame roms to the cache.
(rm_files, mame_list) = mame_cache.getMameItemInfoList(mame_files)
for rm_file in rm_files:
files.remove(rm_file)
for ml in mame_list:
items += [ MameItem(ml[0], ml[1], ml[2], cmd, args, imgpath, parent) ]
elif type == 'SNES':
for file in util.find_matches(files, [ 'smc', 'fig' ]):
items += [ SnesItem(file, cmd, args, imgpath, parent) ]
files.remove(file)
elif type == 'GENERIC':
for file in util.find_matches(files, suffixlist):
items += [ GenericItem(file, cmd, args, imgpath, parent) ]
files.remove(file)
return items
def update(parent, new_files, del_files, new_items, del_items, current_items):
"""
update a directory. Add items to del_items if they had to be removed based on
del_files or add them to new_items based on new_files
"""
(type, cmd, args, shots, suffixlist) = parent.add_args[0]
if type == 'MAME':
for item in current_items:
for file in util.find_matches(del_files, [ 'zip' ] ):
if item.type == 'mame' and item.filename == file:
# In the future will add code to remove the mame rom
# from the cache.
del_items += [ item ]
del_files.remove(file)
elif type == 'SNES':
for file in util.find_matches(del_files, [ 'smc', 'fig' ]):
if item.type == 'snes' and item.filename == file:
del_items += [ item ]
del_files.remove(file)
elif type == 'GENERIC':
for file in util.find_matches(del_files, suffixlist):
if item.type == 'generic' and item.filename == file:
del_items += [ item ]
del_files.remove(file)
new_items += cwd(parent, new_files)
#if 0 /*
# -----------------------------------------------------------------------
# directory.py - Directory handling
# -----------------------------------------------------------------------
# $Id: directory.py,v 1.23 2003/07/18 19:47:24 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: directory.py,v $
# Revision 1.23 2003/07/18 19:47:24 dischi
# add files from datadir to the current directory
#
# Revision 1.22 2003/07/12 21:23:32 dischi
# attribute type to return the disc label
#
# Revision 1.21 2003/07/11 19:44:18 dischi
# close file after parsing
#
# Revision 1.20 2003/07/05 15:53:25 outlyer
# Quiet some debugging stuff.
#
# Revision 1.19 2003/07/03 22:23:16 outlyer
# Prevent a crash I was having.
#
# Revision 1.18 2003/07/02 22:05:16 dischi
# better cache handling
#
# Revision 1.17 2003/07/02 20:07:49 dischi
# use bins support from mmpython
#
# Revision 1.16 2003/06/29 20:45:14 dischi
# mmpython support
#
# Revision 1.15 2003/06/14 00:09:40 outlyer
# The "Smartsort" code. You can enable it in local_conf, it's disabled
# by default. I fixed the smartsort cmpfunc to work a little more
# efficiently though quite frankly, it could probably be optimized better.
#
# Revision 1.14 2003/06/07 11:28:33 dischi
# use an id to find the new selected item (e.g. after creating a fxd file
#
# Revision 1.13 2003/06/03 19:10:28 outlyer
# Prevent a crash if a directory is removed while you're inside it. This just
# wasn't adapted to the new event framework, the bulk was there.
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import os
import traceback
import re
import util
import config
import menu as menu_module
import copy
import rc
import string
import skin
from item import Item
from playlist import Playlist, RandomPlaylist
import video
import audio
import image
import games
import mmpython
import event as em
from item import Item
import gui.PasswordInputBox as PasswordInputBox
import gui.AlertBox as AlertBox
from gui.PopupBox import PopupBox
# XML support
from xml.utils import qp_xml
# Add support for bins album files
from mmpython.image import bins
TRUE = 1
FALSE = 0
DEBUG = config.DEBUG
skin = skin.get_singleton()
dirwatcher_thread = None
class DirItem(Playlist):
"""
class for handling directories
"""
def __init__(self, dir, parent, name = '', display_type = None, add_args = None):
Item.__init__(self, parent)
self.type = 'dir'
self.menuw = None
# variables only for Playlist
self.current_item = 0
self.playlist = []
self.autoplay = FALSE
# variables only for DirItem
self.dir = os.path.abspath(dir)
self.display_type = display_type
self.info = {}
self.mountpoint = None
self.add_args = add_args
# set directory variables to default
all_variables = ('MOVIE_PLAYLISTS', 'DIRECTORY_SORT_BY_DATE',
'DIRECTORY_AUTOPLAY_SINGLE_ITEM', 'COVER_DIR',
'AUDIO_RANDOM_PLAYLIST', 'FORCE_SKIN_LAYOUT',
'AUDIO_FORMAT_STRING','DIRECTORY_SMART_SORT')
for v in all_variables:
setattr(self, v, eval('config.%s' % v))
if name:
self.name = name
elif os.path.isfile(dir + '/album.xml'):
try:
self.name = bins.get_bins_desc(dir)['desc']['title']
except:
self.name = os.path.basename(dir)
else:
self.name = os.path.basename(dir)
# Check for cover in COVER_DIR
if os.path.isfile(config.COVER_DIR+os.path.basename(dir)+'.png'):
self.image = config.COVER_DIR+os.path.basename(dir)+'.png'
if self.display_type:
self.handle_type = self.display_type
if os.path.isfile(config.COVER_DIR+os.path.basename(dir)+'.jpg'):
self.image = config.COVER_DIR+os.path.basename(dir)+'.jpg'
if self.display_type:
self.handle_type = self.display_type
# Check for a cover in current dir, overide COVER_DIR if needed
if os.path.isfile(dir+'/cover.png'):
self.image = dir+'/cover.png'
if self.display_type:
self.handle_type = self.display_type
if os.path.isfile(dir+'/cover.jpg'):
self.image = dir+'/cover.jpg'
if self.display_type:
self.handle_type = self.display_type
if not self.image and self.display_type == 'audio':
images = ()
covers = ()
files =()
def image_filter(x):
return re.match('.*(jpg|png)$', x, re.IGNORECASE)
def cover_filter(x):
return re.search(config.AUDIO_COVER_REGEXP, x, re.IGNORECASE)
# Pick an image if it is the only image in this dir, or it matches
# the configurable regexp
try:
files = os.listdir(dir)
except OSError:
print "oops, os.listdir() error"
traceback.print_exc()
images = filter(image_filter, files)
image = None
if len(images) == 1:
image = os.path.join(dir, images[0])
elif len(images) > 1:
covers = filter(cover_filter, images)
if covers:
image = os.path.join(dir, covers[0])
self.image = image
if not self.image:
f = os.path.join(config.TV_SHOW_DATA_DIR, os.path.basename(dir).lower())
if os.path.isfile(f+'.png'):
self.image = f+'.png'
if os.path.isfile(f+'.jpg'):
self.image = f+'.jpg'
if config.TV_SHOW_INFORMATIONS.has_key(os.path.basename(dir).lower()):
tvinfo = config.TV_SHOW_INFORMATIONS[os.path.basename(dir).lower()]
self.info = tvinfo[1]
if not self.image:
self.image = tvinfo[0]
if not self.xml_file:
self.xml_file = tvinfo[3]
if os.path.isfile(dir+'/folder.fxd'):
self.xml_file = dir+'/folder.fxd'
# set variables to values in xml file
if self.xml_file and os.path.isfile(self.xml_file):
try:
parser = qp_xml.Parser()
f = open(self.xml_file)
var_def = parser.parse(f.read())
f.close()
for top in var_def.children:
if top.name == 'folder':
for node in top.children:
if node.name == 'setvar':
for v in all_variables:
if node.attrs[('', 'name')].upper() == v.upper():
try:
setattr(self, v, int(node.attrs[('', 'val')]))
except ValueError:
setattr(self, v, node.attrs[('', 'val')])
except:
print "Skin XML file %s corrupt" % self.xml_file
traceback.print_exc()
return
if self.DIRECTORY_SORT_BY_DATE == 2 and self.display_type != 'tv':
self.DIRECTORY_SORT_BY_DATE = 0
def copy(self, obj):
"""
Special copy value DirItem
"""
Playlist.copy(self, obj)
if obj.type == 'dir':
self.dir = obj.dir
self.display_type = obj.display_type
self.info = obj.info
def getattr(self, attr):
"""
return the specific attribute as string or an empty string
"""
if attr == 'type':
if self.media:
return 'Directory on disc [%s]' % self.media.label
return 'Directory'
return Item.getattr(self, attr)
def actions(self):
"""
return a list of actions for this item
"""
items = [ ( self.cwd, 'Browse directory' ) ]
# this doen't work right now because we have no playlist
# at this point :-(
# if self.playlist and len(self.playlist) > 1:
# items += [ (RandomPlaylist(self.playlist, self),
# 'Random play all items' ) ]
if ((not self.display_type or self.display_type == 'audio') and
config.AUDIO_RANDOM_PLAYLIST == 1):
items += [ (RandomPlaylist((self.dir, config.SUFFIX_AUDIO_FILES),
self),
'Recursive random play all items') ]
return items
def cwd(self, arg=None, menuw=None):
"""
make a menu item for each file in the directory
"""
if not self.menuw:
self.menuw = menuw
# are we on a ROM_DRIVE and have to mount it first?
for media in config.REMOVABLE_MEDIA:
if string.find(self.dir, media.mountdir) == 0:
util.mount(self.dir)
self.media = media
if self.mountpoint:
util.mount(self.mountpoint)
if os.path.isfile(self.dir + '/.password'):
print 'password protected dir'
pb = PasswordInputBox(text='Enter Password', handler=self.pass_cmp_cb)
pb.show()
# save these so the InputBox callback can pass them to do_cwd
self.arg = arg
self.foo = "bar"
else:
self.do_cwd(arg, menuw)
def pass_cmp_cb(self, word=None):
# read the contents of self.dir/.passwd and compare to word
try:
pwfile = open(self.dir + '/.password')
line = pwfile.readline()
except IOError, e:
print 'error %d (%s) reading password file for %s' % \
(e.errno, e.strerror, self.dir)
return
pwfile.close()
password = line.strip()
if word == password:
self.do_cwd(self.arg, self.menuw)
else:
pb = AlertBox(text='Password incorrect')
pb.show()
return
def do_cwd(self, arg=None, menuw=None):
datadir = util.getdatadir(self)
try:
files = ([ os.path.join(self.dir, fname)
for fname in os.listdir(self.dir) ])
if os.path.isdir(datadir):
for f in ([ os.path.join(datadir, fname)
for fname in os.listdir(datadir) ]):
if not os.path.isdir(f):
files.append(f)
self.all_files = copy.copy(files)
except OSError:
print 'util:match_files(): Got error on dir = "%s"' % self.dir
return
# build play_items for video, audio, image, games
# the interface functions must remove the files they cover, they
# can also remove directories
mmpython_dir = self.dir
if self.media:
if self.media.cached:
mmpython_dir = None
num_changes = 0
else:
mmpython_dir = 'cd://%s:%s:' % (self.media.devicename, self.media.mountdir)
if mmpython_dir:
num_changes = mmpython.check_cache(mmpython_dir)
pop = None
if num_changes > 10:
if self.media:
pop = PopupBox(text='Scanning disc, be patient...')
else:
pop = PopupBox(text='Scanning directory, be patient...')
pop.show()
if num_changes > 0:
mmpython.cache_dir(mmpython_dir)
if self.media:
self.media.cached = TRUE
play_items = []
for t in ( 'video', 'audio', 'image', 'games' ):
if not self.display_type or self.display_type == t:
play_items += eval(t + '.cwd(self, files)')
if self.display_type == 'tv':
play_items += video.cwd(self, files)
if self.DIRECTORY_SORT_BY_DATE:
play_items.sort(lambda l, o: cmp(l.sort('date').upper(),
o.sort('date').upper()))
else:
play_items.sort(lambda l, o: cmp(l.sort().upper(),
o.sort().upper()))
files.sort(lambda l, o: cmp(l.upper(), o.upper()))
# add all playable items to the playlist of the directory
# to play one files after the other
if (not self.display_type or self.display_type == 'audio' or \
self.display_type == 'image' or \
(self.MOVIE_PLAYLISTS and self.display_type == 'video')):
self.playlist = play_items
# build items for sub-directories
dir_items = []
for filename in files:
if (os.path.isdir(filename) and
os.path.basename(filename) != 'CVS' and
os.path.basename(filename) != '.xvpics' and
os.path.basename(filename) != '.thumbnails' and
os.path.basename(filename) != '.pics'):
dir_items += [ DirItem(filename, self, display_type =
self.display_type) ]
if self.DIRECTORY_SMART_SORT:
dir_items.sort(lambda l, o: util.smartsort(l.dir,o.dir))
else:
dir_items.sort(lambda l, o: cmp(l.dir.upper(), o.dir.upper()))
# build items for playlists
pl_items = []
if not self.display_type or self.display_type == 'audio':
for pl in util.find_matches(files, config.SUFFIX_AUDIO_PLAYLISTS):
pl_items += [ Playlist(pl, self) ]
if not self.display_type or self.display_type == 'image':
for file in util.find_matches(files, config.SUFFIX_IMAGE_SSHOW):
pl = Playlist(file, self)
pl.autoplay = TRUE
pl_items += [ pl ]
pl_items.sort(lambda l, o: cmp(l.name.upper(), o.name.upper()))
# all items together
items = []
# random playlist (only active for audio)
if ((not self.display_type or self.display_type == 'audio') and \
len(play_items) > 1 and self.display_type and
config.AUDIO_RANDOM_PLAYLIST == 1):
pl = Playlist(play_items, self)
pl.randomize()
pl.autoplay = TRUE
items += [ pl ]
items += dir_items + pl_items + play_items
self.dir_items = dir_items
self.pl_items = pl_items
self.play_items = play_items
title = self.name
if pop:
pop.destroy()
# closing the poup will rebuild the menu which may umount
# the drive
if self.media:
self.media.mount()
# autoplay
if len(items) == 1 and items[0].actions() and \
self.DIRECTORY_AUTOPLAY_SINGLE_ITEM:
items[0].actions()[0][0](menuw=menuw)
else:
item_menu = menu_module.Menu(title, items, reload_func=self.reload,
item_types = self.display_type,
force_skin_layout = self.FORCE_SKIN_LAYOUT)
if self.xml_file:
item_menu.skin_settings = skin.LoadSettings(self.xml_file)
if menuw:
menuw.pushmenu(item_menu)
global dirwatcher_thread
if not dirwatcher_thread:
dirwatcher_thread = DirwatcherThread(menuw)
dirwatcher_thread.setDaemon(1)
dirwatcher_thread.start()
dirwatcher_thread.cwd(self, item_menu, self.dir, datadir, self.all_files)
self.menu = item_menu
return items
def reload(self):
"""
called when we return to this menu
"""
global dirwatcher_thread
datadir = util.getdatadir(self)
dirwatcher_thread.cwd(self, self.menu, self.dir, datadir, self.all_files)
dirwatcher_thread.scan()
# we changed the menu, don't build a new one
return None
def update(self, new_files, del_files, all_files):
"""
update the current item set. Maybe this function can share some code
with cwd in the future, but it's easier now the way it is
"""
new_items = []
del_items = []
self.all_files = all_files
# check modules if they know something about the deleted/new files
for t in ( 'video', 'audio', 'image', 'games' ):
if not self.display_type or self.display_type == t:
eval(t + '.update')(self, new_files, del_files, \
new_items, del_items, \
self.play_items)
if self.display_type == 'tv':
video.update(self, new_files, del_files,
new_items, del_items, self.play_items)
# store the current selected item
selected = self.menu.selected
# delete play items from the menu
for i in del_items:
self.menu.delete_item(i)
self.play_items.remove(i)
# delete dir items from the menu
for dir in del_files:
for item in self.dir_items:
if item.dir == dir:
self.menu.delete_item(item)
self.dir_items.remove(item)
# delete playlist items from the menu
for pl in del_files:
for item in self.pl_items:
if item.filename == pl:
self.menu.delete_item(item)
self.pl_items.remove(item)
# add new play items to the menu
if new_items:
self.play_items += new_items
if self.DIRECTORY_SORT_BY_DATE:
self.play_items.sort(lambda l, o: cmp(l.sort('date').upper(),
o.sort('date').upper()))
else:
self.play_items.sort(lambda l, o: cmp(l.sort().upper(),
o.sort().upper()))
# add new dir items to the menu
new_dir_items = []
for dir in new_files:
if (os.path.isdir(dir) and
os.path.basename(dir) != 'CVS' and
os.path.basename(dir) != '.xvpics' and
os.path.basename(dir) != '.thumbnails' and
os.path.basename(dir) != '.pics'):
new_dir_items += [ DirItem(dir, self,
display_type = self.display_type) ]
if new_dir_items:
self.dir_items += new_dir_items
self.dir_items.sort(lambda l, o: cmp(l.dir.upper(), o.dir.upper()))
# add new playlist items to the menu
new_pl_items = []
if not self.display_type or self.display_type == 'audio':
for pl in util.find_matches(new_files,
config.SUFFIX_AUDIO_PLAYLISTS):
new_pl_items += [ Playlist(pl, self) ]
if not self.display_type or self.display_type == 'image':
for file in util.find_matches(new_files, config.SUFFIX_IMAGE_SSHOW):
pl = Playlist(file, self)
pl.autoplay = TRUE
new_pl_items += [ pl ]
if new_pl_items:
self.pl_items += new_pl_items
self.pl_items.sort(lambda l, o: cmp(l.name.upper(), o.name.upper()))
items = []
# random playlist (only active for audio)
if ((not self.display_type or self.display_type == 'audio') and \
len(self.play_items) > 1 and self.display_type and
config.AUDIO_RANDOM_PLAYLIST == 1):
# some files changed, rebuild playlist
if new_items or del_items:
pl = Playlist(self.play_items, self)
pl.randomize()
pl.autoplay = TRUE
items += [ pl ]
# reuse old playlist
else:
items += [ self.menu.choices[0] ]
# build a list of all items
items += self.dir_items + self.pl_items + self.play_items
# finally add the items
for i in new_items + new_dir_items + new_pl_items:
self.menu.add_item(i, items.index(i))
if not selected in self.menu.choices:
if hasattr(selected, 'id'):
id = selected.id
for i in self.menu.choices:
if hasattr(i, 'id') and i.id == selected.id:
self.menu.selected = i
break
# reload the menu, use an event to avoid problems because this function
# was called by a thread
if hasattr(self.menu,'skin_force_text_view'):
del self.menu.skin_force_text_view
rc.post_event('MENU_REBUILD')
# ======================================================================
import threading
import thread
import time
class DirwatcherThread(threading.Thread):
def __init__(self, menuw):
threading.Thread.__init__(self)
self.item = None
self.menuw = menuw
self.item_menu = None
self.dir = None
self.datadir = None
self.files = None
self.lock = thread.allocate_lock()
def cwd(self, item, item_menu, dir, datadir, files):
self.lock.acquire()
self.item = item
self.item_menu = item_menu
self.dir = dir
self.datadir = datadir
self.files = files
self.lock.release()
def scan(self):
self.lock.acquire()
try:
files = ([ os.path.join(self.dir, fname)
for fname in os.listdir(self.dir) ])
except OSError:
# the directory is gone
print 'unable to read directory'
# send EXIT to go one menu up:
rc.post_event(em.MENU_BACK_ONE_MENU)
self.lock.release()
return
try:
for f in ([ os.path.join(self.datadir, fname)
for fname in os.listdir(self.datadir) ]):
if not os.path.isdir(f):
files.append(f)
except OSError:
pass
new_files = []
del_files = []
for f in files:
if not f in self.files:
new_files += [ f ]
for f in self.files:
if not f in files:
del_files += [ f ]
if new_files or del_files:
if DEBUG: print 'directory has changed'
self.item.update(new_files, del_files, files)
self.files = files
self.lock.release()
def run(self):
while 1:
if self.dir and self.menuw and \
self.menuw.menustack[-1] == self.item_menu and not rc.app():
self.scan()
time.sleep(2)
#if 0 /*
# -----------------------------------------------------------------------
# genericitem.py - Item for generic objects
# -----------------------------------------------------------------------
# $Id: genericitem.py,v 1.8 2003/05/27 17:53:34 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import os
import config
import game
# Set to 1 for debug output
DEBUG = config.DEBUG
TRUE = 1
FALSE = 0
import rc
import time
import copy
from item import Item
import event as em
from struct import *
from string import *
from re import *
class GenericItem(Item):
def __init__(self, file, cmd = None, args = None, imgpath = None, parent = None):
Item.__init__(self)
self.type = 'generic' # fix value
self.mode = 'file' # file, dvd or vcd
self.filename = file
self.name = os.path.splitext(os.path.basename(file))[0]
self.xml_file = None
self.parent = parent
# find image for this file
# find image for this file
shot = imgpath + '/' + \
os.path.splitext(os.path.basename(file))[0] + ".png"
if os.path.isfile(shot):
self.image = shot
elif os.path.isfile(os.path.splitext(file)[0] + ".png"):
self.image = os.path.splitext(file)[0] + ".png"
command = '--prio=%s %s %s' % (config.GAMES_NICE,
cmd,
args)
romname = os.path.basename(file)
romdir = os.path.dirname(file)
command = '%s "%s"' % (command, file)
self.command = command
self.game_player = game.get_singleton()
def sort(self, mode=None):
"""
Returns the string how to sort this item
"""
return self.name
# ------------------------------------------------------------------------
# actions:
def actions(self):
return [ ( self.play, 'Play' ) ]
def play(self, menuw=None):
self.parent.current_item = self
if not self.menuw:
self.menuw = menuw
if self.menuw.visible:
self.menuw.hide()
print "Playing: %s" % self.filename
self.game_player.play(self)
def stop(self, menuw=None):
self.game_player.stop()
def eventhandler(self, event, menuw=None, mythread=None):
if not mythread == None:
if event == em.STOP:
self.stop()
rc.app(None)
if not menuw == None:
menuw.refresh(reload=1)
elif event == em.MENU:
mythread.app.write('M')
elif event == em.GAMES_CONFIG:
mythread.cmd( 'config' )
elif event == em.PAUSE or event == em.PLAY:
mythread.cmd('pause')
elif event == em.GAMES_RESET:
mythread.cmd('reset')
elif event == em.GAMES_SNAPSHOT:
mythread.cmd('snapshot')
# give the event to the next eventhandler in the list
return Item.eventhandler(self, event, menuw)
#if 0 /*
# -----------------------------------------------------------------------
# mameitem.py - Item for mame objects
# -----------------------------------------------------------------------
# $Id: mameitem.py,v 1.11 2003/06/20 01:31:14 rshortt Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: mameitem.py,v $
# Revision 1.11 2003/06/20 01:31:14 rshortt
# Adding support for a seperate directory for screen/titleshots. They show
# up in the MAME menu like album covers do in the audio menu.
#
# Revision 1.10 2003/06/01 05:06:40 outlyer
# Fixed a missing import as pointed out by Paul de Bruin
#
# Revision 1.9 2003/05/27 17:53:34 dischi
# Added new event handler module
#
# Revision 1.8 2003/04/24 19:56:13 dischi
# comment cleanup for 1.3.2-pre4
#
# Revision 1.7 2003/04/20 12:43:33 dischi
# make the rc events global in rc.py to avoid get_singleton. There is now
# a function app() to get/set the app. Also the events should be passed to
# the daemon plugins when there is no handler for them before. Please test
# it, especialy the mixer functions.
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import os
import re
import config
import util
import game
import rc
# Set to 1 for debug output
DEBUG = config.DEBUG
TRUE = 1
FALSE = 0
import menu
import event as em
import time
import copy
from item import Item
class MameItem(Item):
def __init__(self, title, file, image = None, cmd = None, args = None, imgpath = None, parent = None):
Item.__init__(self)
self.type = 'mame' # fix value
self.mode = 'file' # file, dvd or vcd
self.image = image
self.name = title
self.filename = file
self.xml_file = None
self.parent = parent
# find image for this file
if image == None:
shot = imgpath + '/' + \
os.path.splitext(os.path.basename(file))[0] + ".png"
if os.path.isfile(shot):
self.image = shot
elif os.path.isfile(os.path.splitext(file)[0] + ".png"):
self.image = os.path.splitext(file)[0] + ".png"
command = '--prio=%s %s %s' % (config.GAMES_NICE,
cmd,
args)
# Some files needs special arguments to mame, they can be
# put in a <file>.mame options file. The <file>
# includes the suffix (.zip, etc)!
# The arguments in the options file are added at the end of the
# regular mame arguments.
if os.path.isfile(file + '.mame'):
command += (' ' + open(filename + '.mame').read().strip())
if DEBUG: print 'Read options, command = "%s"' % command
romname = os.path.basename(file)
romdir = os.path.dirname(file)
command = '%s -rp %s "%s"' % (command, romdir, romname)
self.command = command
self.game_player = game.get_singleton()
def sort(self, mode=None):
"""
Returns the string how to sort this item
"""
return self.name
# ------------------------------------------------------------------------
# actions:
def actions(self):
return [ ( self.play, 'Play' ) ]
def play(self, arg=None, menuw=None):
self.parent.current_item = self
if not self.menuw:
self.menuw = menuw
if self.menuw.visible:
self.menuw.hide()
print "Playing: %s" % self.filename
self.game_player.play(self)
def stop(self, menuw=None):
self.game_player.stop()
def eventhandler(self, event, menuw=None, mythread=None):
if not mythread == None:
if event == em.STOP:
self.stop()
rc.app(None)
if not menuw == None:
menuw.refresh(reload=1)
elif event == em.MENU:
mythread.app.write('M')
elif event == em.GAMES_CONFIG:
mythread.cmd( 'config' )
elif event == em.PAUSE or event == em.PLAY:
mythread.cmd('pause')
elif event == em.GAMES_RESET:
mythread.cmd('reset')
elif event == em.GAMES_SNAPSHOT:
mythread.cmd('snapshot')
# give the event to the next eventhandler in the list
return Item.eventhandler(self, event, menuw)
#if 0 /*
# -----------------------------------------------------------------------
# mediamenu.py - Basic menu for all kinds of media
# -----------------------------------------------------------------------
# $Id: mediamenu.py,v 1.8 2003/07/06 19:40:01 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: mediamenu.py,v $
# Revision 1.8 2003/07/06 19:40:01 dischi
# fix menu title
#
# Revision 1.7 2003/05/28 09:10:00 dischi
# bugfix
#
# Revision 1.6 2003/05/27 17:53:35 dischi
# Added new event handler module
#
# Revision 1.5 2003/05/04 12:05:45 dischi
# make it possible to force the mediamenu to text or image view
#
# Revision 1.4 2003/04/24 19:56:36 dischi
# comment cleanup for 1.3.2-pre4
#
# Revision 1.3 2003/04/21 13:02:45 dischi
# Reload the mediamenu everytime we display it, some plugins may have
# changed
#
# Revision 1.2 2003/04/20 12:43:33 dischi
# make the rc events global in rc.py to avoid get_singleton. There is now
# a function app() to get/set the app. Also the events should be passed to
# the daemon plugins when there is no handler for them before. Please test
# it, especialy the mixer functions.
#
# Revision 1.1 2003/04/20 10:53:23 dischi
# moved identifymedia and mediamenu to plugins
#
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import os
import traceback
import copy
import config
import menu as menu_module
import rc
import event as em
from item import Item
from directory import DirItem
TRUE = 1
FALSE = 0
import plugin
#
# Plugin interface to integrate the MediaMenu into Freevo
#
class PluginInterface(plugin.MainMenuPlugin):
"""
Plugin to integrate a meniamenu (video/audio/image/games) into
the Freevo main menu
"""
def __init__(self, type=None, force_text_view=FALSE):
plugin.MainMenuPlugin.__init__(self)
self.type = type
self.force_text_view = force_text_view
def items(self, parent):
import skin
skin = skin.get_singleton()
menu_items = skin.settings.mainmenu.items
icon = ""
if menu_items[self.type].icon:
icon = os.path.join(skin.settings.icon_dir, menu_items[self.type].icon)
return ( menu_module.MenuItem(menu_items[self.type].name, icon=icon,
action=MediaMenu().main_menu,
arg=(self.type,self.force_text_view), type='main',
image=menu_items[self.type].image, parent=parent), )
class MediaMenu(Item):
"""
This is the main menu for audio, video and images. It displays the default
directories and the ROM_DRIVES
"""
def __init__(self):
Item.__init__(self)
self.type = 'mediamenu'
def main_menu_generate(self):
"""
generate the items for the main menu. This is needed when first generating
the menu and if something changes by pressing the EJECT button
"""
items = copy.copy(self.normal_items)
if self.display_type:
plugins = plugin.get('mainmenu_%s' % self.display_type)
else:
plugins = []
for p in plugins:
items += p.items(self)
return items
def main_menu(self, arg=None, menuw=None):
"""
display the (IMAGE|VIDEO|AUDIO|GAMES) main menu
"""
self.display_type, force_text_view = arg
title = 'MEDIA'
dirs = []
self.menuw = menuw
if self.display_type == 'video':
title = 'MOVIE'
dirs += config.DIR_MOVIES
if self.display_type == 'audio':
title = 'AUDIO'
dirs += config.DIR_AUDIO
if self.display_type == 'image':
title = 'IMAGE'
dirs += config.DIR_IMAGES
if self.display_type == 'games':
title = 'GAMES'
dirs += config.DIR_GAMES
self.normal_items = []
# add default items
for d in dirs:
try:
(t, dir) = d[:2]
if len(d) > 2:
add_args = d[2:]
else:
add_args = None
d = DirItem(dir, self, name = t,
display_type = self.display_type, add_args = add_args)
self.normal_items += [ d ]
except:
traceback.print_exc()
item_menu = menu_module.Menu('%s MAIN MENU' % title, self.main_menu_generate(),
item_types = self.display_type, umount_all=1,
reload_func = self.reload)
item_menu._skin_force_text_view = force_text_view
self.menuw = menuw
menuw.pushmenu(item_menu)
def reload(self):
menuw = self.menuw
menu = menuw.menustack[1]
sel = menu.choices.index(menu.selected)
new_choices = self.main_menu_generate()
if not menu.selected in new_choices:
if len(new_choices) <= sel:
menu.selected = new_choices[-1]
else:
menu.selected = new_choices[sel]
menu.choices = new_choices
return menu
def eventhandler(self, event = None, menuw=None):
"""
eventhandler for the main menu. The menu must be regenerated
when a disc in a rom drive changes
"""
if plugin.isevent(event):
if not menuw:
menuw = self.menuw
menu = menuw.menustack[1]
sel = menu.choices.index(menu.selected)
menuw.menustack[1].choices = self.main_menu_generate()
if not menu.selected in menu.choices:
menu.selected = menu.choices[sel]
if menu == menuw.menustack[-1] and not rc.app():
menuw.init_page()
menuw.refresh()
return TRUE
if event in (em.PLAY_END, em.USER_END, em.STOP) and event.context != 'menu':
menuw.show()
return TRUE
# give the event to the next eventhandler in the list
return Item.eventhandler(self, event, menuw)
#if 0 /*
# -----------------------------------------------------------------------
# snesitem.py - Item for snes objects
# -----------------------------------------------------------------------
# $Id: snesitem.py,v 1.8 2003/05/27 17:53:34 dischi Exp $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# $Log: snesitem.py,v $
# Revision 1.8 2003/05/27 17:53:34 dischi
# Added new event handler module
#
# Revision 1.7 2003/04/24 19:56:13 dischi
# comment cleanup for 1.3.2-pre4
#
# Revision 1.6 2003/04/20 12:43:33 dischi
# make the rc events global in rc.py to avoid get_singleton. There is now
# a function app() to get/set the app. Also the events should be passed to
# the daemon plugins when there is no handler for them before. Please test
# it, especialy the mixer functions.
#
# Revision 1.2 2002/12/22 12:59:34 dischi
# Added function sort() to (audio|video|games|image) item to set the sort
# mode. Default is alphabetical based on the name. For mp3s and images
# it's based on the filename. Sort by date is in the code but deactivated
# (see mediamenu.py how to enable it)
#
# Revision 1.1 2002/12/09 14:23:53 dischi
# Added games patch from Rob Shortt to use the interface.py and snes support
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */
#endif
import os
import config
import game
# Set to 1 for debug output
DEBUG = config.DEBUG
TRUE = 1
FALSE = 0
import rc
import time
import copy
from item import Item
import event as em
from struct import *
from string import *
from re import *
# Used to detect the internal rome information, as described in 'SNESKART.DOC v1.3'
snesromFileOffset = [33216, 32704, 65472, 65984]
snesRomCountry = { 0:'Japan', 1:'USA', 2:'Europe, Oceania, Asia', 3:'Sweden', 4:'Finland', 5:'Denmark',
6:'France', 7:'Holland', 8:'Spain', 9:'Germany, Austria, Switz', 10:'Italy',
11:'Hong Kong, China', 12:'Indonesia', 13:'Korea', 255:'International' }
snesromType = [ 0, 1, 2, 3, 4, 5, 19, 227, 246]
snesromROMSize = [ 8, 9, 10, 11, 12]
snesromSRAMSize = [ 0, 1, 2, 3]
snesromLicences = { 0: 'Unknown',
1: 'Nintendo' ,
5: 'Zamuse' ,
8: 'Capcom' ,
9: 'HOT B' ,
10: 'Jaleco' ,
11: 'STORM' ,
15: 'Mebio Software' ,
18: 'Gremlin Graphics' ,
21: 'COBRA Team' ,
22: 'Human/Field' ,
24: 'Hudson Soft' ,
26: 'Yanoman' ,
28: 'Tecmo' ,
30: 'Forum' ,
31: 'Park Place Productions / VIRGIN' ,
33: 'Tokai Engeneering' ,
34: 'POW' ,
35: 'Loriciel / Micro World' ,
38: 'Enix' ,
40: 'Kemco (1)' ,
41: 'Seta Co.,Ltd.' ,
45: 'Visit Co.,Ltd.' ,
51: 'Nintendo' ,
53: 'HECT' ,
61: 'Loriciel' ,
64: 'Seika Corp.' ,
65: 'UBI Soft' ,
71: 'Spectrum Holobyte' ,
73: 'Irem' ,
75: 'Raya Systems/Sculptured Software' ,
76: 'Renovation Pruducts' ,
77: 'Malibu Games' ,
79: 'U.S. Gold' ,
80: 'Absolute Entertainment' ,
81: 'Acclaim' ,
82: 'Activision' ,
83: 'American Sammy' ,
84: 'GameTek' ,
85: 'Hi Tech' ,
86: 'LJN Toys' ,
90: 'Mindscape' ,
93: 'Technos Japan Corp. (Tradewest)' ,
95: 'American Softworks Corp.' ,
96: 'Titus' ,
97: 'Virgin Games' ,
98: 'Maxis' ,
103: 'Ocean' ,
105: 'Electronic Arts' ,
107: 'Laser Beam' ,
110: 'Elite' ,
111: 'Electro Brain' ,
112: 'Infogrames' ,
113: 'Interplay' ,
114: 'LucasArts' ,
115: 'Sculptured Soft' ,
117: 'STORM (Sales Curve)' ,
120: 'THQ Software' ,
121: 'Accolade Inc.' ,
122: 'Triffix Entertainment' ,
124: 'Microprose' ,
127: 'Kemco' ,
129: 'Teichio' ,
130: 'Namcot/Namco Ltd.' ,
132: 'Koei/Koei!' ,
134: 'Tokuma Shoten Intermedia' ,
136: 'DATAM-Polystar' ,
139: 'Bullet-Proof Software' ,
140: 'Vic Tokai' ,
143: 'I Max' ,
145: 'CHUN Soft' ,
146: 'Video System Co., Ltd.' ,
147: 'BEC' ,
151: 'Kaneco' ,
153: 'Pack in Video' ,
154: 'Nichibutsu' ,
155: 'TECMO' ,
156: 'Imagineer Co.' ,
160: 'Wolf Team' ,
164: 'Konami' ,
165: 'K.Amusement' ,
167: 'Takara' ,
169: 'Technos Jap.' ,
170: 'JVC' ,
172: 'Toei Animation' ,
173: 'Toho' ,
175: 'Namcot/Namco Ltd.' ,
177: 'ASCII Co. Activison' ,
178: 'BanDai America' ,
180: 'Enix' ,
182: 'Halken' ,
186: 'Culture Brain' ,
187: 'Sunsoft' ,
188: 'Toshiba EMI/System Vision' ,
189: 'Sony (Japan) / Imagesoft' ,
191: 'Sammy' ,
192: 'Taito' ,
194: 'Kemco' ,
195: 'Square' ,
196: 'NHK' ,
197: 'Data East' ,
198: 'Tonkin House' ,
200: 'KOEI' ,
202: 'Konami USA' ,
205: 'Meldac/KAZe' ,
206: 'PONY CANYON' ,
207: 'Sotsu Agency' ,
209: 'Sofel' ,
210: 'Quest Corp.' ,
211: 'Sigma' ,
214: 'Naxat' ,
216: 'Capcom' ,
217: 'Banpresto' ,
219: 'Hiro' ,
221: 'NCS' ,
222: 'Human Entertainment' ,
223: 'Ringler Studios' ,
224: 'K.K. DCE / Jaleco' ,
226: 'Sotsu Agency' ,
228: 'T&ESoft' ,
229: 'EPOCH Co.,Ltd.' ,
231: 'Athena' ,
232: 'Asmik' ,
233: 'Natsume' ,
234: 'King/A Wave' ,
235: 'Atlus' ,
236: 'Sony Music' ,
238: 'Psygnosis / igs' ,
243: 'Beam Software' ,
244: 'Tec Magik' ,
255: 'Hudson Soft' }
class SnesItem(Item):
def __init__(self, file, cmd = None, args = None, imgpath = None, parent = None):
Item.__init__(self)
self.type = 'snes' # fix value
self.mode = 'file' # file, dvd or vcd
self.filename = file
snesFile = open(file, 'rb')
for offset in snesromFileOffset:
snesFile.seek(offset)
romHeader = snesFile.read(32)
(romName,romHL,romMem,romROM,romSRAM,romCountry,romLic,romVer,romICHK,romCHK) = unpack('21scccccccHH', romHeader)
# Break now if CHECKSUM is OK
if (romICHK | romCHK) == 0xFFFF:
if DEBUG:
print 'SNES rom header detected at offset : %d!!!!' % offset
break
else:
for offset in snesromFileOffset:
snesFile.seek(offset)
romHeader = snesFile.read(32)
(romName,romHL,romMem,romROM,romSRAM,romCountry,romLic,romVer,romICHK,romCHK) = unpack('21scccccccHH', romHeader)
# Some times, the ROM is OK, but the checksum is incorrect, so we do a very dummy ASCII detection
if match('[a-zA-Z0-9 ]{4}', romName[0:4]) != None:
if DEBUG:
print 'SNES rom header detected by ASCII name : %d!!!!' % offset
break
snesFile.close()
if DEBUG:
print 'SNES rom name : %s - %s -> %s' % (ord(romCountry),os.path.basename(file), romName)
# Allocate the name according to the country by checking the rom name againts ASCII codes
if snesromLicences.has_key(ord(romLic)):
romLicTxt = snesromLicences[ord(romLic)]
else:
romLicTxt = 'Unknown'
if snesRomCountry.has_key(ord(romCountry)):
romCountryTxt = snesRomCountry[ord(romCountry)]
else:
romCountryTxt = 'Unknown'
if match('[a-zA-Z0-9 ]{4}', romName[0:4]) == None:
self.name = os.path.splitext(os.path.basename(file))[0] + ' (' + romCountryTxt + ' - ' + romLicTxt + ')'
else:
self.name = capwords(romName) + ' (' + romCountryTxt + ' - ' + romLicTxt + ')'
self.xml_file = None
self.parent = parent
# find image for this file
shot = imgpath + '/' + \
os.path.splitext(os.path.basename(file))[0] + ".png"
if os.path.isfile(shot):
self.image = shot
elif os.path.isfile(os.path.splitext(file)[0] + ".png"):
self.image = os.path.splitext(file)[0] + ".png"
command = '--prio=%s %s %s' % (config.GAMES_NICE,
cmd,
args)
romname = os.path.basename(file)
romdir = os.path.dirname(file)
command = '%s "%s"' % (command, file)
self.command = command
self.game_player = game.get_singleton()
def sort(self, mode=None):
"""
Returns the string how to sort this item
"""
return self.name
# ------------------------------------------------------------------------
# actions:
def actions(self):
return [ ( self.play, 'Play' ) ]
def play(self, menuw=None):
self.parent.current_item = self
if not self.menuw:
self.menuw = menuw
if self.menuw.visible:
self.menuw.hide()
print "Playing: %s" % self.filename
self.game_player.play(self)
def stop(self, menuw=None):
self.game_player.stop()
def eventhandler(self, event, menuw=None, mythread=None):
if not mythread == None:
if event == em.STOP:
self.stop()
rc.app(None)
if not menuw == None:
menuw.refresh(reload=1)
elif event == em.MENU:
mythread.app.write('M')
elif event == em.GAMES_CONFIG:
mythread.cmd( 'config' )
elif event == em.PAUSE or event == em.PLAY:
mythread.cmd('pause')
elif event == em.GAMES_RESET:
mythread.cmd('reset')
elif event == em.GAMES_SNAPSHOT:
mythread.cmd('snapshot')
# give the event to the next eventhandler in the list
return Item.eventhandler(self, event, menuw)