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)

Reply via email to