merge sound changes into .17, and, why not everything else too. 0.9.17
authornemo
Sun, 13 Nov 2011 14:41:02 -0500
branch0.9.17
changeset 6366 1a49a8fcca56
parent 6360 eca20f8990e2 (current diff)
parent 6364 c6adb60239f4 (diff)
child 6368 cd819d9df6f6
merge sound changes into .17, and, why not everything else too.
CMakeLists.txt
--- a/hedgewars/PascalExports.pas	Sun Nov 13 13:18:56 2011 -0500
+++ b/hedgewars/PascalExports.pas	Sun Nov 13 14:41:02 2011 -0500
@@ -355,6 +355,12 @@
 begin
     exit(cMaxTeams);
 end;
+
+procedure HW_memoryWarningCallback; cdecl; export;
+begin
+    ReleaseSound(false);
+end;
+
 {$ENDIF}
 
 end.
--- a/hedgewars/SDLh.pas	Sun Nov 13 13:18:56 2011 -0500
+++ b/hedgewars/SDLh.pas	Sun Nov 13 14:41:02 2011 -0500
@@ -956,6 +956,7 @@
 function  Mix_PauseMusic(music: PMixMusic): LongInt; cdecl; external SDL_MixerLibName;
 function  Mix_ResumeMusic(music: PMixMusic): LongInt; cdecl; external SDL_MixerLibName;
 function  Mix_HaltChannel(channel: LongInt): LongInt; cdecl; external SDL_MixerLibName;
+function  Mix_HaltMusic: LongInt; cdecl; external SDL_MixerLibName;
 
 function  Mix_FadeInChannelTimed(channel: LongInt; chunk: PMixChunk; loops: LongInt; fadems: LongInt; ticks: LongInt): LongInt; cdecl; external SDL_MixerLibName;
 function  Mix_FadeOutChannel(channel: LongInt; fadems: LongInt): LongInt; cdecl; external SDL_MixerLibName;
--- a/hedgewars/uLandGraphics.pas	Sun Nov 13 13:18:56 2011 -0500
+++ b/hedgewars/uLandGraphics.pas	Sun Nov 13 14:41:02 2011 -0500
@@ -233,7 +233,7 @@
                begin
                by:= t div 2; bx:= i div 2;
                end;
-           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                begin
                inc(cnt);
                LandPixels[by, bx]:= LandBackPixel(i, t)
@@ -255,7 +255,7 @@
                begin
                by:= t div 2; bx:= i div 2;
                end;
-           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                begin
                inc(cnt);
                LandPixels[by, bx]:= LandBackPixel(i, t)
@@ -277,7 +277,7 @@
                begin
                by:= t div 2; bx:= i div 2;
                end;
-           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                begin
                inc(cnt);
                LandPixels[by, bx]:= LandBackPixel(i, t)
@@ -298,7 +298,7 @@
                begin
                by:= t div 2; bx:= i div 2;
                end;
-           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+           if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not (disableLandBack) then
                begin
                inc(cnt);
                LandPixels[by, bx]:= LandBackPixel(i, t)
@@ -468,7 +468,7 @@
                     begin
                     by:= ty div 2; bx:= tx div 2;
                     end;
-                if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+                if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                     LandPixels[by, bx]:= LandBackPixel(tx, ty)
                 else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then 
                     LandPixels[by, bx]:= 0
@@ -588,7 +588,7 @@
                 begin
                 by:= ty div 2; bx:= tx div 2;
                 end;
-            if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and not disableLandBack then
+            if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
                 LandPixels[by, bx]:= LandBackPixel(tx, ty)
             else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
                 LandPixels[by, bx]:= 0;
--- a/hedgewars/uSound.pas	Sun Nov 13 13:18:56 2011 -0500
+++ b/hedgewars/uSound.pas	Sun Nov 13 14:41:02 2011 -0500
@@ -41,7 +41,7 @@
 procedure freeModule;
 
 procedure InitSound; // Initiates sound-system if isSoundEnabled.
-procedure ReleaseSound; // Releases sound-system and used resources.
+procedure ReleaseSound(complete: boolean); // Releases sound-system and used resources.
 procedure SoundLoad; // Preloads some sounds for performance reasons.
 
 
@@ -176,24 +176,41 @@
     ChangeVolume(cInitVolume)
 end;
 
-procedure ReleaseSound;
+// when complete is false, this procedure just releases some of the chucks on inactive channels
+// this way music is not stopped, nor are chucks currently being plauyed
+procedure ReleaseSound(complete: boolean);
 var i: TSound;
     t: Longword;
 begin
+    // release and nil all sounds
     for t:= 0 to cMaxTeams do
         if voicepacks[t].name <> '' then
             for i:= Low(TSound) to High(TSound) do
                 if voicepacks[t].chunks[i] <> nil then
-                    Mix_FreeChunk(voicepacks[t].chunks[i]);
-
-    if Mus <> nil then
-        Mix_FreeMusic(Mus);
+                    if complete or (Mix_Playing(lastChan[i]) = 0) then
+                        begin
+                        Mix_HaltChannel(lastChan[i]);
+                        lastChan[i]:= -1;
+                        Mix_FreeChunk(voicepacks[t].chunks[i]);
+                        voicepacks[t].chunks[i]:= nil;
+                        end;
 
-    // make sure all instances of sdl_mixer are unloaded before continuing
-    while Mix_Init(0) <> 0 do
-        Mix_Quit();
+    // stop music
+    if complete then
+        begin
+        if Mus <> nil then
+            begin
+            Mix_HaltMusic();
+            Mix_FreeMusic(Mus);
+            Mus:= nil;
+            end;
 
-    Mix_CloseAudio();
+        // make sure all instances of sdl_mixer are unloaded before continuing
+        while Mix_Init(0) <> 0 do
+            Mix_Quit();
+
+        Mix_CloseAudio();
+        end;
 end;
 
 procedure SoundLoad;
@@ -205,15 +222,16 @@
 
     defVoicepack:= AskForVoicepack('Default');
 
+    // initialize all voices to nil so that they can be loaded when needed
     for t:= 0 to cMaxTeams do
         if voicepacks[t].name <> '' then
             for i:= Low(TSound) to High(TSound) do
                 voicepacks[t].chunks[i]:= nil;
 
+    // preload all the big sound files (>32k) that would otherwise lockup the game
     for i:= Low(TSound) to High(TSound) do
     begin
         defVoicepack^.chunks[i]:= nil;
-        // preload all the big sound files (>32k) that would otherwise lockup the game
         if (i in [sndBeeWater, sndBee, sndCake, sndHellishImpact1, sndHellish, sndHomerun,
                   sndMolotov, sndMortar, sndRideOfTheValkyries, sndYoohoo])
             and (Soundz[i].Path <> ptVoices) and (Soundz[i].FileName <> '') then
@@ -289,7 +307,7 @@
     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd = snd) and  (LastVoice.voicepack = voicepack)) then exit;
     if (snd = sndVictory) or (snd = sndFlawless) then
         begin
-        for i:= 1 to Succ(chanTPU) do StopSound(i);
+        Mix_HaltChannel(-1);
         for i:= 0 to 7 do VoiceList[i].snd:= sndNone;
         LastVoice.snd:= sndNone;
         end;
@@ -480,7 +498,7 @@
 procedure freeModule;
 begin
     if isSoundEnabled then
-        ReleaseSound();
+        ReleaseSound(true);
 end;
 
 end.
--- a/project_files/HedgewarsMobile/Classes/EngineProtocolNetwork.h	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/EngineProtocolNetwork.h	Sun Nov 13 14:41:02 2011 -0500
@@ -22,21 +22,15 @@
 #import <Foundation/Foundation.h>
 #import "SDL_net.h"
 
-@protocol EngineProtocolDelegate <NSObject>
-
--(void) gameHasEndedWithStats:(NSArray *)stats;
-
-@end
 
 @interface EngineProtocolNetwork : NSObject {
-    id<EngineProtocolDelegate> delegate;
-
+    NSArray *statsArray;
     NSOutputStream *stream;
     TCPsocket csd;
     NSInteger enginePort;
 }
 
-@property (nonatomic,assign) id<EngineProtocolDelegate> delegate;
+@property (nonatomic,assign) NSArray *statsArray;
 @property (nonatomic,retain) NSOutputStream *stream;
 @property (assign) TCPsocket csd;
 @property (assign) NSInteger enginePort;
@@ -44,11 +38,10 @@
 
 -(id)   init;
 
-+(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary;
+-(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary;
 +(NSInteger) activeEnginePort;
 
 -(void) engineProtocol:(id) object;
--(void) gameHasEndedWithStats:(NSArray *)stats;
 
 -(int)  sendToEngine:(NSString *)string;
 -(int)  sendToEngineNoSave:(NSString *)string;
--- a/project_files/HedgewarsMobile/Classes/EngineProtocolNetwork.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/EngineProtocolNetwork.m	Sun Nov 13 14:41:02 2011 -0500
@@ -28,12 +28,11 @@
 static NSInteger activeEnginePort;
 
 @implementation EngineProtocolNetwork
-@synthesize delegate, stream, csd, enginePort;
+@synthesize statsArray, stream, csd, enginePort;
 
 -(id) init {
     if (self = [super init]) {
-        self.delegate = nil;
-
+        self.statsArray = nil;
         self.csd = NULL;
         self.stream = nil;
         self.enginePort = [HWUtils randomPort];
@@ -42,31 +41,22 @@
     return self;
 }
 
--(void) gameHasEndedWithStats:(NSArray *)stats {
-    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(gameHasEndedWithStats:)])
-        [self.delegate gameHasEndedWithStats:stats];
-    else
-        DLog(@"Error! delegate == nil");
-}
-
 -(void) dealloc {
-    self.delegate = nil;
+    releaseAndNil(statsArray);
     releaseAndNil(stream);
     [super dealloc];
 }
 
 #pragma mark -
 #pragma mark Spawner functions
-+(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary {
-    id proto = [[self alloc] init];
-    [proto setStream: (onSaveFile) ? [[NSOutputStream alloc] initToFileAtPath:onSaveFile append:YES] : nil];
-    [[proto stream] open];
+-(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary {
+    self.stream = (onSaveFile) ? [[NSOutputStream alloc] initToFileAtPath:onSaveFile append:YES] : nil;
+    [self.stream open];
 
     // +detachNewThreadSelector retain/release self automatically
     [NSThread detachNewThreadSelector:@selector(engineProtocol:)
-                             toTarget:proto
+                             toTarget:self
                            withObject:dictionary];
-    [proto release];
 }
 
 +(NSInteger) activeEnginePort {
@@ -232,7 +222,7 @@
 -(void) engineProtocol:(id) object {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSDictionary *gameConfig = (NSDictionary *)object;
-    NSMutableArray *statsArray = nil;
+    NSMutableArray *tempStats = nil;
     TCPsocket sd;
     IPaddress ip;
     int eProto;
@@ -347,10 +337,10 @@
                 }
                 break;
             case 'i':
-                if (statsArray == nil) {
-                    statsArray = [[NSMutableArray alloc] initWithCapacity:10 - 2];
+                if (tempStats == nil) {
+                    tempStats = [[NSMutableArray alloc] initWithCapacity:10 - 2];
                     NSMutableArray *ranking = [[NSMutableArray alloc] initWithCapacity:4];
-                    [statsArray insertObject:ranking atIndex:0];
+                    [tempStats insertObject:ranking atIndex:0];
                     [ranking release];
                 }
                 NSString *tempStr = [NSString stringWithUTF8String:&buffer[2]];
@@ -359,16 +349,16 @@
                 int index = [arg length] + 3;
                 switch (buffer[1]) {
                     case 'r':           // winning team
-                        [statsArray insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1];
+                        [tempStats insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1];
                         break;
                     case 'D':           // best shot
-                        [statsArray addObject:[NSString stringWithFormat:@"The best shot award won by %s (with %@ points)", &buffer[index], arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"The best shot award won by %s (with %@ points)", &buffer[index], arg]];
                         break;
                     case 'k':           // best hedgehog
-                        [statsArray addObject:[NSString stringWithFormat:@"The best killer is %s with %@ kills in a turn", &buffer[index], arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"The best killer is %s with %@ kills in a turn", &buffer[index], arg]];
                         break;
                     case 'K':           // number of hogs killed
-                        [statsArray addObject:[NSString stringWithFormat:@"%@ hedgehog(s) were killed during this round", arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"%@ hedgehog(s) were killed during this round", arg]];
                         break;
                     case 'H':           // team health/graph
                         break;
@@ -376,16 +366,16 @@
                         // still WIP in statsPage.cpp
                         break;
                     case 'P':           // teams ranking
-                        [[statsArray objectAtIndex:0] addObject:tempStr];
+                        [[tempStats objectAtIndex:0] addObject:tempStr];
                         break;
                     case 's':           // self damage
-                        [statsArray addObject:[NSString stringWithFormat:@"%s thought it's good to shoot his own hedgehogs with %@ points", &buffer[index], arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"%s thought it's good to shoot his own hedgehogs with %@ points", &buffer[index], arg]];
                         break;
                     case 'S':           // friendly fire
-                        [statsArray addObject:[NSString stringWithFormat:@"%s killed %@ of his own hedgehogs", &buffer[index], arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"%s killed %@ of his own hedgehogs", &buffer[index], arg]];
                         break;
                     case 'B':           // turn skipped
-                        [statsArray addObject:[NSString stringWithFormat:@"%s was scared and skipped turn %@ times", &buffer[index], arg]];
+                        [tempStats addObject:[NSString stringWithFormat:@"%s was scared and skipped turn %@ times", &buffer[index], arg]];
                         break;
                     default:
                         DLog(@"Unhandled stat message, see statsPage.cpp");
@@ -393,12 +383,15 @@
                 }
                 break;
             case 'q':
-                // game ended, can remove the savefile and present the statistics of the match
+                // game ended and match finished, statsArray is full of delicious statistics
                 [HWUtils setGameStatus:gsEnded];
-                [self gameHasEndedWithStats:statsArray];
+                self.statsArray = [[NSArray arrayWithArray:tempStats] retain];
+                // closing connection here would trigger a "IPC connection lost" error, so we have to wait until recv fails
                 break;
             case 'Q':
-                // game exited but not completed, nothing to do (just don't save the message)
+                // game exited but not completed, skip this message in the savefile
+                [HWUtils setGameStatus:gsInterrupted];
+                // same here, don't set clientQuit to YES
                 break;
             default:
                 [self dumpRawData:buffer ofSize:msgSize];
@@ -406,9 +399,10 @@
         }
     }
     DLog(@"Engine exited, ending thread");
+
     [self.stream close];
     [self.stream release];
-    [statsArray release];
+    [tempStats release];
 
     // Close the client socket
     SDLNet_TCP_Close(csd);
--- a/project_files/HedgewarsMobile/Classes/GameConfigViewController.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/GameConfigViewController.m	Sun Nov 13 14:41:02 2011 -0500
@@ -223,6 +223,7 @@
                                     script,@"mission_command",
                                     nil];
 
+    [GameInterfaceBridge registerCallingController:self];
     [GameInterfaceBridge startLocalGame:gameDictionary];
     [gameDictionary release];
 }
--- a/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.h	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.h	Sun Nov 13 14:41:02 2011 -0500
@@ -21,15 +21,22 @@
 
 #import <Foundation/Foundation.h>
 
+@class EngineProtocolNetwork;
 
 @interface GameInterfaceBridge : NSObject {
     UIView *blackView;
+    NSString *savePath;
+    EngineProtocolNetwork *proto;
 }
 
 @property (nonatomic,retain) UIView *blackView;
+@property (nonatomic,retain) NSString *savePath;
+@property (nonatomic,retain) EngineProtocolNetwork *proto;
 
 +(void) startLocalGame:(NSDictionary *)withOptions;
 +(void) startSaveGame:(NSString *)atPath;
 +(void) startMissionGame:(NSString *)withScript;
 
++(void) registerCallingController:(UIViewController *)controller;
+
 @end
--- a/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/GameInterfaceBridge.m	Sun Nov 13 14:41:02 2011 -0500
@@ -26,16 +26,22 @@
 #import "AudioManagerController.h"
 #import "ObjcExports.h"
 
+static UIViewController *callingController;
+
 @implementation GameInterfaceBridge
-@synthesize blackView;
+@synthesize blackView, savePath, proto;
 
 #pragma mark -
 #pragma mark Instance methods for engine interaction
 // prepares the controllers for hosting a game
--(void) earlyEngineLaunch:(NSString *)pathOrNil withOptions:(NSDictionary *)optionsOrNil {
+-(void) earlyEngineLaunch:(NSDictionary *)optionsOrNil {
     [self retain];
     [AudioManagerController stopBackgroundMusic];
-    [EngineProtocolNetwork spawnThread:pathOrNil withOptions:optionsOrNil];
+
+    EngineProtocolNetwork *engineProtocol = [[EngineProtocolNetwork alloc] init];
+    self.proto = engineProtocol;
+    [engineProtocol release];
+    [self.proto spawnThread:self.savePath withOptions:optionsOrNil];
 
     // add a black view hiding the background
     CGRect theFrame = [[UIScreen mainScreen] bounds];
@@ -53,15 +59,19 @@
 
     // keep track of uncompleted games
     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
-    [userDefaults setObject:pathOrNil forKey:@"savedGamePath"];
+    [userDefaults setObject:self.savePath forKey:@"savedGamePath"];
     [userDefaults synchronize];
 
     // let's launch the engine using this -perfomSelector so that the runloop can deal with queued messages first
-    [self performSelector:@selector(engineLaunch:) withObject:pathOrNil afterDelay:0.1f];
+    [self performSelector:@selector(engineLaunch) withObject:nil afterDelay:0.1f];
 }
 
 // cleans up everything
 -(void) lateEngineLaunch {
+    // notify views below that they are getting the spotlight again
+    [[[HedgewarsAppDelegate sharedAppDelegate] uiwindow] makeKeyAndVisible];
+    [callingController viewWillAppear:YES];
+
     // remove completed games notification
     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
     [userDefaults setObject:@"" forKey:@"savedGamePath"];
@@ -78,16 +88,36 @@
     // the overlay is not needed any more and can be removed
     [[OverlayViewController mainOverlay] removeOverlay];
 
+    // engine thread *should* be done by now
+    NSArray *stats = self.proto.statsArray;
+    if (stats != nil) {
+        StatsPageViewController *statsPage = [[StatsPageViewController alloc] initWithStyle:UITableViewStyleGrouped];
+        statsPage.statsArray = stats;
+        statsPage.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
+        if ([statsPage respondsToSelector:@selector(setModalPresentationStyle:)])
+            statsPage.modalPresentationStyle = UIModalPresentationPageSheet;
+
+        [callingController presentModalViewController:statsPage animated:YES];
+        [statsPage release];
+    }
+    [stats release];    // we retained the array before
+
+    // can remove the savefile if the replay has ended
+    if ([HWUtils gameType] == gtSave)
+        [[NSFileManager defaultManager] removeItemAtPath:self.savePath error:nil];
+
     // restart music and we're done
     [AudioManagerController playBackgroundMusic];
+    [HWUtils setGameStatus:gsNone];
+    [HWUtils setGameType:gtNone];
     [self release];
 }
 
 // main routine for calling the actual game engine
--(void) engineLaunch:(NSString *)pathOrNil {
+-(void) engineLaunch {
     const char *gameArgs[11];
     CGFloat width, height;
-    NSInteger enginePort = [EngineProtocolNetwork activeEnginePort];
+    NSInteger enginePort = self.proto.enginePort;
     CGFloat screenScale = [[UIScreen mainScreen] safeScale];
     NSString *ipcString = [[NSString alloc] initWithFormat:@"%d",enginePort];
     NSString *localeString = [[NSString alloc] initWithFormat:@"%@.txt",[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
@@ -139,7 +169,7 @@
     gameArgs[ 7] = [[[settings objectForKey:@"music"] stringValue] UTF8String];                 //isMusicEnabled
     gameArgs[ 8] = [[[settings objectForKey:@"alternate"] stringValue] UTF8String];             //cAltDamage
     gameArgs[ 9] = [rotation UTF8String];                                                       //rotateQt
-    gameArgs[10] = ([HWUtils gameType] == gtSave) ? [pathOrNil UTF8String] : NULL;              //recordFileName
+    gameArgs[10] = ([HWUtils gameType] == gtSave) ? [self.savePath UTF8String] : NULL;          //recordFileName
 
     [verticalSize release];
     [horizontalSize release];
@@ -154,12 +184,24 @@
     [self lateEngineLaunch];
 }
 
+-(void) dealloc {
+    releaseAndNil(blackView);
+    releaseAndNil(savePath);
+    releaseAndNil(proto);
+    [super dealloc];
+}
+
 #pragma mark -
 #pragma mark Class methods for setting up the engine from outsite
++(void) registerCallingController:(UIViewController *)controller {
+    callingController = controller;
+}
+
 +(void) startGame:(TGameType) type atPath:(NSString *)path withOptions:(NSDictionary *)config {
     [HWUtils setGameType:type];
     id bridge = [[self alloc] init];
-    [bridge earlyEngineLaunch:path withOptions:config];
+    [bridge setSavePath:path];
+    [bridge earlyEngineLaunch:config];
     [bridge release];
 }
 
@@ -190,27 +232,5 @@
     [missionLine release];
 }
 
-/*
--(void) gameHasEndedWithStats:(NSArray *)stats {
-    // wrap this around a retain/realse to prevent being deallocated too soon
-    [self retain];
-    // display stats page if there is something to display
-    if (stats != nil) {
-        StatsPageViewController *statsPage = [[StatsPageViewController alloc] initWithStyle:UITableViewStyleGrouped];
-        statsPage.statsArray = stats;
-        statsPage.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
-        if ([statsPage respondsToSelector:@selector(setModalPresentationStyle:)])
-            statsPage.modalPresentationStyle = UIModalPresentationPageSheet;
-
-        [self.parentController presentModalViewController:statsPage animated:YES];
-        [statsPage release];
-    }
-
-    // can remove the savefile if the replay has ended
-    if ([HWUtils gameType] == gtSave)
-        [[NSFileManager defaultManager] removeItemAtPath:self.savePath error:nil];
-    [self release];
-}
-*/
 
 @end
--- a/project_files/HedgewarsMobile/Classes/HWUtils.h	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/HWUtils.h	Sun Nov 13 14:41:02 2011 -0500
@@ -22,7 +22,7 @@
 #import <Foundation/Foundation.h>
 
 typedef enum {gtNone, gtLocal, gtSave, gtMission, gtNet} TGameType;
-typedef enum {gsNone, gsLoading, gsInGame, gsEnded} TGameStatus;
+typedef enum {gsNone, gsLoading, gsInGame, gsInterrupted, gsEnded} TGameStatus;
 
 @interface HWUtils : NSObject {
 
--- a/project_files/HedgewarsMobile/Classes/HedgewarsAppDelegate.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/HedgewarsAppDelegate.m	Sun Nov 13 14:41:02 2011 -0500
@@ -88,6 +88,7 @@
     // don't stop music if it is playing
     if ([HWUtils isGameLaunched]) {
         [AudioManagerController releaseCache];
+        HW_memoryWarningCallback();
     }
     MSG_MEMCLEAN();
     // don't clean mainMenuViewController here!!!
--- a/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Sun Nov 13 14:41:02 2011 -0500
@@ -27,7 +27,6 @@
 #import "SavedGamesViewController.h"
 #import "RestoreViewController.h"
 #import "MissionTrainingViewController.h"
-#import "GameInterfaceBridge.h"
 #import "Appirater.h"
 #import "ServerProtocolNetwork.h"
 
@@ -114,7 +113,6 @@
     // prompt for restoring any previous game
     NSString *saveString = [userDefaults objectForKey:@"savedGamePath"];
     if (saveString != nil && [saveString isEqualToString:@""] == NO) {
-        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(launchRestoredGame) name:@"launchRestoredGame" object:nil];
         if (self.restoreViewController == nil) {
             NSString *xibName = [@"RestoreViewController-" stringByAppendingString:(IS_IPAD() ? @"iPad" : @"iPhone")];
             RestoreViewController *restored = [[RestoreViewController alloc] initWithNibName:xibName bundle:nil];
@@ -235,12 +233,6 @@
 }
 
 #pragma mark -
--(void) launchRestoredGame {
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    [GameInterfaceBridge startSaveGame:[[NSUserDefaults standardUserDefaults] objectForKey:@"savedGamePath"]];
-}
-
-#pragma mark -
 -(void) viewDidUnload {
     self.gameConfigViewController = nil;
     self.settingsViewController = nil;
--- a/project_files/HedgewarsMobile/Classes/MissionTrainingViewController.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/MissionTrainingViewController.m	Sun Nov 13 14:41:02 2011 -0500
@@ -74,6 +74,7 @@
         [AudioManagerController playBackSound];
         [[self parentViewController] dismissModalViewControllerAnimated:YES];
     } else {
+        [GameInterfaceBridge registerCallingController:self];
         [GameInterfaceBridge startMissionGame:self.missionName];
     }
 }
--- a/project_files/HedgewarsMobile/Classes/PascalImports.h	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/PascalImports.h	Sun Nov 13 14:41:02 2011 -0500
@@ -98,6 +98,8 @@
     int  HW_getTurnsForCurrentTeam(void);
     int  HW_getMaxNumberOfHogs(void);
     int  HW_getMaxNumberOfTeams(void);
+
+    void HW_memoryWarningCallback(void);
     
 #ifdef __cplusplus
 }
--- a/project_files/HedgewarsMobile/Classes/RestoreViewController.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/RestoreViewController.m	Sun Nov 13 14:41:02 2011 -0500
@@ -36,7 +36,8 @@
 
     if (theButton.tag != 0) {
         [AudioManagerController playClickSound];
-        [[NSNotificationCenter defaultCenter] postNotificationName:@"launchRestoredGame" object:nil];
+        [GameInterfaceBridge registerCallingController:self.parentViewController];
+        [GameInterfaceBridge startSaveGame:[[NSUserDefaults standardUserDefaults] objectForKey:@"savedGamePath"]];
     } else {
         [AudioManagerController playBackSound];
         [defaults setObject:@"" forKey:@"savedGamePath"];
--- a/project_files/HedgewarsMobile/Classes/SavedGamesViewController.m	Sun Nov 13 13:18:56 2011 -0500
+++ b/project_files/HedgewarsMobile/Classes/SavedGamesViewController.m	Sun Nov 13 14:41:02 2011 -0500
@@ -189,6 +189,7 @@
     self.numberOfItems++;
     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
 
+    [GameInterfaceBridge registerCallingController:self];
     [GameInterfaceBridge startSaveGame:currentFilePath];
     [currentFilePath release];
 }
--- a/tools/PascalParser.hs	Sun Nov 13 13:18:56 2011 -0500
+++ b/tools/PascalParser.hs	Sun Nov 13 14:41:02 2011 -0500
@@ -23,7 +23,7 @@
 data TypesAndVars = TypesAndVars [TypeVarDeclaration]
     deriving Show
 data TypeVarDeclaration = TypeDeclaration Identifier TypeDecl
-    | VarDeclaration Bool ([Identifier], TypeDecl) (Maybe Expression)
+    | VarDeclaration Bool ([Identifier], TypeDecl) (Maybe InitExpression)
     | FunctionDeclaration Identifier TypeDecl (Maybe Phrase)
     deriving Show
 data TypeDecl = SimpleType Identifier
@@ -49,7 +49,7 @@
         | WhileCycle Expression Phrase
         | RepeatCycle Expression [Phrase]
         | ForCycle Identifier Expression Expression Phrase
-        | WithBlock Expression Phrase
+        | WithBlock Reference Phrase
         | Phrases [Phrase]
         | SwitchCase Expression [(Expression, Phrase)] (Maybe Phrase)
         | Assignment Reference Expression
@@ -61,17 +61,32 @@
     | StringLiteral String
     | CharCode String
     | NumberLiteral String
+    | FloatLiteral String
     | HexNumber String
     | Reference Reference
     | Null
     deriving Show
 data Reference = ArrayElement [Expression] Reference
     | FunCall [Expression] Reference
+    | BuiltInFunCall [Expression] Reference
     | SimpleReference Identifier
     | Dereference Reference
     | RecordField Reference Reference
     | Address Reference
     deriving Show
+data InitExpression = InitBinOp String InitExpression InitExpression
+    | InitPrefixOp String InitExpression
+    | InitReference Identifier
+    | InitArray [InitExpression]
+    | InitRecord [(Identifier, InitExpression)]
+    | InitFloat String
+    | InitNumber String
+    | InitHexNumber String
+    | InitString String
+    | InitChar String
+    | InitNull
+    deriving Show
+
     
 pascalLanguageDef
     = emptyDef
@@ -88,7 +103,8 @@
             , "type", "var", "const", "out", "array", "packed"
             , "procedure", "function", "with", "for", "to"
             , "downto", "div", "mod", "record", "set", "nil"
-            , "string", "shortstring"
+            , "string", "shortstring", "succ", "pred", "low"
+            , "high"
             ]
     , reservedOpNames= [] 
     , caseSensitive  = False   
@@ -183,7 +199,7 @@
     init <- option Nothing $ do
         char '='
         comments
-        e <- expression
+        e <- initExpression
         comments
         return (Just e)
     return $ VarDeclaration False (ids, t) init
@@ -204,7 +220,7 @@
             return ()
         char '='
         comments
-        e <- expression
+        e <- initExpression
         comments
         return $ VarDeclaration False ([i], UnknownType) (Just e)
         
@@ -213,9 +229,9 @@
     , try (string "shortstring") >> return String
     , arrayDecl
     , recordDecl
+    , sequenceDecl >>= return . Sequence
+    , try (identifier pas) >>= return . SimpleType . Identifier
     , rangeDecl >>= return . RangeType
-    , sequenceDecl >>= return . Sequence
-    , identifier pas >>= return . SimpleType . Identifier
     ] <?> "type declaration"
     where
     arrayDecl = do
@@ -336,7 +352,7 @@
                 liftM Just functionBody
                 else
                 return Nothing
-        return $ [FunctionDeclaration i ret Nothing]
+        return $ [FunctionDeclaration i ret b]
 
 program = do
     string "program"
@@ -371,6 +387,8 @@
     where
     term = comments >> choice [
         parens pas $ expression 
+        , try $ integer pas >>= \i -> notFollowedBy (char '.') >> (return . NumberLiteral . show) i
+        , try $ float pas >>= return . FloatLiteral . show
         , try $ integer pas >>= return . NumberLiteral . show
         , stringLiteral pas >>= return . StringLiteral
         , char '#' >> many digit >>= return . CharCode
@@ -400,8 +418,8 @@
            , Infix (try $ string "or" >> return (BinOp "or")) AssocLeft
            , Infix (try $ string "xor" >> return (BinOp "xor")) AssocLeft
           ]
-        , [  Infix (try $ string "shl" >> return (BinOp "and")) AssocNone
-           , Infix (try $ string "shr" >> return (BinOp "or")) AssocNone
+        , [  Infix (try $ string "shl" >> return (BinOp "shl")) AssocNone
+           , Infix (try $ string "shr" >> return (BinOp "shr")) AssocNone
           ]
         , [Prefix (try (string "not") >> return (PrefixOp "not"))]
         ]
@@ -459,12 +477,12 @@
 withBlock = do
     try $ string "with"
     comments
-    e <- expression
+    r <- reference
     comments
     string "do"
     comments
     o <- phrase
-    return $ WithBlock e o
+    return $ WithBlock r o
     
 repeatCycle = do
     try $ string "repeat"
@@ -543,3 +561,54 @@
             char ';'
             comments
             return u
+
+initExpression = buildExpressionParser table term <?> "initialization expression"
+    where
+    term = comments >> choice [
+        try $ parens pas (commaSep pas $ initExpression) >>= return . InitArray
+        , parens pas (semiSep pas $ recField) >>= return . InitRecord
+        , try $ integer pas >>= \i -> notFollowedBy (char '.') >> (return . InitNumber . show) i
+        , try $ float pas >>= return . InitFloat . show
+        , stringLiteral pas >>= return . InitString
+        , char '#' >> many digit >>= return . InitChar
+        , char '$' >> many hexDigit >>= return . InitHexNumber
+        , try $ string "nil" >> return InitNull
+        , iD >>= return . InitReference
+        ]
+        
+    recField = do
+        i <- iD
+        spaces
+        char ':'
+        spaces
+        e <- initExpression
+        spaces
+        return (i ,e)
+
+    table = [ 
+          [  Infix (char '*' >> return (InitBinOp "*")) AssocLeft
+           , Infix (char '/' >> return (InitBinOp "/")) AssocLeft
+           , Infix (try (string "div") >> return (InitBinOp "div")) AssocLeft
+           , Infix (try (string "mod") >> return (InitBinOp "mod")) AssocLeft
+          ]
+        , [  Infix (char '+' >> return (InitBinOp "+")) AssocLeft
+           , Infix (char '-' >> return (InitBinOp "-")) AssocLeft
+           , Prefix (char '-' >> return (InitPrefixOp "-"))
+          ]
+        , [  Infix (try (string "<>") >> return (InitBinOp "<>")) AssocNone
+           , Infix (try (string "<=") >> return (InitBinOp "<=")) AssocNone
+           , Infix (try (string ">=") >> return (InitBinOp ">=")) AssocNone
+           , Infix (char '<' >> return (InitBinOp "<")) AssocNone
+           , Infix (char '>' >> return (InitBinOp ">")) AssocNone
+           , Infix (char '=' >> return (InitBinOp "=")) AssocNone
+          ]
+        , [  Infix (try $ string "and" >> return (InitBinOp "and")) AssocLeft
+           , Infix (try $ string "or" >> return (InitBinOp "or")) AssocLeft
+           , Infix (try $ string "xor" >> return (InitBinOp "xor")) AssocLeft
+          ]
+        , [  Infix (try $ string "shl" >> return (InitBinOp "and")) AssocNone
+           , Infix (try $ string "shr" >> return (InitBinOp "or")) AssocNone
+          ]
+        , [Prefix (try (string "not") >> return (InitPrefixOp "not"))]
+        ]
+    
\ No newline at end of file
--- a/tools/pas2c.hs	Sun Nov 13 13:18:56 2011 -0500
+++ b/tools/pas2c.hs	Sun Nov 13 14:41:02 2011 -0500
@@ -4,8 +4,16 @@
 import Text.PrettyPrint.HughesPJ
 import Data.Maybe
 import Data.Char
+import Text.Parsec.String
 
 
+pas2C :: String -> IO String
+pas2C fileName = do
+    ptree <- parseFromFile pascalUnit fileName
+    case ptree of
+         (Left a) -> return (show a)
+         (Right a) -> (return . render . pascal2C) a
+
 pascal2C :: PascalUnit -> Doc
 pascal2C (Unit unitName interface implementation init fin) = implementation2C implementation
 
@@ -21,17 +29,38 @@
 tvar2C :: TypeVarDeclaration -> Doc
 tvar2C (FunctionDeclaration (Identifier name) returnType Nothing) = 
     type2C returnType <+> text (name ++ "();")
-
-    
 tvar2C (FunctionDeclaration (Identifier name) returnType (Just phrase)) = 
     type2C returnType <+> text (name ++ "()") 
     $$
     phrase2C phrase
-tvar2C _ = empty
+tvar2C (TypeDeclaration (Identifier i) t) = text "type" <+> text i <+> type2C t <> text ";"
+tvar2C (VarDeclaration isConst (ids, t) mInitExpr) = 
+    if isConst then text "const" else empty
+    <+>
+    type2C t
+    <+>
+    (hsep . punctuate (char ',') . map (\(Identifier i) -> text i) $ ids)
+    <+>
+    initExpr mInitExpr
+    <>
+    text ";"
+    where
+    initExpr Nothing = empty
+    initExpr (Just e) = text "=" <+> initExpr2C e
+
+initExpr2C :: InitExpression -> Doc    
+initExpr2C _ = text "<<expression>>"
 
 type2C :: TypeDecl -> Doc
 type2C UnknownType = text "void"
-type2C _ = text "<<type>>"
+type2C String = text "string"
+type2C (SimpleType (Identifier i)) = text i
+type2C (PointerTo t) = type2C t <> text "*"
+type2C (RecordType tvs) = text "{" $+$ (nest 4 . vcat . map tvar2C $ tvs) $+$ text "}"
+type2C (RangeType r) = text "<<range type>>"
+type2C (Sequence ids) = text "<<sequence type>>"
+type2C (ArrayDecl r t) = text "<<array type>>"
+
 
 phrase2C :: Phrase -> Doc
 phrase2C (Phrases p) = text "{" $+$ (nest 4 . vcat . map phrase2C $ p) $+$ text "}"
@@ -46,15 +75,18 @@
     where
     case2C :: (Expression, Phrase) -> Doc
     case2C (e, p) = text "case" <+> parens (expr2C e) <> char ':' <> nest 4 (phrase2C p $+$ text "break;")
-{-
-        | RepeatCycle Expression Phrase
-        | ForCycle
-        -}
-phrase2C _ = empty
+phrase2C (WithBlock ref p) = text "namespace" <> parens (ref2C ref) $$ (phrase2C $ wrapPhrase p)
+phrase2C (ForCycle (Identifier i) e1 e2 p) = 
+    text "for" <> (parens . hsep . punctuate (char ';') $ [text i <+> text "=" <+> expr2C e1, text i <+> text "<=" <+> expr2C e2, text "++" <> text i])
+    $$
+    phrase2C (wrapPhrase p)
+phrase2C (RepeatCycle e p) = text "do" <+> phrase2C (Phrases p) <+> text "while" <> parens (text "!" <> parens (expr2C e))
+
 
 wrapPhrase p@(Phrases _) = p
 wrapPhrase p = Phrases [p]
 
+
 expr2C :: Expression -> Doc
 expr2C (Expression s) = text s
 expr2C (BinOp op expr1 expr2) = parens $ (expr2C expr1) <+> op2C op <+> (expr2C expr2)
@@ -79,6 +111,7 @@
 ref2C (FunCall params ref) = ref2C ref <> parens (hsep . punctuate (char ',') . map expr2C $ params)
 ref2C (Address ref) = text "&" <> ref2C ref
 
+
 op2C "or" = text "|"
 op2C "and" = text "&"
 op2C "not" = text "!"