#!/usr/bin/python # -*- coding: iso-8859-1 -*- ##################################################################### # Frets on Fire X (FoFiX) # # Copyright (C) 2006 Sami Kyöstilä # # 2008 evilynux # # 2009 FoFiX Team # # 2009 akedrou # # # # 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 # # MERCHANTABILITY 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., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### ##@package FoFiX # Main game executable. # Register the latin-1 encoding import codecs import encodings.iso8859_1 import encodings.utf_8 codecs.register(lambda encoding: encodings.iso8859_1.getregentry()) codecs.register(lambda encoding: encodings.utf_8.getregentry()) assert codecs.lookup("iso-8859-1") assert codecs.lookup("utf-8") #stump: pygst eats --help, so process the command line before that. # Also do this before we import any heavyweight stuff so --help goes through # as efficiently as possible and we can disable pyOpenGL error checking # if we are not asked for it. import getopt import sys import os import Version ## Display command-line usage and exit. # Outputs to stdout unless py2exe'd, in which case the usage is presented # using a MessageBox(). # @param errmsg Optional error message. def _usage(errmsg=None): usage = """Usage: %(prog)s [options] Options: --help, -h Show this help. --config=, -c [configfile] Use this configuration file instead of fofix.ini from its standard location on your platform. Use "reset" to use the usual fofix.ini but clear it first. --fullscreen=, -f [true/false] Force (non-)usage of full-screen mode. --resolution=, -r [resolution] Force a specific resolution to be used. --theme=, -t [theme] Force the specified theme to be used. Remember to quote the theme name if it contains spaces (e.g. %(prog)s -t "Guitar Hero III") --song=, -s [songdir] Play a song in one-shot mode. (See "One-shot mode options" below.) Advanced options: --verbose, -v Verbose messages --debug, -d Write debug file --opengl-error-checking Enable OpenGL error checking One-shot mode options (ignored unless in one-shot mode): --part=, -p [part number] 0: Guitar, 1: Rhythm, 2: Bass, 3: Lead 4: Drum, 5: Vocals --diff=, -l [level of difficulty] 0: Expert, 1: Hard, 2: Medium, 3: Easy (Only applies if "part" is set) --mode=, -m [game mode] 0: Quickplay, 1: Practice, 2: Career """ % {"prog": sys.argv[0]} if errmsg is not None: usage = '%s: %s\n\n%s' % (sys.argv[0], errmsg, usage) if hasattr(sys, 'frozen') and os.name == 'nt': import win32api import win32con win32api.MessageBox(0, usage, '%s %s' % (Version.appNameSexy(), Version.version()), win32con.MB_OK) else: print usage sys.exit(1) try: opts, args = getopt.getopt(sys.argv[1:], "hvdc:f:r:t:s:l:p:m:n:", ["help", "verbose", "debug", "config=", "fullscreen=", "resolution=", "theme=", "song=", "diff=", "part=", "mode=", "nbrplayers=", "opengl-error-checking"]) except getopt.GetoptError, e: _usage(str(e)) # str(e): error message from getopt, e.g. "option --some-invalid-option not recognized" if ('-h', '') in opts or ('--help', '') in opts: _usage() #stump: disable pyOpenGL error checking if we are not asked for it. # This must be before *anything* that may import pyOpenGL! assert 'OpenGL' not in sys.modules if ('--opengl-error-checking', '') not in opts: import OpenGL if OpenGL.__version__ >= '3': OpenGL.ERROR_CHECKING = False import Log import Config from GameEngine import GameEngine from MainMenu import MainMenu from Language import _ import Resource import pygame import traceback ## Main function. def main(): playing = None configFile = None fullscreen = None resolution = None theme = None debug = False difficulty = None part = None mode = 0 nbrplayers = 1 for opt, arg in opts: if opt in ["--verbose", "-v"]: Log.quiet = False if opt in ["--debug", "-d"]: debug = True if opt in ["--config", "-c"]: configFile = arg if opt in ["--fullscreen", "-f"]: fullscreen = arg if opt in ["--resolution", "-r"]: resolution = arg if opt in ["--theme", "-t"]: theme = arg if opt in ["--song", "-s"]: playing = arg if opt in ["--diff", "-l"]: difficulty = arg if opt in ["--part", "-p"]: part = arg #evilynux - Multiplayer and mode selection support if opt in ["--mode", "-m"]: mode = int(arg) if opt in ["--nbrplayers", "-n"]: nbrplayers = int(arg) # Load the configuration file. if configFile is not None: if configFile.lower() == "reset": fileName = os.path.join(Resource.getWritableResourcePath(), Version.appName() + ".ini") os.remove(fileName) config = Config.load(Version.appName() + ".ini", setAsDefault = True) else: config = Config.load(configFile, setAsDefault = True) else: config = Config.load(Version.appName() + ".ini", setAsDefault = True) #Lysdestic - Allow support for manipulating fullscreen via CLI if fullscreen is not None: Config.set("video", "fullscreen", fullscreen) #Lysdestic - Change resolution from CLI if resolution is not None: Config.set("video", "resolution", resolution) #Lysdestic - Alter theme from CLI if theme is not None: Config.set("coffee", "themename", theme) engine = GameEngine(config) engine.cmdPlay = 0 # Check for a valid invocation of one-shot mode. if playing is not None: Log.debug('Validating song directory for one-shot mode.') library = Config.get("game","base_library") basefolder = os.path.join(Version.dataPath(),library,"songs",playing) if not (os.path.exists(os.path.join(basefolder, "song.ini")) and (os.path.exists(os.path.join(basefolder, "notes.mid")) or os.path.exists(os.path.join(basefolder, "notes-unedited.mid"))) and (os.path.exists(os.path.join(basefolder, "song.ogg")) or os.path.exists(os.path.join(basefolder, "guitar.ogg")))): Log.warn("Song directory provided ('%s') is not a valid song directory. Starting up FoFiX in standard mode." % playing) engine.startupMessages.append(_("Song directory provided ('%s') is not a valid song directory. Starting up FoFiX in standard mode.") % playing) playing = None # Set up one-shot mode if the invocation is valid for it. if playing is not None: Log.debug('Entering one-shot mode.') Config.set("game", "selected_library", "songs") Config.set("game", "selected_song", playing) engine.cmdPlay = 1 if difficulty is not None: engine.cmdDiff = int(difficulty) if part is not None: engine.cmdPart = int(part) #evilynux - Multiplayer and mode selection support Config.set("game", "players", nbrplayers) if nbrplayers == 1: Config.set("game", "game_mode", mode) else: Config.set("game", "game_mode", 0) Config.set("game", "multiplayer_mode", mode) if debug: engine.setDebugModeEnabled(not engine.isDebugModeEnabled()) engine.debugLayer.debugOut(engine) engine.quit() return encoding = Config.get("game", "encoding") if encoding is not None: #stump: XXX: Everything I have seen indicates that this is a # horrible, horrible hack. Is there another way? Do we even need this? reload(sys) sys.setdefaultencoding(encoding) engine.setStartupLayer(MainMenu(engine)) #stump: make psyco optional if Config.get("performance", "use_psyco"): try: import psyco psyco.profile() except: Log.error("Unable to enable psyco as requested: ") # Run the main game loop. try: engine.ticksAtStart = pygame.time.get_ticks() while engine.run(): pass except KeyboardInterrupt: Log.notice("Left mainloop due to KeyboardInterrupt.") # don't reraise # Restart the program if the engine is asking that we do so. if engine.restartRequested: Log.notice("Restarting.") engine.audio.close() try: # Extra arguments to insert between the executable we call and our # command line arguments. args = [] # Figure out what executable to call. if hasattr(sys, "frozen"): if os.name == "nt": # When py2exe'd, sys.executable is the name of the EXE. exe = os.path.abspath(unicode(sys.executable, sys.getfilesystemencoding())) elif sys.frozen == "macosx_app": # When py2app'd, sys.executable is a Python interpreter copied # into the same dir where we live. exe = os.path.join(os.path.dirname(sys.executable), 'FoFiX') # FIXME: don't hard-code "FoFiX" here else: raise RuntimeError, "Don't know how to restart when sys.frozen is %s" % repr(sys.frozen) else: # When running from source, sys.executable is the Python interpreter # being used to run the program. exe = sys.executable # Pass the optimization level on. if sys.flags.optimize > 0: args.append('-%s' % ('O' * sys.flags.optimize)) args.append(__file__) os.execv(exe, [sys.executable] + args + sys.argv[1:]) except: Log.error("Restart failed: ") raise # evilynux - MainMenu class already calls this - useless? engine.quit() if __name__ == '__main__': try: main() except (KeyboardInterrupt, SystemExit): raise except: Log.error("Terminating due to unhandled exception: ") _logname = os.path.abspath(Log.logFile.name) _errmsg = "%s\n\n%s\n%s\n%s\n%s" % ( _("Terminating due to unhandled exception:"), traceback.format_exc(), _("If you make a bug report about this error, please include the contents of the following log file:"), _logname, _("The log file already includes the traceback given above.")) if os.name == 'nt': import win32api import win32con if win32api.MessageBox(0, "%s\n\n%s" % (_errmsg, _("Open the logfile now?")), "%s %s" % (Version.appNameSexy(), Version.version()), win32con.MB_YESNO|win32con.MB_ICONSTOP) == win32con.IDYES: Log.logFile.close() os.startfile(_logname) if hasattr(sys, 'frozen'): sys.exit(1) # don't reraise if py2exe'd so the "Errors occurred" box won't appear after this and confuse the user as to which logfile we actually want else: print >>sys.stderr, _errmsg raise