Gestion explicite de KeyboardInterrupt pour être sûr de quitter le jeu sur un ^C.
[minwii.git] / src / minwii / logfilereader.py
index 29425f7..57cdc60 100755 (executable)
@@ -6,120 +6,132 @@ $Id$
 $URL$
 """
 
 $URL$
 """
 
+from types import StringTypes
 from widgets.playingscreen import PlayingScreenBase
 from eventutils import EventDispatcher
 from events import eventCodes
 from synth import Synth
 from musicxml import musicXml2Song
 import pygame
 from widgets.playingscreen import PlayingScreenBase
 from eventutils import EventDispatcher
 from events import eventCodes
 from synth import Synth
 from musicxml import musicXml2Song
 import pygame
+from backwardsfilereader import BackwardsReader
 
 SUPPORTED_FILE_HEADER = 'ENV winwii log format version : 1.0'
 
 
 SUPPORTED_FILE_HEADER = 'ENV winwii log format version : 1.0'
 
+def inplaceread(m) :
+    def readinplace(self, *args, **kw) :
+        pos = self.logfile.tell()
+        self.logfile.seek(0)
+        ret = m(self, *args, **kw)
+        self.logfile.seek(pos)
+        return ret
+    return readinplace
+
 class LogFileReader(object) :
     """
     classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
     """
     
 class LogFileReader(object) :
     """
     classe utilitaire pour l'accès aux données d'un fichier de log MinWii.
     """
     
-    def __init__(self, logfile) :
+    def __init__(self, logfile, mode='r') :
         """ logfile : chemin d'accès au fichier de log MinWii.
             le format supporté est actuellement la version 1.0 uniquement.
         """
         """ logfile : chemin d'accès au fichier de log MinWii.
             le format supporté est actuellement la version 1.0 uniquement.
         """
-        if isinstance(logfile, str) :
-            self.logfile = open(logfile, 'r')
+        if isinstance(logfile, StringTypes) :
+            self.logfile = open(logfile, mode)
         else :
             self.logfile = logfile
         
         firstline = self.next()
         assert firstline == SUPPORTED_FILE_HEADER
         else :
             self.logfile = logfile
         
         firstline = self.next()
         assert firstline == SUPPORTED_FILE_HEADER
-        
     
     
+        
+    @inplaceread
     def getSongFile(self) :
         "retourne le chemin d'accès au fichier musicxml de la chanson"
     def getSongFile(self) :
         "retourne le chemin d'accès au fichier musicxml de la chanson"
-        f = self.logfile
-        pos = f.tell()
-
-        f.seek(0)
         for l in self :
             if l.startswith('APP chanson :') :
                 break
         songfile = l.split(':', 1)[1].strip()
         for l in self :
             if l.startswith('APP chanson :') :
                 break
         songfile = l.split(':', 1)[1].strip()
-        f.seek(pos)
         return songfile
     
         return songfile
     
+    @inplaceread
     def getSoundFontFile(self) :
         "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
     def getSoundFontFile(self) :
         "retourne le chemin d'accès au fichier de la soundfont (*.sf2)"
-        f = self.logfile
-        pos = f.tell()
-        f.seek(0)
         for l in self :
             if l.startswith('ENV soundfont :') :
                 break
         soundFontFile = l.split(':', 1)[1].strip()
         for l in self :
             if l.startswith('ENV soundfont :') :
                 break
         soundFontFile = l.split(':', 1)[1].strip()
-        f.seek(pos)
         return soundFontFile
         return soundFontFile
-
+    
+    @inplaceread
     def getBank(self) :
         "retourne le paramètre bank du synthétiseur (entier)"
     def getBank(self) :
         "retourne le paramètre bank du synthétiseur (entier)"
-        f = self.logfile
-        pos = f.tell()
-        f.seek(0)
         for l in self :
             if l.startswith('APP bank :') :
                 break
         for l in self :
             if l.startswith('APP bank :') :
                 break
-        f.seek(pos)
         bank = l.split(':', 1)[1].strip()
         return int(bank)
     
         bank = l.split(':', 1)[1].strip()
         return int(bank)
     
+    @inplaceread
     def getPreset(self) :
         "retourne le paramètre preset du synthétiseur (entier)"
     def getPreset(self) :
         "retourne le paramètre preset du synthétiseur (entier)"
-        f = self.logfile
-        pos = f.tell()
-        f.seek(0)
         for l in self :
             if l.startswith('APP preset :') :
                 break
         for l in self :
             if l.startswith('APP preset :') :
                 break
-        f.seek(pos)
         preset = l.split(':', 1)[1].strip()
         return int(preset)
 
         preset = l.split(':', 1)[1].strip()
         return int(preset)
 
+    @inplaceread
     def getScreenResolution(self) :
         "retourne la résolution écran (tuple de deux entiers)"
     def getScreenResolution(self) :
         "retourne la résolution écran (tuple de deux entiers)"
-        f = self.logfile
-        pos = f.tell()
-        f.seek(0)
         for l in self :
             if l.startswith('ENV résolution écran :') :
                 break
         screenResolution = eval(l.split(':', 1)[1].strip())
         for l in self :
             if l.startswith('ENV résolution écran :') :
                 break
         screenResolution = eval(l.split(':', 1)[1].strip())
-        f.seek(pos)
         return screenResolution
     
         return screenResolution
     
+    @inplaceread
     def getMode(self) :
         "retourne le niveau de difficulté"
     def getMode(self) :
         "retourne le niveau de difficulté"
-        f = self.logfile
-        pos = f.tell()
         for l in self :
             if l.startswith('APP mode :') :
                 break
             
         mode = l.split(':', 1)[1].strip()
         for l in self :
             if l.startswith('APP mode :') :
                 break
             
         mode = l.split(':', 1)[1].strip()
-        f.geek(pos)
         return mode
     
         return mode
     
+    @inplaceread
+    def getHID(self) :
+        "retourne l'interface homme-machine utilisée"
+        for l in self :
+            if l.startswith('APP HID :') :
+                break
+            
+        mode = l.split(':', 1)[1].strip()
+        return mode
+    
+    @inplaceread
     def getFirstEventTicks(self) :
         "retourne le timecode du premier événement (entier)"
     def getFirstEventTicks(self) :
         "retourne le timecode du premier événement (entier)"
-        f = self.logfile
-        pos = f.tell()
-        f.seek(0)
         for l in self :
             if l.startswith('EVT ') :
                 break
         firstTicks = int(l.split(None, 2)[1])
         for l in self :
             if l.startswith('EVT ') :
                 break
         firstTicks = int(l.split(None, 2)[1])
-        f.seek(pos)
         return firstTicks
     
         return firstTicks
     
+    @inplaceread
+    def getLastEventTicks(self) :
+        "retourne le timecode du dernier événement (entier)"
+        for l in self.getBackwardLineIterator() :
+            if l.startswith('EVT ') :
+                break
+        else :
+            return None
+        
+        lastTicks = int(l.split(None, 2)[1])
+        return lastTicks
+    
     def __del__(self) :
     def __del__(self) :
-        self.logfile.close()
+        self.close()
     
     def __iter__(self) :
         return self
     
     def __iter__(self) :
         return self
@@ -151,6 +163,47 @@ class LogFileReader(object) :
                 ticks, eventName = l.split(None, 3)[1:]
                 ticks = int(ticks)
                 yield ticks, eventName, ''
                 ticks, eventName = l.split(None, 3)[1:]
                 ticks = int(ticks)
                 yield ticks, eventName, ''
+    
+    def getBackwardLineIterator(self) :
+        br = BackwardsReader(self.logfile, BLKSIZE=128)
+        line = br.readline()
+        while line :
+            yield line.strip()
+            line = br.readline()
+    
+    @inplaceread
+    def getMetadata(self) :
+        metadata = {}
+        self.next() # skip identification line.
+        line = self.next()
+        while line.startswith('METADATA ') :
+            line = line.split(None, 1)[1]
+            name, value = [v.strip() for v in line.split(':', 1)]
+            metadata[name] = value
+            line = self.next()
+        return metadata
+    
+    def setMetadata(self, metadata) :
+        f = self.logfile
+        f.seek(0)
+        before = f.readline()
+        line = f.readline()
+        while line.startswith('METADATA ') :
+            line = f.readline()
+        after = line + f.read()
+        
+        lines = []
+        for name, value in metadata :
+            lines.append('METADATA %s : %s' % (name, value.encode('utf-8')))
+        metadata = '\n'.join(lines)
+        f.seek(0)
+        f.write(before)
+        f.write(metadata)
+        f.write(after)
+    
+    def close(self) :
+        self.logfile.close()
+        
                 
 
 class LogFilePlayer(PlayingScreenBase) :
                 
 
 class LogFilePlayer(PlayingScreenBase) :
@@ -222,5 +275,4 @@ class LogFilePlayer(PlayingScreenBase) :
             previousTicks = ticks
         
         self.stop()
             previousTicks = ticks
         
         self.stop()
-        
     
\ No newline at end of file
     
\ No newline at end of file