merge default qmlfrontend
authorunc0rr
Mon, 18 May 2015 00:20:09 +0300
branchqmlfrontend
changeset 10949 6a1f5f452460
parent 10935 3a65fcd7c335 (diff)
parent 10948 18343f00c616 (current diff)
child 10951 89a7f617e091
merge default
--- a/.hgignore	Sat May 16 19:15:08 2015 +0900
+++ b/.hgignore	Mon May 18 00:20:09 2015 +0300
@@ -65,3 +65,6 @@
 glob:*.tar.*
 glob:*.or
 glob:*.res
+glob:build-*
+glob:hedgewars-build-*
+glob:*.pro.user
--- a/CMakeLists.txt	Sat May 16 19:15:08 2015 +0900
+++ b/CMakeLists.txt	Mon May 18 00:20:09 2015 +0300
@@ -27,7 +27,7 @@
     option(LUA_SYSTEM "Use system lua (on)" ON)
 endif()
 
-option(BUILD_ENGINE_LIBRARY "Enable hwengine library (off)" OFF)
+set(BUILD_ENGINE_LIBRARY ON)
 option(ANDROID "Enable Android build (off)" OFF)
 
 option(MINIMAL_FLAGS "Respect system flags as much as possible (off)" OFF)
@@ -224,7 +224,7 @@
     add_subdirectory(project_files/Android-build)
 else(ANDROID)
     add_subdirectory(bin)
-    add_subdirectory(QTfrontend)
+    add_subdirectory(qmlFrontend)
     add_subdirectory(share)
     add_subdirectory(tools)
 endif(ANDROID)
--- a/QTfrontend/net/tcpBase.cpp	Sat May 16 19:15:08 2015 +0900
+++ b/QTfrontend/net/tcpBase.cpp	Mon May 18 00:20:09 2015 +0300
@@ -111,8 +111,6 @@
     m_connected(false),
     IPCSocket(0)
 {
-    process = 0;
-
     if(!IPCServer)
     {
         IPCServer = new QTcpServer(0);
--- a/hedgewars/ArgParsers.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/ArgParsers.pas	Mon May 18 00:20:09 2015 +0300
@@ -121,17 +121,6 @@
     SetVolume(0);
 end;
 
-procedure setIpcPort(port: LongInt; var wrongParameter:Boolean);
-begin
-    if isInternal then
-        ipcPort := port
-    else
-        begin
-        WriteLn(stderr, 'ERROR: use of --port is not allowed');
-        wrongParameter := true;
-        end
-end;
-
 function parseNick(nick: shortstring): shortstring;
 begin
     if isInternal then
@@ -215,12 +204,12 @@
       otherarray: array [0..2] of string = ('--locale','--fullscreen','--showfps');
       mediaarray: array [0..9] of string = ('--fullscreen-width', '--fullscreen-height', '--width', '--height', '--depth', '--volume','--nomusic','--nosound','--locale','--fullscreen');
       allarray: array [0..17] of string = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth','--volume','--nomusic','--nosound','--locale','--fullscreen','--showfps','--altdmg','--frame-interval','--low-quality','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags');
-      reallyAll: array[0..35] of shortstring = (
-                '--prefix', '--user-prefix', '--locale', '--fullscreen-width', '--fullscreen-height', '--width',
+      reallyAll: array[0..32] of shortstring = (
+                '--locale', '--fullscreen-width', '--fullscreen-height', '--width',
                 '--height', '--frame-interval', '--volume','--nomusic', '--nosound',
                 '--fullscreen', '--showfps', '--altdmg', '--low-quality', '--raw-quality', '--stereo', '--nick',
   {deprecated}  '--depth', '--set-video', '--set-audio', '--set-other', '--set-multimedia', '--set-everything',
-  {internal}    '--internal', '--port', '--recorder', '--landpreview',
+  {internal}    '--internal', '--recorder', '--landpreview',
   {misc}        '--stats-only', '--gci', '--help','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags','--lua-test');
 var cmdIndex: byte;
 begin
@@ -232,45 +221,42 @@
 
     while (cmdIndex <= High(reallyAll)) and (cmd <> reallyAll[cmdIndex]) do inc(cmdIndex);
     case cmdIndex of
-        {--prefix}               0 : PathPrefix        := getstringParameter (arg, paramIndex, parseParameter);
-        {--user-prefix}          1 : UserPathPrefix    := getstringParameter (arg, paramIndex, parseParameter);
-        {--locale}               2 : cLocaleFName      := getstringParameter (arg, paramIndex, parseParameter);
-        {--fullscreen-width}     3 : cFullscreenWidth  := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth);
-        {--fullscreen-height}    4 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight);
-        {--width}                5 : cWindowedWidth    := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth);
-        {--height}               6 : cWindowedHeight   := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight);
-        {--frame-interval}       7 : cTimerInterval    := getLongIntParameter(arg, paramIndex, parseParameter);
-        {--volume}               8 : SetVolume          ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) );
-        {--nomusic}              9 : SetMusic           ( false );
-        {--nosound}             10 : SetSound           ( false );
-        {--fullscreen}          11 : cFullScreen       := true;
-        {--showfps}             12 : cShowFPS          := true;
-        {--altdmg}              13 : cAltDamage        := true;
-        {--low-quality}         14 : cReducedQuality   := $FFFFFFFF xor rqLowRes;
-        {--raw-quality}         15 : cReducedQuality   := getLongIntParameter(arg, paramIndex, parseParameter);
-        {--stereo}              16 : setStereoMode      ( getLongIntParameter(arg, paramIndex, parseParameter) );
-        {--nick}                17 : UserNick          := parseNick( getstringParameter(arg, paramIndex, parseParameter) );
+        {--locale}               0 : cLocaleFName      := getstringParameter (arg, paramIndex, parseParameter);
+        {--fullscreen-width}     1 : cFullscreenWidth  := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth);
+        {--fullscreen-height}    2 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight);
+        {--width}                3 : cWindowedWidth    := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth);
+        {--height}               4 : cWindowedHeight   := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight);
+        {--frame-interval}       5 : cTimerInterval    := getLongIntParameter(arg, paramIndex, parseParameter);
+        {--volume}               6 : SetVolume          ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) );
+        {--nomusic}              7 : SetMusic           ( false );
+        {--nosound}              8 : SetSound           ( false );
+        {--fullscreen}           9 : cFullScreen       := true;
+        {--showfps}             10 : cShowFPS          := true;
+        {--altdmg}              11 : cAltDamage        := true;
+        {--low-quality}         12 : cReducedQuality   := $FFFFFFFF xor rqLowRes;
+        {--raw-quality}         13 : cReducedQuality   := getLongIntParameter(arg, paramIndex, parseParameter);
+        {--stereo}              14 : setStereoMode      ( getLongIntParameter(arg, paramIndex, parseParameter) );
+        {--nick}                15 : UserNick          := parseNick( getstringParameter(arg, paramIndex, parseParameter) );
         {deprecated options}
-        {--depth}               18 : setDepth(paramIndex);
-        {--set-video}           19 : parseClassicParameter(videoarray,5,paramIndex);
-        {--set-audio}           20 : parseClassicParameter(audioarray,3,paramIndex);
-        {--set-other}           21 : parseClassicParameter(otherarray,3,paramIndex);
-        {--set-multimedia}      22 : parseClassicParameter(mediaarray,10,paramIndex);
-        {--set-everything}      23 : parseClassicParameter(allarray,14,paramIndex);
+        {--depth}               16 : setDepth(paramIndex);
+        {--set-video}           17 : parseClassicParameter(videoarray,5,paramIndex);
+        {--set-audio}           18 : parseClassicParameter(audioarray,3,paramIndex);
+        {--set-other}           19 : parseClassicParameter(otherarray,3,paramIndex);
+        {--set-multimedia}      20 : parseClassicParameter(mediaarray,10,paramIndex);
+        {--set-everything}      21 : parseClassicParameter(allarray,14,paramIndex);
         {"internal" options}
-        {--internal}            24 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF};
-        {--port}                25 : setIpcPort( getLongIntParameter(arg, paramIndex, parseParameter), parseParameter );
-        {--recorder}            26 : startVideoRecording(paramIndex);
-        {--landpreview}         27 : GameType := gmtLandPreview;
+        {--internal}            22 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF};
+        {--recorder}            23 : startVideoRecording(paramIndex);
+        {--landpreview}         24 : GameType := gmtLandPreview;
         {anything else}
-        {--stats-only}          28 : statsOnlyGame();
-        {--gci}                 29 : GciEasterEgg();
-        {--help}                30 : DisplayUsage();
-        {--no-teamtag}          31 : cTagsMask := cTagsMask and (not htTeamName);
-        {--no-hogtag}           32 : cTagsMask := cTagsMask and (not htName);
-        {--no-healthtag}        33 : cTagsMask := cTagsMask and (not htHealth);
-        {--translucent-tags}    34 : cTagsMask := cTagsMask or htTransparent;
-        {--lua-test}            35 : begin cTestLua := true; SetSound(false); cScriptName := getstringParameter(arg, paramIndex, parseParameter); WriteLn(stdout, 'Lua test file specified: ' + cScriptName);end;
+        {--stats-only}          25 : statsOnlyGame();
+        {--gci}                 26 : GciEasterEgg();
+        {--help}                27 : DisplayUsage();
+        {--no-teamtag}          28 : cTagsMask := cTagsMask and (not htTeamName);
+        {--no-hogtag}           29 : cTagsMask := cTagsMask and (not htName);
+        {--no-healthtag}        30 : cTagsMask := cTagsMask and (not htHealth);
+        {--translucent-tags}    31 : cTagsMask := cTagsMask or htTransparent;
+        {--lua-test}            32: begin cTestLua := true; SetSound(false); cScriptName := getstringParameter(arg, paramIndex, parseParameter); WriteLn(stdout, 'Lua test file specified: ' + cScriptName);end;
     else
         begin
         //Assume the first "non parameter" is the replay file, anything else is invalid
@@ -332,12 +318,12 @@
     paramTotal: LongInt;
     index, nextIndex: LongInt;
     wrongParameter: boolean;
-//var tmpInt: LongInt;
+var tmpInt: LongInt;
 begin
 
     paramIndex:= 1;
     paramTotal:= ParamCount; //-1 because pascal enumeration is inclusive
-    (*
+    
     WriteLn(stdout, 'total parameters: ' + inttostr(paramTotal));
     tmpInt:= 0;
     while (tmpInt <= paramTotal) do
@@ -345,7 +331,7 @@
         WriteLn(stdout, inttostr(tmpInt) + ': ' + {$IFDEF HWLIBRARY}argv[tmpInt]{$ELSE}paramCount(tmpInt){$ENDIF});
         inc(tmpInt);
         end;
-    *)
+    
     wrongParameter:= false;
     while (paramIndex <= paramTotal) do
         begin
@@ -362,26 +348,29 @@
 
 procedure GetParams;
 begin
-    isInternal:= (ParamStr(1) = '--internal');
-
-    UserPathPrefix := _S'.';
-    PathPrefix     := cDefaultPathPrefix;
-    recordFileName := '';
-    parseCommandLine();
-
-    if (isInternal) and (ParamCount<=1) then
+    if ParamCount > 0 then
         begin
-        WriteLn(stderr, '--internal should not be manually used');
-        GameType := gmtSyntax;
-        end;
+        isInternal:= (ParamStr(1) = '--internal');
+
+        recordFileName := '';
+        parseCommandLine();
+
+        if (isInternal) and (ParamCount<=1) then
+            begin
+            WriteLn(stderr, '--internal should not be manually used');
+            GameType := gmtSyntax;
+            end;
 
-    if (not cTestLua) and (not isInternal) and (recordFileName = '') then
-        begin
-        WriteLn(stderr, 'You must specify a replay file');
-        GameType := gmtSyntax;
-        end
-    else if (recordFileName <> '') then
-        WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"');
+        if (not cTestLua) and (not isInternal) and (recordFileName = '') then
+            begin
+            WriteLn(stderr, 'You must specify a replay file');
+            GameType := gmtSyntax;
+            end
+        else if (recordFileName <> '') then
+            WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"');
+        end 
+    else
+        GameType:= gmtSyntax;
 
     if (GameType = gmtSyntax) then
         WriteLn(stderr, 'Please use --help to see possible arguments and their usage');
--- a/hedgewars/CMakeLists.txt	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/CMakeLists.txt	Mon May 18 00:20:09 2015 +0300
@@ -104,6 +104,17 @@
     uGearsUtils.pas
     uTeams.pas
 
+    uFLAmmo.pas
+    uFLData.pas
+    uFLGameConfig.pas
+    uFLIPC.pas
+    uFLNet.pas
+    uFLScripts.pas
+    uFLSchemes.pas
+    uFLTeams.pas
+    uFLTypes.pas
+    uFLUtils.pas
+
     #these interact with everything, so compile last
     uScript.pas
     )
--- a/hedgewars/SDLh.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/SDLh.pas	Mon May 18 00:20:09 2015 +0300
@@ -846,6 +846,8 @@
 
     PSDL_Thread = Pointer;
     PSDL_mutex = Pointer;
+    PSDL_sem = Pointer;
+    PSDL_cond = Pointer;
 
     TSDL_GLattr = (
         SDL_GL_RED_SIZE,
@@ -1068,6 +1070,23 @@
 function  SDL_LockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName {$IFNDEF SDL2}name 'SDL_mutexP'{$ENDIF};
 function  SDL_UnlockMutex(mutex: PSDL_mutex): LongInt; cdecl; external SDLLibName {$IFNDEF SDL2}name 'SDL_mutexV'{$ENDIF};
 
+function  SDL_CreateCond: PSDL_cond; cdecl; external SDLLibName;
+procedure SDL_DestroyCond(cond: PSDL_cond); cdecl; external SDLLibName;
+function  SDL_CondSignal(cond: PSDL_cond): LongInt; cdecl; external SDLLibName;
+function  SDL_CondBroadcast(cond: PSDL_cond): LongInt; cdecl; external SDLLibName;
+function  SDL_CondWait(cond: PSDL_cond; mut: PSDL_mutex): LongInt; cdecl; external SDLLibName;
+function  SDL_CondWaitTimeout(cond: PSDL_cond; mut: PSDL_mutex; ms: Longword): LongInt; cdecl; external SDLLibName;
+
+
+function SDL_CreateSemaphore(initial_value: Longword): PSDL_sem; cdecl; external SDLLibName;
+procedure SDL_DestroySemaphore(sem: PSDL_sem); cdecl; external SDLLibName;
+function SDL_SemWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemTryWait(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemWaitTimeout(sem: PSDL_sem; ms: Longword): LongInt; cdecl; external SDLLibName;
+function SDL_SemPost(sem: PSDL_sem): LongInt; cdecl; external SDLLibName;
+function SDL_SemValue(sem: PSDL_sem): Longword; cdecl; external SDLLibName;
+
+
 function  SDL_GL_SetAttribute(attr: TSDL_GLattr; value: LongInt): LongInt; cdecl; external SDLLibName;
 procedure SDL_GL_SwapBuffers; cdecl; external SDLLibName;
 
--- a/hedgewars/hwLibrary.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/hwLibrary.pas	Mon May 18 00:20:09 2015 +0300
@@ -29,65 +29,97 @@
 
 Library hwLibrary;
 
-uses hwengine, uTypes, uConsts, uVariables, uSound, uCommands, uUtils,
-     uLocale{$IFDEF ANDROID}, jni{$ENDIF};
+uses hwengine
+    , uTypes
+    , uConsts
+    , uVariables
+    , uSound
+    , uCommands
+    , uUtils
+    , uLocale
+    {$IFDEF ANDROID}, jni{$ENDIF}
+    , uFLTypes
+    , uFLGameConfig
+    , uFLIPC
+    , uPhysFSLayer
+    , uFLData
+    , uFLTeams
+    , uFLScripts
+    , uFLSchemes
+    , uFLAmmo
+    , uFLNet
+    ;
 
 {$INCLUDE "config.inc"}
 
 // retrieve protocol information
-procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl; export;
+procedure HW_versionInfo(netProto: PLongInt; versionStr: PPChar); cdecl;
 begin
     netProto^:= cNetProtoVersion;
     versionStr^:= cVersionString;
 end;
 
-function HW_versionString: PChar; cdecl; export;
+function HW_versionString: PChar; cdecl;
 begin
     exit(cVersionString + '-r' + cRevisionString + ' (' + cHashString + ')');
 end;
 
 // equivalent to esc+y; when closeFrontend = true the game exits after memory cleanup
-procedure HW_terminate(closeFrontend: boolean); cdecl; export;
+procedure HW_terminate(closeFrontend: boolean); cdecl;
 begin
     closeFrontend:= closeFrontend; // avoid hint
     ParseCommand('forcequit', true);
 end;
 
-function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl; export;
+function HW_getWeaponNameByIndex(whichone: LongInt): PChar; cdecl;
 begin
     HW_getWeaponNameByIndex:= (str2pchar(trammo[Ammoz[TAmmoType(whichone+1)].NameId]));
 end;
 
-(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl; export;
+(*function HW_getWeaponCaptionByIndex(whichone: LongInt): PChar; cdecl;
 begin
     HW_getWeaponCaptionByIndex:= (str2pchar(trammoc[Ammoz[TAmmoType(whichone+1)].NameId]));
 end;
 
-function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl; export;
+function HW_getWeaponDescriptionByIndex(whichone: LongInt): PChar; cdecl;
 begin
     HW_getWeaponDescriptionByIndex:= (str2pchar(trammod[Ammoz[TAmmoType(whichone+1)].NameId]));
 end;*)
 
-function HW_getNumberOfWeapons: LongInt; cdecl; export;
+function HW_getNumberOfWeapons: LongInt; cdecl;
 begin
     HW_getNumberOfWeapons:= ord(high(TAmmoType));
 end;
 
-function HW_getMaxNumberOfHogs: LongInt; cdecl; export;
+function HW_getMaxNumberOfHogs: LongInt; cdecl;
 begin
     HW_getMaxNumberOfHogs:= cMaxHHIndex + 1;
 end;
 
-function HW_getMaxNumberOfTeams: LongInt; cdecl; export;
+function HW_getMaxNumberOfTeams: LongInt; cdecl;
 begin
     HW_getMaxNumberOfTeams:= cMaxTeams;
 end;
 
-procedure HW_memoryWarningCallback; cdecl; export;
+procedure HW_memoryWarningCallback; cdecl;
 begin
     ReleaseSound(false);
 end;
 
+procedure flibInit(localPrefix, userPrefix: PChar); cdecl;
+begin
+    initIPC;
+    uPhysFSLayer.initModule(localPrefix, userPrefix);
+    uFLNet.initModule;
+end;
+
+procedure flibFree; cdecl;
+begin
+    uFLNet.freeModule;
+    uPhysFSLayer.freemodule;
+    freeIPC;
+end;
+
 {$IFDEF ANDROID}
 function JNI_HW_versionInfoNet(env: PJNIEnv; obj: JObject):JInt;cdecl;
 begin
@@ -119,6 +151,34 @@
     Game;
 {$ELSE}
 exports
+    runQuickGame,
+    runLocalGame,
+    getPreview,
+    registerGUIMessagesCallback,
+    flibInit,
+    flibFree,
+    //game config
+    resetGameConfig,
+    setSeed,
+    getSeed,
+    setTheme,
+    setScript,
+    setScheme,
+    setAmmo,
+    getThemesList,
+    freeThemesList,
+    getThemeIcon,
+    getScriptsList,
+    getSchemesList,
+    getAmmosList,
+    getTeamsList,
+    tryAddTeam,
+    tryRemoveTeam,
+    changeTeamColor,
+    // network
+    connectOfficialServer,
+
+    // dunno what these are
     RunEngine,
     LoadLocaleWrapper,
     HW_versionInfo,
--- a/hedgewars/hwengine.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/hwengine.pas	Mon May 18 00:20:09 2015 +0300
@@ -22,12 +22,8 @@
 {$R res/hwengine.rc}
 {$ENDIF}
 
-{$IFDEF HWLIBRARY}
 unit hwengine;
 interface
-{$ELSE}
-program hwengine;
-{$ENDIF}
 
 uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler
      , uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions
@@ -38,19 +34,12 @@
      {$IFDEF ANDROID}, GLUnit{$ENDIF}
      ;
 
-{$IFDEF HWLIBRARY}
-procedure RunEngine(argc: LongInt; argv: PPChar); cdecl; export;
-
+function  RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export;
 procedure preInitEverything();
 procedure initEverything(complete:boolean);
 procedure freeEverything(complete:boolean);
 
 implementation
-{$ELSE}
-procedure preInitEverything(); forward;
-procedure initEverything(complete:boolean); forward;
-procedure freeEverything(complete:boolean); forward;
-{$ENDIF}
 
 ///////////////////////////////////////////////////////////////////////////////
 function DoTimer(Lag: LongInt): boolean;
@@ -343,8 +332,8 @@
     initEverything(true);
     WriteLnToConsole('Hedgewars engine ' + cVersionString + '-r' + cRevisionString +
                      ' (' + cHashString + ') with protocol #' + inttostr(cNetProtoVersion));
-    AddFileLog('Prefix: "' + shortstring(PathPrefix) +'"');
-    AddFileLog('UserPrefix: "' + shortstring(UserPathPrefix) +'"');
+    //AddFileLog('Prefix: "' + shortstring(PathPrefix) +'"');
+    //AddFileLog('UserPrefix: "' + shortstring(UserPathPrefix) +'"');
 
     for i:= 0 to ParamCount do
         AddFileLog(inttostr(i) + ': ' + ParamStr(i));
@@ -406,7 +395,6 @@
         begin
         if recordFileName = '' then
             begin
-            InitIPC;
             SendIPCAndWaitReply(_S'C');        // ask for game config
             end
         else
@@ -465,8 +453,8 @@
     uLand.initModule;               // computes land
     uLandPainted.initModule;        // computes drawn land
     uIO.initModule;                 // sets up sockets
-    uPhysFSLayer.initModule;
     uScript.initModule;
+    uTeams.initModule;              // clear CurrentTeam variable
 
     if complete then
     begin
@@ -490,7 +478,6 @@
         uStats.initModule;
         uStore.initModule;
         uRender.initModule;
-        uTeams.initModule;
         uVisualGears.initModule;
         uVisualGearsHandlers.initModule;
         uWorld.initModule;
@@ -534,7 +521,6 @@
     uCommands.freeModule;
     uVariables.freeModule;
     uUtils.freeModule;              // closes debug file
-    uPhysFSLayer.freeModule;
     uScript.freeModule;
 end;
 
@@ -544,7 +530,6 @@
 begin
     initEverything(false);
 
-    InitIPC;
     IPCWaitPongEvent;
     TryDo(InitStepsFlags = cifRandomize, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true);
 
@@ -557,18 +542,21 @@
     freeEverything(false);
 end;
 
-{$IFDEF HWLIBRARY}
-procedure RunEngine(argc: LongInt; argv: PPChar); cdecl; export;
+function EngineThread(p: pointer): Longint; cdecl; export;
+begin
+    if GameType = gmtLandPreview then
+        GenLandPreview()
+    else Game();
+
+    EngineThread:= 0
+end;
+
+
+function RunEngine(argc: LongInt; argv: PPChar): Longint; cdecl; export;
 begin
     operatingsystem_parameter_argc:= argc;
     operatingsystem_parameter_argv:= argv;
-{$ELSE}
-begin
-{$ENDIF}
 
-///////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////// m a i n ///////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
 {$IFDEF PAS2C}
     // workaround for pascal's ParamStr and ParamCount
     init(argc, argv);
@@ -577,36 +565,13 @@
 
     GetParams();
 
-    if GameType = gmtLandPreview then
-        GenLandPreview()
-    else if GameType <> gmtSyntax then
-        Game();
-
-    // return 1 when engine is not called correctly
     if GameType = gmtSyntax then
-        {$IFDEF PAS2C}
-        exit(HaltUsageError);
-        {$ELSE}
-        halt(HaltUsageError);
-        {$ENDIF}
-
-    if cTestLua then
-        begin
-        WriteLnToConsole(errmsgLuaTestTerm);
-        {$IFDEF PAS2C}
-        exit(HaltTestUnexpected);
-        {$ELSE}
-        halt(HaltTestUnexpected);
-        {$ENDIF}
-        end;
-
-    {$IFDEF PAS2C}
-    exit(HaltNoError);
-    {$ELSE}
-    halt(HaltNoError);
-    {$ENDIF}
-{$IFDEF HWLIBRARY}
+        RunEngine:= HaltUsageError
+    else
+    begin
+        SDL_CreateThread(@EngineThread{$IFDEF SDL2}, 'engine'{$ENDIF}, nil);
+        RunEngine:= 0
+    end
 end;
-{$ENDIF}
 
 end.
--- a/hedgewars/uDebug.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uDebug.pas	Mon May 18 00:20:09 2015 +0300
@@ -33,12 +33,7 @@
 begin
 WriteLnToConsole(Msg);
 if isFatalError then
-    begin
     ParseCommand('fatal ' + lastConsoleline, true);
-    // hint for the 'coverity' source analyzer
-    // this halt is never actually reached because ParseCommands will halt first
-    halt(HaltFatalError);
-    end;
 end;
 
 procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLAmmo.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,135 @@
+unit uFLAmmo;
+interface
+uses uFLTypes;
+
+function getAmmosList: PPChar; cdecl;
+procedure freeAmmosList;
+
+function ammoByName(s: shortstring): PAmmo;
+procedure sendAmmoConfig(var ammo: TAmmo);
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_AMMO_NAMES = 64;
+type
+    TAmmoArray = array [0..0] of TAmmo;
+    PAmmoArray = ^TAmmoArray;
+var
+    ammoList: PAmmo;
+    ammoNumber: LongInt;
+    listOfAmmoNames: array[0..MAX_AMMO_NAMES] of PChar;
+
+procedure loadAmmo;
+var f: PFSFile;
+    ammo: PAmmo;
+    ammos: PAmmoArray;
+    s: ansistring;
+    i: Longword;
+begin
+    f:= pfsOpenRead('/Config/weapons.ini');
+    ammoNumber:= 0;
+
+    if f <> nil then
+    begin
+        while (not pfsEOF(f)) do
+        begin
+            pfsReadLnA(f, s);
+
+            if (length(s) > 0) and (s[1] <> '[') then
+                inc(ammoNumber);
+        end;
+
+        //inc(ammoNumber); // add some default ammo
+
+        ammoList:= GetMem(sizeof(ammoList^) * (ammoNumber + 1));
+        ammo:= PAmmo(ammoList);
+        pfsSeek(f, 0);
+
+        while (not pfsEOF(f)) do
+        begin
+            pfsReadLnA(f, s);
+
+            i:= 1;
+            while(i <= length(s)) and (s[i] <> '=') do inc(i);
+
+            if i < length(s) then
+            begin
+                ammo^.ammoName:= copy(s, 1, i - 1);
+                delete(s, 1, i);
+                // TODO: split into 4 shortstrings
+                i:= length(s) div 4;
+                ammo^.a:= copy(s, 1, i);
+                ammo^.b:= copy(s, i + 1, i);
+                ammo^.c:= copy(s, i * 2 + 1, i);
+                ammo^.d:= copy(s, i * 3 + 1, i);
+                inc(ammo)
+            end;
+        end;
+
+        pfsClose(f)
+    end;
+end;
+
+
+function getAmmosList: PPChar; cdecl;
+var i, t, l: Longword;
+    ammo: PAmmo;
+begin
+    if ammoList = nil then
+        loadAmmo;
+
+    t:= ammoNumber;
+    if t >= MAX_AMMO_NAMES then 
+        t:= MAX_AMMO_NAMES;
+
+    ammo:= ammoList;
+    for i:= 0 to Pred(t) do
+    begin
+        l:= length(ammo^.ammoName);
+        if l >= 255 then l:= 254;
+        ammo^.ammoName[l + 1]:= #0;
+        listOfAmmoNames[i]:= @ammo^.ammoName[1];
+        inc(ammo)
+    end;
+
+    listOfAmmoNames[t]:= nil;
+
+    getAmmosList:= listOfAmmoNames
+end;
+
+function ammoByName(s: shortstring): PAmmo;
+var i: Longword;
+    ammo: PAmmo;
+begin
+    ammo:= ammoList;
+    i:= 0;
+    while (i < ammoNumber) and (ammo^.ammoName <> s) do
+    begin
+        inc(ammo);
+        inc(i)
+    end;
+
+    if i < ammoNumber then ammoByName:= ammo else ammoByName:= nil
+end;
+
+procedure freeAmmosList;
+begin
+    if ammoList <> nil then
+        FreeMem(ammoList, sizeof(ammoList^) * (ammoNumber + 1))
+end;
+
+
+procedure sendAmmoConfig(var ammo: TAmmo);
+var i: Longword;
+begin
+    with ammo do
+    begin
+        ipcToEngine('eammloadt ' + ammo.a);
+        ipcToEngine('eammprob '  + ammo.b);
+        ipcToEngine('eammdelay ' + ammo.c);
+        ipcToEngine('eammreinf ' + ammo.d);
+    end
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLData.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,85 @@
+unit uFLData;
+interface
+
+function getThemesList: PPChar; cdecl;
+procedure freeThemesList(list: PPChar); cdecl;
+function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl;
+
+const colorsSet: array[0..8] of shortstring = (
+                                               '16712196'
+                                               , '4817089'
+                                               , '1959610'
+                                               , '11878895'
+                                               , '10526880'
+                                               , '2146048'
+                                               , '16681742'
+                                               , '6239749'
+                                               , '16776961');
+
+implementation
+uses uPhysFSLayer;
+
+function getThemesList: PPChar; cdecl;
+var list, res, tmp: PPChar;
+    i, size: Longword;
+begin
+    list:= pfsEnumerateFiles('Themes');
+    size:= 0;
+    tmp:= list;
+    while tmp^ <> nil do
+    begin
+        inc(size);
+        inc(tmp)
+    end;
+
+    res:= GetMem((3 + size) * sizeof(PChar));
+    res^:= PChar(list);
+    inc(res);
+    res^:= PChar(res + size + 2);
+    inc(res);
+
+    getThemesList:= res;
+
+    for i:= 1 to size do
+    begin
+        if pfsExists('/Themes/' + shortstring(list^) + '/icon.png') then
+        begin
+            res^:= list^;
+            inc(res)
+        end;
+
+        inc(list)
+    end;
+
+    res^:= nil
+end;
+
+procedure freeThemesList(list: PPChar); cdecl;
+var listEnd: PPChar;
+begin
+    dec(list);
+    listEnd:= PPChar(list^);
+    dec(list);
+
+    pfsFreeList(PPChar(list^));
+    freeMem(list, (listEnd - list) * sizeof(PChar))
+end;
+
+function getThemeIcon(themeName: PChar; buffer: PChar; buflen: Longword): Longword; cdecl;
+var s: shortstring;
+    f: PFSFile;
+begin
+    s:= '/Themes/' + shortstring(themeName) + '/icon@2x.png';
+
+    f:= pfsOpenRead(s);
+
+    if f = nil then
+        getThemeIcon:= 0
+    else
+    begin
+        getThemeIcon:= pfsBlockRead(f, buffer, buflen);
+        pfsClose(f)
+    end;
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLGameConfig.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,347 @@
+unit uFLGameConfig;
+interface
+uses uFLTypes;
+
+procedure resetGameConfig; cdecl;
+procedure runQuickGame; cdecl;
+procedure runLocalGame; cdecl;
+procedure getPreview; cdecl;
+
+procedure registerGUIMessagesCallback(p: pointer; f: TGUICallback); cdecl;
+
+procedure setSeed(seed: PChar); cdecl;
+function  getSeed: PChar; cdecl;
+procedure setTheme(themeName: PChar); cdecl;
+procedure setScript(scriptName: PChar); cdecl;
+procedure setScheme(schemeName: PChar); cdecl;
+procedure setAmmo(ammoName: PChar); cdecl;
+
+procedure tryAddTeam(teamName: PChar); cdecl;
+procedure tryRemoveTeam(teamName: PChar); cdecl;
+procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl;
+
+implementation
+uses uFLIPC, hwengine, uFLUtils, uFLTeams, uFLData, uFLSChemes, uFLAmmo;
+
+var guiCallbackPointer: pointer;
+    guiCallbackFunction: TGUICallback;
+
+const
+    MAXCONFIGS = 5;
+    MAXARGS = 32;
+
+type
+    TGameConfig = record
+            seed: shortstring;
+            theme: shortstring;
+            script: shortstring;
+            scheme: TScheme;
+            ammo: TAmmo;
+            mapgen: Longint;
+            gameType: TGameType;
+            teams: array[0..7] of TTeam;
+            arguments: array[0..Pred(MAXARGS)] of shortstring;
+            argv: array[0..Pred(MAXARGS)] of PChar;
+            argumentsNumber: Longword;
+            end;
+    PGameConfig = ^TGameConfig;
+
+var
+    currentConfig: TGameConfig;
+
+
+procedure sendConfig(config: PGameConfig);
+var i: Longword;
+begin
+with config^ do
+begin
+    case gameType of
+    gtPreview: begin
+            if script <> '' then
+                ipcToEngine('escript ' + script);
+            ipcToEngine('eseed ' + seed);
+            ipcToEngine('e$mapgen ' + intToStr(mapgen));
+        end;
+    gtLocal: begin
+            if script <> '' then
+                ipcToEngine('escript ' + script);
+            ipcToEngine('eseed ' + seed);
+            ipcToEngine('e$mapgen ' + intToStr(mapgen));
+            ipcToEngine('e$theme ' + theme);
+
+            sendSchemeConfig(scheme);
+
+            i:= 0;
+            while (i < 8) and (teams[i].hogsNumber > 0) do
+                begin
+                    sendAmmoConfig(config^.ammo);
+                    ipcToEngine('eammstore');
+                    sendTeamConfig(teams[i]);
+                    inc(i)
+                end;
+        end;
+    end;
+
+    ipcToEngine('!');
+end;
+end;
+
+procedure queueExecution;
+var pConfig: PGameConfig;
+    i: Longword;
+begin
+    new(pConfig);
+    pConfig^:= currentConfig;
+
+    with pConfig^ do
+        for i:= 0 to Pred(MAXARGS) do
+        begin
+            if arguments[i][0] = #255 then 
+                arguments[i][255]:= #0
+            else
+                arguments[i][byte(arguments[i][0]) + 1]:= #0;
+            argv[i]:= @arguments[i][1]
+        end;
+
+    RunEngine(pConfig^.argumentsNumber, @pConfig^.argv);
+
+    sendConfig(pConfig)
+end;
+
+procedure resetGameConfig; cdecl;
+var i: Longword;
+begin
+    with currentConfig do
+    begin
+        for i:= 0 to 7 do
+            teams[i].hogsNumber:= 0
+    end
+end;
+
+procedure setSeed(seed: PChar); cdecl;
+begin
+    currentConfig.seed:= seed
+end;
+
+
+function getSeed: PChar; cdecl;
+begin
+    getSeed:= str2PChar(currentConfig.seed)
+end;
+
+function getUnusedColor: Longword;
+var i, c: Longword;
+    fColorMatched: boolean;
+begin
+    c:= 0;
+    i:= 0;
+    repeat
+        repeat
+            fColorMatched:= (currentConfig.teams[i].hogsNumber > 0) and (currentConfig.teams[i].color = c);
+            inc(i)
+        until (i >= 8) or (currentConfig.teams[i].hogsNumber = 0) or fColorMatched;
+
+        if fColorMatched then
+        begin
+            i:= 0;
+            inc(c)
+        end;
+    until not fColorMatched;
+
+    getUnusedColor:= c
+end;
+
+procedure runQuickGame; cdecl;
+begin
+    with currentConfig do
+    begin
+        gameType:= gtLocal;
+        arguments[0]:= '';
+        arguments[1]:= '--internal';
+        arguments[2]:= '--nomusic';
+        argumentsNumber:= 3;
+
+        teams[0]:= createRandomTeam;
+        teams[0].color:= 0;
+        teams[1]:= createRandomTeam;
+        teams[1].color:= 1;
+        teams[1].botLevel:= 1;
+
+        queueExecution;
+    end;
+end;
+
+
+procedure getPreview; cdecl;
+begin
+    with currentConfig do
+    begin
+        gameType:= gtPreview;
+        arguments[0]:= '';
+        arguments[1]:= '--internal';
+        arguments[2]:= '--landpreview';
+        argumentsNumber:= 3;
+
+        queueExecution;
+    end;
+end;
+
+procedure runLocalGame; cdecl;
+begin
+    with currentConfig do
+    begin
+        gameType:= gtLocal;
+        arguments[0]:= '';
+        arguments[1]:= '--internal';
+        arguments[2]:= '--nomusic';
+        argumentsNumber:= 3;
+
+        queueExecution;
+    end;
+end;
+
+
+procedure engineMessageCallback(p: pointer; msg: PChar; len: Longword);
+begin
+    if len = 128 * 256 then guiCallbackFunction(guiCallbackPointer, mtPreview, msg, len)
+end;
+
+procedure registerGUIMessagesCallback(p: pointer; f: TGUICallback); cdecl;
+begin
+    guiCallbackPointer:= p;
+    guiCallbackFunction:= f;
+
+    registerIPCCallback(nil, @engineMessageCallback)
+end;
+
+
+procedure tryAddTeam(teamName: PChar); cdecl;
+var msg: ansistring;
+    i, hn, hedgehogsNumber: Longword;
+    team: PTeam;
+    c: Longword;
+begin
+    with currentConfig do
+    begin
+        hedgehogsNumber:= 0;
+        i:= 0;
+
+        while (i < 8) and (teams[i].hogsNumber > 0) do
+        begin
+            inc(i);
+            inc(hedgehogsNumber, teams[i].hogsNumber)
+        end;
+
+        // no free space for a team or reached hogs number maximum
+        if (i > 7) or (hedgehogsNumber >= 48) then exit;
+
+        team:= teamByName(teamName);
+        if team = nil then exit;
+
+        c:= getUnusedColor;
+
+        teams[i]:= team^;
+
+        if i = 0 then hn:= 4 else hn:= teams[i - 1].hogsNumber;
+        if hn > 48 - hedgehogsNumber then hn:= 48 - hedgehogsNumber;
+        teams[i].hogsNumber:= hn;
+
+        teams[i].color:= c;
+
+        msg:= '0' + #10 + teamName;
+        guiCallbackFunction(guiCallbackPointer, mtAddPlayingTeam, @msg[1], length(msg));
+
+        msg:= teamName + #10 + colorsSet[teams[i].color];
+        guiCallbackFunction(guiCallbackPointer, mtTeamColor, @msg[1], length(msg));
+
+        msg:= teamName;
+        guiCallbackFunction(guiCallbackPointer, mtRemoveTeam, @msg[1], length(msg))
+    end
+end;
+
+
+procedure tryRemoveTeam(teamName: PChar); cdecl;
+var msg: ansistring;
+    i: Longword;
+    tn: shortstring;
+begin
+    with currentConfig do
+    begin
+        i:= 0;
+        tn:= teamName;
+        while (i < 8) and (teams[i].teamName <> tn) do
+            inc(i);
+
+        // team not found???
+        if (i > 7) then exit;
+
+        while (i < 7) and (teams[i + 1].hogsNumber > 0) do
+        begin
+            teams[i]:= teams[i + 1];
+            inc(i)
+        end;
+
+        teams[i].hogsNumber:= 0
+    end;
+
+    msg:= teamName;
+
+    guiCallbackFunction(guiCallbackPointer, mtRemovePlayingTeam, @msg[1], length(msg));
+    guiCallbackFunction(guiCallbackPointer, mtAddTeam, @msg[1], length(msg))
+end;
+
+
+procedure changeTeamColor(teamName: PChar; dir: LongInt); cdecl;
+var i, dc: Longword;
+    tn: shortstring;
+    msg: ansistring;
+begin
+    with currentConfig do
+    begin
+        i:= 0;
+        tn:= teamName;
+        while (i < 8) and (teams[i].teamName <> tn) do
+            inc(i);
+        // team not found???
+        if (i > 7) then exit;
+
+        if dir >= 0 then dc:= 1 else dc:= 8;
+        teams[i].color:= (teams[i].color + dc) mod 9;
+
+        msg:= tn + #10 + colorsSet[teams[i].color];
+        guiCallbackFunction(guiCallbackPointer, mtTeamColor, @msg[1], length(msg))
+    end
+end;
+
+procedure setTheme(themeName: PChar); cdecl;
+begin
+    currentConfig.theme:= themeName
+end;
+
+procedure setScript(scriptName: PChar); cdecl;
+begin
+    if scriptName <> 'Normal' then
+        currentConfig.script:= '/Scripts/Multiplayer/' + scriptName + '.lua'
+    else
+        currentConfig.script:= ''
+end;
+
+procedure setScheme(schemeName: PChar); cdecl;
+var scheme: PScheme;
+begin
+    scheme:= schemeByName(schemeName);
+
+    if scheme <> nil then
+        currentConfig.scheme:= scheme^
+end;
+
+procedure setAmmo(ammoName: PChar); cdecl;
+var ammo: PAmmo;
+begin
+    ammo:= ammoByName(ammoName);
+
+    if ammo <> nil then
+        currentConfig.ammo:= ammo^
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLIPC.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,237 @@
+unit uFLIPC;
+interface
+uses SDLh, uFLTypes;
+
+var msgFrontend, msgEngine, msgNet: TIPCMessage;
+    mutFrontend, mutEngine, mutNet: PSDL_mutex;
+    condFrontend, condEngine, condNet: PSDL_cond;
+
+procedure initIPC;
+procedure freeIPC;
+
+procedure ipcToEngine(s: shortstring);
+procedure ipcToEngineRaw(p: pointer; len: Longword);
+//function  ipcReadFromEngine: shortstring;
+//function  ipcCheckFromEngine: boolean;
+
+procedure ipcToNet(s: shortstring);
+procedure ipcToNetRaw(p: pointer; len: Longword);
+
+procedure ipcToFrontend(s: shortstring);
+procedure ipcToFrontendRaw(p: pointer; len: Longword);
+function ipcReadFromFrontend: shortstring;
+function ipcCheckFromFrontend: boolean;
+
+procedure registerIPCCallback(p: pointer; f: TIPCCallback);
+procedure registerNetCallback(p: pointer; f: TIPCCallback);
+
+implementation
+
+var callbackPointerF: pointer;
+    callbackFunctionF: TIPCCallback;
+    callbackListenerThreadF: PSDL_Thread;
+    callbackPointerN: pointer;
+    callbackFunctionN: TIPCCallback;
+    callbackListenerThreadN: PSDL_Thread;
+
+procedure ipcSend(var s: TIPCMessage; var msg: TIPCMessage; mut: PSDL_mutex; cond: PSDL_cond);
+begin
+    SDL_LockMutex(mut);
+
+    while (msg.str[0] > #0) or (msg.buf <> nil) do
+        SDL_CondWait(cond, mut);
+
+    msg:= s;
+    SDL_CondSignal(cond);
+    SDL_UnlockMutex(mut);
+end;
+
+function ipcRead(var msg: TIPCMessage; mut: PSDL_mutex; cond: PSDL_cond): TIPCMessage;
+var tmp: pointer;
+begin
+    SDL_LockMutex(mut);
+    while (msg.str[0] = #0) and (msg.buf = nil) do
+        SDL_CondWait(cond, mut);
+
+    if msg.buf <> nil then
+    begin
+        tmp:= msg.buf;
+        msg.buf:= GetMem(msg.len);
+        Move(tmp^, msg.buf^, msg.len);
+        FreeMem(tmp, msg.len)
+    end;
+
+    ipcRead:= msg;
+
+    msg.str[0]:= #0;
+    msg.buf:= nil;
+
+    SDL_CondSignal(cond);
+    SDL_UnlockMutex(mut)
+end;
+
+function ipcCheck(var msg: TIPCMessage; mut: PSDL_mutex): boolean;
+begin
+    SDL_LockMutex(mut);
+    ipcCheck:= (msg.str[0] > #0) or (msg.buf <> nil);
+    SDL_UnlockMutex(mut)
+end;
+
+procedure ipcToEngine(s: shortstring);
+var msg: TIPCMessage;
+begin
+    msg.str:= s;
+    msg.buf:= nil;
+    ipcSend(msg, msgEngine, mutEngine, condEngine)
+end;
+
+procedure ipcToFrontend(s: shortstring);
+var msg: TIPCMessage;
+begin
+    msg.str:= s;
+    msg.buf:= nil;
+    ipcSend(msg, msgFrontend, mutFrontend, condFrontend)
+end;
+
+procedure ipcToNet(s: shortstring);
+var msg: TIPCMessage;
+begin
+    msg.str:= s;
+    msg.buf:= nil;
+    ipcSend(msg, msgNet, mutNet, condNet)
+end;
+
+procedure ipcToEngineRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+    msg.str[0]:= #0;
+    msg.len:= len;
+    msg.buf:= GetMem(len);
+    Move(p^, msg.buf^, len);
+    ipcSend(msg, msgEngine, mutEngine, condEngine)
+end;
+
+procedure ipcToFrontendRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+    msg.str[0]:= #0;
+    msg.len:= len;
+    msg.buf:= GetMem(len);
+    Move(p^, msg.buf^, len);
+    ipcSend(msg, msgFrontend, mutFrontend, condFrontend)
+end;
+
+procedure ipcToNetRaw(p: pointer; len: Longword);
+var msg: TIPCMessage;
+begin
+    msg.str[0]:= #0;
+    msg.len:= len;
+    msg.buf:= GetMem(len);
+    Move(p^, msg.buf^, len);
+    ipcSend(msg, msgNet, mutNet, condNet)
+end;
+
+function ipcReadFromEngine: TIPCMessage;
+begin
+    ipcReadFromEngine:= ipcRead(msgFrontend, mutFrontend, condFrontend)
+end;
+
+function ipcReadFromFrontend: shortstring;
+begin
+    ipcReadFromFrontend:= ipcRead(msgEngine, mutEngine, condEngine).str
+end;
+
+function ipcReadToNet: TIPCMessage;
+begin
+    ipcReadToNet:= ipcRead(msgNet, mutNet, condNet)
+end;
+
+function ipcCheckFromEngine: boolean;
+begin
+    ipcCheckFromEngine:= ipcCheck(msgFrontend, mutFrontend)
+end;
+
+function ipcCheckFromFrontend: boolean;
+begin
+    ipcCheckFromFrontend:= ipcCheck(msgEngine, mutEngine)
+end;
+
+function  engineListener(p: pointer): Longint; cdecl; export;
+var msg: TIPCMessage;
+begin
+    engineListener:= 0;
+    repeat
+        msg:= ipcReadFromEngine();
+        if msg.buf = nil then
+            callbackFunctionF(callbackPointerF, @msg.str[1], byte(msg.str[0]))
+        else
+        begin
+            callbackFunctionF(callbackPointerF, msg.buf, msg.len);
+            FreeMem(msg.buf, msg.len)
+        end
+    until false
+end;
+
+function  netListener(p: pointer): Longint; cdecl; export;
+var msg: TIPCMessage;
+begin
+    netListener:= 0;
+    repeat
+        msg:= ipcReadToNet();
+        if msg.buf = nil then
+            callbackFunctionN(callbackPointerN, @msg.str[1], byte(msg.str[0]))
+        else
+        begin
+            callbackFunctionN(callbackPointerN, msg.buf, msg.len);
+            FreeMem(msg.buf, msg.len)
+        end
+    until false
+end;
+
+procedure registerIPCCallback(p: pointer; f: TIPCCallback);
+begin
+    callbackPointerF:= p;
+    callbackFunctionF:= f;
+    callbackListenerThreadF:= SDL_CreateThread(@engineListener{$IFDEF SDL2}, 'engineListener'{$ENDIF}, nil);
+end;
+
+procedure registerNetCallback(p: pointer; f: TIPCCallback);
+begin
+    callbackPointerN:= p;
+    callbackFunctionN:= f;
+    callbackListenerThreadN:= SDL_CreateThread(@netListener{$IFDEF SDL2}, 'netListener'{$ENDIF}, nil);
+end;
+
+procedure initIPC;
+begin
+    msgFrontend.str:= '';
+    msgFrontend.buf:= nil;
+    msgEngine.str:= '';
+    msgEngine.buf:= nil;
+    msgNet.str:= '';
+    msgNet.buf:= nil;
+
+    callbackPointerF:= nil;
+    callbackListenerThreadF:= nil;
+
+    mutFrontend:= SDL_CreateMutex;
+    mutEngine:= SDL_CreateMutex;
+    mutNet:= SDL_CreateMutex;
+    condFrontend:= SDL_CreateCond;
+    condEngine:= SDL_CreateCond;
+    condNet:= SDL_CreateCond;
+end;
+
+procedure freeIPC;
+begin
+    SDL_KillThread(callbackListenerThreadF);
+    SDL_KillThread(callbackListenerThreadN);
+    SDL_DestroyMutex(mutFrontend);
+    SDL_DestroyMutex(mutEngine);
+    SDL_DestroyMutex(mutNet);
+    SDL_DestroyCond(condFrontend);
+    SDL_DestroyCond(condEngine);
+    SDL_DestroyCond(condNet);
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLNet.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,295 @@
+unit uFLNet;
+interface
+
+procedure connectOfficialServer;
+
+procedure initModule;
+procedure freeModule;
+
+implementation
+uses SDLh, uFLIPC;
+
+const endCmd: string = #10 + #10;
+
+function getNextChar: char; forward;
+function getCurrChar: char; forward;
+procedure sendNet(s: shortstring); forward;
+
+type TCmdType = (cmd___UNKNOWN__, cmd_WARNING, cmd_TEAM_COLOR, cmd_TEAM_ACCEPTED, cmd_SERVER_VARS, cmd_SERVER_MESSAGE, cmd_SERVER_AUTH, cmd_RUN_GAME, cmd_ROUND_FINISHED, cmd_ROOMS, cmd_PROTO, cmd_PING, cmd_NOTICE, cmd_NICK, cmd_LOBBY_LEFT, cmd_LOBBY_JOINED, cmd_LEFT, cmd_KICKED, cmd_JOINING, cmd_JOINED, cmd_INFO, cmd_HH_NUM, cmd_EM, cmd_CONNECTED, cmd_CLIENT_FLAGS, cmd_CHAT, cmd_BYE, cmd_BANLIST, cmd_ASKPASSWORD);
+
+type
+    TNetState = (netDisconnected, netConnecting, netLoggedIn);
+    TParserState = record
+                       cmd: TCmdType;
+                       l: LongInt;
+                       netState: TNetState;
+                       buf: shortstring;
+                       bufpos: byte;
+                   end;
+    PHandler = procedure;
+
+var state: TParserState;
+
+// generated stuff here
+const letters: array[0..206] of char = ('A', 'S', 'K', 'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', #10, 'B', 'A', 'N', 'L', 'I', 'S', 'T', #10, 'Y', 'E', #10, 'C', 'H', 'A', 'T', #10, 'L', 'I', 'E', 'N', 'T', '_', 'F', 'L', 'A', 'G', 'S', #10, 'O', 'N', 'N', 'E', 'C', 'T', 'E', 'D', #10, 'E', 'M', #10, 'H', 'H', '_', 'N', 'U', 'M', #10, 'I', 'N', 'F', 'O', #10, 'J', 'O', 'I', 'N', 'E', 'D', #10, 'I', 'N', 'G', #10, 'K', 'I', 'C', 'K', 'E', 'D', #10, 'L', 'E', 'F', 'T', #10, 'O', 'B', 'B', 'Y', ':', 'J', 'O', 'I', 'N', 'E', 'D', #10, 'L', 'E', 'F', 'T', #10, 'N', 'I', 'C', 'K', #10, 'O', 'T', 'I', 'C', 'E', #10, 'P', 'I', 'N', 'G', #10, 'R', 'O', 'T', 'O', #10, 'R', 'O', 'O', 'M', 'S', #10, 'U', 'N', 'D', '_', 'F', 'I', 'N', 'I', 'S', 'H', 'E', 'D', #10, 'U', 'N', '_', 'G', 'A', 'M', 'E', #10, 'S', 'E', 'R', 'V', 'E', 'R', '_', 'A', 'U', 'T', 'H', #10, 'M', 'E', 'S', 'S', 'A', 'G', 'E', #10, 'V', 'A', 'R', 'S', #10, 'T', 'E', 'A', 'M', '_', 'A', 'C', 'C', 'E', 'P', 'T', 'E', 'D', #10, 'C', 'O', 'L', 'O', 'R', #10, 'W', 'A', 'R', 'N', 'I', 'N', 'G', #10, #0, #10);
+
+const commands: array[0..206] of integer = (12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, 11, 7, 0, 0, 0, 0, 0, -37, 0, 0, -36, 26, 4, 0, 0, -35, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -34, 0, 0, 0, 0, 0, 0, 0, 0, -33, 3, 0, -32, 7, 0, 0, 0, 0, 0, -31, 5, 0, 0, 0, -30, 11, 0, 0, 0, 3, 0, -29, 0, 0, 0, -28, 7, 0, 0, 0, 0, 0, -27, 22, 4, 0, 0, -26, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, -25, 0, 0, 0, 0, -24, 11, 4, 0, 0, -23, 0, 0, 0, 0, 0, -22, 10, 4, 0, 0, -21, 0, 0, 0, 0, -20, 27, 18, 4, 0, 0, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0, 0, 0, -17, 25, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, -16, 8, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, -14, 20, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, -12, 8, 0, 0, 0, 0, 0, 0, -11, 0, -10);
+
+procedure handler_ASKPASSWORD;
+begin
+end;
+
+procedure handler_BANLIST;
+begin
+end;
+
+procedure handler_BYE;
+begin
+end;
+
+procedure handler_CHAT;
+begin
+end;
+
+procedure handler_CLIENT_FLAGS;
+begin
+end;
+
+procedure handler_CONNECTED;
+begin
+end;
+
+procedure handler_EM;
+begin
+end;
+
+procedure handler_HH_NUM;
+begin
+end;
+
+procedure handler_INFO;
+begin
+end;
+
+procedure handler_JOINED;
+begin
+end;
+
+procedure handler_JOINING;
+begin
+end;
+
+procedure handler_KICKED;
+begin
+end;
+
+procedure handler_LEFT;
+begin
+end;
+
+procedure handler_LOBBY_JOINED;
+begin
+end;
+
+procedure handler_LOBBY_LEFT;
+begin
+end;
+
+procedure handler_NICK;
+begin
+end;
+
+procedure handler_NOTICE;
+begin
+end;
+
+procedure handler_PING;
+begin
+    sendNet('PONG')
+end;
+
+procedure handler_PROTO;
+begin
+end;
+
+procedure handler_ROOMS;
+begin
+end;
+
+procedure handler_ROUND_FINISHED;
+begin
+end;
+
+procedure handler_RUN_GAME;
+begin
+end;
+
+procedure handler_SERVER_AUTH;
+begin
+end;
+
+procedure handler_SERVER_MESSAGE;
+begin
+end;
+
+procedure handler_SERVER_VARS;
+begin
+end;
+
+procedure handler_TEAM_ACCEPTED;
+begin
+end;
+
+procedure handler_TEAM_COLOR;
+begin
+end;
+
+procedure handler_WARNING;
+begin
+end;
+
+procedure handler___UNKNOWN__;
+begin
+    writeln('[NET] Unknown cmd');
+end;
+
+const handlers: array[0..28] of PHandler = (@handler___UNKNOWN__, @handler_WARNING, @handler_TEAM_COLOR, @handler_TEAM_ACCEPTED, @handler_SERVER_VARS, @handler_SERVER_MESSAGE, @handler_SERVER_AUTH, @handler_RUN_GAME, @handler_ROUND_FINISHED, @handler_ROOMS, @handler_PROTO, @handler_PING, @handler_NOTICE, @handler_NICK, @handler_LOBBY_LEFT, @handler_LOBBY_JOINED, @handler_LEFT, @handler_KICKED, @handler_JOINING, @handler_JOINED, @handler_INFO, @handler_HH_NUM, @handler_EM, @handler_CONNECTED, @handler_CLIENT_FLAGS, @handler_CHAT, @handler_BYE, @handler_BANLIST, @handler_ASKPASSWORD);
+
+
+// end of generated stuff
+procedure handleTail;
+var cnt: Longint;
+    c: char;
+begin
+    state.l:= 0;
+
+    c:= getCurrChar;
+    repeat
+        if c = #10 then cnt:= 0 else cnt:= 1;
+        repeat
+            c:= getNextChar;
+            inc(cnt)
+        until (c = #0) or (c = #10);
+    until (c = #0) or (cnt = 1)
+end;
+
+var sock: PTCPSocket;
+    netReaderThread: PSDL_Thread;
+
+function getCurrChar: char;
+begin
+    getCurrChar:= state.buf[state.bufpos]
+end;
+
+function getNextChar: char;
+var r: byte;
+begin
+    if state.bufpos < byte(state.buf[0]) then
+    begin
+        inc(state.bufpos);
+    end else
+    begin
+        r:= SDLNet_TCP_Recv(sock, @state.buf[1], 255);
+        if r > 0 then
+        begin
+            state.bufpos:= 1;
+            state.buf[0]:= char(r);
+        end else
+        begin
+            state.bufpos:= 0;
+            state.buf[0]:= #0;
+        end
+    end;
+
+    getNextChar:= state.buf[state.bufpos];
+end;
+
+function netWriter(sock: PTCPSocket): LongInt; cdecl; export;
+begin
+    netWriter:= 0;
+end;
+
+function netReader(data: pointer): LongInt; cdecl; export;
+var c: char;
+    ipaddr: TIPAddress;
+begin
+    netReader:= 0;
+
+    if SDLNet_ResolveHost(ipaddr, PChar('netserver.hedgewars.org'), 46631) = 0 then
+        sock:= SDLNet_TCP_Open(ipaddr);
+
+    SDL_CreateThread(@netWriter{$IFDEF SDL2}, 'netWriter'{$ENDIF}, sock);
+
+    repeat
+        c:= getNextChar;
+        //writeln('>>>>> ', c, ' [', letters[state.l], '] ', commands[state.l]);
+        if c = #0 then
+            state.netState:= netDisconnected
+        else
+        begin
+            while (letters[state.l] <> c) and (commands[state.l] > 0) do
+                inc(state.l, commands[state.l]);
+
+            if c = letters[state.l] then
+                if commands[state.l] < 0 then
+                begin
+                    state.cmd:= TCmdType(-10 - commands[state.l]);
+                    writeln('[NET] ', state.cmd);
+                    handlers[-10 - commands[state.l]]();
+                    handleTail()
+                end
+                else
+                    inc(state.l)
+            else
+            begin
+                handler___UNKNOWN__();
+                handleTail()
+            end
+        end
+    until state.netState = netDisconnected;
+
+    sock:= nil;
+
+    writeln('[NET] netReader: disconnected');
+end;
+
+procedure sendNet(s: shortstring);
+begin
+    writeln('[NET] Send: ', s);
+    ipcToNet(s + endCmd);
+end;
+
+procedure netSendCallback(p: pointer; msg: PChar; len: Longword);
+begin
+    // W A R N I N G: totally thread-unsafe due to use of sock variable
+    SDLNet_TCP_Send(sock, msg, len);
+end;
+
+procedure connectOfficialServer;
+begin
+    if sock <> nil then
+        exit;
+
+    state.bufpos:= 0;
+    state.buf:= '';
+
+    state.l:= 0;
+    state.netState:= netConnecting;
+
+    netReaderThread:= SDL_CreateThread(@netReader{$IFDEF SDL2}, 'netReader'{$ENDIF}, nil);
+end;
+
+procedure initModule;
+begin
+    sock:= nil;
+
+    SDLNet_Init;
+
+    registerNetCallback(nil, @netSendCallback);
+end;
+
+procedure freeModule;
+begin
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLSchemes.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,202 @@
+unit uFLSchemes;
+interface
+uses uFLTypes;
+
+function getSchemesList: PPChar; cdecl;
+procedure freeSchemesList;
+
+function schemeByName(s: shortstring): PScheme;
+procedure sendSchemeConfig(var scheme: TScheme);
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_SCHEME_NAMES = 64;
+type
+    TSchemeArray = array [0..0] of TScheme;
+    PSchemeArray = ^TSchemeArray;
+var
+    schemesList: PScheme;
+    schemesNumber: LongInt;
+    listOfSchemeNames: array[0..MAX_SCHEME_NAMES] of PChar;
+    tmpScheme: TScheme;
+
+const ints: array[0 .. 16] of record
+            name: shortstring;
+            param: ^LongInt;
+        end = (
+              (name: 'damagefactor'; param: @tmpScheme.damagefactor)
+            , (name: 'turntime'; param: @tmpScheme.turntime)
+            , (name: 'health'; param: @tmpScheme.health)
+            , (name: 'suddendeath'; param: @tmpScheme.suddendeath)
+            , (name: 'caseprobability'; param: @tmpScheme.caseprobability)
+            , (name: 'minestime'; param: @tmpScheme.minestime)
+            , (name: 'landadds'; param: @tmpScheme.landadds)
+            , (name: 'minedudpct'; param: @tmpScheme.minedudpct)
+            , (name: 'explosives'; param: @tmpScheme.explosives)
+            , (name: 'minesnum'; param: @tmpScheme.minesnum)
+            , (name: 'healthprobability'; param: @tmpScheme.healthprobability)
+            , (name: 'healthcaseamount'; param: @tmpScheme.healthcaseamount)
+            , (name: 'waterrise'; param: @tmpScheme.waterrise)
+            , (name: 'healthdecrease'; param: @tmpScheme.healthdecrease)
+            , (name: 'ropepct'; param: @tmpScheme.ropepct)
+            , (name: 'getawaytime'; param: @tmpScheme.getawaytime)
+            , (name: 'worldedge'; param: @tmpScheme.worldedge)
+              );
+const bools: array[0 .. 19] of record
+            name: shortstring;
+            param: ^boolean;
+        end = (
+              (name: 'fortsmode'; param: @tmpScheme.fortsmode)
+            , (name: 'divteams'; param: @tmpScheme.divteams)
+            , (name: 'solidland'; param: @tmpScheme.solidland)
+            , (name: 'border'; param: @tmpScheme.border)
+            , (name: 'lowgrav'; param: @tmpScheme.lowgrav)
+            , (name: 'laser'; param: @tmpScheme.laser)
+            , (name: 'invulnerability'; param: @tmpScheme.invulnerability)
+            , (name: 'mines'; param: @tmpScheme.mines)
+            , (name: 'vampiric'; param: @tmpScheme.vampiric)
+            , (name: 'karma'; param: @tmpScheme.karma)
+            , (name: 'artillery'; param: @tmpScheme.artillery)
+            , (name: 'randomorder'; param: @tmpScheme.randomorder)
+            , (name: 'king'; param: @tmpScheme.king)
+            , (name: 'placehog'; param: @tmpScheme.placehog)
+            , (name: 'sharedammo'; param: @tmpScheme.sharedammo)
+            , (name: 'disablegirders'; param: @tmpScheme.disablegirders)
+            , (name: 'disablewind'; param: @tmpScheme.disablewind)
+            , (name: 'morewind'; param: @tmpScheme.morewind)
+            , (name: 'tagteam'; param: @tmpScheme.tagteam)
+            , (name: 'bottomborder'; param: @tmpScheme.bottomborder)
+              );
+
+
+procedure loadSchemes;
+var f: PFSFile;
+    scheme: PScheme;
+    schemes: PSchemeArray;
+    s: shortstring;
+    l, i, ii: Longword;
+    isFound: boolean;
+begin
+    f:= pfsOpenRead('/Config/schemes.ini');
+    schemesNumber:= 0;
+
+    if f <> nil then
+    begin
+        while (not pfsEOF(f)) and (schemesNumber = 0) do
+        begin
+            pfsReadLn(f, s);
+
+            if copy(s, 1, 5) = 'size=' then
+                schemesNumber:= strToInt(midStr(s, 6));
+        end;
+
+        //inc(schemesNumber); // add some default schemes
+
+        schemesList:= GetMem(sizeof(schemesList^) * (schemesNumber + 1));
+        schemes:= PSchemeArray(schemesList);
+
+        while (not pfsEOF(f)) do
+        begin
+            pfsReadLn(f, s);
+
+            i:= 1;
+            while(i <= length(s)) and (s[i] <> '\') do inc(i);
+
+            if i < length(s) then
+            begin
+                l:= strToInt(copy(s, 1, i - 1));
+                delete(s, 1, i);
+
+                if (l <= schemesNumber) and (l > 0) then
+                begin
+                    if copy(s, 1, 5) = 'name=' then
+                        schemes^[l - 1].schemeName:= midStr(s, 6)
+                    else if copy(s, 1, 12) = 'scriptparam=' then
+                        schemes^[l - 1].scriptparam:= midStr(s, 13) else
+                    begin
+                        ii:= 0;
+                        repeat
+                            isFound:= readInt(ints[ii].name, s, PLongInt(ints[ii].param - @tmpScheme + @schemes^[l - 1])^);
+                            inc(ii)
+                        until isFound or (ii > High(ints));
+
+                        if not isFound then
+                            begin
+                                ii:= 0;
+                                repeat
+                                    isFound:= readBool(bools[ii].name, s, PBoolean(bools[ii].param - @tmpScheme + @schemes^[l - 1])^);
+                                    inc(ii)
+                                until isFound or (ii > High(bools));
+                            end;
+                    end;
+                end;
+            end;
+        end;
+
+        pfsClose(f)
+    end;
+end;
+
+
+function getSchemesList: PPChar; cdecl;
+var i, t, l: Longword;
+    scheme: PScheme;
+begin
+    if schemesList = nil then
+        loadSchemes;
+
+    t:= schemesNumber;
+    if t >= MAX_SCHEME_NAMES then 
+        t:= MAX_SCHEME_NAMES;
+
+    scheme:= schemesList;
+    for i:= 0 to Pred(t) do
+    begin
+        l:= length(scheme^.schemeName);
+        if l >= 255 then l:= 254;
+        scheme^.schemeName[l + 1]:= #0;
+        listOfSchemeNames[i]:= @scheme^.schemeName[1];
+        inc(scheme)
+    end;
+
+    listOfSchemeNames[t]:= nil;
+
+    getSchemesList:= listOfSchemeNames
+end;
+
+function schemeByName(s: shortstring): PScheme;
+var i: Longword;
+    scheme: PScheme;
+begin
+    scheme:= schemesList;
+    i:= 0;
+    while (i < schemesNumber) and (scheme^.schemeName <> s) do
+    begin
+        inc(scheme);
+        inc(i)
+    end;
+
+    if i < schemesNumber then schemeByName:= scheme else schemeByName:= nil
+end;
+
+procedure freeSchemesList;
+begin
+    if schemesList <> nil then
+        FreeMem(schemesList, sizeof(schemesList^) * (schemesNumber + 1))
+end;
+
+
+procedure sendSchemeConfig(var scheme: TScheme);
+var i: Longword;
+begin
+    with scheme do
+    begin
+        if scheme.turntime <> 45 then
+            ipcToEngine('e$turntime ' + inttostr(scheme.turntime * 1000));
+        if scheme.minesnum <> 4 then
+            ipcToEngine('e$minesnum ' + inttostr(scheme.minesnum));
+    end
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLScripts.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,128 @@
+unit uFLScripts;
+interface
+uses uFLTypes;
+
+function getScriptsList: PPChar; cdecl;
+procedure freeScriptsList;
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_SCRIPT_NAMES = 64;
+type
+    TScript = record
+            scriptName: shortstring;
+            description: shortstring;
+            gameScheme, weapons: shortstring;
+        end;
+    PScript = ^TScript;
+var
+    scriptsList: PScript;
+    scriptsNumber: Longword;
+    listOfScriptNames: array[0..MAX_SCRIPT_NAMES] of PChar;
+
+procedure loadScript(var script: TScript; scriptName, fileName: shortstring);
+var f: PFSFile;
+begin
+    underScore2Space(scriptName);
+    script.scriptName:= scriptName;
+    script.description:= scriptName + ' script description';
+
+    f:= pfsOpenRead(copy(fileName, 1, length(fileName) - 4) + '.txt');
+
+    script.gameScheme:= '';
+    script.weapons:= '';
+
+    if f <> nil then
+    begin
+        if not pfsEOF(f) then
+        begin
+            pfsReadLn(f, script.gameScheme);
+
+            if not pfsEOF(f) then
+                pfsReadLn(f, script.weapons);
+        end;
+
+        pfsClose(f)
+    end
+end;
+
+procedure loadScripts;
+var filesList, tmp: PPChar;
+    script: PScript;
+    s: shortstring;
+    l: Longword;
+begin
+    filesList:= pfsEnumerateFiles('/Scripts/Multiplayer');
+    scriptsNumber:= 1;
+
+    tmp:= filesList;
+    while tmp^ <> nil do
+    begin
+        s:= shortstring(tmp^);
+        l:= length(s);
+        if (l > 4) and (copy(s, l - 3, 4) = '.lua') then inc(scriptsNumber);
+        inc(tmp)
+    end;
+
+    scriptsList:= GetMem(sizeof(scriptsList^) * (scriptsNumber + 1));
+
+    script:= scriptsList;
+
+    // add 'normal' script
+    script^.scriptName:= 'Normal';
+    script^.description:= 'Normal gameplay';
+    inc(script);
+
+    // fill the rest from *.lua list
+    tmp:= filesList;
+    while tmp^ <> nil do
+    begin
+        s:= shortstring(tmp^);
+        l:= length(s);
+        if (l > 4) and (copy(s, l - 3, 4) = '.lua') then 
+            begin
+                loadScript(script^, copy(s, 1, l - 4), '/Config/Scripts/' + s);
+                inc(script)
+            end;
+        inc(tmp)
+    end;
+
+    pfsFreeList(filesList)
+end;
+
+
+function getScriptsList: PPChar; cdecl;
+var i, t, l: Longword;
+    script: PScript;
+begin
+    if scriptsList = nil then
+        loadScripts;
+
+    t:= scriptsNumber;
+    if t >= MAX_SCRIPT_NAMES then 
+        t:= MAX_SCRIPT_NAMES;
+
+    script:= scriptsList;
+    for i:= 0 to Pred(t) do
+    begin
+        l:= length(script^.scriptName);
+        if l >= 255 then l:= 254;
+        script^.scriptName[l + 1]:= #0;
+        listOfScriptNames[i]:= @script^.scriptName[1];
+        inc(script)
+    end;
+
+    listOfScriptNames[t]:= nil;
+
+    getScriptsList:= listOfScriptNames
+end;
+
+
+procedure freeScriptsList;
+begin
+    if scriptsList <> nil then
+        FreeMem(scriptsList, sizeof(scriptsList^) * scriptsNumber)
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLTeams.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,182 @@
+unit uFLTeams;
+interface
+uses uFLTypes;
+
+function createRandomTeam: TTeam;
+procedure sendTeamConfig(var team: TTeam);
+
+function getTeamsList: PPChar; cdecl;
+procedure freeTeamsList;
+
+function teamByName(s: shortstring): PTeam;
+
+implementation
+uses uFLUtils, uFLIPC, uPhysFSLayer, uFLData;
+
+const MAX_TEAM_NAMES = 128;
+var
+    teamsList: PTeam;
+    teamsNumber: Longword;
+    listOfTeamNames: array[0..MAX_TEAM_NAMES] of PChar;
+
+
+function createRandomTeam: TTeam;
+var t: TTeam;
+    i: Longword;
+begin
+    with t do
+    begin
+        teamName:= 'team' + inttostr(random(100));
+
+        for i:= 0 to 7 do
+            with hedgehogs[i] do
+            begin
+                name:= 'hedgehog ' + inttostr(i);
+                hat:= 'NoHat'
+            end;
+
+        botLevel:= 0;
+        hogsNumber:= 4
+    end;
+    createRandomTeam:= t
+end;
+
+
+procedure sendTeamConfig(var team: TTeam);
+var i: Longword;
+begin
+    with team do
+    begin
+        ipcToEngine('eaddteam <hash> ' + colorsSet[color] + ' ' + teamName);
+        for i:= 0 to Pred(hogsNumber) do
+        begin
+            ipcToEngine('eaddhh ' + inttostr(botLevel) + ' 100 hog');// + hedgehogs[i].name);
+            //ipcToEngine('ehat ' + hedgehogs[i].hat);
+        end;
+    end
+end;
+
+
+procedure loadTeam(var team: TTeam; fileName: shortstring);
+var f: PFSFile;
+    section: LongInt;
+    l: shortstring;
+begin
+    section:= -1;
+    f:= pfsOpenRead(fileName);
+
+    while (not pfsEOF(f)) do
+    begin
+        pfsReadLn(f, l);
+
+        if l = '' then
+        else if l = '[Team]' then 
+            section:= 0
+        else if l[1] = '[' then
+            section:= -1
+        else if section = 0 then
+        begin // [Team]
+            if copy(l, 1, 5) = 'Name=' then
+                team.teamName:= midStr(l, 6)
+            else if copy(l, 1, 6) = 'Grave=' then
+                team.graveName:= midStr(l, 7)
+            else if copy(l, 1, 5) = 'Fort=' then
+                team.fortName:= midStr(l, 6)
+            else if copy(l, 1, 5) = 'Flag=' then
+                team.flag:= midStr(l, 6)
+        end;
+        // TODO: load hedgehogs and other stuff
+        team.botLevel:= 1
+    end;
+
+    pfsClose(f)
+end;
+
+
+procedure loadTeams;
+var filesList, tmp: PPChar;
+    team: PTeam;
+    s: shortstring;
+    l: Longword;
+begin
+    filesList:= pfsEnumerateFiles('/Config/Teams');
+    teamsNumber:= 0;
+
+    tmp:= filesList;
+    while tmp^ <> nil do
+    begin
+        s:= shortstring(tmp^);
+        l:= length(s);
+        if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then inc(teamsNumber);
+        inc(tmp)
+    end;
+
+    // TODO: no teams at all?
+    teamsList:= GetMem(sizeof(teamsList^) * teamsNumber);
+
+    team:= teamsList;
+    tmp:= filesList;
+    while tmp^ <> nil do
+    begin
+        s:= shortstring(tmp^);
+        l:= length(s);
+        if (l > 4) and (copy(s, l - 3, 4) = '.hwt') then 
+            begin
+                loadTeam(team^, '/Config/Teams/' + s);
+                inc(team)
+            end;
+        inc(tmp)
+    end;
+
+    pfsFreeList(filesList)
+end;
+
+
+function getTeamsList: PPChar; cdecl;
+var i, t, l: Longword;
+    team: PTeam;
+begin
+    if teamsList = nil then
+        loadTeams;
+
+    t:= teamsNumber;
+    if t >= MAX_TEAM_NAMES then 
+        t:= MAX_TEAM_NAMES;
+
+    team:= teamsList;
+    for i:= 0 to Pred(t) do
+    begin
+        l:= length(team^.teamName);
+        if l >= 255 then l:= 254;
+        team^.teamName[l + 1]:= #0;
+        listOfTeamNames[i]:= @team^.teamName[1];
+        inc(team)
+    end;
+
+    listOfTeamNames[t]:= nil;
+
+    getTeamsList:= listOfTeamNames
+end;
+
+function teamByName(s: shortstring): PTeam;
+var i: Longword;
+    team: PTeam;
+begin
+    team:= teamsList;
+    i:= 0;
+    while (i < teamsNumber) and (team^.teamName <> s) do
+    begin
+        inc(team);
+        inc(i)
+    end;
+
+    if i < teamsNumber then teamByName:= team else teamByName:= nil
+end;
+
+procedure freeTeamsList;
+begin
+    if teamsList <> nil then
+        FreeMem(teamsList, sizeof(teamsList^) * teamsNumber)
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLTypes.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,86 @@
+unit uFLTypes;
+interface
+
+type
+    TMessageType = (mtPreview, mtAddPlayingTeam, mtRemovePlayingTeam, mtAddTeam, mtRemoveTeam
+                   , mtTeamColor);
+
+    TIPCMessage = record
+                   str: shortstring;
+                   len: Longword;
+                   buf: Pointer
+               end;
+
+    TIPCCallback = procedure (p: pointer; msg: PChar; len: Longword);
+    TGUICallback = procedure (p: pointer; msgType: TMessageType; msg: PChar; len: Longword); cdecl;
+
+    TGameType = (gtPreview, gtLocal);
+    THedgehog = record
+            name: shortstring;
+            hat: shortstring;
+            end;
+    TTeam = record
+            teamName: shortstring;
+            flag: shortstring;
+            graveName: shortstring;
+            fortName: shortstring;
+            owner: shortstring;
+            color: Longword;
+            extDriven: boolean;
+            botLevel: Longword;
+            hedgehogs: array[0..7] of THedgehog;
+            hogsNumber: Longword;
+        end;
+    PTeam = ^TTeam;
+
+    TScheme = record
+            schemeName
+            , scriptparam : shortstring;
+            fortsmode
+            , divteams
+            , solidland
+            , border
+            , lowgrav
+            , laser
+            , invulnerability
+            , mines
+            , vampiric
+            , karma
+            , artillery
+            , randomorder
+            , king
+            , placehog
+            , sharedammo
+            , disablegirders
+            , disablewind
+            , morewind
+            , tagteam
+            , bottomborder: boolean;
+            damagefactor
+            , turntime
+            , health
+            , suddendeath
+            , caseprobability
+            , minestime
+            , landadds
+            , minedudpct
+            , explosives
+            , minesnum
+            , healthprobability
+            , healthcaseamount
+            , waterrise
+            , healthdecrease
+            , ropepct
+            , getawaytime
+            , worldedge: LongInt
+        end;
+    PScheme = ^TScheme;
+    TAmmo = record
+            ammoName: shortstring;
+            a, b, c, d: shortstring;
+        end;
+    PAmmo = ^TAmmo;
+
+implementation
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uFLUtils.pas	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,80 @@
+unit uFLUtils;
+interface
+
+function str2PChar(const s: shortstring): PChar;
+function intToStr(n: LongInt): shortstring;
+function strToInt(s: shortstring): LongInt;
+function midStr(s: shortstring; pos: byte): shortstring;
+procedure underScore2Space(var s: shortstring);
+function readInt(name, input: shortstring; var value: LongInt): boolean;
+function readBool(name, input: shortstring; var value: boolean): boolean;
+
+implementation
+
+var
+    str2PCharBuffer: array[0..255] of char;
+
+function str2PChar(const s: shortstring): PChar;
+var i: Integer;
+begin
+   for i:= 1 to Length(s) do
+      begin
+      str2PCharBuffer[i - 1] := s[i];
+      end;
+   str2PCharBuffer[Length(s)]:= #0;
+   str2PChar:= @(str2PCharBuffer[0]);
+end;
+
+function intToStr(n: LongInt): shortstring;
+begin
+    str(n, intToStr)
+end;
+
+function strToInt(s: shortstring): LongInt;
+begin
+val(s, strToInt);
+end;
+
+function midStr(s: shortstring; pos: byte): shortstring;
+begin
+    midStr:= copy(s, pos, length(s) - pos + 1)
+end;
+
+procedure underScore2Space(var s: shortstring);
+var i: LongInt;
+begin
+    for i:= length(s) downto 1 do
+        if s[i] = '_' then s[i]:= ' '
+end;
+
+function readInt(name, input: shortstring; var value: LongInt): boolean;
+var l: LongInt;
+begin
+    name:= name + '=';
+    l:= length(name);
+
+    if copy(input, 1, l) = name then
+    begin
+        value:= strToInt(midStr(input, l + 1));
+        readInt:= true
+    end
+    else
+        readInt:= false
+end;
+
+function readBool(name, input: shortstring; var value: boolean): boolean;
+var l: LongInt;
+begin
+    name:= name + '=';
+    l:= length(name);
+
+    if copy(input, 1, l) = name then
+    begin
+        value:= (length(input) > l) and (input[l + 1] <> 'f');
+        readBool:= true
+    end
+    else
+        readBool:= false
+end;
+
+end.
--- a/hedgewars/uIO.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uIO.pas	Mon May 18 00:20:09 2015 +0300
@@ -25,7 +25,6 @@
 procedure initModule;
 procedure freeModule;
 
-procedure InitIPC;
 procedure SendIPC(s: shortstring);
 procedure SendIPCXY(cmd: char; X, Y: LongInt);
 procedure SendIPCRaw(p: pointer; len: Longword);
@@ -39,7 +38,7 @@
 procedure doPut(putX, putY: LongInt; fromAI: boolean);
 
 implementation
-uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug;
+uses uConsole, uConsts, uVariables, uCommands, uUtils, uDebug, uFLIPC;
 
 const
     cSendEmptyPacketTime = 1000;
@@ -55,11 +54,9 @@
             2: (str: shortstring);
             end;
 
-var IPCSock: PTCPSocket;
-    fds: PSDLNet_SocketSet;
+var
     isPonged: boolean;
-    SocketString: shortstring;
-
+    
     headcmd: PCmd;
     lastcmd: PCmd;
 
@@ -101,23 +98,6 @@
 dispose(tmp)
 end;
 
-procedure InitIPC;
-var ipaddr: TIPAddress;
-begin
-    WriteToConsole('Init SDL_Net... ');
-    SDLTry(SDLNet_Init = 0, true);
-    fds:= SDLNet_AllocSocketSet(1);
-    SDLTry(fds <> nil, true);
-    WriteLnToConsole(msgOK);
-    WriteToConsole('Establishing IPC connection to tcp 127.0.0.1:' + IntToStr(ipcPort) + ' ');
-    {$HINTS OFF}
-    SDLTry(SDLNet_ResolveHost(ipaddr, PChar('127.0.0.1'), ipcPort) = 0, true);
-    {$HINTS ON}
-    IPCSock:= SDLNet_TCP_Open(ipaddr);
-    SDLTry(IPCSock <> nil, true);
-    WriteLnToConsole(msgOK)
-end;
-
 procedure ParseChatCommand(command: shortstring; message: shortstring;
                            messageStartIndex: Byte);
 var
@@ -177,31 +157,9 @@
 end;
 
 procedure IPCCheckSock;
-var i: LongInt;
-    s: shortstring;
 begin
-    if IPCSock = nil then
-        exit;
-
-    fds^.numsockets:= 0;
-    SDLNet_AddSocket(fds, IPCSock);
-
-    while SDLNet_CheckSockets(fds, 0) > 0 do
-    begin
-        i:= SDLNet_TCP_Recv(IPCSock, @s[1], 255 - Length(SocketString));
-        if i > 0 then
-        begin
-            s[0]:= char(i);
-            SocketString:= SocketString + s;
-            while (Length(SocketString) > 1) and (Length(SocketString) > byte(SocketString[1])) do
-            begin
-                ParseIPCCommand(copy(SocketString, 2, byte(SocketString[1])));
-                Delete(SocketString, 1, Succ(byte(SocketString[1])))
-            end
-        end
-    else
-        OutError('IPC connection lost', true)
-    end;
+    while ipcCheckFromFrontend() do
+        ParseIPCCommand(ipcReadFromFrontend())
 end;
 
 procedure LoadRecordFromFile(fileName: shortstring);
@@ -261,18 +219,11 @@
 
 procedure flushBuffer();
 begin
-    if IPCSock <> nil then
-        begin
-        SDLNet_TCP_Send(IPCSock, @sendBuffer.buf, sendBuffer.count);
-        flushDelayTicks:= 0;
-        sendBuffer.count:= 0
-        end
+
 end;
 
 procedure SendIPC(s: shortstring);
 begin
-if IPCSock <> nil then
-    begin
     if s[0] > #251 then
         s[0]:= #251;
 
@@ -281,37 +232,22 @@
     AddFileLog('[IPC out] '+ sanitizeCharForLog(s[1]));
     inc(s[0], 2);
 
-    if isSyncedCommand(s[1]) then
-        begin
-        if sendBuffer.count + byte(s[0]) >= cSendBufferSize then
-            flushBuffer();
-
-        Move(s, sendBuffer.buf[sendBuffer.count], byte(s[0]) + 1);
-        inc(sendBuffer.count, byte(s[0]) + 1);
-
-        if (s[1] = 'N') or (s[1] = '#') then
-            flushBuffer();
-        end else
-        SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])))
-    end
+    ipcToFrontend(s)
 end;
 
 procedure SendIPCRaw(p: pointer; len: Longword);
 begin
-if IPCSock <> nil then
-    begin
-    SDLNet_TCP_Send(IPCSock, p, len)
-    end
+    ipcToFrontendRaw(p, len)
 end;
 
 procedure SendIPCXY(cmd: char; X, Y: LongInt);
 var s: shortstring;
 begin
-s[0]:= #9;
-s[1]:= cmd;
-SDLNet_Write32(X, @s[2]);
-SDLNet_Write32(Y, @s[6]);
-SendIPC(s)
+    s[0]:= #9;
+    s[1]:= cmd;
+    SDLNet_Write32(X, @s[2]);
+    SDLNet_Write32(Y, @s[6]);
+    SendIPC(s)
 end;
 
 procedure IPCWaitPongEvent;
@@ -445,13 +381,13 @@
 procedure chFatalError(var s: shortstring);
 begin
     SendIPC('E' + s);
-    // TODO: should we try to clean more stuff here?
+{    // TODO: should we try to clean more stuff here?
     SDL_Quit;
 
     if IPCSock <> nil then
         halt(HaltFatalError)
     else
-        halt(HaltFatalErrorNoIPC);
+        halt(HaltFatalErrorNoIPC);}
 end;
 
 procedure doPut(putX, putY: LongInt; fromAI: boolean);
@@ -503,12 +439,9 @@
 begin
     RegisterVariable('fatal', @chFatalError, true );
 
-    IPCSock:= nil;
-
     headcmd:= nil;
     lastcmd:= nil;
     isPonged:= false;
-    SocketString:= '';
 
     hiTicks:= 0;
     flushDelayTicks:= 0;
@@ -518,10 +451,6 @@
 procedure freeModule;
 begin
     while headcmd <> nil do RemoveCmd;
-    SDLNet_FreeSocketSet(fds);
-    SDLNet_TCP_Close(IPCSock);
-    SDLNet_Quit();
-
 end;
 
 end.
--- a/hedgewars/uInputHandler.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uInputHandler.pas	Mon May 18 00:20:09 2015 +0300
@@ -331,7 +331,7 @@
 for i:= 1 to 10 do DefaultBinds[KeyNameToCode('f'+IntToStr(i))]:= 'slot '+char(i+48);
 for i:= 1 to 5  do DefaultBinds[KeyNameToCode(IntToStr(i))]:= 'timer '+IntToStr(i);
 
-loadBinds('dbind', cPathz[ptData] + '/settings.ini');
+loadBinds('dbind', cPathz[ptConfig] + '/settings.ini');
 end;
 
 procedure SetBinds(var binds: TBinds);
--- a/hedgewars/uMisc.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uMisc.pas	Mon May 18 00:20:09 2015 +0300
@@ -283,11 +283,12 @@
 
 // allocate and fill structure that will be passed to new thread
 New(image); // will be disposed in SaveScreenshot()
-if dump = 2 then
+{if dump = 2 then
      image^.filename:= shortstring(UserPathPrefix) + filename + '_landpixels' + ext
 else if dump = 1 then
      image^.filename:= shortstring(UserPathPrefix) + filename + '_land' + ext
-else image^.filename:= shortstring(UserPathPrefix) + filename + ext;
+else image^.filename:= shortstring(UserPathPrefix) + filename + ext;}
+image^.filename:= filename + ext;
 
 if dump <> 0 then
     begin
--- a/hedgewars/uPhysFSLayer.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uPhysFSLayer.pas	Mon May 18 00:20:09 2015 +0300
@@ -13,7 +13,7 @@
     {$linklib physlayer}
 {$ENDIF}
 
-procedure initModule;
+procedure initModule(localPrefix, userPrefix: PChar);
 procedure freeModule;
 
 type PFSFile = pointer;
@@ -23,11 +23,14 @@
 
 function pfsOpenRead(fname: shortstring): PFSFile;
 function pfsClose(f: PFSFile): boolean;
+function pfsSeek(f: PFSFile; pos: QWord): boolean;
 
 procedure pfsReadLn(f: PFSFile; var s: shortstring);
 procedure pfsReadLnA(f: PFSFile; var s: ansistring);
 function pfsBlockRead(f: PFSFile; buf: pointer; size: Int64): Int64;
 function pfsEOF(f: PFSFile): boolean;
+function pfsEnumerateFiles(dir: shortstring): PPChar;
+procedure pfsFreeList(list: PPChar);
 
 function pfsExists(fname: shortstring): boolean;
 
@@ -48,9 +51,12 @@
 function PHYSFS_openRead(fname: PChar): PFSFile; cdecl; external PhysfsLibName;
 function PHYSFS_eof(f: PFSFile): LongBool; cdecl; external PhysfsLibName;
 function PHYSFS_readBytes(f: PFSFile; buffer: pointer; len: Int64): Int64; cdecl; external PhysfsLibName;
+function PHYSFS_seek(f: PFSFile; pos: QWord): LongBool; cdecl; external PhysfsLibName;
 function PHYSFS_close(f: PFSFile): LongBool; cdecl; external PhysfsLibName;
 function PHYSFS_exists(fname: PChar): LongBool; cdecl; external PhysfsLibName;
 function PHYSFS_getLastError(): PChar; cdecl; external PhysfsLibName;
+function PHYSFS_enumerateFiles(dir: PChar): PPChar; cdecl; external PhysfsLibName;
+procedure PHYSFS_freeList(list: PPChar); cdecl; external PhysfsLibName;
 {$ELSE}
 function PHYSFS_readBytes(f: PFSFile; buffer: pointer; len: Int64): Int64;
 begin
@@ -83,11 +89,25 @@
     exit(PHYSFS_close(f))
 end;
 
+function pfsSeek(f: PFSFile; pos: QWord): boolean;
+begin
+    exit(PHYSFS_seek(f, 0));
+end;
+
 function pfsExists(fname: shortstring): boolean;
 begin
     exit(PHYSFS_exists(Str2PChar(fname)))
 end;
 
+function pfsEnumerateFiles(dir: shortstring): PPChar;
+begin
+    exit(PHYSFS_enumerateFiles(Str2PChar(dir)))
+end;
+
+procedure pfsFreeList(list: PPChar);
+begin
+    PHYSFS_freeList(list)
+end;
 
 procedure pfsReadLn(f: PFSFile; var s: shortstring);
 var c: char;
@@ -138,9 +158,9 @@
 procedure pfsMount(path: ansistring; mountpoint: PChar);
 begin
     if PHYSFS_mount(PChar(path), mountpoint, false) then
-        AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : ok')
+        //AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : ok')
     else
-        AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : FAILED ("' + shortstring(PHYSFS_getLastError()) + '")');
+        //AddFileLog('[PhysFS] mount ' + shortstring(path) + ' at ' + shortstring(mountpoint) + ' : FAILED ("' + shortstring(PHYSFS_getLastError()) + '")');
 end;
 
 procedure pfsMountAtRoot(path: ansistring);
@@ -148,20 +168,16 @@
     pfsMount(path, PChar(_S'/'));
 end;
 
-procedure initModule;
+procedure initModule(localPrefix, userPrefix: PChar);
 var i: LongInt;
     cPhysfsId: shortstring;
     fp: PChar;
 begin
-{$IFDEF HWLIBRARY}
     //TODO: http://icculus.org/pipermail/physfs/2011-August/001006.html
     cPhysfsId:= GetCurrentDir() + {$IFDEF DARWIN}{$IFNDEF IPHONEOS}'/Hedgewars.app/Contents/MacOS/' + {$ENDIF}{$ENDIF} ' hedgewars';
-{$ELSE}
-    cPhysfsId:= ParamStr(0);
-{$ENDIF}
 
     i:= PHYSFS_init(Str2PChar(cPhysfsId));
-    AddFileLog('[PhysFS] init: ' + inttostr(i));
+    //AddFileLog('[PhysFS] init: ' + inttostr(i));
 
     // mount system fonts paths first
     for i:= low(cFontsPaths) to high(cFontsPaths) do
@@ -171,14 +187,12 @@
                 pfsMount(ansistring(fp), PChar('/Fonts'));
         end;
 
-    pfsMountAtRoot(PathPrefix);
-    pfsMountAtRoot(UserPathPrefix + ansistring('/Data'));
+    pfsMountAtRoot(localPrefix);
+    pfsMountAtRoot(userPrefix + ansistring('/Data'));
+    pfsMount(userPrefix, PChar('/Config'));
 
     hedgewarsMountPackages;
 
-    // need access to teams and frontend configs (for bindings)
-    pfsMountAtRoot(UserPathPrefix);
-
     if cTestLua then
         begin
             pfsMountAtRoot(ansistring(ExtractFileDir(cScriptName)));
--- a/hedgewars/uRender.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uRender.pas	Mon May 18 00:20:09 2015 +0300
@@ -102,8 +102,7 @@
 
 implementation
 uses {$IFNDEF PAS2C} StrUtils, {$ENDIF}SysUtils, uVariables, uUtils, uConsts
-     {$IFDEF GL2}, uMatrix, uConsole{$ENDIF}
-     {$IF NOT DEFINED(SDL2) AND DEFINED(USE_VIDEO_RECORDING)}, glut {$ENDIF};
+     {$IFDEF GL2}, uMatrix, uConsole{$ENDIF};
 
 {$IFDEF USE_TOUCH_INTERFACE}
 const
--- a/hedgewars/uTypes.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uTypes.pas	Mon May 18 00:20:09 2015 +0300
@@ -45,7 +45,7 @@
     TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps,
             ptMapCurrent, ptDemos, ptSounds, ptGraves, ptFonts, ptForts, ptLocale,
             ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps,
-            ptSuddenDeath, ptButtons, ptShaders);
+            ptSuddenDeath, ptButtons, ptShaders, ptConfig);
 
     // Available sprites for displaying stuff
     TSprite = (sprWater, sprCloud, sprBomb, sprBigDigit, sprFrame,
--- a/hedgewars/uUtils.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uUtils.pas	Mon May 18 00:20:09 2015 +0300
@@ -329,7 +329,7 @@
 
 
 function Str2PChar(const s: shortstring): PChar;
-var i :Integer ;
+var i: Integer;
 begin
    for i:= 1 to Length(s) do
       begin
@@ -547,7 +547,7 @@
 {$ENDIF}
 {$I-}
     rwfailed:= false;
-    if (length(UserPathPrefix) > 0) then
+    (*if (length(UserPathPrefix) > 0) then
         begin
         {$IFNDEF PAS2C}
         // create directory if it doesn't exist
@@ -567,10 +567,10 @@
             inc(i)
             end;
         end;
-
+    *)
 {$IFNDEF PAS2C}
     // if everything fails, write to stderr
-    if (length(UserPathPrefix) = 0) or (rwfailed) then
+    //if (length(UserPathPrefix) = 0) or (rwfailed) then
         logFile:= stderr;
 {$ENDIF}
 {$I+}
--- a/hedgewars/uVariables.pas	Sat May 16 19:15:08 2015 +0900
+++ b/hedgewars/uVariables.pas	Mon May 18 00:20:09 2015 +0300
@@ -36,14 +36,11 @@
     cNewScreenWidth    : LongInt;
     cNewScreenHeight   : LongInt;
     cScreenResizeDelay : LongWord;
-    ipcPort            : Word;
     AprilOne           : boolean;
     cFullScreen        : boolean;
     cLocaleFName       : shortstring;
     cLocale            : shortstring;
     cTimerInterval     : LongInt;
-    PathPrefix         : ansistring;
-    UserPathPrefix     : ansistring;
     cShowFPS           : boolean;
     cFlattenFlakes     : boolean;
     cFlattenClouds     : boolean;
@@ -260,11 +257,11 @@
     // these consts are here because they would cause circular dependencies in uConsts/uTypes
     cPathz: array[TPathType] of shortstring = (
         '',                              // ptNone
-        '//',                            // ptData
+        '/',                             // ptData
         '/Graphics',                     // ptGraphics
         '/Themes',                       // ptThemes
         '/Themes/Bamboo',                // ptCurrTheme
-        '/Teams',                        // ptTeams
+        '/Config/Teams',                 // ptTeams
         '/Maps',                         // ptMaps
         '',                              // ptMapCurrent
         '/Demos',                        // ptDemos
@@ -281,7 +278,8 @@
         '/Missions/Maps',                // ptMissionMaps
         '/Graphics/SuddenDeath',         // ptSuddenDeath
         '/Graphics/Buttons',             // ptButton
-        '/Shaders'                       // ptShaders
+        '/Shaders',                      // ptShaders
+        '/Config'                        // ptConfig
     );
 
 var
@@ -2464,13 +2462,10 @@
     cLocaleFName    := 'en.txt';
     cFullScreen     := false;
 
-    UserPathPrefix  := '';
-    ipcPort         := 0;
     recordFileName  := '';
     UserNick        := '';
     cStereoMode     := smNone;
     GrayScale       := false;
-    PathPrefix      := './';
     GameType        := gmtLocal;
     cOnlyStats      := False;
     cScriptName     := '';
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/OfficialChallenges/racer_#17.hwmap	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,1 @@
+AAAA4Xic43vHWMfAc4SFi4FrB5DFrsEiy8DCyriegTGGRYeBIYldlIGfkX02A/di9qIO7sWsrxhARMf/K0AWLy9bUwffXiDBEst2qPP/b3bRzQwO7FIXGO3YpUAGmC0GGrVlMbsui9JiEPcSkLgDMn7LJRZWpkwGoIT6JSDxn4HvCWPvZq7NjHWbQc64wX2FSYmB7yVj8w0+VqZ0Bp6TLAybgY5kvcFzhOk0AwMDu+gmkNMuMFYC3ceSw3bhApM7u0AXgwgjbyvDJwZ9Bj5p9tBW/nvsoQwAuyw7Aw==
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/CMakeLists.txt	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 2.8.11)
+
+project(hedgewars)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+find_package(OpenGL)
+
+find_package(Qt5 COMPONENTS Core Qml Quick Gui)
+
+qt5_add_resources(qresources qmlFrontend.qrc)
+
+add_executable(hedgewars WIN32
+    main
+    hwengine
+    previewimageprovider
+    themeiconprovider
+    qtquick2applicationviewer/qtquick2applicationviewer
+    flib.h
+    ${qresources}
+    )
+
+include_directories(${OPENGL_INCLUDE_DIR})
+
+target_link_libraries(hedgewars Qt5::Core Qt5::Gui Qt5::Quick Qt5::Qml)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/flib.h	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,64 @@
+#ifndef FLIB_H
+#define FLIB_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum MessageType {
+    MSG_PREVIEW
+    , MSG_ADDPLAYINGTEAM
+    , MSG_REMOVEPLAYINGTEAM
+    , MSG_ADDTEAM
+    , MSG_REMOVETEAM
+    , MSG_TEAMCOLOR
+};
+
+typedef union string255_
+    {
+        struct {
+            unsigned char s[256];
+        };
+        struct {
+            unsigned char len;
+            unsigned char str[255];
+        };
+    } string255;
+
+typedef void RunEngine_t(int argc, const char ** argv);
+typedef void registerGUIMessagesCallback_t(void * context, void (*)(void * context, MessageType mt, const char * msg, uint32_t len));
+typedef void getPreview_t();
+typedef void runQuickGame_t();
+typedef void runLocalGame_t();
+typedef void resetGameConfig_t();
+typedef void setSeed_t(const char * seed);
+typedef char *getSeed_t();
+typedef void setTheme_t(const char * themeName);
+typedef void setScript_t(const char * scriptName);
+typedef void setScheme_t(const char * schemeName);
+typedef void setAmmo_t(const char * ammoName);
+typedef void flibInit_t(const char * localPrefix, const char * userPrefix);
+typedef void flibFree_t();
+
+typedef char **getThemesList_t();
+typedef void freeThemesList_t(char **list);
+typedef uint32_t getThemeIcon_t(char * theme, char * buffer, uint32_t size);
+
+typedef char **getScriptsList_t();
+typedef char **getSchemesList_t();
+typedef char **getAmmosList_t();
+
+typedef char **getTeamsList_t();
+typedef void tryAddTeam_t(const char * teamName);
+typedef void tryRemoveTeam_t(const char * teamName);
+typedef void changeTeamColor_t(const char * teamName, int32_t dir);
+
+typedef void connectOfficialServer_t();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FLIB_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/hwengine.cpp	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,267 @@
+#include <QLibrary>
+#include <QtQml>
+#include <QDebug>
+#include <QPainter>
+#include <QUuid>
+
+#include "hwengine.h"
+#include "previewimageprovider.h"
+#include "themeiconprovider.h"
+
+extern "C" {
+    RunEngine_t *flibRunEngine;
+    registerGUIMessagesCallback_t *flibRegisterGUIMessagesCallback;
+    setSeed_t *flibSetSeed;
+    getSeed_t *flibGetSeed;
+    setTheme_t *flibSetTheme;
+    setScript_t *flibSetScript;
+    setScheme_t *flibSetScheme;
+    setAmmo_t *flibSetAmmo;
+    getPreview_t *flibGetPreview;
+    runQuickGame_t *flibRunQuickGame;
+    runLocalGame_t *flibRunLocalGame;
+    flibInit_t *flibInit;
+    flibFree_t *flibFree;
+    resetGameConfig_t * flibResetGameConfig;
+    getThemesList_t *flibGetThemesList;
+    freeThemesList_t *flibFreeThemesList;
+    getThemeIcon_t *flibGetThemeIcon;
+    getScriptsList_t *flibGetScriptsList;
+    getSchemesList_t *flibGetSchemesList;
+    getAmmosList_t *flibGetAmmosList;
+    getTeamsList_t *flibGetTeamsList;
+    tryAddTeam_t * flibTryAddTeam;
+    tryRemoveTeam_t * flibTryRemoveTeam;
+    changeTeamColor_t * flibChangeTeamColor;
+
+    connectOfficialServer_t * flibConnectOfficialServer;
+}
+
+Q_DECLARE_METATYPE(MessageType);
+
+HWEngine::HWEngine(QQmlEngine *engine, QObject *parent) :
+    QObject(parent),
+    m_engine(engine)
+{
+    qRegisterMetaType<MessageType>("MessageType");
+
+    QLibrary hwlib("./libhwengine.so");
+
+    if(!hwlib.load())
+        qWarning() << "Engine library not found" << hwlib.errorString();
+
+    flibRunEngine = (RunEngine_t*) hwlib.resolve("RunEngine");
+    flibRegisterGUIMessagesCallback = (registerGUIMessagesCallback_t*) hwlib.resolve("registerGUIMessagesCallback");
+    flibGetSeed = (getSeed_t*) hwlib.resolve("getSeed");
+    flibGetPreview = (getPreview_t*) hwlib.resolve("getPreview");
+    flibRunQuickGame = (runQuickGame_t*) hwlib.resolve("runQuickGame");
+    flibRunLocalGame = (runLocalGame_t*) hwlib.resolve("runLocalGame");
+    flibInit = (flibInit_t*) hwlib.resolve("flibInit");
+    flibFree = (flibFree_t*) hwlib.resolve("flibFree");
+
+    flibSetSeed = (setSeed_t*) hwlib.resolve("setSeed");
+    flibSetTheme = (setTheme_t*) hwlib.resolve("setTheme");
+    flibSetScript = (setScript_t*) hwlib.resolve("setScript");
+    flibSetScheme = (setScheme_t*) hwlib.resolve("setScheme");
+    flibSetAmmo = (setAmmo_t*) hwlib.resolve("setAmmo");
+
+    flibGetThemesList = (getThemesList_t*) hwlib.resolve("getThemesList");
+    flibFreeThemesList = (freeThemesList_t*) hwlib.resolve("freeThemesList");
+    flibGetThemeIcon = (getThemeIcon_t*) hwlib.resolve("getThemeIcon");
+
+    flibGetScriptsList = (getScriptsList_t*) hwlib.resolve("getScriptsList");
+    flibGetSchemesList = (getSchemesList_t*) hwlib.resolve("getSchemesList");
+    flibGetAmmosList = (getAmmosList_t*) hwlib.resolve("getAmmosList");
+
+    flibResetGameConfig = (resetGameConfig_t*) hwlib.resolve("resetGameConfig");
+    flibGetTeamsList = (getTeamsList_t*) hwlib.resolve("getTeamsList");
+    flibTryAddTeam = (tryAddTeam_t*) hwlib.resolve("tryAddTeam");
+    flibTryRemoveTeam = (tryRemoveTeam_t*) hwlib.resolve("tryRemoveTeam");
+    flibChangeTeamColor = (changeTeamColor_t*) hwlib.resolve("changeTeamColor");
+
+    flibConnectOfficialServer = (connectOfficialServer_t*) hwlib.resolve("connectOfficialServer");
+
+    flibInit("/usr/home/unC0Rr/Sources/Hedgewars/Hedgewars-GC/share/hedgewars/Data", "/usr/home/unC0Rr/.hedgewars");
+    flibRegisterGUIMessagesCallback(this, &guiMessagesCallback);
+
+    ThemeIconProvider * themeIcon = (ThemeIconProvider *)m_engine->imageProvider(QLatin1String("theme"));
+    themeIcon->setFileContentsFunction(flibGetThemeIcon);
+
+    fillModels();
+}
+
+HWEngine::~HWEngine()
+{
+    flibFree();
+}
+
+void HWEngine::getPreview()
+{
+    flibSetSeed(QUuid::createUuid().toString().toLatin1());
+    flibGetPreview();
+}
+
+void HWEngine::runQuickGame()
+{
+    flibSetSeed(QUuid::createUuid().toString().toLatin1());
+    flibRunQuickGame();
+}
+
+void HWEngine::runLocalGame()
+{
+    flibRunLocalGame();
+}
+
+
+static QObject *hwengine_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+    Q_UNUSED(scriptEngine)
+
+    HWEngine *hwengine = new HWEngine(engine);
+    return hwengine;
+}
+
+void HWEngine::exposeToQML()
+{
+    qDebug("HWEngine::exposeToQML");
+    qmlRegisterSingletonType<HWEngine>("Hedgewars.Engine", 1, 0, "HWEngine", hwengine_singletontype_provider);
+}
+
+
+void HWEngine::guiMessagesCallback(void *context, MessageType mt, const char * msg, uint32_t len)
+{
+    HWEngine * obj = (HWEngine *)context;
+    QByteArray b = QByteArray(msg, len);
+
+    qDebug() << "FLIPC in" << b.size() << b;
+
+    QMetaObject::invokeMethod(obj, "engineMessageHandler", Qt::QueuedConnection, Q_ARG(MessageType, mt), Q_ARG(QByteArray, b));
+}
+
+void HWEngine::engineMessageHandler(MessageType mt, const QByteArray &msg)
+{
+    switch(mt)
+    {
+    case MSG_PREVIEW: {
+        PreviewImageProvider * preview = (PreviewImageProvider *)m_engine->imageProvider(QLatin1String("preview"));
+        preview->setPixmap(msg);
+        emit previewImageChanged();
+        break;
+    }
+    case MSG_ADDPLAYINGTEAM: {
+        QStringList l = QString::fromUtf8(msg).split('\n');
+        emit playingTeamAdded(l[1], l[0].toInt(), true);
+        break;
+    }
+    case MSG_REMOVEPLAYINGTEAM: {
+        emit playingTeamRemoved(msg);
+        break;
+    }
+    case MSG_ADDTEAM: {
+        emit localTeamAdded(msg, 0);
+        break;
+    }
+    case MSG_REMOVETEAM: {
+        emit localTeamRemoved(msg);
+        break;
+    }
+    case MSG_TEAMCOLOR: {
+        QStringList l = QString::fromUtf8(msg).split('\n');
+        emit teamColorChanged(l[0], QColor::fromRgba(l[1].toInt()).name());
+        break;
+    }
+    }
+}
+
+QString HWEngine::currentSeed()
+{
+    return QString::fromLatin1(flibGetSeed());
+}
+
+void HWEngine::fillModels()
+{
+    QStringList resultModel;
+
+    char ** themes = flibGetThemesList();
+    for (char **i = themes; *i != NULL; i++)
+        resultModel << QString::fromUtf8(*i);
+    flibFreeThemesList(themes);
+
+    m_engine->rootContext()->setContextProperty("themesModel", QVariant::fromValue(resultModel));
+
+    // scripts model
+    resultModel.clear();
+    for (char **i = flibGetScriptsList(); *i != NULL; i++)
+        resultModel << QString::fromUtf8(*i);
+
+    m_engine->rootContext()->setContextProperty("scriptsModel", QVariant::fromValue(resultModel));
+
+    // schemes model
+    resultModel.clear();
+    for (char **i = flibGetSchemesList(); *i != NULL; i++)
+        resultModel << QString::fromUtf8(*i);
+
+    m_engine->rootContext()->setContextProperty("schemesModel", QVariant::fromValue(resultModel));
+
+    // ammos model
+    resultModel.clear();
+    for (char **i = flibGetAmmosList(); *i != NULL; i++)
+        resultModel << QString::fromUtf8(*i);
+
+    m_engine->rootContext()->setContextProperty("ammosModel", QVariant::fromValue(resultModel));
+}
+
+void HWEngine::getTeamsList()
+{
+    char ** teams = flibGetTeamsList();
+    for (char **i = teams; *i != NULL; i++) {
+        QString team = QString::fromUtf8(*i);
+
+        emit localTeamAdded(team, 0);
+    }
+}
+
+void HWEngine::tryAddTeam(const QString &teamName)
+{
+    flibTryAddTeam(teamName.toUtf8().constData());
+}
+
+void HWEngine::tryRemoveTeam(const QString &teamName)
+{
+    flibTryRemoveTeam(teamName.toUtf8().constData());
+}
+
+void HWEngine::resetGameConfig()
+{
+    flibResetGameConfig();
+}
+
+void HWEngine::changeTeamColor(const QString &teamName, int dir)
+{
+    flibChangeTeamColor(teamName.toUtf8().constData(), dir);
+}
+
+void HWEngine::connect(const QString &host, quint16 port)
+{
+    flibConnectOfficialServer();
+}
+
+void HWEngine::setTheme(const QString &theme)
+{
+    flibSetTheme(theme.toUtf8().constData());
+}
+
+void HWEngine::setScript(const QString &script)
+{
+    flibSetScript(script.toUtf8().constData());
+}
+
+void HWEngine::setScheme(const QString &scheme)
+{
+    flibSetScheme(scheme.toUtf8().constData());
+}
+
+void HWEngine::setAmmo(const QString &ammo)
+{
+    flibSetAmmo(ammo.toUtf8().constData());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/hwengine.h	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,62 @@
+#ifndef HWENGINE_H
+#define HWENGINE_H
+
+#include <QObject>
+#include <QByteArray>
+#include <QVector>
+#include <QPixmap>
+
+#include "flib.h"
+
+class QQmlEngine;
+
+class HWEngine : public QObject
+{
+    Q_OBJECT
+public:
+    explicit HWEngine(QQmlEngine * engine, QObject *parent = 0);
+    ~HWEngine();
+
+    static void exposeToQML();
+    Q_INVOKABLE void getPreview();
+    Q_INVOKABLE void runQuickGame();
+    Q_INVOKABLE void runLocalGame();
+    Q_INVOKABLE QString currentSeed();
+    Q_INVOKABLE void getTeamsList();
+    Q_INVOKABLE void resetGameConfig();
+
+    Q_INVOKABLE void setTheme(const QString & theme);
+    Q_INVOKABLE void setScript(const QString & script);
+    Q_INVOKABLE void setScheme(const QString & scheme);
+    Q_INVOKABLE void setAmmo(const QString & ammo);
+
+    Q_INVOKABLE void tryAddTeam(const QString & teamName);
+    Q_INVOKABLE void tryRemoveTeam(const QString & teamName);
+    Q_INVOKABLE void changeTeamColor(const QString & teamName, int dir);
+
+    Q_INVOKABLE void connect(const QString & host, quint16 port);
+
+signals:
+    void previewImageChanged();
+    void localTeamAdded(const QString & teamName, int aiLevel);
+    void localTeamRemoved(const QString & teamName);
+
+    void playingTeamAdded(const QString & teamName, int aiLevel, bool isLocal);
+    void playingTeamRemoved(const QString & teamName);
+
+    void teamColorChanged(const QString & teamName, const QString & colorValue);
+
+public slots:
+
+private:
+    QQmlEngine * m_engine;
+
+    static void guiMessagesCallback(void * context, MessageType mt, const char * msg, uint32_t len);
+    void fillModels();
+
+private slots:
+    void engineMessageHandler(MessageType mt, const QByteArray &msg);
+};
+
+#endif // HWENGINE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/main.cpp	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,27 @@
+#include <QtGui/QGuiApplication>
+#include <QQmlEngine>
+
+#include "qtquick2applicationviewer/qtquick2applicationviewer.h"
+#include "hwengine.h"
+#include "previewimageprovider.h"
+#include "themeiconprovider.h"
+
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    HWEngine::exposeToQML();
+
+    Q_INIT_RESOURCE(qmlFrontend);
+
+    QtQuick2ApplicationViewer viewer;
+
+    viewer.engine()->addImageProvider(QLatin1String("preview"), new PreviewImageProvider());
+    viewer.engine()->addImageProvider(QLatin1String("theme"), new ThemeIconProvider());
+
+    viewer.setSource(QUrl("qrc:/qml/qmlFrontend/main.qml"));
+    viewer.showExpanded();
+
+    return app.exec();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/previewimageprovider.cpp	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,36 @@
+#include "previewimageprovider.h"
+
+PreviewImageProvider::PreviewImageProvider()
+        : QQuickImageProvider(QQuickImageProvider::Pixmap)
+{
+}
+
+QPixmap PreviewImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+{
+    Q_UNUSED(id);
+    Q_UNUSED(requestedSize);
+
+    if (size)
+        *size = m_px.size();
+
+    return m_px;
+}
+
+void PreviewImageProvider::setPixmap(const QByteArray &px)
+{
+    QVector<QRgb> colorTable;
+    colorTable.resize(256);
+    for(int i = 0; i < 256; ++i)
+        colorTable[i] = qRgba(255, 255, 0, i);
+
+    const quint8 *buf = (const quint8*) px.constData();
+    QImage im(buf, 256, 128, QImage::Format_Indexed8);
+    im.setColorTable(colorTable);
+
+    m_px = QPixmap::fromImage(im, Qt::ColorOnly);
+    //QPixmap pxres(px.size());
+    //QPainter p(&pxres);
+
+    //p.fillRect(pxres.rect(), linearGrad);
+    //p.drawPixmap(0, 0, px);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/previewimageprovider.h	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,21 @@
+#ifndef PREVIEWIMAGEPROVIDER_H
+#define PREVIEWIMAGEPROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QPixmap>
+#include <QSize>
+
+class PreviewImageProvider : public QQuickImageProvider
+{
+public:
+    PreviewImageProvider();
+
+    QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+
+    void setPixmap(const QByteArray & px);
+
+private:
+    QPixmap m_px;
+};
+
+#endif // PREVIEWIMAGEPROVIDER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/Connect.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+    HWButton {
+        id: btnNetConnect
+        x: 80
+        y: 80
+        width: 256
+        height: 128
+
+        onClicked: HWEngine.connect("netserver.hedgewars.org", 46631);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/First.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Rectangle {
+    HWButton {
+        id: btnLocalGame
+        x: 8
+        y: 80
+        width: 166
+        height: 166
+
+        onClicked: pages.currentPage = "LocalGame"
+    }
+
+    HWButton {
+        id: btnNetwork
+        x: 192
+        y: 80
+        width: 166
+        height: 166
+
+        onClicked: pages.currentPage = "Connect"
+    }
+
+    HWButton {
+        id: btnAbout
+        x: 100
+        y: 16
+        width: 200
+        height: 50
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/GameConfig.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,298 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+
+Rectangle {
+    HWButton {
+        id: btnPreview
+        x: 50
+        y: 16
+        width: 256
+        height: 128
+
+        onClicked: HWEngine.getPreview()
+
+        Connections {
+            target: HWEngine
+            onPreviewImageChanged: previewImage.source = "image://preview/" + HWEngine.currentSeed()
+        }
+
+        Image {
+            id: previewImage
+            x: 0
+            y: 0
+            width: 256
+            height: 128
+            cache: false
+        }
+    }
+
+    HWButton {
+        id: btnRunGame
+        x: 600
+        y: 440
+        width: 32
+        height: 32
+
+        onClicked: HWEngine.runLocalGame()
+    }
+
+    HWComboBox {
+        id: cbTheme
+        x: 50
+        y: 160
+        width: 256
+        height: 64
+
+        model: themesModel
+        delegate: Rectangle {
+            height: 25
+            width: 100
+            color: "transparent"
+
+            property alias itemIconSource: themeIcon.source
+            property alias itemText: themeName.text
+
+            Row {
+                Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+                Text {id: themeName; text: modelData }
+            }
+
+            MouseArea {
+                 z: 1
+                 anchors.fill: parent
+                 onClicked: {
+                     cbTheme.currentIndex = index
+                     HWEngine.setTheme(themeName.text)
+                 }
+            }
+        }
+    }
+
+    HWComboBox {
+        id: cbScript
+        x: 50
+        y: 256
+        width: 256
+        height: 64
+
+        model: scriptsModel
+        delegate: Rectangle {
+            height: 25
+            width: 100
+            color: "transparent"
+
+            property string itemIconSource: ""
+            property alias itemText: scriptName.text
+
+            Row {
+                //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+                Text {id: scriptName; text: modelData }
+            }
+
+            MouseArea {
+                 z: 1
+                 anchors.fill: parent
+                 onClicked: {
+                     cbScript.currentIndex = index
+                     HWEngine.setScript(scriptName.text)
+                 }
+            }
+        }
+    }
+
+    HWComboBox {
+        id: cbScheme
+        x: 50
+        y: 336
+        width: 256
+        height: 64
+
+        model: schemesModel
+        delegate: Rectangle {
+            height: 25
+            width: 100
+            color: "transparent"
+
+            property string itemIconSource: ""
+            property alias itemText: schemeName.text
+
+            Row {
+                //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+                Text {id: schemeName; text: modelData }
+            }
+
+            MouseArea {
+                 z: 1
+                 anchors.fill: parent
+                 onClicked: {
+                     cbScheme.currentIndex = index
+                     HWEngine.setScheme(schemeName.text)
+                 }
+            }
+        }
+    }
+
+
+    HWComboBox {
+        id: cbAmmo
+        x: 50
+        y: 416
+        width: 256
+        height: 64
+
+        model: ammosModel
+        delegate: Rectangle {
+            height: 25
+            width: 100
+            color: "transparent"
+
+            property string itemIconSource: ""
+            property alias itemText: ammoName.text
+
+            Row {
+                //Image {id: themeIcon; width: height; height: parent.height; source: "image://theme/" + modelData}
+                Text {id: ammoName; text: modelData }
+            }
+
+            MouseArea {
+                 z: 1
+                 anchors.fill: parent
+                 onClicked: {
+                     cbAmmo.currentIndex = index
+                     HWEngine.setAmmo(ammoName.text)
+                 }
+            }
+        }
+    }
+
+    ListView {
+        id: playingTeamsList
+        x: 440
+        y: 16
+        width: 100
+        height: 192
+        highlight: Rectangle { color: "#eaea00"; radius: 4 }
+        focus: true
+        clip: true
+
+        model: ListModel {
+            id: playingTeamsModel
+        }
+
+        delegate: Rectangle {
+            id: teamDelegate
+            height: 24
+            width: parent.width
+            radius: 8
+            border.width: 2
+            border.color: "#eaea00"
+
+            Row {
+                Rectangle {
+                    height: 20
+                    width: height
+                    color: teamColor
+                    border.width: 2
+                    border.color: "#eaea00"                    
+
+                    MouseArea {
+                        z: 1
+                        anchors.fill: parent
+                        acceptedButtons: Qt.LeftButton | Qt.RightButton
+                        onClicked: {
+                            if (mouse.button === Qt.LeftButton)
+                                HWEngine.changeTeamColor(name, 1)
+                            else if (mouse.button === Qt.RightButton)
+                                HWEngine.changeTeamColor(name, -1)
+                        }
+                        onWheel: HWEngine.changeTeamColor(name, -wheel.angleDelta.y)
+                   }
+                }
+
+                Text { text: name
+                    MouseArea {
+                        z: 1
+                        anchors.fill: parent
+                        onClicked: HWEngine.tryRemoveTeam(name)
+                   }
+                }
+            }
+
+
+        }
+
+        Connections {
+            target: HWEngine
+            onPlayingTeamAdded: playingTeamsModel.append({
+                                                             "aiLevel": aiLevel
+                                                             , "name": teamName
+                                                             , "local": isLocal
+                                                             , "teamColor": "#000000"
+                                                         })
+            onPlayingTeamRemoved: {
+                var i = playingTeamsModel.count - 1;
+                while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i
+
+                if(i >= 0) playingTeamsModel.remove(i, 1)
+            }
+            onTeamColorChanged: {
+                var i = playingTeamsModel.count - 1;
+                while ((i >= 0) && (playingTeamsModel.get(i).name !== teamName)) --i
+
+                if(i >= 0) playingTeamsModel.setProperty(i, "teamColor", colorValue)
+            }
+        }
+    }
+
+    ListView {
+        id: localTeamsList
+        x: 440
+        y: 224
+        width: 100
+        height: 192
+        highlight: Rectangle { color: "#eaea00"; radius: 4 }
+        focus: true
+        clip: true
+
+        model: ListModel {
+            id: localTeamsModel
+        }
+
+        delegate: Rectangle {
+            id: localTeamDelegate
+            height: 24
+            width: parent.width
+            radius: 8
+            border.width: 2
+            border.color: "#eaea00"
+
+            Row {
+                Text { text: name }
+            }
+
+            MouseArea {
+                 z: 1
+                 anchors.fill: parent
+                 onClicked: HWEngine.tryAddTeam(name)
+            }
+        }
+
+        Connections {
+            target: HWEngine
+            onLocalTeamAdded: localTeamsModel.append({"aiLevel": aiLevel, "name": teamName})
+            onLocalTeamRemoved: {
+                var i = localTeamsModel.count - 1;
+                while ((i >= 0) && (localTeamsModel.get(i).name !== teamName)) --i
+
+                if(i >= 0) localTeamsModel.remove(i, 1)
+            }
+        }
+    }
+
+    Component.onCompleted: {
+        HWEngine.resetGameConfig()
+        HWEngine.getTeamsList()
+        HWEngine.getPreview()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/HWButton.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,42 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: hwbutton
+    width: 360
+    height: 360
+    color: "#15193a"
+    radius: 8
+    border.width: 4
+    opacity: 1
+
+    signal clicked()
+
+    Behavior on border.color {
+        ColorAnimation {}
+    }
+
+    MouseArea {
+        id: mousearea
+        anchors.fill: parent
+        hoverEnabled: true
+        onClicked: parent.clicked()
+    }
+
+    states: [
+        State {
+            when: mousearea.containsMouse
+
+            PropertyChanges {
+                target: hwbutton
+                border.color: "#eaea00"
+            }
+        }
+        , State {
+            when: !mousearea.containsMouse
+
+            PropertyChanges {
+                target: hwbutton
+                border.color: "#ea761d"
+            }
+    }]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/HWComboBox.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,57 @@
+import QtQuick 2.0
+import QtQuick.Window 2.1
+
+HWButton {
+    property alias model: itemsList.model
+    property alias delegate: itemsList.delegate
+    property alias currentIndex: itemsList.currentIndex
+
+    Window {
+        id: selection
+        visibility: Window.Hidden
+        modality: Qt.WindowModal
+        flags: Qt.Dialog
+
+        ListView {
+            id: itemsList
+            x: 0
+            y: 64
+            anchors.fill: parent
+            anchors.bottomMargin: 32
+            highlight: Rectangle { color: "#eaea00"; radius: 4 }
+            focus: true
+
+            onCurrentItemChanged: {
+                cbIcon.source = currentItem.itemIconSource
+                cbText.text = currentItem.itemText
+            }
+        }
+
+        HWButton {
+            x: parent.width - 32
+            y: parent.height - 32
+            width: 32
+            height: 32
+
+            onClicked: selection.visibility = Window.Hidden;
+        }
+    }
+
+    Row {
+        anchors.fill: parent
+        anchors.margins: 4
+
+        Image {
+            id: cbIcon
+            width: height
+            height: parent.height
+        }
+
+        Text {
+            id: cbText
+            height: parent.height
+        }
+    }
+
+    onClicked: selection.visibility = Window.Windowed
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/LocalGame.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+import Hedgewars.Engine 1.0
+
+Rectangle {
+    HWButton {
+        id: btnQuickGame
+        x: 8
+        y: 66
+        width: 150
+        height: 150
+
+        onClicked: HWEngine.runQuickGame()
+    }
+
+    HWButton {
+        id: btnMultiplayer
+        x: 192
+        y: 66
+        width: 150
+        height: 150
+
+        onClicked: pages.currentPage = "GameConfig"
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qml/qmlFrontend/main.qml	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,36 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: pages
+    width: 800
+    height: 600
+
+    property variant pagesList  : [
+        "First"
+        , "LocalGame"
+        , "GameConfig"
+        , "Connect"
+    ];
+
+    property string  currentPage : "First";
+
+    Repeater {
+        model: pagesList;
+
+        delegate: Loader {
+            active: false
+            asynchronous: true
+            anchors.fill: parent
+            visible: (currentPage === modelData)
+            source: "%1.qml".arg(modelData)
+            onVisibleChanged:      loadIfNotLoaded();
+            Component.onCompleted: loadIfNotLoaded();
+
+            function loadIfNotLoaded ()
+            {
+                if (visible && !active)
+                    active = true;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qmlFrontend.qrc	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,11 @@
+<RCC>
+    <qresource prefix="/">
+        <file>qml/qmlFrontend/First.qml</file>
+        <file>qml/qmlFrontend/GameConfig.qml</file>
+        <file>qml/qmlFrontend/HWButton.qml</file>
+        <file>qml/qmlFrontend/HWComboBox.qml</file>
+        <file>qml/qmlFrontend/LocalGame.qml</file>
+        <file>qml/qmlFrontend/main.qml</file>
+        <file>qml/qmlFrontend/Connect.qml</file>
+    </qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.cpp	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,81 @@
+// checksum 0x4f6f version 0x90005
+/*
+  This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+  QtQuick2ApplicationViewer is a convenience class containing mobile device specific
+  code such as screen orientation handling. Also QML paths and debugging are
+  handled here.
+  It is recommended not to modify this file, since newer versions of Qt Creator
+  may offer an updated version of it.
+*/
+
+#include "qtquick2applicationviewer.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtQml/QQmlEngine>
+
+class QtQuick2ApplicationViewerPrivate
+{
+    QString mainQmlFile;
+    friend class QtQuick2ApplicationViewer;
+    static QString adjustPath(const QString &path);
+};
+
+QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path)
+{
+#if defined(Q_OS_MAC)
+    if (!QDir::isAbsolutePath(path))
+        return QString::fromLatin1("%1/../Resources/%2")
+                .arg(QCoreApplication::applicationDirPath(), path);
+#elif defined(Q_OS_BLACKBERRY)
+    if (!QDir::isAbsolutePath(path))
+        return QString::fromLatin1("app/native/%1").arg(path);
+#elif !defined(Q_OS_ANDROID)
+    QString pathInInstallDir =
+            QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path);
+    if (QFileInfo(pathInInstallDir).exists())
+        return pathInInstallDir;
+    pathInInstallDir =
+            QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path);
+    if (QFileInfo(pathInInstallDir).exists())
+        return pathInInstallDir;
+#endif
+    return path;
+}
+
+QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent)
+    : QQuickView(parent)
+    , d(new QtQuick2ApplicationViewerPrivate())
+{
+    connect(engine(), SIGNAL(quit()), SLOT(close()));
+    setResizeMode(QQuickView::SizeRootObjectToView);
+}
+
+QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer()
+{
+    delete d;
+}
+
+void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file)
+{
+    d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file);
+#ifdef Q_OS_ANDROID
+    setSource(QUrl(QLatin1String("assets:/")+d->mainQmlFile));
+#else
+    setSource(QUrl::fromLocalFile(d->mainQmlFile));
+#endif
+}
+
+void QtQuick2ApplicationViewer::addImportPath(const QString &path)
+{
+    engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path));
+}
+
+void QtQuick2ApplicationViewer::showExpanded()
+{
+#if defined(Q_WS_SIMULATOR) || defined(Q_OS_QNX)
+    showFullScreen();
+#else
+    show();
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.h	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,33 @@
+// checksum 0xfde6 version 0x90005
+/*
+  This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+  QtQuick2ApplicationViewer is a convenience class containing mobile device specific
+  code such as screen orientation handling. Also QML paths and debugging are
+  handled here.
+  It is recommended not to modify this file, since newer versions of Qt Creator
+  may offer an updated version of it.
+*/
+
+#ifndef QTQUICK2APPLICATIONVIEWER_H
+#define QTQUICK2APPLICATIONVIEWER_H
+
+#include <QtQuick/QQuickView>
+
+class QtQuick2ApplicationViewer : public QQuickView
+{
+    Q_OBJECT
+
+public:
+    explicit QtQuick2ApplicationViewer(QWindow *parent = 0);
+    virtual ~QtQuick2ApplicationViewer();
+
+    void setMainQmlFile(const QString &file);
+    void addImportPath(const QString &path);
+
+    void showExpanded();
+
+private:
+    class QtQuick2ApplicationViewerPrivate *d;
+};
+
+#endif // QTQUICK2APPLICATIONVIEWER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/qtquick2applicationviewer/qtquick2applicationviewer.pri	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,180 @@
+# checksum 0x7b0d version 0x90005
+# This file was generated by the Qt Quick 2 Application wizard of Qt Creator.
+# The code below adds the QtQuick2ApplicationViewer to the project and handles
+# the activation of QML debugging.
+# It is recommended not to modify this file, since newer versions of Qt Creator
+# may offer an updated version of it.
+
+QT += qml quick
+
+SOURCES += $$PWD/qtquick2applicationviewer.cpp
+HEADERS += $$PWD/qtquick2applicationviewer.h
+INCLUDEPATH += $$PWD
+# This file was generated by an application wizard of Qt Creator.
+# The code below handles deployment to Android and Maemo, aswell as copying
+# of the application data to shadow build directories on desktop.
+# It is recommended not to modify this file, since newer versions of Qt Creator
+# may offer an updated version of it.
+
+defineTest(qtcAddDeployment) {
+for(deploymentfolder, DEPLOYMENTFOLDERS) {
+    item = item$${deploymentfolder}
+    greaterThan(QT_MAJOR_VERSION, 4) {
+        itemsources = $${item}.files
+    } else {
+        itemsources = $${item}.sources
+    }
+    $$itemsources = $$eval($${deploymentfolder}.source)
+    itempath = $${item}.path
+    $$itempath= $$eval($${deploymentfolder}.target)
+    export($$itemsources)
+    export($$itempath)
+    DEPLOYMENT += $$item
+}
+
+MAINPROFILEPWD = $$PWD
+
+android-no-sdk {
+    for(deploymentfolder, DEPLOYMENTFOLDERS) {
+        item = item$${deploymentfolder}
+        itemfiles = $${item}.files
+        $$itemfiles = $$eval($${deploymentfolder}.source)
+        itempath = $${item}.path
+        $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target)
+        export($$itemfiles)
+        export($$itempath)
+        INSTALLS += $$item
+    }
+
+    target.path = /data/user/qt
+
+    export(target.path)
+    INSTALLS += target
+} else:android {
+    for(deploymentfolder, DEPLOYMENTFOLDERS) {
+        item = item$${deploymentfolder}
+        itemfiles = $${item}.files
+        $$itemfiles = $$eval($${deploymentfolder}.source)
+        itempath = $${item}.path
+        $$itempath = /assets/$$eval($${deploymentfolder}.target)
+        export($$itemfiles)
+        export($$itempath)
+        INSTALLS += $$item
+    }
+
+    x86 {
+        target.path = /libs/x86
+    } else: armeabi-v7a {
+        target.path = /libs/armeabi-v7a
+    } else {
+        target.path = /libs/armeabi
+    }
+
+    export(target.path)
+    INSTALLS += target
+} else:win32 {
+    copyCommand =
+    for(deploymentfolder, DEPLOYMENTFOLDERS) {
+        source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source)
+        source = $$replace(source, /, \\)
+        sourcePathSegments = $$split(source, \\)
+        target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments)
+        target = $$replace(target, /, \\)
+        target ~= s,\\\\\\.?\\\\,\\,
+        !isEqual(source,$$target) {
+            !isEmpty(copyCommand):copyCommand += &&
+            isEqual(QMAKE_DIR_SEP, \\) {
+                copyCommand += $(COPY_DIR) \"$$source\" \"$$target\"
+            } else {
+                source = $$replace(source, \\\\, /)
+                target = $$OUT_PWD/$$eval($${deploymentfolder}.target)
+                target = $$replace(target, \\\\, /)
+                copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\"
+            }
+        }
+    }
+    !isEmpty(copyCommand) {
+        copyCommand = @echo Copying application data... && $$copyCommand
+        copydeploymentfolders.commands = $$copyCommand
+        first.depends = $(first) copydeploymentfolders
+        export(first.depends)
+        export(copydeploymentfolders.commands)
+        QMAKE_EXTRA_TARGETS += first copydeploymentfolders
+    }
+} else:unix {
+    maemo5 {
+        desktopfile.files = $${TARGET}.desktop
+        desktopfile.path = /usr/share/applications/hildon
+        icon.files = $${TARGET}64.png
+        icon.path = /usr/share/icons/hicolor/64x64/apps
+    } else:!isEmpty(MEEGO_VERSION_MAJOR) {
+        desktopfile.files = $${TARGET}_harmattan.desktop
+        desktopfile.path = /usr/share/applications
+        icon.files = $${TARGET}80.png
+        icon.path = /usr/share/icons/hicolor/80x80/apps
+    } else { # Assumed to be a Desktop Unix
+        copyCommand =
+        for(deploymentfolder, DEPLOYMENTFOLDERS) {
+            source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source)
+            source = $$replace(source, \\\\, /)
+            macx {
+                target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target)
+            } else {
+                target = $$OUT_PWD/$$eval($${deploymentfolder}.target)
+            }
+            target = $$replace(target, \\\\, /)
+            sourcePathSegments = $$split(source, /)
+            targetFullPath = $$target/$$last(sourcePathSegments)
+            targetFullPath ~= s,/\\.?/,/,
+            !isEqual(source,$$targetFullPath) {
+                !isEmpty(copyCommand):copyCommand += &&
+                copyCommand += $(MKDIR) \"$$target\"
+                copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\"
+            }
+        }
+        !isEmpty(copyCommand) {
+            copyCommand = @echo Copying application data... && $$copyCommand
+            copydeploymentfolders.commands = $$copyCommand
+            first.depends = $(first) copydeploymentfolders
+            export(first.depends)
+            export(copydeploymentfolders.commands)
+            QMAKE_EXTRA_TARGETS += first copydeploymentfolders
+        }
+    }
+    !isEmpty(target.path) {
+        installPrefix = $${target.path}
+    } else {
+        installPrefix = /opt/$${TARGET}
+    }
+    for(deploymentfolder, DEPLOYMENTFOLDERS) {
+        item = item$${deploymentfolder}
+        itemfiles = $${item}.files
+        $$itemfiles = $$eval($${deploymentfolder}.source)
+        itempath = $${item}.path
+        $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target)
+        export($$itemfiles)
+        export($$itempath)
+        INSTALLS += $$item
+    }
+
+    !isEmpty(desktopfile.path) {
+        export(icon.files)
+        export(icon.path)
+        export(desktopfile.files)
+        export(desktopfile.path)
+        INSTALLS += icon desktopfile
+    }
+
+    isEmpty(target.path) {
+        target.path = $${installPrefix}/bin
+        export(target.path)
+    }
+    INSTALLS += target
+}
+
+export (ICON)
+export (INSTALLS)
+export (DEPLOYMENT)
+export (LIBS)
+export (QMAKE_EXTRA_TARGETS)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/themeiconprovider.cpp	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,38 @@
+#include <QByteArray>
+#include <QDebug>
+
+#include "themeiconprovider.h"
+#include "flib.h"
+
+ThemeIconProvider::ThemeIconProvider()
+    : QQuickImageProvider(QQuickImageProvider::Image)
+{
+    getThemeIcon = 0;
+}
+
+void ThemeIconProvider::setFileContentsFunction(getThemeIcon_t *f)
+{
+    getThemeIcon = f;
+}
+
+QImage ThemeIconProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+{
+    Q_UNUSED(requestedSize);
+
+    if(!getThemeIcon)
+        return QImage();
+
+    QByteArray buf;
+    buf.resize(65536);
+
+    char * bufptr = buf.data();
+    uint32_t fileSize = getThemeIcon(id.toUtf8().data(), bufptr, buf.size());
+    buf.truncate(fileSize);
+    qDebug() << "ThemeIconProvider file size = " << fileSize;
+
+    QImage img = QImage::fromData(buf);
+
+    if (size)
+        *size = img.size();
+    return img;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlFrontend/themeiconprovider.h	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,21 @@
+#ifndef THEMEICONPROVIDER_H
+#define THEMEICONPROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QImage>
+
+#include "flib.h"
+
+class ThemeIconProvider : public QQuickImageProvider
+{
+public:
+    ThemeIconProvider();
+
+    void setFileContentsFunction(getThemeIcon_t *f);
+
+    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+private:
+    getThemeIcon_t *getThemeIcon;
+};
+
+#endif // THEMEICONPROVIDER_H
--- a/share/hedgewars/Data/Scripts/OfficialChallenges.lua	Sat May 16 19:15:08 2015 +0900
+++ b/share/hedgewars/Data/Scripts/OfficialChallenges.lua	Mon May 18 00:20:09 2015 +0300
@@ -36,6 +36,8 @@
             return("Racer Challenge #6")
         elseif LandDigest == "M256715557Scripts/Multiplayer/Racer.lua" then
             return("Racer Challenge #15")
+        elseif LandDigest == "M-1389184823Scripts/Multiplayer/Racer.lua" then
+            return("Racer Challenge #17")
         end
     end
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/protocolParser.hs	Mon May 18 00:20:09 2015 +0300
@@ -0,0 +1,125 @@
+module Main where
+
+import Text.PrettyPrint.HughesPJ
+import qualified Data.MultiMap as MM
+import Data.Maybe
+import Data.List
+import Data.Char
+
+data HWProtocol = Command String [CmdParam]
+data CmdParam = Skip
+              | SS
+              | LS
+              | IntP
+              | Many [CmdParam]
+data ClientStates = NotConnected
+                  | JustConnected
+                  | ServerAuth
+                  | Lobby
+
+data ParseTree = PTPrefix String [ParseTree]
+               | PTCommand String HWProtocol
+
+cmd = Command
+cmd1 s p = Command s [p]
+cmd2 s p1 p2 = Command s [p1, p2]
+
+breakCmd (PTCommand (c:cs) params) = (c, PTCommand cs params)
+
+commands = [
+        cmd "CONNECTED" [Skip, IntP]
+        , cmd1 "NICK" SS
+        , cmd1 "PROTO" IntP
+        , cmd1 "ASKPASSWORD" SS
+        , cmd1 "SERVER_AUTH" SS
+        , cmd1 "JOINING" SS
+        , cmd1 "TEAM_ACCEPTED" SS
+        , cmd1 "HH_NUM" $ Many [SS]
+        , cmd1 "TEAM_COLOR" $ Many [SS]
+        , cmd1 "TEAM_ACCEPTED" SS
+        , cmd1 "BANLIST" $ Many [SS]
+        , cmd1 "JOINED" $ Many [SS]
+        , cmd1 "LOBBY:JOINED" $ Many [SS]
+        , cmd2 "LOBBY:LEFT" SS LS
+        , cmd2 "CLIENT_FLAGS" SS $ Many [SS]
+        , cmd2 "LEFT" SS $ Many [SS]
+        , cmd1 "SERVER_MESSAGE" LS
+        , cmd1 "ERROR" LS
+        , cmd1 "NOTICE" LS
+        , cmd1 "WARNING" LS
+        , cmd1 "JOINING" SS
+        , cmd1 "EM" $ Many [LS]
+        , cmd1 "PING" $ Many [SS]
+        , cmd2 "CHAT" SS LS
+        , cmd2 "SERVER_VARS" SS LS
+        , cmd2 "BYE" SS LS
+        , cmd1 "INFO" $ Many [SS]
+        , cmd1 "ROOMS" $ Many [SS]
+        , cmd "KICKED" []
+        , cmd "RUN_GAME" []
+        , cmd "ROUND_FINISHED" []
+    ]
+
+unknowncmd = PTPrefix "$" [PTCommand "$" $ Command "__UNKNOWN__" [Many [SS]]]
+
+groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])]
+groupByFirstChar = MM.assocs . MM.fromList . map breakCmd
+
+makePT cmd@(Command n p) = PTCommand n cmd
+
+buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]]
+bpt cmds = if isJust emptyNamed then cmdLeaf $ fromJust emptyNamed else subtree
+    where
+        emptyNamed = find (\(_, (PTCommand n _:_)) -> null n) assocs
+        assocs = groupByFirstChar cmds
+        subtree = map buildsub assocs
+        buildsub (c, cmds) = let st = bpt cmds in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st
+        maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd
+        maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss
+        cmdLeaf (c, (hwc:_)) = [PTPrefix [c] [hwc]]
+
+dumpTree = vcat . map dt
+    where
+    dt (PTPrefix s st) = text s $$ (nest 1 $ vcat $ map dt st)
+    dt _ = empty
+
+pas2 = buildSwitch $ buildParseTree commands
+    where
+        buildSwitch cmds = text "case getNextChar of" $$ (nest 4 . vcat $ map buildCase cmds) $$ elsePart
+        buildCase (PTCommand {}) = text "#10: <call cmd handler>;"
+        buildCase (PTPrefix (s:ss) cmds) = quotes (char s) <> text ": " <> consumePrefix ss (buildSwitch cmds)
+        consumePrefix "" = id
+        consumePrefix str = (text "consume" <> (parens . quotes $ text str) <> semi $$)
+        zeroChar = text "#0: state:= pstDisconnected;"
+        elsePart = text "else <unknown cmd> end;"
+
+renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [cmds, l, s, bodies, c]
+    where
+        maybeQuotes s = if null $ tail s then quotes $ text s else text s
+        l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = "
+            <> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi
+        s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = "
+            <> parens (hsep . punctuate comma $ map text commands) <> semi
+        c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = "
+            <> parens (hsep . punctuate comma $ map (text . (++) "@handler_") $ reverse fixedNames) <> semi
+        fixedNames = map fixName handlers
+        fixName = map fixChar
+        fixChar c | isLetter c = c
+                  | otherwise = '_'
+        bodies = vcat $ punctuate (char '\n') $ map handlerBody fixedNames
+        handlerBody n = text "procedure handler_" <> text n <> semi
+            $+$ text "begin" 
+            $+$ text "end" <> semi
+        cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ map ((<>) (text "cmd_") . text) $ reverse fixedNames) <> semi
+
+pas = renderArrays $ buildTables $ buildParseTree commands
+    where
+        buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3)
+        walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) =
+            (lc, 1:sh, pc - 1, "#10":tbl1, show pc:tbl2, (n:t3):tbl3)
+        walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix
+        lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3)
+        lvldown (lc, s1:s2:sh, pc, tbl1, t:tbl2, t31:t32:tbl3) = (lc, s1+s2:sh, pc, tbl1, (if null t32 then "0" else show s1):tbl2, (t31 ++ t32):tbl3)
+        fpf c (lc, s:sh, pc, tbl1, tbl2, tbl3) = (lc + 1, s+1:sh, pc, [c]:tbl1, "0":tbl2, tbl3)
+
+main = putStrLn $ renderStyle style{lineLength = 80} pas