diff --git a/CREDITS b/CREDITS index 0a7cfbc3b..a01b062c8 100644 --- a/CREDITS +++ b/CREDITS @@ -109,7 +109,7 @@ Death Legion, kawaii_kumiko69, gamexprt1, acrox999, p_025, treckzy, slash666, mrhoievo, Dillusional, SneakHouse, Borisdsp, Don Tonberry, Gugered, NewCreature, rock8200, Leixner, Trinidude4, ver, -evilbobthebob, deufeufeu, max26199 +evilbobthebob, deufeufeu, max26199, Steven Knapman _%tutorial%_ ======================================================= diff --git a/src/Drum.py b/src/Drum.py index 1aa9fcae0..9c6223e16 100644 --- a/src/Drum.py +++ b/src/Drum.py @@ -195,7 +195,7 @@ def __init__(self, engine, playerObj, editorMode = False, player = 0): #if self.engine.config.get("game", "battle_Tune") == 1: # self.battleObjectsEnabled.append(9) - Log.debug(self.battleObjectsEnabled) + Log.debug("Battle Objects Enabled: "+str(self.battleObjectsEnabled)) self.battleNextObject = 0 self.battleObjects = [0] * 3 self.battleBeingUsed = [0] * 2 diff --git a/src/FoFiX.py b/src/FoFiX.py index 7f559414f..e9d310c4f 100644 --- a/src/FoFiX.py +++ b/src/FoFiX.py @@ -4,6 +4,8 @@ # 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 # @@ -21,9 +23,8 @@ # MA 02110-1301, USA. # ##################################################################### -""" -Main game executable. -""" +##@package FoFiX +# Main game executable. # Register the latin-1 encoding import codecs @@ -34,48 +35,85 @@ assert codecs.lookup("iso-8859-1") assert codecs.lookup("utf-8") -import Config -from GameEngine import GameEngine -from MainMenu import MainMenu -import Log -import Version - +#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 codecs -import Resource -import pygame +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] -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 - --config=, -c [configfile] Use this instead of fofix.ini - --fullscreen=, -f [true/false] Change fullscreen settings in fofix.ini - --resolution=, -r [resolution] Change game resolution from commandline. - --theme, -t (theme) Starts using this theme. Needs to be in " " marks. (IE- -t "Guitar Hero III") - --song=, -s [songdir] Play a song from the commandline - The following settings only apply if "song" is set. - --diff=, -l [level of difficulty] 0: Expert, 1: Hard, 2: Medium, 3: Easy (Only if 'part' is set) + --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] } +""" % {"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) -debuglevel = 0 #MFH - experimental, leave at 0 -import linecache +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() -#if __name__ == "__main__": -def main(): - """Main thread""" +#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 - try: - opts, args = getopt.getopt(sys.argv[1:], "vdc:f:r:t:s:l:p:m:n:", ["verbose", "debug", "config=", "fullscreen=", "resolution=", "theme=", "song=", "diff=", "part=", "mode=", "nbrplayers="]) - except getopt.GetoptError: - print usage - sys.exit(1) - +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 @@ -110,179 +148,152 @@ def main(): mode = int(arg) if opt in ["--nbrplayers", "-n"]: nbrplayers = int(arg) - - while 1: - if configFile != 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: + + # 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 != None: - Config.set("video", "fullscreen", fullscreen) + #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 != None: - Config.set("video", "resolution", resolution) + #Lysdestic - Change resolution from CLI + if resolution is not None: + Config.set("video", "resolution", resolution) - #Lysdestic - Alter theme from CLI - if theme != None: - Config.set("coffee", "themename", theme) - - if playing != None: - 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) - playing = None + #Lysdestic - Alter theme from CLI + if theme is not None: + Config.set("coffee", "themename", theme) - engine = GameEngine(config) - engine.cmdPlay = 0 - - if playing != None: - 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) + engine = GameEngine(config) + engine.cmdPlay = 0 - if debug == True: - engine.setDebugModeEnabled(not engine.isDebugModeEnabled()) - engine.debugLayer.debugOut(engine) - engine.quit() - break - - encoding = Config.get("game", "encoding") - if encoding != None: - reload(sys) - sys.setdefaultencoding(encoding) - engine.setStartupLayer(MainMenu(engine)) + # 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) - #stump: make psyco optional - if Config.get("performance", "use_psyco"): - try: - import psyco - psyco.profile() - except: - Log.warn("Unable to enable psyco.") + 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: - engine.ticksAtStart = pygame.time.get_ticks() - while engine.run(): - pass - except KeyboardInterrupt: - pass - if engine.restartRequested: - Log.notice("Restarting.") - engine.audio.close() - try: - # Determine whether were running from an exe or not - if hasattr(sys, "frozen"): - if os.name == "nt": - os.execl("FoFiX.exe", "FoFiX.exe", *sys.argv[1:]) - elif sys.frozen == "macosx_app": - import string - import subprocess - appname = string.join(string.split(sys.executable, '/')[:-1], '/') - appname = appname+"/FoFiX" - subprocess.Popen(`appname`, shell=True) - else: - os.execl("./FoFiX", "./FoFiX", *sys.argv[1:]) + 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: - # stump: sys.executable points to the active python interpreter - os.execl(sys.executable, sys.executable, "FoFiX.py", *sys.argv[1:]) - except: - Log.warn("Restart failed.") - raise - break - else: - break + 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() -class Trace: - """Class for tracing script - - This class is developed by cyke64. - Lisenced under GNU GPL. - """ - def __init__(self, runfile, f_all=u'traceit.txt', - f_main=u'traceitmain.txt'): - self.out_all=open(f_all, 'w') - self.out_main=open(f_main, 'w') - self.runfile = runfile - - def go(self): - sys.settrace(self.traceit) - - def stop(self): - sys.settrace(None) - self.out_all.close() - self.out_main.close() - - def traceit(self, frame, event, arg): - lineno = frame.f_lineno - name = frame.f_globals['__name__'] - if (frame.f_globals).has_key('__file__'): - file_trace=frame.f_globals['__file__'] - line = linecache.getline(file_trace, lineno) - else: - file_trace = self.runfile - line = linecache.getline(file_trace, lineno) - self.out_main.write('%s*%s*\n*%s*\n' \ - % (event, lineno, line.rstrip())) - self.out_all.write('%s*%s*of %s(%s)\n*%s*\n' \ - %(event, lineno, name, file_trace, line.rstrip())) - e32.ao_sleep(0) - return self.traceit - -def call_callback(func): - """Catch exception - - This function is developed by jethro.fn. - """ - def call_func(*args, **kwds): - import traceback - #global exceptions - #global script_lock - try: - return func(*args, **kwds) - except: - # Collect Exceptions - #exception = ''.join(traceback.format_exception(*sys.exc_info())) - #exceptions.append(exception) - - Log.error("Exception traceback: " + str(traceback.format_exception(*sys.exc_info())) ) - - # Signal lock in main thread (immediate termination) - #if debuglevel == 2: script_lock.signal() - return call_func - if __name__ == '__main__': - try: - if debuglevel == 2: - trace = Trace('FoFiX.py') - trace.go() - call_callback(main()) - trace.stop() - elif debuglevel == 1: - call_callback(main()) - else: - main() - except: - raise + 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 diff --git a/src/Guitar.py b/src/Guitar.py index 52baed592..a787cfafa 100644 --- a/src/Guitar.py +++ b/src/Guitar.py @@ -185,7 +185,7 @@ def __init__(self, engine, playerObj, editorMode = False, player = 0, bass = Fal #if self.engine.config.get("game", "battle_Tune") == 1: # self.battleObjectsEnabled.append(9) - Log.debug(self.battleObjectsEnabled) + Log.debug("Battle Objects Enabled: "+str(self.battleObjectsEnabled)) self.battleNextObject = 0 self.battleObjects = [0] * 3 self.battleBeingUsed = [0] * 2 diff --git a/src/GuitarScene.py b/src/GuitarScene.py index 09c93fdcd..86edf3c6b 100644 --- a/src/GuitarScene.py +++ b/src/GuitarScene.py @@ -65,12 +65,8 @@ from OpenGL.GL import * -#MFH: experimental 2D font rendering module -import lamina - #stump: needed for continuous star fillup -import Image -import ImageDraw +from PIL import Image, ImageDraw from Svg import ImgDrawing #blazingamer: Little fix for RB Score font @@ -727,22 +723,6 @@ def createClient(self, libraryName, songName, Players): self.currentSimpleMidiLyricLine = "" self.noMoreMidiLineLyrics = False - - #self.fontMode = self.engine.config.get("game", "font_rendering_mode") #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # self.laminaScreen = None - # if self.fontMode == 1: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # #self.laminaScreen = lamina.LaminaScreenSurface(0.985) - # self.laminaScreen = lamina.LaminaScreenSurface(1.0) - # self.laminaScreen.clear() - # self.laminaScreen.refresh() - # self.laminaScreen.refreshPosition() - # elif self.fontMode == 2: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # #self.laminaScreen = lamina.LaminaScreenSurface(0.985) - # self.laminaFrame_soloAcc = lamina.LaminaPanelSurface(quadDims=(-1,-1,500,500)) - # self.laminaFrame_soloAcc.surf.fill( (0,0,255) ) - # self.laminaFrame_soloAcc.refresh() - - self.screenCenterX = self.engine.video.screen.get_rect().centerx self.screenCenterY = self.engine.video.screen.get_rect().centery @@ -944,17 +924,11 @@ def createClient(self, libraryName, songName, Players): self.rbOverdriveBarGlowFadeOut = False self.counting = self.engine.config.get("video", "counting") - - #Dialogs.changeLoadingSplashScreenText(self.engine, splash, phrase + " \n " + _("Loading Song...")) - #MFH - this is where song loading originally took place, and the loading screen was spawned. self.engine.resource.load(self, "song", lambda: loadSong(self.engine, songName, library = libraryName, part = [player.part for player in self.playerList], practiceMode = self.playerList[0].practiceMode, practiceSpeed = self.playerList[0].practiceSpeed), synch = True, onLoad = self.songLoaded) # glorandwarf: show the loading splash screen and load the song synchronously - #Dialogs.hideLoadingSplashScreen(self.engine, splash) - #splash = None - #splash = Dialogs.showLoadingSplashScreen(self.engine, phrase) Dialogs.changeLoadingSplashScreenText(self.engine, splash, phrase + " \n " + _("Preparing Note Phrases...")) @@ -1460,11 +1434,6 @@ def createClient(self, libraryName, songName, Players): # evilynux - Load stage background(s) stageMode = self.engine.config.get("game", "stage_mode") - # if stageMode == 3: - # self.stage.loadVideo(self.libraryName, self.songName) - # else: - # if stageMode == 3: - # self.engine.config.set("game", "stage_mode", 0) self.stage.load(self.libraryName, self.songName, self.playerList[0].practiceMode) #MFH - this determination logic should happen once, globally -- not repeatedly. @@ -2316,10 +2285,6 @@ def setCamera(self): self.camera.target = (0.0, 0.0, 4.0) self.camera.origin = (0.0, 3.0*self.boardY, -3.0) - #if self.fontMode == 1: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # self.laminaScreen.refreshPosition() #needs to be called whenever camera position changes - #above does not work...... - def freeResources(self): self.engine.view.setViewport(1,0) @@ -2340,6 +2305,8 @@ def freeResources(self): self.song.tracks = None self.song.eventTracks = None self.song.midiEventTracks = None + if self.whammyEffect == 1: + self.song.resetInstrumentPitch(-1) self.song = None self.rockOff = None @@ -2413,8 +2380,6 @@ def freeResources(self): scoreCard.lastNoteEvent = None if self.coOpType: self.coOpScoreCard.lastNoteEvent = None - if self.whammyEffect == 1: - self.song.resetInstrumentPitch(-1) def getHandicap(self): @@ -3138,48 +3103,13 @@ def updateGuitarSolo(self, playerNum): #soloFont.render(soloText, (0.5 - Tw/2, yOffset),(1, 0, 0),txtSize) #rock band else: #left self.solo_boxXOffset[i] += self.solo_Tw[i]/2 - #soloFont.render(soloText, (xOffset, yOffset),(1, 0, 0),txtSize) #left-justified - - # elif self.fontMode==1: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # #only update if the text will have changed! - # #trying new rendering method... - # tempSurface = self.solo_soloFont.pygameFontRender(self.solo_soloText[i], antialias=False, color=(0,0,255), background=(0,0,0) ) - # # Create a rectangle - # self.soloAcc_Rect[i] = tempSurface.get_rect() - # # Center the rectangle - # self.soloAcc_Rect[i].centerx = self.screenCenterX - # self.soloAcc_Rect[i].centery = self.screenCenterY - # # Blit the text - # #self.engine.video.screen.blit(tempSurface, tempRect) - # self.laminaScreen.surf.blit(tempSurface, self.soloAcc_Rect[i]) - # #self.laminaScreen.refresh() #needs to be called whenever text contents change - # self.laminaScreen.refresh([self.soloAcc_Rect[i]]) #needs to be called whenever text contents change - # #self.laminaScreen.refreshPosition() #needs to be called whenever camera position changes - # #self.laminaScreen.display() - - # elif self.fontMode==2: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # #trying new rendering method... - # tempSurface = self.solo_soloFont.pygameFontRender(self.solo_soloText[i], antialias=False, color=(0,0,255), background=(0,0,0) ) - # # Create a rectangle - # tempRect = tempSurface.get_rect() - # # Center the rectangle - # #tempRect.centerx = self.screenCenterX - # #tempRect.centery = self.screenCenterY - # # Blit the text - # #self.engine.video.screen.blit(tempSurface, tempRect) - # self.laminaFrame_soloAcc.surf.blit(tempSurface, tempRect) - # self.laminaFrame_soloAcc.refresh() self.guitarSoloShown[i] = True else: #not currently a guitar solo - clear Lamina solo accuracy surface (but only once!) if self.guitarSoloShown[i]: self.guitarSoloShown[i] = False - self.currentGuitarSoloLastHitNotes[i] = 1 - # if self.fontMode==1 and self.soloAcc_Rect[i]: - # self.laminaScreen.clear() - # self.laminaScreen.refresh(self.soloAcc_Rect[i]) - + self.currentGuitarSoloLastHitNotes[i] = 1 #MFH - single, global BPM here instead of in instrument objects: @@ -4695,15 +4625,6 @@ def endPick(self, num): self.scoring[num].addScore(scoreTemp) def render3D(self): - if self.engine.config.get("game", "stage_mode") == 3: - if self.countdown <= 0: - if self.pause == True or self.failed == True: - self.stage.vidPlayer.paused = True - else: - self.stage.vidPlayer.paused = False - else: - self.stage.vidPlayer.paused = True - self.stage.render(self.visibility) def renderVocals(self): @@ -9115,16 +9036,6 @@ def render(self, visibility, topMost): #QQstarS: Fix this function for mostly. self.engine.drawImage(self.soloFrame, scale = (tempWScale,tempHScale), coord = (self.wPlayer[i]*self.solo_boxXOffset[i],self.solo_boxYOffset[i])) self.solo_soloFont.render(self.solo_soloText[i], (self.solo_xOffset[i], self.solo_yOffset[i]),(1, 0, 0),self.solo_txtSize) - #self.solo_soloFont.render("test", (0.5,0.0) ) #appears to render text from given position, down / right... - #self.solo_soloFont.render("test", (0.5,0.5) ) #this test confirms that the Y scale is in units relative to the X pixel width - 1280x960 yes but 1280x1024 NO - - #this test locates the constant that the font rendering routine always considers the "bottom" of the screen - #self.solo_soloFont.render("test", (0.5,0.75-self.solo_Th[i]), scale=self.solo_txtSize ) #ah-ha! 4:3 AR viewport = 0.75 max! - - - #self.engine.view.setViewport(1,0) - #except Exception, e: - # Log.warn("Unable to render guitar solo accuracy text: %s" % e) if self.coOpType: #1 BRE in co-op scoreCard = self.coOpScoreCard if i == 0: @@ -9621,12 +9532,6 @@ def render(self, visibility, topMost): #QQstarS: Fix this function for mostly. finally: - # if self.fontMode==1: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # self.laminaScreen.refreshPosition() - # self.laminaScreen.display() - # elif self.fontMode==2: #0 = oGL Hack, 1=LaminaScreen, 2=LaminaFrames - # self.laminaFrame_soloAcc.display() - #self.engine.view.setViewport(1,0) self.engine.view.resetProjection() diff --git a/src/Log.py b/src/Log.py index 4063aa320..c8c45c901 100644 --- a/src/Log.py +++ b/src/Log.py @@ -3,6 +3,7 @@ # # # Frets on Fire # # Copyright (C) 2006 Sami Kyöstilä # +# 2009 John Stumpo # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # @@ -20,15 +21,24 @@ # MA 02110-1301, USA. # ##################################################################### +##@package Log +# Functions for various types of logging that FoFiX needs to do. + import sys import os import Resource import Version +import traceback +import time +import warnings +## Whether to output log entries to stdout in addition to the logfile. quiet = True -if os.name == "posix": # evilynux - logfile in ~/.fretsonfire/ for GNU/Linux and MacOS X + +## File object representing the logfile. +if os.name == "posix": # evilynux - logfile in ~/.fofix/ for GNU/Linux and MacOS X # evilynux - Under MacOS X, put the logs in ~/Library/Logs - if( os.uname()[0] == "Darwin" ): + if os.uname()[0] == "Darwin": logFile = open(os.path.join(Resource.getWritableResourcePath(), "..", "..", "Logs", Version.appName() + ".log"), "w") @@ -36,44 +46,76 @@ logFile = open(os.path.join(Resource.getWritableResourcePath(), Version.appName() + ".log"), "w") else: logFile = open(Version.appName() + ".log", "w") #MFH - local logfile! - + +## Character encoding to use for logging. encoding = "iso-8859-1" -if "-v" in sys.argv: +if "-v" in sys.argv or "--verbose" in sys.argv: quiet = False - + +## Labels for different priorities, as output to the logfile. +labels = { + "warn": "(W)", + "debug": "(D)", + "notice": "(N)", + "error": "(E)", +} + +## Labels for different priorities, as output to stdout. if os.name == "posix": - labels = { + displaylabels = { "warn": "\033[1;33m(W)\033[0m", "debug": "\033[1;34m(D)\033[0m", "notice": "\033[1;32m(N)\033[0m", "error": "\033[1;31m(E)\033[0m", } else: - labels = { - "warn": "(W)", - "debug": "(D)", - "notice": "(N)", - "error": "(E)", - } + displaylabels = labels -def log(cls, msg): - msg = unicode(msg).encode(encoding, "ignore") +## Generic logging function. +# @param cls Priority class for the message +# @param msg Log message text +def _log(cls, msg): + if not isinstance(msg, unicode): + msg = unicode(msg, encoding).encode(encoding, "ignore") + timeprefix = "[%12.6f] " % (time.time() - _initTime) if not quiet: - print labels[cls] + " " + msg - tempTrace = None - if cls == "error": - import traceback - tempTrace = traceback.format_exc() - if tempTrace: - if not quiet: - print tempTrace - print >>logFile, labels[cls] + " " + msg + tempTrace - logFile.flush() #stump: truncated tracebacks be gone! + print timeprefix + displaylabels[cls] + " " + msg + print >>logFile, timeprefix + labels[cls] + " " + msg + logFile.flush() #stump: truncated logfiles be gone! + +## Log a major error. +# If this is called while handling an exception, the traceback will +# be automatically included in the log. +# @param msg Error message text +def error(msg): + if sys.exc_info() == (None, None, None): + #warnings.warn("Log.error() called without an active exception", UserWarning, 2) #stump: should we enforce this? + _log("error", msg) else: - print >>logFile, labels[cls] + " " + msg + _log("error", msg + "\n" + traceback.format_exc()) + +## Log a warning. +# @param msg Warning message text +def warn(msg): + _log("warn", msg) + +## Log a notice. +# @param msg Notice message text +def notice(msg): + _log("notice", msg) + +## Log a debug message. +# @param msg Debug message text +def debug(msg): + _log("debug", msg) + +## A hook to catch Python warnings. +def _showwarning(*args, **kw): + warn("A Python warning was issued:\n" + warnings.formatwarning(*args, **kw)) + _old_showwarning(*args, **kw) +_old_showwarning = warnings.showwarning +warnings.showwarning = _showwarning -warn = lambda msg: log("warn", msg) -debug = lambda msg: log("debug", msg) -notice = lambda msg: log("notice", msg) -error = lambda msg: log("error", msg) +_initTime = time.time() +debug("Logging initialized: " + time.asctime()) diff --git a/src/MainMenu.py b/src/MainMenu.py index d3cd2f533..6f1a6f959 100644 --- a/src/MainMenu.py +++ b/src/MainMenu.py @@ -42,8 +42,7 @@ import Player import Version from Shader import shaders - -#myfingershurt: needed for multi-OS file fetching +import sys import os #myfingershurt: needed for random menu music: @@ -304,6 +303,9 @@ def __init__(self, engine): engine.mainMenu = self #Points engine.mainMenu to the one and only MainMenu object instance + ## whether the main menu has come into view at least once + self.shownOnce = False + def settingsMenu(self): if self.engine.advSettings: self.settingsMenuObject = Settings.SettingsMenu(self.engine) @@ -315,7 +317,19 @@ def shown(self): self.engine.view.pushLayer(self.menu) self.engine.stopServer() shaders.checkIfEnabled() - + if not self.shownOnce: + self.shownOnce = True + if hasattr(sys, 'frozen'): + #stump: Check whether this is a non-svn binary being run from an svn working copy. + if os.path.isdir(os.path.join('src', '.svn')) and 'development' not in Version.version(): + Dialogs.showMessage(self.engine, _('This binary release is being run from a Subversion working copy. This is not the correct way to run FoFiX from Subversion. Please see one of the following web pages to set your Subversion working copy up correctly:') + + '\n\nhttp://code.google.com/p/fofix/wiki/RunningUnderPython26' + + '\nhttp://code.google.com/p/fofix/wiki/RequiredSourceModules') + #stump: Check whether this is an svn binary not being run from an svn working copy + elif not os.path.isdir(os.path.join('src', '.svn')) and 'development' in Version.version(): + Dialogs.showMessage(self.engine, _('This binary was built from a Subversion working copy but is not running from one. The FoFiX Team will not provide any support whatsoever for this binary. Please see the following site for official binary releases:') + + '\n\nhttp://code.google.com/p/fofix/') + def runMusic(self): if not self.song.isPlaying(): #re-randomize if self.files: diff --git a/src/Stage.py b/src/Stage.py index 36d22f8f2..5bd342c11 100644 --- a/src/Stage.py +++ b/src/Stage.py @@ -34,215 +34,12 @@ from Language import _ import Version # Provides dataPath -# try: -# from VideoPlayer import VideoPlayer -# videoAvailable = True -# except: -# videoAvailable = False - -class Layer(object): - """ - A graphical stage layer that can have a number of animation effects associated with it. - """ - def __init__(self, stage, drawing): - """ - Constructor. - - @param stage: Containing Stage - @param drawing: SvgDrawing for this layer. Make sure this drawing is rendered to - a texture for performance reasons. - """ - self.stage = stage - self.drawing = drawing - self.position = (0.0, 0.0) - self.angle = 0.0 - self.scale = (1.0, 1.0) - self.color = (1.0, 1.0, 1.0, 1.0) - self.srcBlending = GL_SRC_ALPHA - self.dstBlending = GL_ONE_MINUS_SRC_ALPHA - self.effects = [] - - def render(self, visibility): - """ - Render the layer. - - @param visibility: Floating point visibility factor (1 = opaque, 0 = invisibile) - """ - w, h, = self.stage.engine.view.geometry[2:4] - v = 1.0 - self.drawing.transform.reset() - self.drawing.transform.translate(w / 2, h / 2) - if v > .01: - self.color = (self.color[0], self.color[1], self.color[2], visibility) - if self.position[0] < -.25: - self.drawing.transform.translate(-v * w, 0) - elif self.position[0] > .25: - self.drawing.transform.translate(v * w, 0) - self.drawing.transform.scale(self.scale[0], -self.scale[1]) - self.drawing.transform.translate(self.position[0] * w / 2, -self.position[1] * h / 2) - self.drawing.transform.rotate(self.angle) - - # Blend in all the effects - for effect in self.effects: - effect.apply() - - glBlendFunc(self.srcBlending, self.dstBlending) - self.drawing.draw(color = self.color) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - -class Effect(object): - """ - An animationn effect that can be attached to a Layer. - """ - def __init__(self, layer, options): - """ - Constructor. - - @param layer: Layer to attach this effect to. - @param options: Effect options (default in parens): - intensity - Floating point effect intensity (1.0) - trigger - Effect trigger, one of "none", "beat", - "quarterbeat", "pick", "miss" ("none") - period - Trigger period in ms (200.0) - delay - Trigger delay in periods (0.0) - profile - Trigger profile, one of "step", "linstep", - "smoothstep" - """ - self.layer = layer - self.stage = layer.stage - self.intensity = float(options.get("intensity", 1.0)) - self.trigger = getattr(self, "trigger" + options.get("trigger", "none").capitalize()) - self.period = float(options.get("period", 500.0)) - self.delay = float(options.get("delay", 0.0)) - self.triggerProf = getattr(self, options.get("profile", "linstep")) - - def apply(self): - pass - - def triggerNone(self): - return 0.0 - - def triggerBeat(self): - if not self.stage.lastBeatPos: - return 0.0 - t = self.stage.pos - self.delay * self.stage.beatPeriod - self.stage.lastBeatPos - return self.intensity * (1.0 - self.triggerProf(0, self.stage.beatPeriod, t)) - - def triggerQuarterbeat(self): - if not self.stage.lastQuarterBeatPos: - return 0.0 - t = self.stage.pos - self.delay * (self.stage.beatPeriod / 4) - self.stage.lastQuarterBeatPos - return self.intensity * (1.0 - self.triggerProf(0, self.stage.beatPeriod / 4, t)) - - def triggerPick(self): - if not self.stage.lastPickPos: - return 0.0 - t = self.stage.pos - self.delay * self.period - self.stage.lastPickPos - return self.intensity * (1.0 - self.triggerProf(0, self.period, t)) - - def triggerMiss(self): - if not self.stage.lastMissPos: - return 0.0 - t = self.stage.pos - self.delay * self.period - self.stage.lastMissPos - return self.intensity * (1.0 - self.triggerProf(0, self.period, t)) - - def step(self, threshold, x): - return (x > threshold) and 1 or 0 - - def linstep(self, min, max, x): - if x < min: - return 0 - if x > max: - return 1 - return (x - min) / (max - min) - - def smoothstep(self, min, max, x): - if x < min: - return 0 - if x > max: - return 1 - def f(x): - return -2 * x**3 + 3*x**2 - return f((x - min) / (max - min)) - - def sinstep(self, min, max, x): - return math.cos(math.pi * (1.0 - self.linstep(min, max, x))) - - def getNoteColor(self, note): - if note >= len(Theme.fretColors) - 1: - return Theme.fretColors[-1] - elif note <= 0: - return Theme.fretColors[0] - f2 = note % 1.0 - f1 = 1.0 - f2 - c1 = Theme.fretColors[int(note)] - c2 = Theme.fretColors[int(note) + 1] - return (c1[0] * f1 + c2[0] * f2, \ - c1[1] * f1 + c2[1] * f2, \ - c1[2] * f1 + c2[2] * f2) - -class LightEffect(Effect): - def __init__(self, layer, options): - Effect.__init__(self, layer, options) - self.lightNumber = int(options.get("light_number", 0)) - self.ambient = float(options.get("ambient", 0.5)) - self.contrast = float(options.get("contrast", 0.5)) - - def apply(self): - if len(self.stage.averageNotes) < self.lightNumber + 2: - self.layer.color = (0.0, 0.0, 0.0, 0.0) - return - - t = self.trigger() - t = self.ambient + self.contrast * t - c = self.getNoteColor(self.stage.averageNotes[self.lightNumber]) - self.layer.color = (c[0] * t, c[1] * t, c[2] * t, self.intensity) - -class RotateEffect(Effect): - def __init__(self, layer, options): - Effect.__init__(self, layer, options) - self.angle = math.pi / 180.0 * float(options.get("angle", 45)) - - def apply(self): - if not self.stage.lastMissPos: - return - - t = self.trigger() - self.layer.drawing.transform.rotate(t * self.angle) - -class WiggleEffect(Effect): - def __init__(self, layer, options): - Effect.__init__(self, layer, options) - self.freq = float(options.get("frequency", 6)) - self.xmag = float(options.get("xmagnitude", 0.1)) - self.ymag = float(options.get("ymagnitude", 0.1)) - - def apply(self): - t = self.trigger() - - w, h = self.stage.engine.view.geometry[2:4] - p = t * 2 * math.pi * self.freq - s, c = t * math.sin(p), t * math.cos(p) - self.layer.drawing.transform.translate(self.xmag * w * s, self.ymag * h * c) - -class ScaleEffect(Effect): - def __init__(self, layer, options): - Effect.__init__(self, layer, options) - self.xmag = float(options.get("xmagnitude", .1)) - self.ymag = float(options.get("ymagnitude", .1)) - - def apply(self): - t = self.trigger() - self.layer.drawing.transform.scale(1.0 + self.xmag * t, 1.0 + self.ymag * t) class Stage(object): def __init__(self, guitarScene, configFileName): self.scene = guitarScene self.engine = guitarScene.engine self.config = Config.MyConfigParser() - self.backgroundLayers = [] - self.foregroundLayers = [] - self.textures = {} self.reset() self.wFull = None #MFH - needed for new stage background handling @@ -286,75 +83,6 @@ def __init__(self, guitarScene, configFileName): Log.warn("Stage folder does not exist: %s" % self.pathfull) self.mode = 1 # Fallback to song-specific stage suffix = ".jpg" - - # Build the layers - for i in range(32): - section = "layer%d" % i - if self.config.has_section(section): - def get(value, type = str, default = None): - if self.config.has_option(section, value): - return type(self.config.get(section, value)) - return default - - xres = get("xres", int, 256) - yres = get("yres", int, 256) - texture = get("texture") - - try: - drawing = self.textures[texture] - except KeyError: - drawing = self.engine.loadImgDrawing(self, None, os.path.join("themes", self.themename, texture), textureSize = (xres, yres)) - self.textures[texture] = drawing - - layer = Layer(self, drawing) - - layer.position = (get("xpos", float, 0.0), get("ypos", float, 0.0)) - layer.scale = (get("xscale", float, 1.0), get("yscale", float, 1.0)) - layer.angle = math.pi * get("angle", float, 0.0) / 180.0 - layer.srcBlending = globals()["GL_%s" % get("src_blending", str, "src_alpha").upper()] - layer.dstBlending = globals()["GL_%s" % get("dst_blending", str, "one_minus_src_alpha").upper()] - layer.color = (get("color_r", float, 1.0), get("color_g", float, 1.0), get("color_b", float, 1.0), get("color_a", float, 1.0)) - - # Load any effects - fxClasses = { - "light": LightEffect, - "rotate": RotateEffect, - "wiggle": WiggleEffect, - "scale": ScaleEffect, - } - - for j in range(32): - fxSection = "layer%d:fx%d" % (i, j) - if self.config.has_section(fxSection): - type = self.config.get(fxSection, "type") - - if not type in fxClasses: - continue - - options = self.config.options(fxSection) - options = dict([(opt, self.config.get(fxSection, opt)) for opt in options]) - - fx = fxClasses[type](layer, options) - layer.effects.append(fx) - - if get("foreground", int): - self.foregroundLayers.append(layer) - else: - self.backgroundLayers.append(layer) - -# def loadVideo(self, libraryName, songName): -# if not videoAvailable: -# return -1 -# if self.songStage == 1 and os.path.exists(os.path.join(libraryName, songName, "video.mp4")): -# vidSource = os.path.join(libraryName, songName, "video.mp4") -# else: -# vidSource = os.path.join(Version.dataPath(), "video.mp4") - -# winWidth, winHeight = (self.engine.view.geometry[2], self.engine.view.geometry[3]) -# self.vidPlayer = VideoPlayer(-1, vidSource, (winWidth, winHeight), -# mute = True, loop = True) -# self.engine.view.pushLayer(self.vidPlayer) -# self.vidPlayer.paused = True def load(self, libraryName, songName, practiceMode = False): # evilynux - Fixes a self.background not defined crash @@ -567,14 +295,6 @@ def triggerBeat(self, pos, beat): self.beat = beat self.averageNotes = self.averageNotes[-4:] + self.averageNotes[-1:] - def _renderLayers(self, layers, visibility): - self.engine.view.setOrthogonalProjection(normalize = True) - try: - for layer in layers: - layer.render(visibility) - finally: - self.engine.view.resetProjection() - def run(self, pos, period): self.pos = pos self.beatPeriod = period @@ -591,7 +311,6 @@ def run(self, pos, period): def render(self, visibility): if self.mode != 3: self.renderBackground() - self._renderLayers(self.backgroundLayers, visibility) if shaders.enable("stage"): height = 0.0 for i in shaders.var["color"].keys(): @@ -611,6 +330,4 @@ def render(self, visibility): shaders.disable() self.scene.renderGuitar() - self._renderLayers(self.foregroundLayers, visibility) - diff --git a/src/Svg.py b/src/Svg.py index cc9ccf5ea..5b31877b5 100644 --- a/src/Svg.py +++ b/src/Svg.py @@ -32,7 +32,7 @@ import Log import Config from Texture import Texture, TextureException -import Image +from PIL import Image import DummyAmanith as amanith haveAmanith = True diff --git a/src/Texture.py b/src/Texture.py index e399c4c72..53b3f16e1 100644 --- a/src/Texture.py +++ b/src/Texture.py @@ -24,11 +24,10 @@ import Log import Config -import Image +from PIL import Image import pygame import StringIO -import PngImagePlugin -import JpegImagePlugin + from OpenGL.GL import * from OpenGL.GLU import * diff --git a/src/Version.py b/src/Version.py index abf241b96..34ddb8ec3 100644 --- a/src/Version.py +++ b/src/Version.py @@ -22,7 +22,7 @@ import sys import os -VERSION = '3.120' +VERSION = '3.121' URL = 'http://fofix.googlecode.com' def appName(): @@ -34,9 +34,9 @@ def appNameSexy(): def revision(): import svntag try: - revision = "alpha (r%d)" % int(svntag.get_svn_info(os.path.dirname(__file__))['revnum']) + revision = " Maintenance (r%d)" % int(svntag.get_svn_info(os.path.dirname(__file__))['revnum']) except: - revision = "Final" + revision = " Final" return revision # evilynux: Returns version number w.r.t. frozen state @@ -48,9 +48,9 @@ def version(): us = os.path.abspath(unicode(sys.executable, sys.getfilesystemencoding())) version = win32api.GetFileVersionInfo(us, r'\StringFileInfo\%04x%04x\ProductVersion' % win32api.GetFileVersionInfo(us, r'\VarFileInfo\Translation')[0]) else: - version = VERSION + version = "%s%s" % ( VERSION, revision() ) else: - version = "%s %s" % ( VERSION, revision() ) + version = "%s%s" % ( VERSION, revision() ) return version def dataPath(): diff --git a/src/VideoPlayer.py b/src/VideoPlayer.py deleted file mode 100644 index 252c6af52..000000000 --- a/src/VideoPlayer.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python -# -*- coding: iso-8859-1 -*- -##################################################################### -# Video Playback Layer for FoFiX # -# Copyright (C) 2009 Pascal Giard # -# # -# 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. # -##################################################################### -import os -import sys -import gobject - -from math import fabs as abs # Absolute value - -# Almighty GStreamer -import gst -from gst.extend import discoverer # Video property detection - -import pygame -from pygame.locals import * - -from OpenGL.GL import * -from OpenGL.GLU import * -# Array-based drawing -from numpy import array, float32 - -from View import View, BackgroundLayer -import Log - -# Simple video player -class VideoPlayer(BackgroundLayer): - def __init__(self, framerate, vidSource, (winWidth, winHeight) = (None, None), mute = False, loop = False): - self.updated = False - self.videoList = None - self.videoTex = None - self.videoBuffer = None - self.videoSrc = vidSource - self.mute = mute - self.loop = loop - if winWidth is not None and winHeight is not None: - self.winWidth, self.winHeight = winWidth, winHeight - else: # default - self.winWidth, self.winHeight = (640, 480) - Log.warning("VideoPlayer: No resolution specified (default %dx%d)", - self.winWidth, self.winHeight) - self.vidWidth, self.vidHeight = -1, -1 - self.fps = framerate - self.clock = pygame.time.Clock() - self.paused = False - self.finished = False - self.discovered = False - self.loadVideo(vidSource) # Load the video - - # Load a new video: - # 1) Detect video resolution - # 2) Setup OpenGL texture - # 3) Setup GStreamer pipeline - def loadVideo(self, vidSource): - if not os.path.exists(vidSource): - Log.error("Video %s does not exist!" % vidSource) - self.videoSrc = vidSource - d = discoverer.Discoverer(self.videoSrc) - d.connect('discovered', self.videoDiscover) - d.discover() - gobject.threads_init() # Start C threads - while not self.discovered: - # Force C threads iteration - gobject.MainLoop().get_context().iteration(True) - self.textureSetup() - self.videoSetup() - - # Use GStreamer's video discoverer to autodetect video properties - def videoDiscover(self, d, isMedia): - if isMedia and d.is_video: - self.vidWidth, self.vidHeight = d.videowidth, d.videoheight - # Force mute if no sound track is available or - # else you'll get nothing but a black screen! - if not d.is_audio and not self.mute: - Log.warn("Video has no sound ==> forcing mute.") - self.mute = True - else: - Log.error("Invalid video file: %s" % self.videoSrc) - self.discovered = True - - def textureSetup(self): - blankSurface = pygame.Surface((self.vidWidth, self.vidHeight), - HWSURFACE, 24) - blankSurface.fill((0,0,0)) - - surfaceData = pygame.image.tostring(blankSurface, "RGB", True) - self.videoBuffer = surfaceData - self.videoTex = glGenTextures(1) - glBindTexture(GL_TEXTURE_2D, self.videoTex) - gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, - self.vidWidth, self.vidHeight, GL_RGB, - GL_UNSIGNED_BYTE, surfaceData) - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MIN_FILTER, GL_LINEAR) - - # Resize video (polygon) to respect resolution ratio - # (The math is actually simple, take the time to draw it down if required) - winRes = float(self.winWidth)/float(self.winHeight) - vidRes = float(self.vidWidth)/float(self.vidHeight) - vtxX = 1.0 - vtxY = 1.0 - if winRes > vidRes: - r = float(self.winHeight)/float(self.vidHeight) - vtxX = 1.0 - abs(self.winWidth-r*self.vidWidth) / (float(self.winWidth)) - elif winRes < vidRes: - r = float(self.winWidth)/float(self.vidWidth) - vtxY = 1.0 - abs(self.winHeight-r*self.vidHeight) / (float(self.winHeight)) - - # Vertices - videoVtx = array([[-vtxX, vtxY], - [ vtxX, -vtxY], - [ vtxX, vtxY], - [-vtxX, vtxY], - [-vtxX, -vtxY], - [ vtxX, -vtxY]], dtype=float32) - # Texture coordinates - videoTex = array([[0.0, 1.0], - [1.0, 0.0], - [1.0, 1.0], - [0.0, 1.0], - [0.0, 0.0], - [1.0, 0.0]], dtype=float32) - - # Create a compiled OpenGL call list and do array-based drawing - # Could have used GL_QUADS but IIRC triangles are recommended - self.videoList = glGenLists(1) - glNewList(self.videoList, GL_COMPILE) - glEnable(GL_TEXTURE_2D) - glColor3f(1., 1., 1.) - glEnableClientState(GL_VERTEX_ARRAY) - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointerf(videoVtx) - glTexCoordPointerf(videoTex) - glDrawArrays(GL_TRIANGLE_STRIP, 0, videoVtx.shape[0]) - glDisableClientState(GL_VERTEX_ARRAY) - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_TEXTURE_2D) - glEndList() - - # Setup GStreamer's pipeline - # Note: playbin2 seems also suitable, we might want to experiment with it - # if decodebin is proven problematic - def videoSetup(self): - with_audio = "" - if not self.mute: - with_audio = "! queue ! audioconvert ! audiorate ! audioresample ! autoaudiosink" - s = "filesrc name=input ! decodebin2 name=dbin dbin. ! ffmpegcolorspace ! video/x-raw-rgb ! fakesink name=output signal-handoffs=true sync=true dbin. %s" % with_audio - self.player = gst.parse_launch(s) - self.input = self.player.get_by_name('input') - self.fakeSink = self.player.get_by_name('output') - self.input.set_property("location", self.videoSrc) - self.fakeSink.connect ("handoff", self.newFrame) - # Catch the end of file as well as errors - # FIXME: - # Messages are sent if i use the following in run(): - # gobject.MainLoop().get_context().iteration(True) - # BUT the main python thread then freezes after ~5 seconds... - # unless we use gobject.idle_add(self.player.elements) - bus = self.player.get_bus() - bus.add_signal_watch() - bus.enable_sync_message_emission() - bus.connect("message", self.onMessage) - # Required to prevent the main python thread from freezing, why?! - # Thanks to max26199 for finding this! - gobject.idle_add(self.player.elements) - - # Handle bus event e.g. end of video or unsupported formats/codecs - def onMessage(self, bus, message): - type = message.type -# print "Message %s" % type - # End of video - if type == gst.MESSAGE_EOS: - if self.loop: - self.player.set_state(gst.STATE_NULL) - # HACKISH: Need to recreate the pipepile altogether... - # For some reason going through STATE_NULL, STATE_READY, STATE_PLAYING - # doesn't work as I would expect. - self.videoSetup() - else: - self.player.set_state(gst.STATE_NULL) - self.finished = True - # Error - elif type == gst.MESSAGE_ERROR: - err, debug = message.parse_error() - Log.error("GStreamer error: %s" % err, debug) - self.player.set_state(gst.STATE_NULL) - self.finished = True - elif type == gst.MESSAGE_WARNING: - warning, debug = message.parse_warning() - Log.warn("GStreamer warning: %s" % warning, debug) - # elif type == gst.MESSAGE_STATE_CHANGED: - # oldstate, newstate, pending = message.parse_state_changed() - # Log.debug("GStreamer state: %s" % newstate) - - # Handle new video frames coming from the decoder - def newFrame(self, sink, buffer, pad): - self.videoBuffer = buffer - self.updated = True - - def textureUpdate(self): - if self.updated: - img = pygame.image.frombuffer(self.videoBuffer, - (self.vidWidth, self.vidHeight), - 'RGB') - glBindTexture(GL_TEXTURE_2D, self.videoTex) - surfaceData = pygame.image.tostring(img ,'RGB', True) - # Use linear filtering - glTexImage2D(GL_TEXTURE_2D, 0, 3, self.vidWidth, self.vidHeight, 0, - GL_RGB, GL_UNSIGNED_BYTE, surfaceData) - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, - GL_TEXTURE_MIN_FILTER, GL_LINEAR) - self.updated = False - - def shown(self): - gobject.threads_init() - - def hidden(self): - self.player.set_state(gst.STATE_NULL) - - def run(self, ticks = None): - if self.paused == True: - self.player.set_state(gst.STATE_PAUSED) - else: - self.player.set_state(gst.STATE_PLAYING) - self.finished = False - gobject.MainLoop().get_context().iteration(True) - self.clock.tick(self.fps) - - # Render texture to polygon - # Note: Both visibility and topMost are currently unused. - def render(self, visibility = 1.0, topMost = False): - try: - self.textureUpdate() - # Save and clear both transformation matrices - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - # Draw the polygon and apply texture - glBindTexture(GL_TEXTURE_2D, self.videoTex) - glCallList(self.videoList) - # Restore both transformation matrices - glPopMatrix() - glMatrixMode(GL_PROJECTION) - glPopMatrix() - except: - Log.error("Error attempting to play video") diff --git a/src/VideoPlayerTest.py b/src/VideoPlayerTest.py deleted file mode 100644 index aa9c368cc..000000000 --- a/src/VideoPlayerTest.py +++ /dev/null @@ -1,152 +0,0 @@ -##################################################################### -# -*- coding: iso-8859-1 -*- # -# # -# FoFiX # -# Copyright (C) 2009 Pascal Giard # -# # -# 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. # -##################################################################### - -import unittest -import os -import pygame -from pygame.locals import * -from OpenGL.GL import * - -from GameEngine import GameEngine -import Config -import Version -from VideoPlayer import VideoPlayer - -# Please change these values to fit your needs. -# Note that video codecs/format supported depends on the gstreamer plugins -# you've installed. On my machine that means pretty much anything e.g. XviD, -# Ogg Theora, Flash, FFmpeg H.264, etc.). -framerate = 24 # Number of frames per seconds (-1 = as fast as it can) -# vidSource # Path to video; relative to FoFiX data/ directory - -# FIXME: Should autodetect video width and height values. - -# Video examples -#===================== -# XviD/MPEG-4 -vidSource = "t1.avi" - -# FFmpeg H.264 -# vidSource = "t2.m4v" - -# Macromedia Flash -# vidSource = "t3.flv" -# vidSource = "t3.1.flv" # 24 seconds - -# Xiph Ogg Theora -# vidSource = "t4.ogv" - -class VideoPlayerTest(unittest.TestCase): - # Simplest way to use the video player, use it as a Layer - def testVideoPlayerLayer(self): - config = Config.load(Version.appName() + ".ini", setAsDefault = True) - self.e = GameEngine(config) - winWidth, winHeight = (self.e.view.geometry[2], self.e.view.geometry[3]) - vidPlayer = VideoPlayer(framerate, self.src, (winWidth, winHeight), - loop = False) - self.e.view.pushLayer(vidPlayer) - while not vidPlayer.finished: - self.e.run() - self.e.view.popLayer(vidPlayer) - self.e.audio.close() - self.e.quit() - - # Keep tight control over the video player - def testVideoPlayerSlave(self): - winWidth, winHeight = 800, 600 - pygame.init() - flags = DOUBLEBUF|OPENGL|HWPALETTE|HWSURFACE - pygame.display.set_mode((winWidth, winHeight), flags) - vidPlayer = VideoPlayer(framerate, self.src, (winWidth, winHeight)) - glViewport(0, 0, winWidth, winHeight) # Both required as... - glScissor(0, 0, winWidth, winHeight) # ...GameEngine changes it - glClearColor(0, 0, 0, 1.) - while not vidPlayer.finished: - vidPlayer.run() - vidPlayer.render() - pygame.display.flip() - pygame.quit() - - # Grab the texture, use the CallList and do whatever we want with it; - # We could also _just_ use the texture and take care of the polygons ourselves - def testVideoPlayerSlaveShowOff(self): - winWidth, winHeight = 500, 500 - pygame.init() - flags = DOUBLEBUF|OPENGL|HWPALETTE|HWSURFACE - pygame.display.set_mode((winWidth, winHeight), flags) - vidPlayer = VideoPlayer(-1, self.src, (winWidth, winHeight)) - glViewport(0, 0, winWidth, winHeight) # Both required as... - glScissor(0, 0, winWidth, winHeight) # ...GameEngine changes it - glClearColor(0, 0, 0, 1.) - x, y = 0.0, 1.0 - fx, fy, ftheta = 1, 1, 1 - theta = 0.0 - time = 0.0 - clock = pygame.time.Clock() - while not vidPlayer.finished: - vidPlayer.run() - vidPlayer.textureUpdate() - # Save and clear both transformation matrices - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - glClear(GL_COLOR_BUFFER_BIT) - glColor3f(1., 1., 1.) - glBindTexture(GL_TEXTURE_2D, vidPlayer.videoTex) - glTranslatef(x, y, 0) - glRotatef(theta, 0, 0, 1.) - glScalef(.5, .5, 1.) - glCallList(vidPlayer.videoList) - - # Restore both transformation matrices - glPopMatrix() - glMatrixMode(GL_PROJECTION) - glPopMatrix() - - pygame.display.flip() - - x = (x + fx*time) - y = (y + fy*time) - theta = theta + ftheta - if x > 1.0 or x < -1.0: - fx = fx * -1 - if y > 1.0 or y < -1.0: - fy = fy * -1 - if theta > 90 or theta < -90: - ftheta = ftheta * -1 - time = time + 0.00001 - clock.tick(60) - pygame.quit() - - def setUp(self): - self.src = os.path.join(Version.dataPath(), vidSource) - self.assert_(os.path.exists(self.src), "File %s does not exist!" % self.src) - - def tearDown(self): - pass - -if __name__ == "__main__": - unittest.main() diff --git a/src/Vocalist.py b/src/Vocalist.py index 4ec36710c..3bf6523f0 100644 --- a/src/Vocalist.py +++ b/src/Vocalist.py @@ -33,8 +33,7 @@ import Theme #stump: needed for continuous star fillup (akedrou - stealing for vocals) -import Image -import ImageDraw +from PIL import Image, ImageDraw from Svg import ImgDrawing diffMod = {0: 1.4, 1: 1.6, 2: 1.75, 3: 1.9} diff --git a/src/lamina.py b/src/lamina.py deleted file mode 100644 index e775aad46..000000000 --- a/src/lamina.py +++ /dev/null @@ -1,325 +0,0 @@ -""" -Lamina module - -When you set the display mode for OpenGL, you enable all the coolness of 3D rendering, -but you disable the bread-and-butter raster SDL functionality like fill() and blit(). -Since the GUI libraries use those surface methods extensively, they cannot readily be -used in OpenGL displays. - -Lamina provides the LaminaPanelSurface and LaminaScreenSurface classes, which bridge -between the two. - -The 'surf' attribute is a surface, and can be drawn on, blitted to, and passed to GUI -rendering functions for alteration. The 'display' method displays the surface as a -transparent textured quad in the OpenGL model-space. The 'refresh' method indicates that -the surface has changed, and that the texture needs regeneration. The 'clear' method -restores the blank and transparent original condition. - -Usage is vaguely like this incomplete pseudocode: - - # create gui with appropriate constructor - gui = GUI_Constructor() - - # create LaminaPanelSurface - gui_screen = lamina.LaminaPanelSurface( (640,480), (-1,1,2,2) ) - - # draw widgets on surface - gui.draw( gui_screen.surf ) - - # hide mouse cursor - pygame.mouse.set_visible(0) - - while 1: - - # do input events .... - # pass events to gui - gui.doevent(...) - - # detect changes to surface - changed = gui.update( gui_screen.surf ) - if changed: - # and mark for update - gui_screen.refresh() - - # draw opengl geometry ..... - - # display gui - # opengl code to set modelview matrix for desired gui surface pos - gui_screen.display() - - -If your gui screen is not sized to match the display, hide the system -mouse cursor, and use the convertMousePos method to position your own -OpenGL mouse cursor (a cone or something). The result of the -convertMousePos is 2D, (x,y) so you need to draw the mouse with the same -modelview matrix as drawing the LaminaPanelSurface itself. - - mouse_pos = gui_screen.convertMousePos(mouseX, mouseY) - glTranslate(*mouse_pos) - # opengl code to display your mouse object (a cone?) - - -The distribution package includes several demo scripts which are functional. Refer -to them for details of working code. -""" - -import OpenGL.GLU as oglu -import OpenGL.GL as ogl - -import pygame -import math - -def load_texture(surf): - """Load surface into texture object. Return texture object. - @param surf: surface to make texture from. - """ - txtr = ogl.glGenTextures(1) - textureData = pygame.image.tostring(surf, "RGBA", 1) - - ogl.glEnable(ogl.GL_TEXTURE_2D) - ogl.glBindTexture(ogl.GL_TEXTURE_2D, txtr) - width, height = surf.get_size() - ogl.glTexImage2D( ogl.GL_TEXTURE_2D, 0, ogl.GL_RGBA, width, height, 0, - ogl.GL_RGBA, ogl.GL_UNSIGNED_BYTE, textureData ) - ogl.glTexParameterf(ogl.GL_TEXTURE_2D, ogl.GL_TEXTURE_MAG_FILTER, ogl.GL_NEAREST) - ogl.glTexParameterf(ogl.GL_TEXTURE_2D, ogl.GL_TEXTURE_MIN_FILTER, ogl.GL_NEAREST) - ogl.glDisable(ogl.GL_TEXTURE_2D) - return txtr - -def overlay_texture(txtr, surf, r): - """Load surface into texture object, replacing part of txtr - given by rect r. - @param txtr: texture to add to - @param surf: surface to copy from - @param r: rectangle indicating area to overlay. - """ - subsurf = surf.subsurface(r) - textureData = pygame.image.tostring(subsurf, "RGBA", 1) - - hS, wS = surf.get_size() - rect = pygame.Rect(r.x,hS-(r.y+r.height),r.width,r.height) - - ogl.glEnable(ogl.GL_TEXTURE_2D) - ogl.glBindTexture(ogl.GL_TEXTURE_2D, txtr) - ogl.glTexSubImage2D(ogl.GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width, rect.height, - ogl.GL_RGBA, ogl.GL_UNSIGNED_BYTE, textureData ) - ogl.glDisable(ogl.GL_TEXTURE_2D) - -class LaminaPanelSurface(object): - """Surface for imagery to overlay. - @ivar surf: surface - @ivar dims: tuple with corners of quad - """ - - def __init__(self, quadDims=(-1,-1,2,2), winSize=None): - """Initialize new instance. - @param winSize: tuple (width, height) - @param quadDims: tuple (left, top, width, height) - """ - if not winSize: - winSize = pygame.display.get_surface().get_size() - self._txtr = None - self._winSize = winSize - left, top, width, height = quadDims - right, bottom = left+width, top-height - self._qdims = quadDims - self.dims = (left,top,0), (right,top,0), (right,bottom,0), (left,bottom,0) - self.clear() - - def clear(self): - """Restore the total transparency to the surface. """ - powerOfTwo = 64 - while powerOfTwo < max(*self._winSize): - powerOfTwo *= 2 - raw = pygame.Surface((powerOfTwo, powerOfTwo), pygame.SRCALPHA, 32) - self._surfTotal = raw.convert_alpha() - self._usable = 1.0*self._winSize[0]/powerOfTwo, 1.0*self._winSize[1]/powerOfTwo - self.surf = self._surfTotal.subsurface(0,0,self._winSize[0],self._winSize[1]) - self.regen() - - def regen(self): - """Force regen of texture object. Call after change to the GUI appearance. """ - if self._txtr: - ogl.glDeleteTextures([self._txtr]) - self._txtr = None - - def refresh(self, dirty=None): - """Refresh the texture from the surface. - @param dirty: list of rectangles to update, None for whole panel - """ - if not self._txtr: - self._txtr = load_texture(self._surfTotal) - else: - wS, hS = self._surfTotal.get_size() - if dirty is None: - dirty = [pygame.Rect(0,0,wS,hS)] - for r in dirty: - overlay_texture(self._txtr,self._surfTotal,r) - - def convertMousePos(self, pos): - """Converts 2d pixel mouse pos to 2d gl units. - @param pos: 2-tuple with x,y of mouse - """ - x0, y0 = pos - x = x0/self._winSize[0]*self._qdims[2] + self._qdims[0] - y = y0/self._winSize[1]*self._qdims[3] + self._qdims[1] - return x, y - - def display(self): - """Draw surface to a quad. Call as part of OpenGL rendering code.""" - ogl.glEnable(ogl.GL_BLEND) - ogl.glBlendFunc(ogl.GL_SRC_ALPHA, ogl.GL_ONE_MINUS_SRC_ALPHA) - ogl.glEnable(ogl.GL_TEXTURE_2D) - ogl.glBindTexture(ogl.GL_TEXTURE_2D, self._txtr) - ogl.glTexEnvf(ogl.GL_TEXTURE_ENV, ogl.GL_TEXTURE_ENV_MODE, ogl.GL_REPLACE) - - ogl.glBegin(ogl.GL_QUADS) - ogl.glTexCoord2f(0.0, 1.0) - ogl.glVertex3f(*self.dims[0]) - ogl.glTexCoord2f(self._usable[0], 1.0) - ogl.glVertex3f(*self.dims[1]) - ogl.glTexCoord2f(self._usable[0], 1-self._usable[1]) - ogl.glVertex3f(*self.dims[2]) - ogl.glTexCoord2f(0.0, 1-self._usable[1]) - ogl.glVertex3f(*self.dims[3]) - ogl.glEnd() - ogl.glDisable(ogl.GL_BLEND) - ogl.glDisable(ogl.GL_TEXTURE_2D) - - def testMode(self): - """Draw red/transparent checkerboard. """ - w, h = self._winSize[0]*0.25, self._winSize[1]*0.25 - Rect = pygame.Rect - pygame.draw.rect(self.surf, (250,0,0), Rect(0,0,w,h), 0) - pygame.draw.rect(self.surf, (250,0,0), Rect(2*w,0,w,h), 0) - - pygame.draw.rect(self.surf, (250,0,0), Rect(w,h,w,h), 0) - pygame.draw.rect(self.surf, (250,0,0), Rect(3*w,h,w,h), 0) - - pygame.draw.rect(self.surf, (250,0,0), Rect(0,2*h,w,h), 0) - pygame.draw.rect(self.surf, (250,0,0), Rect(2*w,2*h,w,h), 0) - - pygame.draw.rect(self.surf, (250,0,0), Rect(w,3*h,w,h), 0) - pygame.draw.rect(self.surf, (250,0,0), Rect(3*w,3*h,w,h), 0) - self.clear = None - - -class LaminaScreenSurface(LaminaPanelSurface): - """Surface for imagery to overlay. Autofits to actual display. - @ivar surf: surface - @ivar dims: tuple with corners of quad - """ - - def __init__(self, depth=0): - """Initialize new instance. - @param depth: (0-1) z-value, if you want to draw your own 3D - cursor, set this to a small non-zero value to allow room in - front of this overlay to draw the cursor. (0.1 is a first guess) - """ - self._txtr = None - self._depth = depth - self.setup() - - def setup(self): - """Setup stuff, after pygame is inited. """ - self._winSize = pygame.display.get_surface().get_size() - self.refreshPosition() - self.clear() - - def refreshPosition(self): - """Recalc where in modelspace quad needs to be to fill screen.""" - depth = self._depth - bottomleft = oglu.gluUnProject(0, 0, depth) - bottomright = oglu.gluUnProject(self._winSize[0], 0, depth) - topleft = oglu.gluUnProject(0, self._winSize[1], depth) - topright = oglu.gluUnProject(self._winSize[0], self._winSize[1], depth) - self.dims = topleft, topright, bottomright, bottomleft - width = topright[0] - topleft[0] - height = topright[1] - bottomright[1] - self._qdims = topleft[0], topleft[1], width, height - - -class LaminaScreenSurface2(LaminaScreenSurface): - """Surface that defers initialization to setup method. """ - - def __init__(self, depth=0): - """Initialize new instance. """ - self._txtr = None - self._depth = depth - - def refreshPosition(self): - """Recalc where in modelspace quad needs to be to fill screen.""" - self._dirty = True - self._qdims = None, None - - def getPoint(self, pt): - """Get x,y coords of pt in 3d space.""" - pt2 = oglu.gluProject(*pt) - return int(pt2[0]), int(pt2[1]), pt2[2] - - def update(self): - pass - - def commit(self): - pass - - def display(self): - """Display texture. """ - if self._dirty: - depth = self._depth - topleft = oglu.gluUnProject(0,self._winSize[1],depth) - assert topleft, topleft - if topleft[0:2] != self._qdims[0:2]: - bottomleft = oglu.gluUnProject(0,0,depth) - bottomright = oglu.gluUnProject(self._winSize[0],0,depth) - topright = oglu.gluUnProject(self._winSize[0],self._winSize[1],depth) - self.dims = topleft, topright, bottomright, bottomleft - width = topright[0] - topleft[0] - height = topright[1] - bottomright[1] - self._qdims = topleft[0], topleft[1], width, height - LaminaScreenSurface.display(self) - - -class LaminaScreenSurface3(LaminaScreenSurface): - """Surface that accepts a 3d point to constructor, and - locates surface parallel to screen through given point. - - Defers initialization to setup method, like LSS2. - """ - - def __init__(self, point=(0,0,0)): - """Initialize new instance. """ - self._txtr = None - self._point = point - self._depth = None - - def refreshPosition(self): - """Recalc where in modelspace quad needs to be to fill screen.""" - self._dirty = True - - def getPoint(self, pt): - """Get x,y coords of pt in 3d space.""" - pt2 = oglu.gluProject(*pt) - return pt2[0], pt2[1] - - def update(self): - pass - - def commit(self): - pass - - def display(self): - """Display texture. """ - if self._dirty: - depth = oglu.gluProject(*self._point)[2] - if depth != self._depth: - bottomleft = oglu.gluUnProject(0,0,depth) - bottomright = oglu.gluUnProject(self._winSize[0],0,depth) - topleft = oglu.gluUnProject(0,self._winSize[1],depth) - topright = oglu.gluUnProject(self._winSize[0],self._winSize[1],depth) - self.dims = topleft, topright, bottomright, bottomleft - width = topright[0] - topleft[0] - height = topright[1] - bottomright[1] - self._qdims = topleft[0], topleft[1], width, height - self._depth = depth - LaminaScreenSurface.display(self) diff --git a/src/setup_exe.py b/src/setup_exe.py index 22d5b1931..f44465c3e 100644 --- a/src/setup_exe.py +++ b/src/setup_exe.py @@ -1,175 +1,167 @@ -##################################################################### -# -*- coding: iso-8859-1 -*- # -# # -# Frets on Fire X # -# Copyright (C) 2009 FoFiX Team # -# 2006 Sami Kyöstilä # -# # -# 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. # -##################################################################### - -# Keyboard Hero setup script -from distutils.core import setup -import sys, SceneFactory, Version, glob, os - -try: - import py2exe - from py2exe.resources.VersionInfo import RT_VERSION - from py2exe.resources.VersionInfo import Version as VersionResource -except ImportError: - pass - -#stump: if we're running pyOpenGL 3, do the necessary black magic. -# Also bring in modules used by pygame 1.9 if necessary. -# For some reason py2exe doesn't include them. -import OpenGL -import pygame -extraIncludes = [] -if int(OpenGL.__version__[0]) > 2: - extraIncludes += [ - "OpenGL.platform.win32", - "OpenGL.arrays.ctypesarrays", - "OpenGL.arrays.numpymodule", - "OpenGL.arrays.lists", - "OpenGL.arrays.numbers", - "OpenGL.arrays.strings", #stump: needed by shader code - ] -if tuple(int(i) for i in pygame.__version__[:5].split('.')) >= (1, 9, 0): - extraIncludes += [ - "heapq", - "bisect", - ] - -options = { - "py2exe": { - "dist_dir": "../dist", - "includes": SceneFactory.scenes + extraIncludes, - "excludes": [ - "glew.gl.apple", - "glew.gl.ati", - "glew.gl.atix", - "glew.gl.hp", - "glew.gl.ibm", - "glew.gl.ingr", - "glew.gl.intel", - "glew.gl.ktx", - "glew.gl.mesa", - "glew.gl.oml", - "glew.gl.pgi", - "glew.gl.rend", - "glew.gl.s3", - "glew.gl.sgi", - "glew.gl.sgis", - "glew.gl.sgix", - "glew.gl.sun", - "glew.gl.sunx", - "glew.gl.threedfx", - "glew.gl.win", - "ode", - "_ssl", - "bz2", - "email", - "calendar", - "bisect", - "difflib", - "doctest", - "ftplib", - "getpass", - "gopherlib", - "heapq", - "macpath", - "macurl2path", - "GimpGradientFile", - "GimpPaletteFile", - "PaletteFile", - "macosx", - "Tkinter", - "Pyrex", - "distutils", - ], - "dll_excludes": [ - "msvcp90.dll", - "mswsock.dll", - "powrprof.dll" - ], - "optimize": 2, - } -} - -dataFiles = [ - "default.ttf", - "title.ttf", - "international.ttf", - "key.dae", - "note.dae", - "cassette.dae", - "label.dae", - "library.dae", - "library_label.dae", - "glow.png", -] - - -dataFiles = ["../data/" + f for f in dataFiles] - -def songFiles(song, extra = []): - return ["../data/songs/%s/%s" % (song, f) for f in ["guitar.ogg", "notes.mid", "song.ini", "song.ogg"] + extra] - -dataFiles = [ - ("data", dataFiles), - ("data/translations", glob.glob("../data/translations/*.mo")), -] - -#stump: sometimes py2.6 py2exe thinks parts of pygame are "system" DLLs... -__orig_isSystemDLL = py2exe.build_exe.isSystemDLL -def isSystemDLL(pathname): - if pathname.lower().find('pygame') != -1: - return 0 - return __orig_isSystemDLL(pathname) -py2exe.build_exe.isSystemDLL = isSystemDLL - -#evilynux: Grab version info from Version class -def setupWindows(): - setup(#stump: these arguments interfere with the version tagging code, - # but they don't really do anything important anyway. When the - # version tagging code was modified, they suddenly became a valid - # source of info for py2exe to synthesize a version info resource - # of its own, which supersedes the one specified further down. - #version = Version.VERSION, - #description = "Rockin' it Oldskool!", - #name = Version.appNameSexy(), - #url = Version.URL, - windows = [ - { - "script": "FoFiX.py", - "icon_resources": [(1, "fofix.ico")], - "other_resources": [(RT_VERSION, 1, VersionResource( - #stump: the parameter below must consist only of up to four numerical fields separated by dots - Version.VERSION, - file_description="Frets on Fire X", - legal_copyright=r"© 2008-2009 FoFiX Team. GNU GPL v2 or later.", - company_name="FoFiX Team", - internal_name="FoFiX.exe", - original_filename="FoFiX.exe", - product_name=Version.appNameSexy(), - #stump: when run from the exe, FoFiX will claim to be "FoFiX v" + product_version - product_version=Version.version() - ).resource_bytes())] - } - ], - zipfile = "data/library.zip", - data_files = dataFiles, - options = options) +##################################################################### +# -*- coding: iso-8859-1 -*- # +# # +# Frets on Fire X # +# Copyright (C) 2009 FoFiX Team # +# 2006 Sami Kyöstilä # +# # +# 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. # +##################################################################### + +# Keyboard Hero setup script +from distutils.core import setup +import sys, SceneFactory, Version, glob, os + +try: + import py2exe + from py2exe.resources.VersionInfo import RT_VERSION + from py2exe.resources.VersionInfo import Version as VersionResource +except ImportError: + pass + +#stump: Start building a list of forced includes. +extraIncludes = [ + "PIL.PngImagePlugin", + "PIL.JpegImagePlugin", +] + +#stump: if we're running pyOpenGL 3, do the necessary black magic. +import OpenGL +import pygame +if int(OpenGL.__version__[0]) > 2: + extraIncludes += [ + "OpenGL.platform.win32", + "OpenGL.arrays.ctypesarrays", + "OpenGL.arrays.numpymodule", + "OpenGL.arrays.lists", + "OpenGL.arrays.numbers", + "OpenGL.arrays.strings", #stump: needed by shader code + ] + +#stump: The pyopengl-accelerator format handlers import this +# module using the Python/C API, so py2exe doesn't know that +# it is needed. +try: + from OpenGL_accelerate import formathandler + extraIncludes.append("OpenGL_accelerate.formathandler") +except ImportError: + pass + +options = { + "py2exe": { + "dist_dir": "../dist", + "includes": SceneFactory.scenes + extraIncludes, + "excludes": [ + "ode", + "_ssl", + "bz2", + "email", + "calendar", + "difflib", + "doctest", + "ftplib", + "getpass", + "gopherlib", + "macpath", + "macurl2path", + "multiprocessing", + "PIL.GimpGradientFile", + "PIL.GimpPaletteFile", + "PIL.PaletteFile", + "GimpGradientFile", #stump: we still need the non-PIL names for these + "GimpPaletteFile", # because they get included under these names when + "PaletteFile", # excluded above... + "macosx", + "Tkinter", + "Pyrex", + "distutils", + "pydoc", + "py_compile", + "compiler", + ], + "dll_excludes": [ + "msvcp90.dll", + "mswsock.dll", + "powrprof.dll" + ], + "optimize": 2, + } +} + +dataFiles = [ + "default.ttf", + "title.ttf", + "international.ttf", + "key.dae", + "note.dae", + "cassette.dae", + "label.dae", + "library.dae", + "library_label.dae", + "glow.png", +] + + +dataFiles = ["../data/" + f for f in dataFiles] + +def songFiles(song, extra = []): + return ["../data/songs/%s/%s" % (song, f) for f in ["guitar.ogg", "notes.mid", "song.ini", "song.ogg"] + extra] + +dataFiles = [ + ("data", dataFiles), + ("data/translations", glob.glob("../data/translations/*.mo")), +] + +#stump: sometimes py2.6 py2exe thinks parts of pygame are "system" DLLs... +__orig_isSystemDLL = py2exe.build_exe.isSystemDLL +def isSystemDLL(pathname): + if pathname.lower().find('pygame') != -1: + return 0 + return __orig_isSystemDLL(pathname) +py2exe.build_exe.isSystemDLL = isSystemDLL + +#evilynux: Grab version info from Version class +def setupWindows(): + setup(#stump: these arguments interfere with the version tagging code, + # but they don't really do anything important anyway. When the + # version tagging code was modified, they suddenly became a valid + # source of info for py2exe to synthesize a version info resource + # of its own, which supersedes the one specified further down. + #version = Version.VERSION, + #description = "Rockin' it Oldskool!", + #name = Version.appNameSexy(), + #url = Version.URL, + windows = [ + { + "script": "FoFiX.py", + "icon_resources": [(1, "fofix.ico")], + "other_resources": [(RT_VERSION, 1, VersionResource( + #stump: the parameter below must consist only of up to four numerical fields separated by dots + Version.VERSION, + file_description="Frets on Fire X", + legal_copyright=r"© 2008-2009 FoFiX Team. GNU GPL v2 or later.", + company_name="FoFiX Team", + internal_name="FoFiX.exe", + original_filename="FoFiX.exe", + product_name=Version.appNameSexy(), + #stump: when run from the exe, FoFiX will claim to be "FoFiX v" + product_version + product_version=Version.version() + ).resource_bytes())] + } + ], + zipfile = "data/library.zip", + data_files = dataFiles, + options = options)