Merge
authorMedo <smaxein@googlemail.com>
Mon, 27 Aug 2012 17:40:16 +0200
changeset 7613 ce6ead3327b2
parent 7590 0be267033fb3 (diff)
parent 7612 047c6692a2e7 (current diff)
child 7687 c73fd8cfa7c0
Merge
hedgewars/hwengine.pas
hedgewars/uLand.pas
hedgewars/uLandTemplates.pas
--- a/.hgignore	Mon Aug 27 11:05:09 2012 +0200
+++ b/.hgignore	Mon Aug 27 17:40:16 2012 +0200
@@ -40,7 +40,7 @@
 glob:*.rej
 glob:project_files/Android-build/SDL-android-project/jni/**
 glob:project_files/Android-build/SDL-android-project/obj
-glob:project_files/Android-build/SDL-android-project/libs
+glob:project_files/Android-build/SDL-android-project/libs/armeabi*
 glob:project_files/Android-build/SDL-android-project/bin
 glob:project_files/Android-build/SDL-android-project/gen
 glob:project_files/Android-build/SDL-android-project/local.properties
--- a/QTfrontend/CMakeLists.txt	Mon Aug 27 11:05:09 2012 +0200
+++ b/QTfrontend/CMakeLists.txt	Mon Aug 27 17:40:16 2012 +0200
@@ -130,7 +130,6 @@
     achievements.h
     binds.h
     ui_hwform.h
-    KB.h
     hwconsts.h
     sdlkeys.h
     )
--- a/QTfrontend/game.cpp	Mon Aug 27 11:05:09 2012 +0200
+++ b/QTfrontend/game.cpp	Mon Aug 27 17:40:16 2012 +0200
@@ -28,7 +28,6 @@
 #include "gameuiconfig.h"
 #include "gamecfgwidget.h"
 #include "teamselect.h"
-#include "KB.h"
 #include "proto.h"
 #include "ThemeModel.h"
 
@@ -225,20 +224,6 @@
             emit ErrorMessage(QString("Last two engine messages:\n") + QString().append(msg.mid(2)).left(size - 4));
             return;
         }
-        case 'K':
-        {
-            ulong kb = msg.mid(2).toULong();
-            if (kb==1)
-            {
-                qWarning("%s", KBMessages[kb - 1].toLocal8Bit().constData());
-                return;
-            }
-            if (kb && kb <= KBmsgsCount)
-            {
-                emit ErrorMessage(KBMessages[kb - 1]);
-            }
-            return;
-        }
         case 'i':
         {
             emit GameStats(msg.at(2), QString::fromUtf8(msg.mid(3)));
@@ -264,7 +249,6 @@
             int size = msg.size();
             QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4));
             emit SendChat(msgbody);
-            // FIXME: /me command doesn't work here
             QByteArray buf;
             HWProto::addStringToBuffer(buf, "s" + HWProto::formatChatMsg(config->netNick(), msgbody) + "\x20\x20");
             demo.append(buf);
@@ -283,8 +267,7 @@
             {
                 emit SendNet(msg);
             }
-            if (msg.at(1) != 's')
-                demo.append(msg);
+            demo.append(msg);
         }
     }
 }
--- a/QTfrontend/net/newnetclient.h	Mon Aug 27 11:05:09 2012 +0200
+++ b/QTfrontend/net/newnetclient.h	Mon Aug 27 17:40:16 2012 +0200
@@ -66,27 +66,6 @@
         bool m_game_connected;
         RoomsListModel * m_roomsListModel;
 
-        template <typename T>
-        void SendCfgStrNet(T a)
-        {
-            QByteArray strmsg;
-            strmsg.append(a);
-            quint8 sz = strmsg.size();
-            QByteArray enginemsg = QByteArray((char *)&sz, 1) + strmsg;
-            QString _msg = delimeter + QString(enginemsg.toBase64());
-            RawSendNet(_msg);
-        }
-
-        template <typename T>
-        void SendCfgStrLoc(T a)
-        {
-            QByteArray strmsg;
-            strmsg.append(QString(a).toUtf8());
-            quint8 sz = strmsg.size();
-            QByteArray enginemsg = QByteArray((char *)&sz, 1) + strmsg;
-            emit FromNet(enginemsg);
-        }
-
         QStringList cmdbuf;
 
         void RawSendNet(const QString & buf);
--- a/hedgewars/hwLibrary.pas	Mon Aug 27 11:05:09 2012 +0200
+++ b/hedgewars/hwLibrary.pas	Mon Aug 27 17:40:16 2012 +0200
@@ -99,14 +99,18 @@
     JNI_HW_versionInfoVersion := envderef^.NewStringUTF(env, PChar(cVersionString));
 end;
 
+procedure JNI_HW_GenLandPreview(env: PJNIEnv; c: JClass; port: JInt); cdecl;
+begin
+	GenLandPreview(port);
+end;
+
 exports
     JNI_HW_versionInfoNet name Java_Prefix+'HWversionInfoNetProto', 
     JNI_HW_versionInfoVersion name Java_Prefix+'HWversionInfoVersion', 
-    GenLandPreview name Java_Prefix + 'GenLandPreview',
+    JNI_HW_GenLandPreview name Java_Prefix + 'HWGenLandPreview',
     HW_getNumberOfweapons name Java_Prefix + 'HWgetNumberOfWeapons',
     HW_getMaxNumberOfHogs name Java_Prefix + 'HWgetMaxNumberOfHogs',
     HW_getMaxNumberOfTeams name Java_Prefix + 'HWgetMaxNumberOfTeams',
-    HW_terminate name Java_Prefix + 'HWterminate',
     Game;
 {$ELSE}
 exports
--- a/hedgewars/hwengine.pas	Mon Aug 27 11:05:09 2012 +0200
+++ b/hedgewars/hwengine.pas	Mon Aug 27 17:40:16 2012 +0200
@@ -256,7 +256,7 @@
         CurrTime:= SDL_GetTicks();
         if PrevTime + longword(cTimerInterval) <= CurrTime then
         begin
-            isTerminated:= DoTimer(CurrTime - PrevTime);
+            isTerminated := isTerminated or DoTimer(CurrTime - PrevTime);
             PrevTime:= CurrTime
         end
         else SDL_Delay(1);
--- a/hedgewars/uLand.pas	Mon Aug 27 11:05:09 2012 +0200
+++ b/hedgewars/uLand.pas	Mon Aug 27 17:40:16 2012 +0200
@@ -127,7 +127,7 @@
     SDL_FreeSurface(tmpsurf);
 end;
 
-procedure SetPoints(var Template: TEdgeTemplate; var pa: TPixAr);
+procedure SetPoints(var Template: TEdgeTemplate; var pa: TPixAr; fps: PPointArray);
 var i: LongInt;
 begin
 with Template do
@@ -148,7 +148,7 @@
                if pa.ar[i].x <> NTPX then
                    pa.ar[i].x:= LAND_WIDTH - 1 - pa.ar[i].x;
             for i:= 0 to pred(FillPointsCount) do
-                FillPoints^[i].x:= LAND_WIDTH - 1 - FillPoints^[i].x;
+                fps^[i].x:= LAND_WIDTH - 1 - fps^[i].x;
             end;
 
 (*  Experiment in making this option more useful
@@ -181,9 +181,9 @@
             end;
         for i:= 0 to pred(FillPointsCount) do
             begin
-            dec(FillPoints^[i].y, 100);
-            if FillPoints^[i].y < 0 then
-                FillPoints^[i].y:= 0;
+            dec(fps^[i].y, 100);
+            if fps^[i].y < 0 then
+                fps^[i].y:= 0;
             end;
         end;
 
@@ -192,7 +192,7 @@
         for i:= 0 to pred(BasePointsCount) do
             pa.ar[i].y:= LAND_HEIGHT - 1 - pa.ar[i].y;
         for i:= 0 to pred(FillPointsCount) do
-            FillPoints^[i].y:= LAND_HEIGHT - 1 - FillPoints^[i].y;
+            fps^[i].y:= LAND_HEIGHT - 1 - fps^[i].y;
         end;
     end
 end;
@@ -202,13 +202,15 @@
 var pa: TPixAr;
     i: Longword;
     y, x: Longword;
+    fps: TPointArray;
 begin
+    fps:=Template.FillPoints^;
     ResizeLand(Template.TemplateWidth, Template.TemplateHeight);
     for y:= 0 to LAND_HEIGHT - 1 do
         for x:= 0 to LAND_WIDTH - 1 do
             Land[y, x]:= lfBasic;
     {$HINTS OFF}
-    SetPoints(Template, pa);
+    SetPoints(Template, pa, @fps);
     {$HINTS ON}
     for i:= 1 to Template.BezierizeCount do
         begin
@@ -225,7 +227,7 @@
 
     with Template do
         for i:= 0 to pred(FillPointsCount) do
-            with FillPoints^[i] do
+            with fps[i] do
                 FillLand(x, y);
 
     DrawEdge(pa, lfBasic);
--- a/hedgewars/uLandTemplates.pas	Mon Aug 27 11:05:09 2012 +0200
+++ b/hedgewars/uLandTemplates.pas	Mon Aug 27 17:40:16 2012 +0200
@@ -24,10 +24,12 @@
 
 const NTPX = Low(SmallInt);
 
-type TPointArray = array[0..64] of TSDL_Rect;
+type TRectArray = array[0..64] of TSDL_Rect;
+     PRectArray = ^TRectArray;
+     TPointArray = array[0..64] of TPoint;
      PPointArray = ^TPointArray;
      TEdgeTemplate = record
-                     BasePoints: PPointArray;
+                     BasePoints: PRectArray;
                      BasePointsCount: Longword;
                      FillPoints: PPointArray;
                      FillPointsCount: Longword;
--- a/hedgewars/uStats.pas	Mon Aug 27 11:05:09 2012 +0200
+++ b/hedgewars/uStats.pas	Mon Aug 27 17:40:16 2012 +0200
@@ -185,10 +185,12 @@
 begin
 if time > 4000 then
     begin
+	{$IFNDEF MOBILE}
     writeln(stdout, 'FLIGHT');
     writeln(stdout, Gear^.Hedgehog^.Team^.TeamName);
     writeln(stdout, inttostr(time));
     writeln(stdout, '');
+	{$ENDIF}
     end
 end;
 
@@ -290,6 +292,7 @@
 if KilledHHs > 0 then
     SendStat(siKilledHHs, IntToStr(KilledHHs));
 
+{$IFNDEF MOBILE}
 // now to console
 if winnersClan <> nil then 
     begin
@@ -301,6 +304,7 @@
     writeln(stdout, 'DRAW');
 
 writeln(stdout, '');
+{$ENDIF}
 end;
 
 procedure initModule;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/Android Support library/NOTICE.txt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,195 @@
+Notice for all the files in this folder.
+------------------------------------------------------------
+
+
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/ini4j/LICENSE.txt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/ini4j/NOTICE.txt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,13 @@
+Copyright 2005,2009 Ivan SZKIBA
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/AndroidManifest.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/AndroidManifest.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -1,54 +1,105 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="org.hedgewars.hedgeroid"
-      android:versionCode="8"
-      android:installLocation="preferExternal" android:versionName="0.2">
-    <uses-sdk android:targetSdkVersion="14" android:minSdkVersion="7"></uses-sdk>
-    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
-    <application android:label="@string/app_name" android:icon="@drawable/icon">
-        <activity android:name=".MainActivity"
-                  android:label="@string/app_name"
- 		  		  android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+    package="org.hedgewars.hedgeroid"
+    android:installLocation="preferExternal"
+    android:versionCode="8"
+    android:versionName="0.2" >
+
+    <uses-sdk
+        android:minSdkVersion="7"
+        android:targetSdkVersion="14" >
+    </uses-sdk>
+
+    <uses-permission android:name="android.permission.INTERNET" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
+    </uses-permission>
+
+    <application
+        android:icon="@drawable/icon"
+        android:label="@string/app_name" >
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="landscape" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".SDLActivity"
-                  android:label="@string/app_name"
- 		  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-		  android:screenOrientation='landscape'>
+        <activity
+            android:name=".SDLActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+        </activity>
+        <activity
+            android:name=".Downloader.DownloadFragment"
+            android:label="@string/app_name"
+            android:theme="@android:style/Theme.Dialog" >
         </activity>
-        
-        <activity android:name=".Downloader.DownloadFragment"
-                  android:label="@string/app_name"
-				  android:theme="@android:style/Theme.Dialog">
+        <activity
+            android:name=".Downloader.DownloadListActivity"
+            android:label="@string/app_name"
+            android:launchMode="singleTop"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
+
+        <service android:name=".Downloader.DownloadService" />
+		
+        <activity
+            android:name=".LocalRoomActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="landscape" >
+        </activity>
+        <activity
+            android:name=".TeamListActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
         </activity>
-        
-        <activity android:name=".Downloader.DownloadListActivity"
-                  android:label="@string/app_name"
-				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-				  android:screenOrientation='landscape'
-				  android:launchMode="singleTop"/>
-        
-        <service android:name=".Downloader.DownloadService"/>
-        
-        <activity android:name="StartGameActivity"
-                  android:label="@string/app_name"
-				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-				  android:screenOrientation='landscape'>
+        <activity
+            android:name="TeamCreatorActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+            android:windowSoftInputMode="stateUnchanged" >
+        </activity>
+        <activity
+            android:name=".LobbyActivity"
+            android:label="@string/title_activity_lobby"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
+        </activity>
+        <activity
+            android:name=".NetRoomActivity"
+            android:label="@string/title_activity_room"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
         </activity>
-        <activity android:name="TeamSelectionActivity"
-                  android:label="@string/app_name"
-				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-				  android:screenOrientation='landscape'>
+        <activity
+            android:name=".WeaponsetListActivity"
+            android:label="@string/title_activity_weaponset_list"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
+        </activity>
+        <activity
+            android:name=".WeaponsetCreatorActivity"
+            android:label="@string/title_activity_weaponset_creator"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
         </activity>
-        <activity android:name="TeamCreatorActivity"
-                  android:label="@string/app_name"
-				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-				  android:screenOrientation='landscape'
-				  android:windowSoftInputMode="stateUnchanged">
+        <activity
+            android:name=".SchemeListActivity"
+            android:label="@string/title_activity_scheme_list"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
+        </activity>
+        <activity
+            android:name=".SchemeCreatorActivity"
+            android:label="@string/title_activity_scheme_creator"
+            android:screenOrientation="landscape"
+            android:windowSoftInputMode="adjustPan" >
         </activity>
     </application>
-</manifest> 
+</manifest>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Airplane.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/AmmoMenu/Ammos.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/AmmoMenu/Ammos_bw.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_42.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_anarchy.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_balrog.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_bars.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_birdy.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_bloodyblade.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_brittany.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_bustamove.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_cog.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_crossedswords.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_dragonrb.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_earth.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_earth2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_eyeofhorus.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_face.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_fcw.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_female.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_galaxy.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_hax0r.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_hurrah.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_iluvu.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_lips.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_magicskull.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_male.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_mog.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_music.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_pacman.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_pacman2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_pentagram.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_pirate.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_pokemon.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_scout.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_shoppa.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_sonic.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_spider.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_star.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_swordshield.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_swordshield2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_vampire.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Flags/cm_yinyang.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Badger.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Cherry.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Duck2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Earth.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Egg.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Flower.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Ghost.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Grave.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Plinko.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Rip.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Rubberduck.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Simple.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/Statue.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/bp2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/bubble.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/chest.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/coffin.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/deadhog.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/eyecross.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/heart.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/money.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/mouton1.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/octopus.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/plant2.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/plant3.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/pokeball.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/pyramid.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/ring.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/skull.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Graves/star.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Hats/android.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Hedgehog/amKamikaze.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Hedgehog/amSineGun.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/LandIce.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Molotov.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Progress.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/RCPlane.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/Snow.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/SuddenDeath/SDFlake.png has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Graphics/botlevels.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Locale.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,18 @@
+-- Library for localizing strings in lua scripts
+
+local lang = loadfile(GetUserDataPath() .. "Locale/" .. tostring(L) .. ".lua")
+
+if lang ~= nil then
+    lang()
+else
+    lang = loadfile(GetDataPath() .. "Locale/" .. tostring(L) .. ".lua")
+    if lang ~= nil then
+        lang()
+    end
+end
+
+function loc(text)
+    if lang ~= nil and locale ~= nil and locale[text] ~= nil then return locale[text]
+    else return text
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Balanced_Random_Weapon.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+locked
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Balanced_Random_Weapon.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,148 @@
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+local weapons = { amGrenade, amClusterBomb, amBazooka, amBee, amShotgun, amMine, amDEagle, amDynamite, amFirePunch, amWhip, amPickHammer, amBaseballBat, amMortar, amCake, amSeduction, amWatermelon, amHellishBomb, amDrill, amBallgun, amRCPlane, amSniperRifle, amMolotov, amBirdy, amBlowTorch, amGasBomb, amFlamethrower, amSMine, amKamikaze }
+
+--                      G,C,B,B,S,M,D,D,F,W,P,B,M,C,S,W,H,D,B,R,S,M,B,B,G,F,S,K
+local weapons_values = {1,1,1,2,1,1,1,2,1,1,1,2,1,3,1,3,3,2,3,3,1,1,2,1,1,2,2,1}
+
+local airweapons = { amAirAttack, amMineStrike, amNapalm, amDrillStrike }
+
+--                         A,M,N,D
+local airweapons_values = {2,2,2,2}
+
+local utilities = { amTeleport, amGirder, amSwitch, amLowGravity, amResurrector, amRope, amParachute, amJetpack, amPortalGun, amSnowball }
+
+--                        T,G,S,L,R,R,P,J,P,S
+local utilities_values = {1,2,2,1,2,2,1,2,2,2}
+
+function randomAmmo()
+    local n = 3   --"points" to be allocated on weapons
+
+    --pick random weapon and subtract cost
+    local r = GetRandom(table.maxn(weapons_values)) + 1
+    local picked_items = {}
+    table.insert(picked_items, weapons[r])
+    n = n - weapons_values[r]
+
+
+    --choose any weapons or utilities to use up remaining n
+
+    while n > 0 do
+        local items = {}
+        local items_values = {}
+
+        for i, w in pairs(weapons_values) do
+            local used = false
+            if w <= n then
+                --check that this weapon hasn't been given already
+                for j, k in pairs(picked_items) do
+                    if weapons[i] == k then
+                        used = true
+                    end
+                end
+                if not used then
+                    table.insert(items_values, w)
+                    table.insert(items, weapons[i])
+                end
+            end
+        end
+
+        for i, w in pairs(utilities_values) do
+            local used = false
+            if w <= n then
+                --check that this weapon hasn't been given already
+                for j, k in pairs(picked_items) do
+                    if utilities[i] == k then
+                        used = true
+                    end
+                end
+                if not used then
+                    table.insert(items_values, w)
+                    table.insert(items, utilities[i])
+                end
+            end
+        end
+
+        local r = GetRandom(table.maxn(items_values)) + 1
+        table.insert(picked_items, items[r])
+        n = n - items_values[r]
+    end
+
+    return picked_items
+end
+
+function assignAmmo(hog)
+    local name = GetHogTeamName(hog)
+    local processed = getTeamValue(name, "processed")
+    if processed == nil or not processed then
+        local ammo = getTeamValue(name, "ammo")
+        if ammo == nil then
+            ammo = randomAmmo()
+            setTeamValue(name, "ammo", ammo)
+        end
+        for i, w in pairs(ammo) do
+            AddAmmo(hog, w)
+        end
+        setTeamValue(name, "processed", true)
+    end
+end
+
+function reset(hog)
+    setTeamValue(GetHogTeamName(hog), "processed", false)
+end
+
+function onGameInit()
+    GameFlags = band(bor(GameFlags, gfResetWeps), bnot(gfPerHogAmmo))
+    Goals = loc("Each turn you get 1-3 random weapons")
+end
+
+function onGameStart()
+    trackTeams()
+    if MapHasBorder() == false then
+        for i, w in pairs(airweapons) do
+            table.insert(weapons, w)
+        end
+        for i, w in pairs(airweapons_values) do
+            table.insert(weapons_values, w)
+        end
+    end
+end
+
+function onAmmoStoreInit()
+    SetAmmo(amSkip, 9, 0, 0, 0)
+
+    SetAmmo(amExtraDamage, 0, 1, 0, 1)
+    SetAmmo(amInvulnerable, 0, 1, 0, 1)
+    SetAmmo(amExtraTime, 0, 1, 0, 1)
+    SetAmmo(amLaserSight, 0, 1, 0, 1)
+    SetAmmo(amVampiric, 0, 1, 0, 1)
+
+    for i, w in pairs(utilities) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+    for i, w in pairs(weapons) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+    for i, w in pairs(airweapons) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+end
+
+function onNewTurn()
+    runOnGears(assignAmmo)
+    runOnGears(reset)
+    setTeamValue(GetHogTeamName(CurrentHedgehog), "ammo", nil)
+end
+
+function onGearAdd(gear)
+    if GetGearType(gear) == gtHedgehog then
+        trackGear(gear)
+    end
+end
+
+function onGearDelete(gear)
+    trackDeletion(gear)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Capture_the_Flag.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Capture_the_Flag.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,630 @@
+---------------------------------------
+-- CAPTURE_THE_FLAG GAMEPLAY MODE 0.5
+-- by mikade
+---------------------------------------
+
+-- Version History
+---------
+-- 0.1
+---------
+
+-- [conversion from map-dependant CTF_Blizzard to map independant Capture the Flag]
+-- added an intial starting stage where flagspawn is decided by the players (weapon set will require a jetpack unless I set)
+-- changed the flag from a crate to a visual gear, and all associated methods and checks relating to flags (five hours later, lol)
+-- changed starting/respawning positioning to accommodate different map sizes
+-- added another circle to mark flag spawn
+-- added gameFlag filter
+-- changed scoring feedback
+-- cleaned up some code
+
+-- removing own flag from spawning point no longer possible
+-- destroying flags no longer possible.
+-- added basic glowing circle effect to spawn area
+-- added expanding circle to fgear itself
+
+-- removed teleporters
+-- removed random crate drops (this should be decided by scheme)
+-- removed set map criteria like minesNum, turnTime, explosives etc. except for sudden death
+-- removed weapon defintions
+-- removed placement and respawning methods, hopefully divideTeams will have this covered
+
+---------
+-- 0.2
+---------
+
+-- [now with user friendliness]
+-- flag is now placed wherever you end up at the end of your first turn, this ensures that it is always placed by turn 3
+-- removed a bunch of backup code and no-longer needed variables / methods from CTF_Blizzard days
+-- removed an aura that was still mistakenly hanging about
+-- added an in-game note about placements
+-- added an in-game note about the rules of the game
+-- added translation support and loc()'ed everything
+-- changed things so the seed is no longer always the same...
+
+-- In this version:
+---------
+-- 0.3
+---------
+-- [fufufufu kamikaze fix]
+-- added nill checks to make sure the player doesn't generate errors by producing a nil value in hhs[] when he uses kamikaze
+-- added a check to make sure the player doesn't kamikaze straight down and make the flag's starting point underwater
+-- added a check to make sure the player drops the flag if he has it and he uses kamikaze
+
+--------
+-- 0.4
+--------
+
+-- remove user-branding and version numbers
+-- removed some stuff that wasn't needed
+-- fix piano strike exploit
+-- changed delay to allow for better portals
+-- changed starting feedback a little
+-- increased the radius around the circle indicating the flag thief so that it doesn't obscure his health
+
+--------
+-- 0.5
+--------
+
+-- add support for more players
+-- allow limited sudden death
+-- stop TimeBox ruining my life
+-- profit???
+
+-----------------
+--SCRIPT BEGINS
+-----------------
+
+-- enable awesome translaction support so we can use loc() wherever we want
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+
+---------------------------------------------------------------
+----------lots of bad variables and things
+----------because someone is too lazy
+----------to read about tables properly
+------------------ "Oh well, they probably have the memory"
+
+local gameStarted = false
+local gameTurns = 0
+
+--------------------------
+-- hog and team tracking variales
+--------------------------
+
+local numhhs = 0 -- store number of hedgehogs
+local hhs = {} -- store hedgehog gears
+
+local numTeams --  store the number of teams in the game
+local teamNameArr = {}	-- store the list of teams
+local teamSize = {}	-- store how many hogs per team
+local teamIndex = {} -- at what point in the hhs{} does each team begin
+
+-------------------
+-- flag variables
+-------------------
+
+local fPlaced = {} -- has the flag been placed TRUE/FALSE
+
+local fGear = {}	-- pointer to the visual gears that represent the flag
+local fGearX = {}
+local fGearY = {}
+
+local fThief = {}	-- pointer to the hogs who stole the flags
+local fIsMissing = {}	-- have the flags been destroyed or captured
+local fNeedsRespawn = {}	-- do the flags need to be respawned
+local fCaptures = {}	-- the team "scores" how many captures
+local fSpawnX = {}		-- spawn X for flags
+local fSpawnY = {}		-- spawn Y for flags
+
+local fThiefX = {}
+local fThiefY = {}
+local FTTC = 0 -- flag thief tracker counter
+
+local fSpawnC = {} -- spawn circle marker
+local fCirc = {} -- flag/carrier marker circles
+local fCol = {} -- colour of the clans
+
+local fGearRad = 0
+local fGearRadMin = 5
+local fGearRadMax = 33
+local fGearTimer = 0
+
+------------------------
+--flag methods
+------------------------
+
+function CheckScore(teamID)
+
+	if teamID == 0 then
+		alt = 1
+	elseif teamID == 1 then
+		alt = 0
+	end
+
+	if fCaptures[teamID] == 3 then
+		for i = 0, (numhhs-1) do
+			if hhs[i] ~= nil then
+				if GetHogClan(hhs[i]) == alt then
+					SetEffect(hhs[i], heResurrectable, false)
+					SetHealth(hhs[i],0)
+				end
+			end
+		end
+		if CurrentHedgehog ~= nil then
+			ShowMission(loc("GAME OVER!"), loc("Victory for the ") .. GetHogTeamName(CurrentHedgehog), loc("Hooray!"), 0, 0)
+		end
+	end
+
+end
+
+function DoFlagStuff(gear)
+
+	if (gear == fGear[0]) then
+		wtf = 0
+		bbq = 1
+	elseif (gear == fGear[1]) then
+		wtf = 1
+		bbq = 0
+	end
+
+	-- player has successfully captured the enemy flag
+	if (GetHogClan(CurrentHedgehog) == wtf) and (CurrentHedgehog == fThief[bbq]) and (fIsMissing[wtf] == false) then
+
+		DeleteVisualGear(fGear[wtf])
+		fGear[wtf] = nil -- the flag has now disappeared
+
+		fIsMissing[wtf] = false
+		fNeedsRespawn[wtf] = true
+		fIsMissing[bbq] = false
+		fNeedsRespawn[bbq] = true
+		fCaptures[wtf] = fCaptures[wtf] +1
+		ShowMission(loc("You have SCORED!!"), GetHogTeamName(CurrentHedgehog) .. ": " .. fCaptures[wtf], loc("Opposing Team: ") .. fCaptures[bbq], 0, 0)
+		PlaySound(sndVictory)
+		fThief[bbq] = nil -- player no longer has the enemy flag
+		CheckScore(wtf)
+
+	--if the player is returning the flag
+	elseif (GetHogClan(CurrentHedgehog) == wtf) and (fIsMissing[wtf] == true) then
+
+		DeleteVisualGear(fGear[wtf])
+		fGear[wtf] = nil -- the flag has now disappeared
+
+		fNeedsRespawn[wtf] = true
+		HandleRespawns() -- this will set fIsMissing[wtf] to false :)
+		AddCaption(loc("Flag returned!"))
+
+	--if the player is taking the enemy flag
+	elseif GetHogClan(CurrentHedgehog) == bbq then
+
+		DeleteVisualGear(fGear[wtf])
+		fGear[wtf] = nil -- the flag has now disappeared
+
+		fIsMissing[wtf] = true
+		for i = 0,numhhs-1 do
+			if CurrentHedgehog ~= nil then
+				if CurrentHedgehog == hhs[i] then
+					fThief[wtf] = hhs[i]
+				end
+			end
+		end
+		AddCaption(loc("Flag captured!"))
+
+	end
+
+end
+
+function CheckFlagProximity()
+
+	for i = 0, 1 do
+		if fGear[i] ~= nil then
+
+			g1X = fGearX[i]
+			g1Y = fGearY[i]
+
+			g2X, g2Y = GetGearPosition(CurrentHedgehog)
+
+			q = g1X - g2X
+			w = g1Y - g2Y
+			dist = (q*q) + (w*w)
+
+			if dist < 500 then --1600
+				DoFlagStuff(fGear[i])
+			end
+		end
+	end
+
+end
+
+
+function HandleRespawns()
+
+	for i = 0, 1 do
+
+		if fNeedsRespawn[i] == true then
+			fGear[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
+			fGearX[i] = fSpawnX[i]
+			fGearY[i] = fSpawnY[i]
+
+			fNeedsRespawn[i] = false
+			fIsMissing[i] = false -- new, this should solve problems of a respawned flag being "returned" when a player tries to score
+			AddCaption(loc("Flag respawned!"))
+		end
+
+	end
+
+end
+
+
+function FlagThiefDead(gear)
+
+	if (gear == fThief[0]) then
+		wtf = 0
+		bbq = 1
+	elseif (gear == fThief[1]) then
+		wtf = 1
+		bbq = 0
+	end
+
+	if fThief[wtf] ~= nil then
+		-- falls into water
+		--ShowMission(LAND_HEIGHT,  fThiefY[wtf], (LAND_HEIGHT - fThiefY[wtf]), 0, 0)
+		if (LAND_HEIGHT - fThiefY[wtf]) < 15 then
+			fIsMissing[wtf] = true
+			fNeedsRespawn[wtf] = true
+			HandleRespawns()
+		else	--normally
+			fGearX[wtf]  =  fThiefX[wtf]
+			fGearY[wtf]  =  fThiefY[wtf]
+			fGear[wtf] = AddVisualGear(fGearX[wtf],fGearY[wtf],vgtCircle,0,true)
+		end
+
+		AddVisualGear(fThiefX[wtf], fThiefY[wtf], vgtBigExplosion, 0, false)
+		fThief[wtf] = nil
+	end
+
+end
+
+function HandleCircles()
+
+	fGearTimer = fGearTimer + 1
+	if fGearTimer == 50 then
+		fGearTimer = 0
+		fGearRad = fGearRad + 1
+		if fGearRad > fGearRadMax then
+			fGearRad = fGearRadMin
+		end
+	end
+
+	for i = 0, 1 do
+
+		--SetVisualGearValues(fSpawnC[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, 50, 3, fCol[i]) -- draw a circ for spawning area
+
+		if fIsMissing[i] == false then -- draw a flag marker at the flag's spawning place
+			SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], 20, 20, 0, 10, 0, 33, 3, fCol[i])
+			if fGear[i] ~= nil then -- draw the flag gear itself
+				SetVisualGearValues(fGear[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, fGearRad, 2, fCol[i])
+			end
+		elseif (fIsMissing[i] == true) and (fNeedsRespawn[i] == false) then
+			if fThief[i] ~= nil then -- draw circle round flag carrier			-- 33
+				SetVisualGearValues(fCirc[i], fThiefX[i], fThiefY[i], 20, 200, 0, 0, 100, 50, 3, fCol[i])
+				--AddCaption("circle marking carrier")
+			elseif fThief[i] == nil then -- draw cirle round dropped flag
+				--g1X,g1Y,g4,g5,g6,g7,g8,g9,g10,g11 =  GetVisualGearValues(fGear[i])
+				--SetVisualGearValues(fCirc[i], g1X, g1Y, 20, 200, 0, 0, 100, 33, 2, fCol[i])
+				SetVisualGearValues(fCirc[i], fGearX[i], fGearY[i], 20, 200, 0, 0, 100, 33, 3, fCol[i])
+				--AddCaption('dropped circle marker')
+				if fGear[i] ~= nil then -- flag gear itself
+					--SetVisualGearValues(fGear[i], g1X, g1Y, 20, 200, 0, 0, 100, 10, 4, fCol[i])
+					SetVisualGearValues(fGear[i], fGearX[i], fGearY[i], 20, 200, 0, 0, 100, fGearRad, 2, fCol[i])
+					--AddCaption('dropped flag itself')
+				end
+			end
+		end
+
+		if fNeedsRespawn[i] == true then -- if the flag has been destroyed, no need for a circle
+			SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, 0, 0, fCol[i])
+			--AddCaption("needs respawn = true. flag 'destroyed'?")
+		end
+	end
+
+end
+
+------------------------
+-- general methods
+------------------------
+
+function CheckDistance(gear1, gear2)
+
+	g1X, g1Y = GetGearPosition(gear1)
+	g2X, g2Y = GetGearPosition(gear2)
+
+	g1X = g1X - g2X
+	g1Y = g1Y - g2Y
+	z = (g1X*g1X) + (g1Y*g1Y)
+
+	dist = z
+
+	return dist
+
+end
+
+function RebuildTeamInfo()
+
+
+	-- make a list of individual team names
+	for i = 0, (TeamsCount-1) do
+		teamNameArr[i] = i
+		teamSize[i] = 0
+		teamIndex[i] = 0
+	end
+	numTeams = 0
+
+	for i = 0, (numhhs-1) do
+
+		z = 0
+		unfinished = true
+		while(unfinished == true) do
+
+			newTeam = true
+			tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name
+
+			if tempHogTeamName == teamNameArr[z] then
+				newTeam = false
+				unfinished = false
+			end
+
+			z = z + 1
+
+			if z == TeamsCount then
+				unfinished = false
+				if newTeam == true then
+					teamNameArr[numTeams] = tempHogTeamName
+					numTeams = numTeams + 1
+				end
+			end
+
+		end
+
+	end
+
+	-- find out how many hogs per team, and the index of the first hog in hhs
+	for i = 0, numTeams-1 do
+
+		for z = 0, numhhs-1 do
+			if GetHogTeamName(hhs[z]) == teamNameArr[i] then
+				if teamSize[i] == 0 then
+					teamIndex[i] = z -- should give starting index
+				end
+				teamSize[i] = teamSize[i] + 1
+				--add a pointer so this hog appears at i in hhs
+			end
+		end
+
+	end
+
+end
+
+function StartTheGame()
+
+	gameStarted = true
+	AddCaption(loc("Game Started!"))
+
+	for i = 0, 1 do
+
+		-- if someone uses kamikaze downwards, this can happen as the hog won't respawn
+		if (LAND_HEIGHT - fSpawnY[i]) < 0 then
+			tempG = AddGear(0, 0, gtTarget, 0, 0, 0, 0)
+     			FindPlace(tempG, true, 0, LAND_WIDTH, true)
+			fSpawnX[i], fSpawnY[i] = GetGearPosition(tempG)
+			DeleteGear(tempG)
+		end
+
+		fGear[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
+		fCirc[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
+		fSpawnC[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
+
+		fGearX[i] = fSpawnX[i]
+		fGearY[i] = fSpawnY[i]
+
+		fCol[i] = GetClanColor(i)
+		fIsMissing[i] = false
+		fNeedsRespawn[i] = false
+		fCaptures[i] = 0
+
+		--SetVisualGearValues(zxc, 1000,1000, 20, 100, 0,    10,                     1,         100,        5,      GetClanColor(0))
+
+		SetVisualGearValues(fSpawnC[i], fSpawnX[i],fSpawnY[i], 20, 100, 0, 10, 0, 75, 5, fCol[i])
+
+	end
+
+end
+
+------------------------
+-- game methods
+------------------------
+
+function onGameInit()
+
+	GameFlags = band(bor(GameFlags, gfDivideTeams), bnot(gfKing + gfForts))
+	--SuddenDeathTurns = 999 -- suddendeath is off, effectively
+	WaterRise = 0
+	Delay = 10
+
+end
+
+
+function onGameStart()
+
+	--ShowMission(loc(caption), loc(subcaption), loc(goal), 0, 0)
+	ShowMission(loc("CAPTURE THE FLAG"), loc("Flags, and their home base will be placed where each team ends their first turn."), "", 0, 0)
+
+	RebuildTeamInfo()
+
+	-- should gfDivideTeams do this automatically?
+	--[[for i = 0, (TeamsCount-1) do
+		for g = teamIndex[i], (teamIndex[i]+teamSize[i]-1) do
+			if GetHogClan(hhs[g]) == 0 then
+				FindPlace(hhs[g], false, 0, LAND_WIDTH/2)
+			elseif GetHogClan(hhs[g]) == 1 then
+				FindPlace(hhs[g], false, LAND_WIDTH/2, LAND_WIDTH)
+			end
+		end
+	end]]
+
+	fPlaced[0] = false
+	fPlaced[1] = false
+
+	--zxc = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
+
+
+	--SetVisualGearValues(zxc, 1000,1000, 20, 255, 1,    10,                     0,         200,        1,      GetClanColor(0))
+					--minO,max0 -glowyornot	--pulsate timer	 -- fuckall      -- radius -- width  -- colour
+end
+
+
+function onNewTurn()
+
+	gameTurns = gameTurns + 1
+
+	if lastTeam ~= GetHogTeamName(CurrentHedgehog) then
+		lastTeam = GetHogTeamName(CurrentHedgehog)
+	end
+
+	--AddCaption("Handling respawns")
+	if gameStarted == true then
+		HandleRespawns()
+	--new method of placing starting flags
+	elseif gameTurns == 1 then
+		ShowMission(loc("CAPTURE THE FLAG"), loc("Flags, and their home base will be placed where each team ends their first turn."), "", 0, 0)
+	elseif gameTurns == 2 then
+		fPlaced[0] = true
+		ShowMission(loc("CAPTURE THE FLAG"), loc("RULES OF THE GAME [Press ESC to view]"), loc(" - Return the enemy flag to your base to score | - First team to 3 captures wins | - You may only score when your flag is in your base | - Hogs will drop the flag if killed, or drowned | - Dropped flags may be returned or recaptured | - Hogs respawn when killed"), 0, 0)
+	elseif gameTurns == 3 then
+		fPlaced[1] = true
+		StartTheGame()
+	end
+
+end
+
+function onGameTick()
+
+	-- onRessurect calls AFTER you have resurrected,
+	-- so keeping track of x,y a few milliseconds before
+	-- is useful
+	--FTTC = FTTC + 1
+	--if FTTC == 100 then
+	--	FTTC = 0
+		for i = 0,1 do
+			if fThief[i] ~= nil then
+				fThiefX[i] = GetX(fThief[i])
+				fThiefY[i] = GetY(fThief[i])
+			end
+		end
+	--end
+
+	-- things we wanna check often
+	if (CurrentHedgehog ~= nil) then
+		--AddCaption(LAND_HEIGHT - GetY(CurrentHedgehog))
+		--AddCaption(GetX(CurrentHedgehog) .. "; " .. GetY(CurrentHedgehog))
+		--CheckTeleporters()
+
+	end
+
+	if gameStarted == true then
+		HandleCircles()
+		if CurrentHedgehog ~= nil then
+			CheckFlagProximity()
+		end
+	elseif CurrentHedgehog ~= nil then -- if the game hasn't started yet, keep track of where we are gonna put the flags on turn end
+
+		if GetHogClan(CurrentHedgehog) == 0 then
+			i = 0
+		elseif GetHogClan(CurrentHedgehog) == 1 then
+			i = 1
+		end
+
+		fSpawnX[i] = GetX(CurrentHedgehog)
+		fSpawnY[i] = GetY(CurrentHedgehog)
+
+	end
+
+end
+
+function onGearResurrect(gear)
+
+	--AddCaption("A gear has been resurrected!")
+
+	-- mark the flag thief as dead if he needed a respawn
+	for i = 0,1 do
+		if gear == fThief[i] then
+			FlagThiefDead(gear)
+		end
+	end
+
+	-- should be covered by gfDivideTeams, actually
+	-- place hogs belonging to each clan either left or right side of map
+	--if GetHogClan(gear) == 0 then
+	--	FindPlace(gear, false, 0, LAND_WIDTH/2)
+	--elseif GetHogClan(gear) == 1 then
+	--	FindPlace(gear, false, LAND_WIDTH/2, LAND_WIDTH)
+	--end
+
+	AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+
+end
+
+function InABetterPlaceNow(gear)
+	for i = 0, (numhhs-1) do
+		if gear == hhs[i] then
+
+			for i = 0,1 do
+				if gear == fThief[i] then
+					FlagThiefDead(gear)
+				end
+			end
+			hhs[i] = nil
+		end
+	end
+end
+
+function onHogHide(gear)
+	 InABetterPlaceNow(gear)
+end
+
+function onHogRestore(gear)
+	match = false
+	for i = 0, (numhhs-1) do
+		if (hhs[i] == nil) and (match == false) then
+			hhs[i] = gear
+			--AddCaption(GetHogName(gear) .. " has reappeared it seems!")
+			match = true
+		end
+	end
+end
+
+
+function onGearAdd(gear)
+
+	if GetGearType(gear) == gtHedgehog then
+		hhs[numhhs] = gear
+		numhhs = numhhs + 1
+		SetEffect(gear, heResurrectable, true)
+
+	elseif GetGearType(gear) == gtPiano then
+
+		for i = 0, 1 do
+			if CurrentHedgehog == fThief[i] then
+				FlagThiefDead(gear)
+			end
+		end
+
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if GetGearType(gear) == gtHedgehog then
+		InABetterPlaceNow(gear)
+	end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Highlander.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Highlander.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,233 @@
+--------------------------------
+-- HIGHLANDER / HOGS OF WAR
+-- version 0.3c
+-- by mikade
+--------------------------------
+
+-----------
+--0.1
+-----------
+
+-- concept test
+
+-----------
+--0.2
+-----------
+
+-- remove tardis till Henek fixes his tracker
+-- change wep crates to health crates
+-- reset arb turntimevalue
+-- include randomOrder
+-- Until fixed .17 methods come out, remove switches and resurrector
+-- on request, removed kamikaze and piano weapons
+-- provisional fixing of bugs that can't actually be fixed yet
+
+-----------
+--0.3
+-----------
+
+-- meh, update incorrect display
+-- may change this in the future to have switches
+-- but for now people are used to it without, so~
+
+-- mudball is now counted as a utility
+
+-----------
+--0.3b
+-----------
+
+-- cleaned up code and got rid of unneccessary vars
+-- mudball is a weapon again
+-- landgun is now a utility
+-- extra time, vampirism utility removed
+-- hammer wep removed
+-- all hogs have kamikaze
+
+-----------
+--0.3c
+-----------
+
+-- restructured some code
+-- added napalm (whoops) to list of possible weapons you can get
+-- hogs no longer recieve airstrike-related weps on border maps
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+local airWeapons = 	{amAirAttack, amMineStrike, amNapalm, amDrillStrike --[[,amPiano]]}
+
+local atkArray = 	{
+					amBazooka, amBee, amMortar, amDrill, --[[amSnowball,]]
+					amGrenade, amClusterBomb, amMolotov, amWatermelon, amHellishBomb, amGasBomb,
+					amShotgun, amDEagle, amFlamethrower, amSniperRifle, amSineGun,
+					amFirePunch, amWhip, amBaseballBat, --[[amKamikaze,]] amSeduction, --[[amHammer,]]
+					amMine, amDynamite, amCake, amBallgun, amRCPlane, amSMine,
+					amRCPlane, amSMine,
+					amBirdy
+					}
+
+local utilArray = 	{
+					amBlowTorch, amPickHammer, amGirder, amPortalGun,
+					amRope, amParachute, amTeleport, amJetpack,
+					amInvulnerable, amLaserSight, --[[amVampiric,]]
+					amLowGravity, amExtraDamage, --[[amExtraTime,]]
+					amLandGun
+					--[[,amTardis, amResurrector, amSwitch]]
+					}
+
+local wepArray = 	{}
+
+local currName
+local lastName
+local started = false
+local switchStage = 0
+
+function StartingSetUp(gear)
+
+	for i = 1, #wepArray do
+		setGearValue(gear,wepArray[i],0)
+	end
+
+	setGearValue(gear,amKamikaze,1)
+
+	i = 1 + GetRandom(#atkArray)
+	setGearValue(gear,atkArray[i],1)
+
+	i = 1 + GetRandom(#utilArray)
+	setGearValue(gear,utilArray[i],1)
+
+	SetHealth(gear, 100)
+
+end
+
+--[[function SaveWeapons(gear)
+
+	-
+	for i = 1, (#wepArray) do
+		setGearValue(gear, wepArray[i], GetAmmoCount(gear, wepArray[i]) )
+		 --AddAmmo(gear, wepArray[i], getGearValue(gear,wepArray[i]) )
+	end
+
+end]]
+
+function ConvertValues(gear)
+
+	for i = 1, #wepArray do
+		AddAmmo(gear, wepArray[i], getGearValue(gear,wepArray[i]) )
+	end
+
+
+end
+
+
+function TransferWeps(gear)
+
+	if CurrentHedgehog ~= nil then
+
+		for i = 1, #wepArray do
+			val = getGearValue(gear,wepArray[i])
+			if val ~= 0 then
+				setGearValue(CurrentHedgehog, wepArray[i], val)
+				AddAmmo(CurrentHedgehog, wepArray[i], val)
+			end
+		end
+
+	end
+
+end
+
+function onGameInit()
+	GameFlags = gfInfAttack + gfRandomOrder
+	HealthCaseProb = 100
+end
+
+function onGameStart()
+
+
+	ShowMission	(
+				loc("HIGHLANDER"),
+				loc("Not all hogs are born equal."),
+
+				"- " .. loc("Eliminate enemy hogs and take their weapons.") .. "|" ..
+				"- " .. loc("Per-Hog Ammo") .. "|" ..
+				"- " .. loc("Weapons reset.") .. "|" ..
+				"- " .. loc("Unlimited Attacks") .. "|" ..
+				"", 4, 4000
+				)
+
+	if MapHasBorder() == false then
+        for i, w in pairs(airWeapons) do
+            table.insert(atkArray, w)
+        end
+    end
+
+	for i, w in pairs(atkArray) do
+        table.insert(wepArray, w)
+	end
+
+	for i, w in pairs(utilArray) do
+        table.insert(wepArray, w)
+	end
+
+	runOnGears(StartingSetUp)
+	runOnGears(ConvertValues)
+
+
+end
+
+function onNewTurn()
+--
+end
+
+
+function onGameTick20()
+
+	if (CurrentHedgehog ~= nil) then
+
+		currName = GetHogName(CurrentHedgehog)
+
+		if (currName ~= lastName) then
+			AddCaption(loc("Switched to ") .. currName .. "!")
+			ConvertValues(CurrentHedgehog)
+		end
+
+		lastName = currName
+	end
+
+end
+
+--[[function onHogHide(gear)
+	-- waiting for Henek
+end
+
+function onHogRestore(gear)
+	-- waiting for Henek
+end]]
+
+function onGearAdd(gear)
+
+	--if GetGearType(gear) == gtSwitcher then
+	--	SaveWeapons(CurrentHedgehog)
+	--end
+
+	if (GetGearType(gear) == gtHedgehog) then
+		trackGear(gear)
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if (GetGearType(gear) == gtHedgehog) then --or (GetGearType(gear) == gtResurrector) then
+		TransferWeps(gear)
+		trackDeletion(gear)
+	end
+
+end
+
+function onAmmoStoreInit()
+	SetAmmo(amSkip, 9, 0, 0, 0)
+	SetAmmo(amKamikaze, 9, 0, 0, 0)
+	--SetAmmo(amSwitch, 9, 0, 0, 0) -------1
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/No_Jumping.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/No_Jumping.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,34 @@
+--------------------------------
+-- NO JUMPING
+--------------------------------
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+
+local specialGear = nil
+
+function onGameInit()
+    Goals = loc("Jumping is disabled")
+end
+
+function onNewTurn()
+	SetInputMask(band(0xFFFFFFFF, bnot(gmLJump + gmHJump)))
+end
+
+function onGearAdd(gear)
+
+	if (GetGearType(gear) == gtJetpack) or (GetGearType(gear) == gtRope) or (GetGearType(gear) == gtParachute) then
+		specialGear = gear
+		SetInputMask(band(0xFFFFFFFF, bnot(gmHJump)))
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if (GetGearType(gear) == gtJetpack) or (GetGearType(gear) == gtRope) or (GetGearType(gear) == gtParachute) then
+		specialGear = nil
+		SetInputMask(band(0xFFFFFFFF, bnot(gmLJump + gmHJump)))
+	end
+
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Racer.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Shoppa
+Shoppa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Racer.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,700 @@
+
+------------------------------------------
+-- RACER 0.5
+-- map-independant racing script
+-- by mikade
+-----------------------------------------
+
+-----------------------------------
+--0.1: took all the code from crazy racer and scrapped most of it
+-----------------------------------
+
+-- Removed tumbler system
+-- Removed extra adds like boosters etc
+-- Added experimental waypoint placement system
+-- More user feedback
+-- Reduced race complexity limit to 5 waypoints
+-- stop placement at complexity limit reached and end turn
+-- guys dont keep racing after dying
+-- invulnerable feasibility
+-- reverted time keeping method
+-- reduced feedback display time
+-- colour-coded addcaptions
+-- cleaned up code
+-- support for more players properly added
+-- tardis fix
+-- remove airstrikes
+
+-- i think the remainder 0 .456 sec of the tracktime isnt getting reset on newturn
+
+-- update feedback
+
+-------
+-- 0.2
+-------
+
+-- allow gameflags
+-- extend time to 90s
+-- remove other air-attack based weps
+-- turn off water rise for sd
+
+-------
+-- 0.3
+-------
+
+-- prevent WP being placed in land
+-- prevent waypoints being placed outside border
+
+-------
+-- 0.4
+-------
+
+-- update user feedback
+-- add more sounds
+
+-------
+-- 0.5
+-------
+
+-- fix ghost disappearing if hog falls in water or somehow dies
+-- lengthen ghost tracking interval to improve performance on slower machines
+-- increase waypoint limit to 8
+-- allow for persistent showmission information
+
+-----------------------------
+-- SCRIPT BEGINS
+-----------------------------
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+
+------------------
+-- Got Variables?
+------------------
+
+local fMod = 1000000 -- 1
+local roundLimit = 3
+local roundNumber = 0
+local firstClan = 10
+
+local fastX = {}
+local fastY = {}
+local fastCount = 0
+local fastIndex = 0
+local fastColour
+
+local currX = {}
+local currY = {}
+local currCount = 0
+
+--------------------------
+-- hog and team tracking variales
+--------------------------
+
+local numhhs = 0 -- store number of hedgehogs
+local hhs = {} -- store hedgehog gears
+
+local numTeams --  store the number of teams in the game
+local teamNameArr = {}	-- store the list of teams
+local teamClan = {}
+local teamSize = {}	-- store how many hogs per team
+local teamIndex = {} -- at what point in the hhs{} does each team begin
+
+local teamComment = {}
+local teamScore = {}
+
+-------
+-- racer vars
+--------
+
+local cGear = nil
+
+local bestClan = nil
+local bestTime = nil
+
+local gameBegun = false
+local gameOver = false
+local racerActive = false
+local trackTime = 0
+
+local wpCirc = {}
+local wpX = {}
+local wpY = {}
+local wpCol = {}
+local wpActive = {}
+local wpRad = 450 --75
+local wpCount = 0
+local wpLimit = 8
+
+local roundN
+local lastRound
+local RoundHasChanged
+
+-------------------
+-- general methods
+-------------------
+
+function RebuildTeamInfo()
+
+
+	-- make a list of individual team names
+	for i = 0, (TeamsCount-1) do
+		teamNameArr[i] = " " -- = i
+		teamSize[i] = 0
+		teamIndex[i] = 0
+		teamScore[i] = 100000
+	end
+	numTeams = 0
+
+	for i = 0, (numhhs-1) do
+
+		z = 0
+		unfinished = true
+		while(unfinished == true) do
+
+			newTeam = true
+			tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name
+
+			if tempHogTeamName == teamNameArr[z] then
+				newTeam = false
+				unfinished = false
+			end
+
+			z = z + 1
+
+			if z == TeamsCount then
+				unfinished = false
+				if newTeam == true then
+					teamNameArr[numTeams] = tempHogTeamName
+					numTeams = numTeams + 1
+				end
+			end
+
+		end
+
+	end
+
+	-- find out how many hogs per team, and the index of the first hog in hhs
+	for i = 0, (numTeams-1) do
+		for z = 0, (numhhs-1) do
+			if GetHogTeamName(hhs[z]) == teamNameArr[i] then
+				teamClan[i] = GetHogClan(hhs[z])
+				if teamSize[i] == 0 then
+					teamIndex[i] = z -- should give starting index
+				end
+				teamSize[i] = teamSize[i] + 1
+				--add a pointer so this hog appears at i in hhs
+			end
+		end
+
+	end
+
+end
+
+
+-----------------
+-- RACER METHODS
+-----------------
+
+function CheckWaypoints()
+
+	trackFinished = true
+
+	for i = 0, (wpCount-1) do
+
+		g1X, g1Y = GetGearPosition(CurrentHedgehog)
+		g2X, g2Y = wpX[i], wpY[i]
+
+		g1X = g1X - g2X
+		g1Y = g1Y - g2Y
+		dist = (g1X*g1X) + (g1Y*g1Y)
+
+		--if i == 0 then
+		--	AddCaption(dist .. "/" .. (wpRad*wpRad) )
+		--end
+
+		NR = (48/100*wpRad)/2
+
+		if dist < (NR*NR) then
+		--if dist < (wpRad*wpRad) then
+			--AddCaption("howdy")
+			wpActive[i] = true
+			wpCol[i] = GetClanColor(GetHogClan(CurrentHedgehog)) -- new				--GetClanColor(1)
+			SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i])
+
+			wpRem = 0
+			for k = 0, (wpCount-1) do
+				if wpActive[k] == false then
+					wpRem = wpRem + 1
+				end
+			end
+
+			AddCaption(loc("Way-Points Remaining") .. ": " .. wpRem,0xffba00ff,capgrpAmmoinfo)
+
+		end
+
+		if wpActive[i] == false then
+			trackFinished = false
+		end
+
+	end
+
+	return(trackFinished)
+
+end
+
+function AdjustScores()
+
+	if bestTime == nil then
+		bestTime = 100000
+		bestClan = 10
+		bestTimeComment = "N/A"
+	end
+
+	newScore = false
+
+	-- update this clan's time if the new track is better
+	for i = 0, (numTeams-1) do
+		if teamClan[i] == GetHogClan(CurrentHedgehog) then
+			if trackTime < teamScore[i] then
+				teamScore[i] = trackTime
+				newScore = true
+			else
+				newScore = false
+			end
+		end
+	end
+
+	--bestTime = 100000
+	--bestClan = 10
+
+	-- find the best time out of those so far
+	for i = 0, (numTeams-1) do
+		if teamScore[i] < bestTime then
+			bestTime = teamScore[i]
+			bestClan = teamClan[i]
+		end
+	end
+
+	if bestTime ~= 100000 then
+		bestTimeComment = (bestTime/1000) ..loc("s")
+	end
+
+	if newScore == true then
+		if trackTime == bestTime then -- best time of the race
+			ShowMission(loc("RACER"),
+			loc("TRACK COMPLETED"),
+			loc("NEW RACE RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" ..
+			loc("WINNING TIME: ") .. bestTimeComment, 0, 4000)
+			PlaySound(sndHomerun)
+		else	-- best time for the clan
+			ShowMission(loc("RACER"),
+			loc("TRACK COMPLETED"),
+			loc("NEW CLAN RECORD: ") .. (trackTime/1000) ..loc("s") .. "|" ..
+			loc("WINNING TIME: ") .. bestTimeComment, 4, 4000)
+		end
+	else -- not any kind of new score
+		ShowMission(loc("RACER"),
+		loc("TRACK COMPLETED"),
+		loc("TIME: ") .. (trackTime/1000) ..loc("s") .. "|" ..
+		loc("WINNING TIME: ") .. bestTimeComment, -amSkip, 4000)
+		PlaySound(sndHellish)
+	end
+
+
+	--------
+	--new
+	--------
+
+	if bestTime == trackTime then
+		--AddCaption("wooooooooooooooooooooooooooooo")
+
+		fastColour = GetClanColor(GetHogClan(CurrentHedgehog))
+
+		for i = 0, (currCount-1) do
+			fastX[i] = currX[i]
+			fastY[i] = currY[i]
+		end
+
+		fastCount = currCount
+		fastIndex = 0
+
+		--currCount = 0 -- is this needed?
+
+	else
+		currCount = 0
+		fastIndex = 0
+	end
+
+
+end
+
+function onNewRound()
+
+	roundNumber = roundNumber + 1
+
+	totalComment = ""
+	for i = 0, (TeamsCount-1) do
+			if teamNameArr[i] ~= " " then				-- teamScore[teamClan[i]]
+				teamComment[i] = teamNameArr[i] .. ": " .. (teamScore[i]/1000) .. loc("s|")
+				totalComment = totalComment .. teamComment[i]
+			elseif teamNameArr[i] == " " then
+				teamComment[i] = "|"
+			end
+	end
+
+	ShowMission(	loc("RACER"),
+					loc("STATUS UPDATE"),
+					loc("Rounds Complete: ") .. roundNumber .. "/" .. roundLimit .. "|" .. " " .. "|" ..
+					loc("Best Team Times: ") .. "|" .. totalComment, 0, 4000)
+
+	-- end game if its at round limit
+	if roundNumber == roundLimit then
+		for i = 0, (numhhs-1) do
+			if GetHogClan(hhs[i]) ~= bestClan then
+				SetEffect(hhs[i], heResurrectable, false)
+				SetHealth(hhs[i],0)
+			end
+		end
+		gameOver = true
+		TurnTimeLeft = 1
+	end
+
+end
+
+function CheckForNewRound()
+
+	-------------
+	------ new
+	-------------
+
+	--[[turnN = turnN + 1
+	if gameBegun == false then
+		if turnN == 2 then
+			for i = 0, (numhhs-1) do
+				if hhs[i] ~= nil then
+					SetEffect(hhs[i], heResurrectable, false)
+					SetHealth(hhs[i],0)
+				end
+			end
+			gameOver = true
+			TurnTimeLeft = 1
+		end
+	else
+
+
+	end]]
+
+	--[[if roundBegun == true then
+
+		if RoundHasChanged == true then
+			roundN = roundN + 1
+			RoundHasChanged = false
+			onNewRound()
+		end
+
+		if lastRound ~= TotalRounds then -- new round, but not really
+
+			if RoundHasChanged == false then
+				RoundHasChanged = true
+			end
+
+		end
+
+		AddCaption("RoundN:" .. roundN .. "; " .. "TR: " .. TotalRounds)
+
+		lastRound = TotalRounds
+
+	end]]
+
+	------------
+	----- old
+	------------
+
+	if GetHogClan(CurrentHedgehog) == firstClan then
+		onNewRound()
+	end
+
+end
+
+function DisableTumbler()
+	currCount = 0
+	fastIndex = 0
+	TurnTimeLeft = 0
+	racerActive = false -- newadd
+end
+
+function HandleGhost()
+
+	-- get the current xy of the racer at this point
+	currX[currCount] = GetX(CurrentHedgehog)
+	currY[currCount] = GetY(CurrentHedgehog)
+	currCount = currCount + 1
+
+	-- draw a ping of smoke where the fastest player was at this point
+	if (fastCount ~= 0) and (fastIndex < fastCount) then
+
+		fastIndex = fastIndex + 1
+
+		tempE = AddVisualGear(fastX[fastIndex], fastY[fastIndex], vgtSmoke, 0, false)
+		g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+		SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, fastColour )
+
+		--AddCaption("fC: " .. fastIndex .. " / " .. fastCount)
+
+	else
+
+		--AddCaption("excep fC: " .. fastIndex .. " / " .. fastCount)
+
+	end
+
+
+
+end
+
+----------------------------------
+-- GAME METHODS / EVENT HANDLERS
+----------------------------------
+
+function onGameInit()
+	GameFlags = GameFlags + gfInfAttack + gfInvulnerable
+	CaseFreq = 0
+	TurnTime = 90000
+	WaterRise = 0
+end
+
+
+function onGameStart()
+
+	roundN = 0
+	lastRound = TotalRounds
+	RoundHasChanged = false -- true
+
+	RebuildTeamInfo()
+
+	ShowMission	(
+				loc("RACER"),
+				loc("a Hedgewars mini-game"),
+
+				loc("Build a track and race.") .. "|" ..
+				loc("Round Limit:") .. " " .. roundLimit .. "|" ..
+
+				"", 4, 4000
+				)
+end
+
+function PlaceWayPoint(x,y)
+
+	if (wpCount < wpLimit) then -- seems to not work with a hedgehog nil chek
+
+		wpX[wpCount] = x
+		wpY[wpCount] = y
+		wpCol[wpCount] = 0xffffffff
+		wpCirc[wpCount] = AddVisualGear(wpX[wpCount],wpY[wpCount],vgtCircle,0,true)
+																		--100
+		SetVisualGearValues(wpCirc[wpCount], wpX[wpCount], wpY[wpCount], 20, 100, 1, 10, 0, wpRad, 5, wpCol[wpCount])
+
+		wpCount = wpCount + 1
+
+		AddCaption(loc("Waypoint placed.") .. " " .. loc("Available points remaining: ") .. (wpLimit-wpCount))
+
+	end
+
+end
+
+function onNewTurn()
+
+	CheckForNewRound()
+
+	racerActive = false
+
+	trackTime = 0
+
+	currCount = 0 -- hopefully this solves problem
+	AddAmmo(CurrentHedgehog, amAirAttack, 0)
+	gTimer = 0
+
+	-- Set the waypoints to unactive on new round
+	for i = 0,(wpCount-1) do
+		wpActive[i] = false
+		wpCol[i] = 0xffffffff
+		SetVisualGearValues(wpCirc[i], wpX[i], wpY[i], 20, 100, 1, 10, 0, wpRad, 5, wpCol[i])
+	end
+
+	-- Handle Starting Stage of Game
+	if (gameOver == false) and (gameBegun == false) then
+		if wpCount >= 3 then
+			gameBegun = true
+			roundNumber = 0
+			firstClan = GetHogClan(CurrentHedgehog)
+			ShowMission(loc("RACER"),
+			loc("GAME BEGUN!!!"),
+			loc("Complete the track as fast as you can!"), 2, 4000)
+		else
+			ShowMission(loc("RACER"),
+			loc("NOT ENOUGH WAYPOINTS"),
+			loc("Place more waypoints using the 'Air Attack' weapon."), 2, 4000)
+			AddAmmo(CurrentHedgehog, amAirAttack, 4000)
+            ParseCommand("setweap " .. string.char(amAirAttack))
+		end
+	end
+
+	if gameOver == true then
+		gameBegun = false
+		racerActive = false -- newadd
+	end
+
+	AddAmmo(CurrentHedgehog, amTardis, 0)
+	AddAmmo(CurrentHedgehog, amDrillStrike, 0)
+	AddAmmo(CurrentHedgehog, amMineStrike, 0)
+	AddAmmo(CurrentHedgehog, amNapalm, 0)
+	AddAmmo(CurrentHedgehog, amPiano, 0)
+
+end
+
+function onGameTick20()
+
+	-- airstrike detected, convert this into a potential waypoint spot
+	if cGear ~= nil then
+		x,y = GetGearPosition(cGear)
+        if x > -9000 then
+            x,y = GetGearTarget(cGear)
+
+
+            if TestRectForObstacle(x-20, y-20, x+20, y+20, true) then
+                AddCaption(loc("Please place the way-point in the open, within the map boundaries."))
+                PlaySound(sndDenied)
+            elseif (y > WaterLine-50) then
+                AddCaption(loc("Please place the way-point further from the waterline."))
+                PlaySound(sndDenied)
+            else
+                PlaceWayPoint(x, y)
+                if wpCount == wpLimit then
+                    AddCaption(loc("Race complexity limit reached."))
+                    DisableTumbler()
+                end
+            end
+        else
+            DeleteGear(cGear)
+        end
+        SetGearPosition(cGear, -10000, 0)
+	end
+
+
+	-- start the player tumbling with a boom once their turn has actually begun
+	if racerActive == false then
+
+		if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then
+
+			-- if the gamehas started put the player in the middle of the first
+			--waypoint that was placed
+			if gameBegun == true then
+				AddCaption(loc("Good to go!"))
+				racerActive = true
+				trackTime = 0
+
+				SetGearPosition(CurrentHedgehog, wpX[0], wpY[0])
+				AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1)
+				FollowGear(CurrentHedgehog)
+
+				HideMission()
+
+			else
+				-- still in placement mode
+			end
+
+		end
+	end
+
+
+
+	-- has the player started his tumbling spree?
+	if (CurrentHedgehog ~= nil) then
+
+		--airstrike conversion used to be here
+
+		-- if the RACE has started, show tracktimes and keep tabs on waypoints
+		if (racerActive == true) and (gameBegun == true) then
+
+			--ghost
+			if GameTime%40 == 0 then
+				HandleGhost()
+			end
+
+			trackTime = trackTime + 20
+
+			if GameTime%100 == 0 then
+                
+                if trackTime%1000 == 0 then
+                    AddCaption((trackTime/1000)..'.0',GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2)
+                else
+                    AddCaption(trackTime/1000,GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage2)
+                end
+
+				if (CheckWaypoints() == true) then
+					AdjustScores()
+					racerActive = false
+					DisableTumbler()
+				end
+
+			end
+
+		end
+
+
+
+		-- if the player has expended his tunbling time, stop him tumbling
+		if TurnTimeLeft <= 20 then
+			DisableTumbler()
+		end
+
+	end
+
+end
+
+function onGearResurrect(gear)
+
+	AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+
+	if gear == CurrentHedgehog then
+		DisableTumbler()
+	end
+
+	-- if the player stops and "dies" or flies into water, stop him racing
+	--[[if gear == CurrentHedgehog then
+		DisableTumbler()
+		ShowMission(loc("RACER"),
+		loc("TRACK FAILED!"),
+		loc("WINNING TIME: ") .. bestTimeComment, -amSkip, 4000)
+	end]]
+
+end
+
+function onGearAdd(gear)
+
+	if GetGearType(gear) == gtHedgehog then
+		hhs[numhhs] = gear
+		numhhs = numhhs + 1
+		SetEffect(gear, heResurrectable, true)
+	end
+
+	if GetGearType(gear) == gtAirAttack then
+		cGear = gear
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if GetGearType(gear) == gtAirAttack then
+		cGear = nil
+	end
+
+end
+
+--[[function onAmmoStoreInit()
+	SetAmmo(amRope, 9, 0, 0, 0)
+	SetAmmo(amJetpack, 9, 0, 0, 0)
+	SetAmmo(amSkip, 9, 0, 0, 0)
+end]]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Random_Weapon.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+locked
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Random_Weapon.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,113 @@
+-- Random Weapons, example for gameplay scripts
+
+-- Load the library for localisation ("loc" function)
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+
+-- Load the gear tracker
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+-- List of available weapons
+local weapons = { amGrenade, amClusterBomb, amBazooka, amBee, amShotgun,
+            amMine, amDEagle, amDynamite, amFirePunch, amWhip, amPickHammer,
+            amBaseballBat, amTeleport, amMortar, amCake, amSeduction,
+            amWatermelon, amHellishBomb, amDrill, amBallgun, amRCPlane,
+            amSniperRifle, amMolotov, amBirdy, amBlowTorch, amGasBomb,
+            amFlamethrower, amSMine, amHammer }
+
+-- List of weapons that attack from the air
+local airweapons = { amAirAttack, amMineStrike, amNapalm, amDrillStrike }
+
+-- Function that assigns the team their weapon
+function assignAmmo(hog)
+    -- Get name of the current team
+    local name = GetHogTeamName(hog)
+    -- Get whither the team has been processed
+    local processed = getTeamValue(name, "processed")
+    -- If it has not, process it
+    if processed == nil or not processed then
+        -- Get the ammo for this hog's team
+        local ammo = getTeamValue(name, "ammo")
+        -- If there is no ammo, get a random one from the list and store it
+        if ammo == nil then
+            ammo = weapons[GetRandom(table.maxn(weapons)) + 1]
+            setTeamValue(name, "ammo", ammo)
+        end
+        -- Add the ammo for the hog
+        AddAmmo(hog, ammo)
+        -- Mark as processed
+        setTeamValue(name, "processed", true)
+    end
+end
+
+-- Mark team as not processed
+function reset(hog)
+    setTeamValue(GetHogTeamName(hog), "processed", false)
+end
+
+function onGameInit()
+    -- Limit flags that can be set, but allow game schemes to be used
+    GameFlags = band(bor(GameFlags, gfResetWeps), bnot(gfInfAttack))
+    -- Set a custom game goal that will show together with the scheme ones
+    Goals = loc("Each turn you get one random weapon")
+end
+
+function onGameStart()
+    -- Initialize the tracking of hogs and teams
+    trackTeams()
+    -- Add air weapons to the game if the border is not active
+    if MapHasBorder() == false then
+        for i, w in pairs(airweapons) do
+            table.insert(weapons, w)
+        end
+    end
+end
+
+function onAmmoStoreInit()
+    -- Allow skip at all times
+    SetAmmo(amSkip, 9, 0, 0, 0)
+
+    -- Let utilities be available through crates
+    SetAmmo(amParachute, 0, 1, 0, 1)
+    SetAmmo(amGirder, 0, 1, 0, 2)
+    SetAmmo(amSwitch, 0, 1, 0, 1)
+    SetAmmo(amLowGravity, 0, 1, 0, 1)
+    SetAmmo(amExtraDamage, 0, 1, 0, 1)
+    SetAmmo(amInvulnerable, 0, 1, 0, 1)
+    SetAmmo(amExtraTime, 0, 1, 0, 1)
+    SetAmmo(amLaserSight, 0, 1, 0, 1)
+    SetAmmo(amVampiric, 0, 1, 0, 1)
+    SetAmmo(amJetpack, 0, 1, 0, 1)
+    SetAmmo(amPortalGun, 0, 1, 0, 1)
+    SetAmmo(amResurrector, 0, 1, 0, 1)
+
+    -- Allow weapons to be used
+    for i, w in pairs(weapons) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+    -- Allow air weapons to be used
+    for i, w in pairs(airweapons) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+end
+
+function onNewTurn()
+    -- Give every team their weapons, so one can plan during anothers turn
+    runOnGears(assignAmmo)
+    -- Mark all teams as not processed
+    runOnGears(reset)
+    -- Set the current teams weapons to nil so they will get new after the turn has ended
+    setTeamValue(GetHogTeamName(CurrentHedgehog), "ammo", nil)
+end
+
+function onGearAdd(gear)
+    -- Catch hedgehogs for the tracker
+    if GetGearType(gear) == gtHedgehog then
+        trackGear(gear)
+    end
+end
+
+function onGearDelete(gear)
+    -- Remove hogs that are gone
+    trackDeletion(gear)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Space_Invasion.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Space_Invasion.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2435 @@
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+---------------------------------------------------
+---------------------------------------------------
+---------------------------------------------------
+--- Space Invasion Code Follows (1.1)
+---------------------------------------------------
+---------------------------------------------------
+-- VERSION HISTORY
+----------------
+-- version 0.1
+----------------
+-- conversion of tumbler into space invasion
+-- a million and one changes
+-- bells and whistles
+
+----------------
+-- version 0.2
+----------------
+-- code slowly getting cleaner, it still looks like a spaghetti monster tho
+-- lots of console tracking :/
+-- all visual gears are now compulsary (will probably revert this)
+-- implemented fMod to try combat desyncs and bring this in line with dev
+
+----------------
+-- version 0.3
+----------------
+-- values of scoring changed to 3:10, and now based on vCircScore
+-- time gained from killing a red circ increased from 3 to 4
+-- circles now spawn at a distance of at least 800 or until sanity limit
+-- roundsLimit now based off MinesTime (kinda, its an experiment)
+
+-----------------
+--0.4
+-----------------
+-- commented out a lot of WriteLnToConsoles (dont need them at this point)
+-- added some different WriteLnToConsoles
+-- changed some of the collision detect for explosives in checkvarious()
+
+-----------------
+--0.5
+-----------------
+-- added implementation for a projectile shield
+-- added a "bonus" orange invader that partially recharges player shield
+-- added a tough "blueboss" blue invader
+-- expanded user feedback
+-- circles now have health and are capable of being merely "damaged"
+-- redid a lot of the collision code and added CircleDamaged
+-- added more sounds to events
+-- added more visual gears
+
+-----------------
+--0.6
+-----------------
+-- removed a few WriteLns
+-- added randomized grunts on circ damage
+-- added (mostly) graceful fading out of circles :D:
+-- changed odds for circles
+-- changed user feedback
+-- fixed the location of the explosion where player bashes into circ
+
+-----------------
+--0.7
+-----------------
+-- added PlaySound(sndSuddenDeath) when ammo gets depleted
+-- added an extra "Ammo Depleted" note if user presses fire while empty
+-- specified how much shield power is gained on shield powerup collection
+-- changed odds for circles AGAIN, ammo is now sliiightly more common
+-- switched most of the explosions/smoke effects back to non-critical vgears (with a few exceptions)
+-- tumbletime is now based off turntime and is variable
+-- delete explosives in DeleteFarFlungBarrel rather than explode them on map boundaries to save on performance
+-- utilized the improved AddCaption to tint / prevent overrides
+-- temporarily disabled bugged sort that displays teams according to their score
+-- reluctantly changed the colour of the bonus circ to purple
+-- standarized point notation
+-- added some missing locs
+-- commented out remaining WriteLnToConsoles for the meanwhile with the prefix "nw"
+
+-- ACHIEIVEMENTS added
+-- (during one turn) aka repeatable
+-- Ammo Manic (Destroy 3 green circles for + 5 points)
+-- Drone Hunter (Destroy 5 red circles for + 10 points)
+-- Shield Seeker (Destroy 3 purple circles for +10 points)
+-- Boss Slayer (Destroy 2 blue circles for +25 points)
+
+-- Shield Master (disolve 5 shells for +10 points)
+-- Shield Miser (don't use your shield at all (3.5*roundkills)+2 points)
+
+-- Depleted Kamikaze! (kamikaze into a blue/red circ when you are out of ammo) 5pts
+-- Timed Kamikaze! (kamikaze into a blue/red circ when you only have 5s left) 10pts
+-- Kamikaze Expert (combination of the above two) 15pts
+
+-- Multi-shot (destroy more than 1 invader with a single bullet) 15pts
+-- X-Hit Combo (destroy another invader in less than 3 seconds) chainLength*2 points
+
+-- Accuracy Bonus (80% accuracy at the end of your turn with more than 5 shots fired) 15pts
+
+--(during the length of the game) aka non-repeatable
+-- 10/25/50 kills (+25/+50/+100 points)
+
+-----------------
+--0.8
+-----------------
+-- added a HUD for turntimeleft, ammo, shield
+-- shieldhealth hits 0 properly
+
+------------------------
+-- version 0.8.1
+------------------------
+
+-- stop hiding non-existant 4th Tag
+-- redraw HUD on screen resolution change
+
+------------------------
+-- version 0.9
+------------------------
+-- time for more 'EXPERIMENTS' mwahahahahahaha D:
+-- (hopefully) balanced Shield Miser
+-- bosses are no longer a redunkulous 50 points, but toned down to 30
+-- experimental radar (it's INTERACTIVE and math-heavy :D) (visual gears are safe... right? D:)
+-- bugfix and balance for multishot
+
+------------------------
+-- version 1.0
+------------------------
+-- if only version numbers actually worked like this, wouldn't that be awful :D
+-- added surfer achievement
+-- increased value of shield miser by 1 point per kill (OP?)
+
+------------------------
+-- version 1.1
+------------------------
+-- fixed radar so that blips dont go past circs when you get very close
+-- added a missing loc for shield depletion
+-- increased delay to 1000 to try stop noobies missing their turn
+-- added sniper achievement for hits from over a 1000000 away
+-- added achievement for 3 "sniper" shots in a round
+-- added achievement for 3 "point blank" shots in a round
+-- added "fierce Competition" achievement for shooting an enemy hog (once per round)
+-- some support for more weapons later
+
+--------------------------
+--notes for later
+--------------------------
+-- maybe add a check for a tie, IMPOSSIBRU THERE ARE NO TIES
+-- more achievements? (3 kamikazes in a row, supreme shield expert/miser etc?)
+
+-- if more weps are added, replace primshotsfired all over the place
+
+-- look for derp and let invaders shoot again
+
+-- more weps? flamer/machineballgun,
+-- some kind of bomb that just drops straight down
+-- "fire and forget" missile
+-- shockwave
+
+-- some kind of ability-meter that lets you do something awesome when you are
+-- doing really well in a given round.
+-- probably new kind of shield that pops any invaders who come near
+
+-- fix game never ending bug
+-- fix radar
+-- new invader: golden snitch, doesn't show up on your radar
+
+-- maybe replace (48/100*vCircRadius[i])/2 with something better
+
+
+--[[CAPTION CATEGORIES
+-----------------
+capgrpGameState
+-----------------
+AddCaption(LOC_NOT("Sniper!") .. " +10 " .. LOC_NOT("points") .. "!",0xffba00ff,capgrpAmmostate)
+--they call me bullsye
+--point blank combo
+--fierce Competition
+-----------------
+capgrpAmmostate
+-----------------
+AddCaption( chainLength .. LOC_NOT("-chain! +") .. chainLength*2 .. LOC_NOT(" points!"),0xffba00ff,capgrpAmmostate)
+AddCaption(LOC_NOT("Multi-shot! +15 points!"),0xffba00ff,capgrpAmmostate)
+
+-----------------
+capgrpAmmoinfo
+-----------------
+AddCaption(LOC_NOT("Shield Miser! +20 points!"),0xffba00ff,capgrpAmmoinfo)
+AddCaption(LOC_NOT("Shield Master! +10 points!"),0xffba00ff,capgrpAmmoinfo)
+
+-----------------
+capgrpVolume
+-----------------
+AddCaption(LOC_NOT("Boom! +25 points!"),0xffba00ff,capgrpVolume)
+AddCaption(LOC_NOT("BOOM! +50 points!"),0xffba00ff,capgrpVolume)
+AddCaption(LOC_NOT("BOOM! BOOM! BOOM! +100 points!"),0xffba00ff,capgrpVolume)
+AddCaption(LOC_NOT("Accuracy Bonus! +15 points!"),0xffba00ff,capgrpVolume)
+AddCaption(LOC_NOT("Surfer! +15 points!"),0xffba00ff,capgrpVolume)
+
+-----------------
+capgrpMessage
+-----------------
+AddCaption(LOC_NOT("Ammo Depleted!"),0xff0000ff,capgrpMessage)
+AddCaption(LOC_NOT("Ammo: ") .. primShotsLeft)
+AddCaption(LOC_NOT("Shield Depleted"),0xff0000ff,capgrpMessage)
+AddCaption( LOC_NOT("Shield ON:") .. " " .. shieldHealth - 80 .. " " .. LOC_NOT("Power Remaining") )
+AddCaption(LOC_NOT("Shield OFF:") .. " " .. shieldHealth - 80 .. " " .. LOC_NOT("Power Remaining") )
+
+AddCaption(LOC_NOT("Time Extended!") .. "+" .. 4 .. LOC_NOT("s"), 0xff0000ff,capgrpMessage )
+AddCaption("+" .. 3 .. " " .. LOC_NOT("Ammo"), 0x00ff00ff,capgrpMessage)
+AddCaption(LOC_NOT("Shield boosted! +30 power"), 0xff00ffff,capgrpMessage)
+AddCaption(LOC_NOT("Shield is fully recharged!"), 0xffae00ff,capgrpMessage)
+AddCaption(LOC_NOT("Boss defeated! +50 points!"), 0x0050ffff,capgrpMessage)
+
+AddCaption(LOC_NOT("GOTCHA!"))
+AddCaption(LOC_NOT("Kamikaze Expert! +15 points!"),0xffba00ff,capgrpMessage)
+AddCaption(LOC_NOT("Depleted Kamikaze! +5 points!"),0xffba00ff,capgrpMessage)
+AddCaption(LOC_NOT("Timed Kamikaze! +10 points!"),0xffba00ff,capgrpMessage)
+
+-----------------
+capgrpMessage2
+-----------------
+AddCaption(LOC_NOT("Drone Hunter! +10 points!"),0xffba00ff,capgrpMessage2)
+AddCaption(LOC_NOT("Ammo Maniac! +5 points!"),0xffba00ff,capgrpMessage2)
+AddCaption(LOC_NOT("Shield Seeker! +10 points!"),0xffba00ff,capgrpMessage2)
+AddCaption(LOC_NOT("Boss Slayer! +25 points!"),0xffba00ff,capgrpMessage2)
+]]
+
+----------------------------------
+-- so I herd u liek wariables
+----------------------------------
+
+--local fMod = 1	-- for use in .15 single player only, otherwise desync
+local fMod = 1000000 -- use this for dev and .16+ games
+
+-- some console stuff
+local shellID = 0
+local explosivesID = 0
+
+-- gaudyRacer
+local boosterOn = false
+local roundLimit = 3	-- no longer set here (see version history)
+local roundNumber = 0
+local firstClan = 10
+local gameOver = false
+local gameBegun = false
+
+local bestClan = 10
+local bestScore = 0
+local sdScore = {}
+local sdName = {}
+local sdKills = {}
+
+local roundN = 0
+local lastRound
+local RoundHasChanged = true
+
+--------------------------
+-- hog and team tracking variales
+--------------------------
+
+local numhhs = 0
+local hhs = {}
+
+local numTeams
+local teamNameArr = {}
+local teamClan = {}
+local teamSize = {}
+local teamIndex = {}
+
+local teamComment = {}
+local teamScore = {}
+local teamCircsKilled = {}
+local teamSurfer = {}
+
+-- stats variables
+--local teamRed = {}
+--local teamBlue = {}
+--local teamOrange = {}
+--local teamGreen = {}
+local roundKills = 0
+local RK = 0
+local GK = 0
+local BK = 0
+local OK = 0
+local SK = 0
+local shieldMiser = true
+local fierceComp = false
+local chainCounter = 0
+local chainLength = 0
+local shotsFired = 0
+local shotsHit = 0
+local SurfTime = 0
+local sniperHits = 0
+local pointBlankHits = 0
+---------------------
+-- tumbler goods
+---------------------
+
+local leftOn = false
+local rightOn = false
+local upOn = false
+local downOn = false
+
+----------------
+-- TUMBLER
+local wep = {}
+local wepAmmo = {}
+local wepCol = {}
+local wepIndex = 0
+local wepCount = 0
+local fireTimer = 0
+----------------
+
+
+
+local primShotsMax = 5
+local primShotsLeft = 0
+
+local TimeLeft = 0
+local stopMovement = false
+local tumbleStarted = false
+
+local beam = false
+local pShield
+local shieldHealth
+
+local shockwave
+local shockwaveHealth = 0
+local shockwaveRad = 300
+
+local vTag = {}
+
+-----------------------------------------------
+-- CIRCLY GOODIES
+-----------------------------------------------
+
+local CirclesAreGo = false
+local playerIsFine = true
+local targetHit = false
+
+local FadeAlpha = 0 -- used to fade the circles out gracefully when player dies
+local pTimer = 0 -- tracking projectiles following player
+
+--local m2Count = 0		-- handle speed of circs
+
+local vCirc = {}
+local vCCount = 0
+
+local rCirc = {}
+local rCircX = {}
+local rCircY = {}
+local rAlpha = 255
+local radShotsLeft = 0
+
+local vCircActive = {}
+local vCircHealth = {}
+local vType = {}
+local vCounter = {}		-- how often this circ gets to "fire" etc
+local vCounterLim = {} -- when vCounter == vCounterLim circle performs its special
+local vCircScore = {} -- how many points killing this invader gives
+
+local vCircRadMax = {}
+local vCircRadMin = {}
+local vCircRadDir = {}
+local vCircRadCounter = {}
+
+local vCircDX = {}
+local vCircDY = {}
+
+local vCircX = {}
+local vCircY = {}
+local vCircMinA = {}
+local vCircMaxA = {}
+local vCircType = {}
+local vCircPulse = {}
+local vCircFuckAll = {}
+local vCircRadius = {}
+local vCircWidth = {}
+local vCircCol = {}
+
+-------------------------------------------
+-- some lazy copypasta/modified methods
+-------------------------------------------
+
+
+
+function HideTags()
+
+	for i = 0, 2 do
+		SetVisualGearValues(vTag[i],0,0,0,0,0,1,0, 0, 240000, 0xffffff00)
+	end
+
+end
+
+function DrawTag(i)
+
+	zoomL = 1.3
+
+	xOffset = 40
+
+	if i == 0 then
+		yOffset = 40
+		tCol = 0xffba00ff
+		tValue = TimeLeft
+	elseif i == 1 then
+		zoomL = 1.1
+		yOffset = 70
+		tCol = 0x00ff00ff
+		tValue = wepAmmo[wepIndex] --primShotsLeft
+	elseif i == 2 then
+		zoomL = 1.1
+		xOffset = 40 + 35
+		yOffset = 70
+		tCol = 0xa800ffff
+		tValue = shieldHealth - 80
+	end
+
+	DeleteVisualGear(vTag[i])
+	vTag[i] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
+	g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vTag[i])
+	SetVisualGearValues	(
+				vTag[i], 		--id
+				-(ScreenWidth/2) + xOffset,	--xoffset
+				ScreenHeight - yOffset, --yoffset
+				0, 			--dx
+				0, 			--dy
+				zoomL, 			--zoom
+				1, 			--~= 0 means align to screen
+				g7, 			--frameticks
+				tValue, 		--value
+				240000, 		--timer
+				tCol		--GetClanColor( GetHogClan(CurrentHedgehog) )
+				)
+
+end
+
+function RebuildTeamInfo()
+
+	-- make a list of individual team names
+	for i = 0, (TeamsCount-1) do
+		teamNameArr[i] = " " -- = i
+		teamSize[i] = 0
+		teamIndex[i] = 0
+		teamScore[i] = 0
+		teamCircsKilled[i] = 0
+		teamSurfer[i] = false
+	end
+	numTeams = 0
+
+	for i = 0, (numhhs-1) do
+
+		z = 0
+		unfinished = true
+		while(unfinished == true) do
+
+			newTeam = true
+			tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name
+
+			if tempHogTeamName == teamNameArr[z] then
+				newTeam = false
+				unfinished = false
+			end
+
+			z = z + 1
+
+			if z == (TeamsCount-1) then
+				unfinished = false
+				if newTeam == true then
+					teamNameArr[numTeams] = tempHogTeamName
+					numTeams = numTeams + 1
+				end
+			end
+
+		end
+
+	end
+
+	-- find out how many hogs per team, and the index of the first hog in hhs
+	for i = 0, (TeamsCount-1) do
+
+		for z = 0, (numhhs-1) do
+			if GetHogTeamName(hhs[z]) == teamNameArr[i] then
+				teamClan[i] = GetHogClan(hhs[z])
+				if teamSize[i] == 0 then
+					teamIndex[i] = z -- should give starting index
+				end
+				teamSize[i] = teamSize[i] + 1
+				--add a pointer so this hog appears at i in hhs
+			end
+		end
+
+	end
+
+end
+
+-- control
+function AwardPoints(p)
+
+	for i = 0,(TeamsCount-1) do
+		if teamClan[i] == GetHogClan(CurrentHedgehog) then
+			teamScore[i] = teamScore[i] + p
+		end
+	end
+
+end
+
+function AwardKills(t)
+
+	roundKills = roundKills + 1
+
+	for i = 0,(TeamsCount-1) do
+		if teamClan[i] == GetHogClan(CurrentHedgehog) then
+			teamCircsKilled[i] = teamCircsKilled[i] + 1
+
+			if teamCircsKilled[i] == 10 then
+				AddCaption(loc("Boom!") .. " +25 " .. loc("points").."!",0xffba00ff,capgrpVolume)
+				AwardPoints(25)
+			elseif teamCircsKilled[i] == 25 then
+				AddCaption(loc("BOOM!") .. " +50 " .. loc("points") .. "!",0xffba00ff,capgrpVolume)
+				AwardPoints(50)
+			elseif teamCircsKilled[i] == 50 then
+				AddCaption(loc("BOOM!") .. loc("BOOM!") .. loc("BOOM!") .. " +100 " .. loc("points") .. "!",0xffba00ff,capgrpVolume)
+				AwardPoints(100)
+			end
+
+			--[[
+			if t == "R" then
+				redCircsKilled[i] = redCircsKilled[i] + 1
+			end
+			--etc
+			--etc
+			]]
+		end
+	end
+
+end
+
+-----------------
+
+function bubbleSort(table)
+
+	for i = 1, #table do
+        for j = 2, #table do
+            if table[j] < table[j-1] then
+
+				temp = table[j-1]
+				t2 = sdName[j-1]
+				t3 = sdKills[j-1]
+
+				table[j-1] = table[j]
+                sdName[j-1] = sdName[j]
+				sdKills[j-1] = sdKills[j]
+
+				table[j] = temp
+				sdName[j] = t2
+				sdKills[j] = t3
+
+            end
+        end
+    end
+
+    return
+
+end
+
+-----------------
+
+function CommentOnScore()
+
+	for i = 0,(TeamsCount-1) do
+		sdScore[i] = teamScore[i]
+		sdKills[i] = teamCircsKilled[i]
+		sdName[i] = teamNameArr[i]
+	end
+
+	--bubbleSort(sdScore)
+
+	for i = 0,(TeamsCount-1) do
+		if sdName[i] ~= " " then
+			teamComment[i] = sdName[i] .. " |" ..
+			loc("SCORE") .. ": " .. sdScore[i] .. " " .. loc("points") .. "|" ..
+			loc("KILLS") .. ": " .. sdKills[i] .. " " .. loc("invaders destroyed") .. "|" ..
+			" " .. "|"
+		elseif sdName[i] == " " then
+			teamComment[i] = "|"
+		end
+	end
+
+	entireC = ""
+	for i = (TeamsCount-1),0,-1 do
+		entireC = entireC .. teamComment[i]
+	end
+
+	ShowMission("SPACE INVASION", loc("STATUS UPDATE"), loc("Rounds Complete") .. ": " .. roundNumber .. "/" .. roundLimit .. "| " .. "|" .. loc("Team Scores") .. ": |" ..entireC, 4, 1)
+
+end
+
+function onNewRound()
+	roundNumber = roundNumber + 1
+
+	CommentOnScore()
+
+	-- end game if its at round limit
+	if roundNumber == roundLimit then
+
+		for i = 0, (TeamsCount-1) do
+			if teamScore[i] > bestScore then
+				bestScore = teamScore[i]
+				bestClan = teamClan[i]
+			end
+		end
+
+		for i = 0, (numhhs-1) do
+			if GetHogClan(hhs[i]) ~= bestClan then
+				SetEffect(hhs[i], heResurrectable, false)
+				SetHealth(hhs[i],0)
+			end
+		end
+		gameOver = true
+		TurnTimeLeft = 0	--1
+		TimeLeft = 0
+	end
+end
+
+-- gaudy racer
+function CheckForNewRound()
+
+	----------
+	-- new
+	----------
+
+	--[[if gameBegun == true then
+
+		if RoundHasChanged == true then
+			roundN = roundN + 1
+			RoundHasChanged = false
+			onNewRound()
+		end
+
+		if lastRound ~= TotalRounds then -- new round, but not really
+
+			if RoundHasChanged == false then
+				RoundHasChanged = true
+			end
+
+		end
+
+		--AddCaption("RoundN:" .. roundN .. "; " .. "TR: " .. TotalRounds)
+		lastRound = TotalRounds
+
+	end]]
+
+	----------
+	-- old
+	----------
+	if GetHogClan(CurrentHedgehog) == firstClan then
+		onNewRound()
+	end
+
+end
+
+
+----------------------------------------
+-- some tumbler/space invaders methods
+----------------------------------------
+
+function isATrackedGear(gear)
+	if 	(GetGearType(gear) == gtExplosives) or
+		(GetGearType(gear) == gtShell) or
+		(GetGearType(gear) == gtFlame) or-- new -- gtBall
+		(GetGearType(gear) == gtBall)
+	then
+		return(true)
+	else
+		return(false)
+	end
+end
+
+function setNewGearValues(gear)
+
+	if GetGearType(gear) == gtShell then
+		lfs = 50	-- roughly 5 seconds
+		shellID = shellID + 1
+		setGearValue(gear,"ID",shellID)
+		--nw WriteLnToConsole("Just assigned ID " .. getGearValue(gear,"ID") .. " to this shell")
+	elseif GetGearType(gear) == gtBall then
+		lfs = 5 --70	-- 7s
+	elseif GetGearType(gear) == gtExplosives then
+		lfs = 15	-- 1.5s
+		explosivesID = explosivesID + 1
+		setGearValue(gear,"ID",explosivesID)
+		setGearValue(gear,"XP", GetX(gear))
+		setGearValue(gear,"YP", GetY(gear))
+		--nw WriteLnToConsole("Just assigned ID " .. getGearValue(gear,"ID") .. " to this explosives")
+	elseif GetGearType(gear) == gtFlame then
+		lfs = 5	-- 0.5s
+	else
+		lfs = 100
+	end
+
+	setGearValue(gear,"lifespan",lfs)
+	--WriteLnToConsole("I also set its lifespan to " .. lfs)
+
+
+end
+
+function HandleLifeSpan(gear)
+
+	decreaseGearValue(gear,"lifespan")
+
+	--WriteLnToConsole("Just decreased the lifespan of a gear to " .. getGearValue(gear,"lifespan"))
+	--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+
+	if getGearValue(gear,"lifespan") == 0 then
+
+		if GetGearType(gear) == gtShell then
+			AddVisualGear(GetX(gear), GetY(gear), vgtExplosion, 0, false)
+			WriteLnToConsole("about to delete a shell due to lifespan == 0")
+		--elseif GetGearType(gear) == gtBall then
+		--	AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, true)
+		elseif GetGearType(gear) == gtExplosives then
+			AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+			--nw WriteLnToConsole("about to delete a explosive due to lifespan == 0")
+		elseif GetGearType(gear) == gtFlame then
+			AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
+			--WriteLnToConsole("about to delete flame due to lifespan == 0")
+		end
+
+		DeleteGear(gear)
+
+	end
+
+end
+
+-- this prevents ugly barrel clipping sounds when a barrel flies off map limits
+function DeleteFarFlungBarrel(gear)
+
+	if GetGearType(gear) == gtExplosives then
+		if 	(GetX(gear) < -1900) or
+			(GetX(gear) > 6200) or
+			(GetY(gear) < -3400)
+		then
+			AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+			DeleteGear(gear)
+			--SetHealth(gear, 0)
+			--WriteLnToConsole("I'm setting barrel ID " .. getGearValue(gear,"ID") .. " to 0 health because it's been flung too close to the map edges. at Game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+		end
+
+	end
+
+end
+
+-----------------------
+--EVENT HANDLERS
+-- action keys
+-----------------------
+
+function HandleFlameThrower()
+
+	--
+	--flamer
+
+	fireTimer = fireTimer + 1
+	if fireTimer == 6 then	-- 6
+		fireTimer = 0
+
+		if (wep[wepIndex] == loc("Flamer") ) and (preciseOn == true) and (wepAmmo[wepIndex] > 0) and (stopMovement == false) and (tumbleStarted == true) then
+
+			wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
+			AddCaption(
+			loc("Flamer") .. ": " ..
+			(wepAmmo[wepIndex]/800*100) - (wepAmmo[wepIndex]/800*100)%2 .. "%",
+			wepCol[2],
+			capgrpMessage2
+			)
+			DrawTag(3)
+
+			dx, dy = GetGearVelocity(CurrentHedgehog)					--gtFlame -- gtSnowball -- gtAirBomb
+			shell = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtFlame, 0, 0, 0, 0)
+
+			xdev = 1 + GetRandom(35)	--25
+			xdev = xdev / 100
+
+			r = GetRandom(2)
+			if r == 1 then
+				xdev = xdev*-1
+			end
+
+			ydev = 1 + GetRandom(35)	--25
+			ydev = ydev / 100
+
+			r = GetRandom(2)
+			if r == 1 then
+				ydev = ydev*-1
+			end
+
+								--4.5	or 2.5 nonflames				--4.5
+			SetGearVelocity(shell, (dx*4.5)+(xdev*fMod), (dy*4.5)+(ydev*fMod))	--10
+
+		end
+
+	end
+
+
+end
+
+function ChangeWeapon()
+
+	wepIndex = wepIndex + 1
+	if wepIndex == wepCount then
+		wepIndex = 0
+	end
+
+	AddCaption(wep[wepIndex] .. " " .. loc("selected!"), wepCol[wepIndex],capgrpAmmoinfo )
+	AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
+
+end
+
+--function onTimer()
+
+	-- experimental wep
+	--[[SetVisualGearValues(shockwave, GetX(CurrentHedgehog), GetY(CurrentHedgehog), 40, 255, 1, 10, 0, 300, 1, 0xff33ffff)
+	AddCaption("boom")
+	PlaySound(sndWarp)
+	shockwaveHealth = 100
+	shockwaveRad = 100]]
+
+
+	--change wep
+	--ChangeWeapon()
+
+	-- booster
+	--[[if boosterOn == false then
+		boosterOn = true
+	else
+		boosterOn = false
+	end]]
+
+--end
+
+-- o rite dis wan iz liek synched n stuff hope full lee
+-- old method
+--[[function onPrecise()
+
+
+	-- Fire Barrel
+	if (primShotsLeft > 0) and (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
+
+		shotsFired = shotsFired +1
+
+		morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtExplosives, 0, 0, 0, 1)
+
+		primShotsLeft = primShotsLeft - 1
+
+		if primShotsLeft == 0 then
+			PlaySound(sndSuddenDeath)
+			AddCaption(loc("Ammo Depleted!"),0xff0000ff,capgrpMessage)
+		else
+			AddCaption(loc("Ammo") .. ": " .. primShotsLeft)
+		end
+		DrawTag(1)
+
+		CopyPV(CurrentHedgehog, morte) -- new addition
+		x,y = GetGearVelocity(morte)
+
+		x = x*2
+		y = y*2
+		SetGearVelocity(morte, x, y)
+
+
+	elseif (primShotsLeft == 0) and (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
+		AddCaption(loc("Ammo Depleted!"),0xff0000ff,capgrpMessage)
+	end
+
+
+end]]
+
+-- derp tumbler
+function onPrecise()
+
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) and (wepAmmo[wepIndex] > 0) then
+
+		wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
+		--AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
+
+		if wep[wepIndex] == loc("Barrel Launcher") then
+			shotsFired = shotsFired +1
+
+			morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtExplosives, 0, 0, 0, 1)
+			CopyPV(CurrentHedgehog, morte) -- new addition
+			x,y = GetGearVelocity(morte)
+			x = x*2
+			y = y*2
+			SetGearVelocity(morte, x, y)
+
+			if wepAmmo[wepIndex] == 0 then
+			PlaySound(sndSuddenDeath)
+			AddCaption(loc("Ammo Depleted!"),0xff0000ff,capgrpMessage)
+			else
+				--AddCaption(loc("Ammo") .. ": " .. wepAmmo[wepIndex])
+			end
+			DrawTag(1)
+
+		elseif wep[wepIndex] == loc("Mine Deployer") then
+			morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtAirBomb, 0, 0, 0, 0)
+			SetTimer(morte, 1000)
+			DrawTag(1)
+		end
+
+	elseif (wepAmmo[wepIndex] == 0) and (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
+		AddCaption(loc("Ammo Depleted!"),0xff0000ff,capgrpMessage)
+	end
+
+	preciseOn = true
+
+end
+
+function onPreciseUp()
+	preciseOn = false
+end
+
+function onLJump()
+
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
+		shieldMiser = false
+		if shieldHealth == 80 then
+			AddCaption(loc("Shield Depleted"),0xff0000ff,capgrpMessage)
+			PlaySound(sndMineTick)
+			PlaySound(sndSwitchHog)
+		elseif (beam == false) and (shieldHealth > 80) then
+			beam = true
+			SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), 40, 255, 1, 10, 0, 300, 1, 0xa800ffff)
+			AddCaption( loc("Shield ON:") .. " " .. shieldHealth - 80 .. " " .. loc("Power Remaining") )
+			PlaySound(sndWarp)
+		else
+			beam = false
+			SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), 0, 0, 1, 10, 0, 0, 0, 0xa800ffff)
+			AddCaption(loc("Shield OFF:") .. " " .. shieldHealth - 80 .. " " .. loc("Power Remaining") )
+		end
+	end
+end
+
+function onHJump()
+
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) and
+	(rAlpha == 255) and (radShotsLeft > 0) then
+		rPingTimer = 0
+		rAlpha = 0
+		radShotsLeft = radShotsLeft -1
+		AddCaption(loc("Pings left:") .. " " .. radShotsLeft,GetClanColor(GetHogClan(CurrentHedgehog)),capgrpMessage)
+	end
+
+end
+
+-----------------
+-- movement keys
+-----------------
+
+function onLeft()
+	leftOn = true
+end
+
+function onRight()
+	rightOn = true
+end
+
+function onUp()
+	upOn = true
+end
+
+function onDown()
+	downOn = true
+end
+
+function onDownUp()
+	downOn = false
+end
+
+function onUpUp()
+	upOn = false
+end
+
+function onLeftUp()
+	leftOn = false
+end
+
+function onRightUp()
+	rightOn = false
+end
+
+--------------------------
+-- other event handlers
+--------------------------
+
+function onGameInit()
+	GameFlags = 0 + gfRandomOrder
+	Theme = "EarthRise"
+	CaseFreq = 0
+	HealthCaseProb = 0
+	MinesNum = 0
+	Explosives = 0
+	Delay = 1000
+
+	for i = 0, 3 do
+		vTag[0] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
+	end
+
+	HideTags()
+
+	wep[0] = loc("Barrel Launcher")
+	wep[1] = loc("Mine Deployer")
+	wep[2] = loc("Flamer")
+
+	wepCol[0] = 0x78818eff
+	wepCol[1] = 0xa12a77ff
+	wepCol[2] = 0xf49318ff
+
+	wepCount = 3
+
+end
+
+function onGameStart()
+
+	if (MinesTime == -1000) or (MinesTime == 0) then
+		roundLimit = 3
+	else
+		roundLimit = (MinesTime / 1000)
+	end
+
+	ShowMission	(
+				"SPACE INVASION",
+				loc("a Hedgewars mini-game"),
+
+				loc("Destroy invaders to score points.") .. "|" ..
+				" " .. "|" ..
+
+				loc("Round Limit") .. ": " .. roundLimit .. "|" ..
+				loc("Turn Time") .. ": " .. (TurnTime/1000) .. loc("sec") .. "|" ..
+				" " .. "|" ..
+
+				loc("Movement: [Up], [Down], [Left], [Right]") .. "|" ..
+				loc("Fire") .. ": " .. loc("[Left Shift]") .. "|" ..
+				loc("Toggle Shield") .. ": " .. loc("[Enter]") .. "|" ..
+				loc("Radar Ping") .. ": " .. loc("[Backspace]") .. "|" ..
+
+				--" " .. "|" ..
+				--LOC_NOT("Invaders List: ") .. "|" ..
+				--LOC_NOT("Blue Jabberwock: (50 points)") .. "|" ..
+				--LOC_NOT("Red Warbler: (10 points)") .. "|" ..
+				--LOC_NOT("Orange Gob: (5 points)") .. "|" ..
+				--LOC_NOT("Green Wrangler: (3 points)") .. "|" ..
+
+
+				"", 4, 4000
+				)
+
+	CreateMeSomeCircles()
+	RebuildTeamInfo() -- control
+	lastRound = TotalRounds
+
+end
+
+function onScreenResize()
+
+	-- redraw Tags so that their screen locations are updated
+	if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
+			DrawTag(0)
+			DrawTag(1)
+			DrawTag(2)
+	end
+
+end
+
+function onNewTurn()
+
+	--primShotsLeft = primShotsMax
+	radShotsLeft = 2
+	stopMovement = false
+	tumbleStarted = false
+	boosterOn = false
+	beam = false
+	shieldHealth = 30 + 80 -- 50 = 5 secs, roughly
+	shockwaveHealth = 0
+
+	RK = 0
+	GK = 0
+	BK = 0
+	OK = 0
+	SK = 0
+	roundKills = 0
+	shieldMiser = true
+	fierceComp = false
+	shotsFired = 0
+	shotsHit = 0
+	sniperHits = 0
+	pointBlankHits = 0
+	chainLength = 0
+	chainCounter = 0
+	SurfTime = 12
+
+	-------------------------
+	-- gaudy racer
+	-------------------------
+	CheckForNewRound()
+
+	-- Handle Starting Stage of Game
+	if (gameOver == false) and (gameBegun == false) then
+		gameBegun = true
+		roundNumber = 0 -- 0
+		firstClan = GetHogClan(CurrentHedgehog)
+	end
+
+	if gameOver == true then
+		gameBegun = false
+		stopMovement = true
+		tumbleStarted = false
+		SetMyCircles(false)
+	end
+
+
+	-------
+	-- tumbler
+	----
+
+	wepAmmo[0] = 5
+	wepAmmo[1] = 2
+	wepAmmo[2] = 5000
+	wepIndex = 2
+	ChangeWeapon()
+
+
+	HideTags()
+
+	---------------
+	---------------
+	--AddCaption("num g: " .. numGears() )
+	--WriteLnToConsole("onNewTurn, I just set a bunch of variables to their necessary states. This was done at:")
+	--WriteLnToConsole("The above occured at Game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+end
+
+function ThingsToBeRunOnGears(gear)
+
+	HandleLifeSpan(gear)
+	DeleteFarFlungBarrel(gear)
+
+	if CirclesAreGo == true then
+		CheckVarious(gear)
+		ProjectileTrack(gear)
+	end
+
+end
+
+
+function onGameTick20()
+
+
+	--WriteLnToConsole("Start of GameTick")
+
+	HandleCircles()
+
+	-- derp
+	--if shockwaveHealth > 0 then
+	--	shockwaveHealth = shockwaveHealth - 1
+	--	shockwaveRad = shockwaveRad + 5
+	--end
+
+
+	if GameTime%100 == 0 then
+
+		if beam == true then
+			shieldHealth = shieldHealth - 1
+			if shieldHealth < 80 then -- <= 80
+				shieldHealth = 80
+				beam = false
+				AddCaption(loc("Shield Depleted"),0xff0000ff,capgrpMessage)
+				PlaySound(sndMineTick)
+				PlaySound(sndSwitchHog)
+			end
+		end
+
+
+
+		--nw WriteLnToConsole("Starting ThingsToBeRunOnGears()")
+
+		runOnGears(ThingsToBeRunOnGears)
+
+		--nw WriteLnToConsole("Finished ThingsToBeRunOnGears()")
+
+		--runOnGears(HandleLifeSpan)
+		--runOnGears(DeleteFarFlungBarrel)
+
+		if CirclesAreGo == true and CurrentHedgehog ~= nil then
+			CheckDistances()
+			--runOnGears(CheckVarious)	-- used to be in handletracking for some bizarre reason
+			--runOnGears(ProjectileTrack)
+		end
+
+		-- white smoke trail as player falls from the sky
+		if (TimeLeft <= 0) and (stopMovement == true) and (CurrentHedgehog ~= nil) then
+			j,k = GetGearVelocity(CurrentHedgehog)
+			if (j ~= 0) and (k ~= 0) then
+				AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, true)
+			end
+		end
+
+		--nw WriteLnToConsole("Finished 100Timer")
+
+	end
+
+
+	-- start the player tumbling with a boom once their turn has actually begun
+	if (tumbleStarted == false) and (gameOver == false) then
+		if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then
+			--AddCaption(LOC_NOT("Good to go!"))
+			tumbleStarted = true
+			TimeLeft = div(TurnTime, 1000)	--45
+			FadeAlpha = 0
+			rAlpha = 255
+			AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1)
+			DrawTag(0)
+			DrawTag(1)
+			DrawTag(2)
+			SetMyCircles(true)
+		end
+	end
+
+	--WriteLnToConsole("Finished initial check")
+
+	if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
+
+		--AddCaption(GetX(CurrentHedgehog) .. ";" .. GetY(CurrentHedgehog) )
+
+		-- Calculate and display turn time
+		if GameTime%1000 == 0 then
+			TimeLeft = TimeLeft - 1
+
+			if TimeLeft >= 0 then
+				--AddCaption(LOC_NOT("Time Left: ") .. TimeLeft)
+				DrawTag(0)
+			end
+
+		end
+
+		--WriteLnToConsole("Finished timeleft calculations")
+
+		-------------------------------
+		-- Player has run out of luck (out of time or hit by gtShell)
+		-------------------------------
+		-- checks in FloatyThings
+		if PlayerIsFine() == false then
+			TimeLeft = 0
+		end
+
+		--WriteLnToConsole("successfully checked playerIsFine")
+
+		if (TimeLeft == 0) then
+			if (stopMovement == false) then	--time to stop the player
+				stopMovement = true
+				boosterOn = false
+				beam = false
+				upOn = false
+				down = false
+				leftOn = false
+				rightOn = false
+				SetMyCircles(false)
+				HideTags()
+				rAlpha = 255
+				--nw WriteLnToConsole("Player is out of luck")
+
+				if shieldMiser == true then
+
+					p = (roundKills*3.5) - ((roundKills*3.5)%1) + 2
+
+					AddCaption(loc("Shield Miser!") .." +" .. p .." ".. loc("points") .. "!",0xffba00ff,capgrpAmmoinfo)
+					AwardPoints(p)
+				end
+
+				if ((shotsHit / shotsFired * 100) >= 80) and (shotsFired > 4) then
+					AddCaption(loc("Accuracy Bonus!") .. " +15 " .. loc("points") .. "!",0xffba00ff,capgrpVolume)
+					AwardPoints(15)
+				end
+
+			end
+		else -- remove this if you want tumbler to fall slowly on death
+		-------------------------------
+		-- Player is still in luck
+		-------------------------------
+
+
+			--WriteLnToConsole("about to do chainCounter checks")
+			if chainCounter > 0 then
+				chainCounter = chainCounter -1
+				if chainCounter == 0 then
+					chainLength = 0
+				end
+			end
+
+			-- handle movement based on IO
+			if GameTime%100 == 0 then -- 100
+				--nw WriteLnToConsole("Start of Player MoveTimer")
+
+				---------------
+				-- new trail code
+				---------------
+				-- the trail lets you know you have 5s left to pilot, akin to birdy feathers
+				if (TimeLeft <= 5) and (TimeLeft > 0) then							--vgtSmoke
+					tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+					SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, GetClanColor(GetHogClan(CurrentHedgehog)) )
+				end
+				--------------
+				--------------
+
+				------------------------
+				-- surfer achievement
+				------------------------
+
+				if (WaterLine - GetY(CurrentHedgehog)) < 15 then
+					SurfTime = SurfTime -1
+				end
+
+				if SurfTime ~= 12 then
+
+					SurfTime = SurfTime - 1
+					if SurfTime <= 0 then
+						for i = 0,(TeamsCount-1) do
+							if teamClan[i] == GetHogClan(CurrentHedgehog) and (teamSurfer[i] == false) then
+								teamSurfer[i] = true
+								SurfTime = 12
+								AddCaption(loc("Surfer! +15 points!"),0xffba00ff,capgrpVolume)
+								AwardPoints(15)
+							end
+						end
+					end
+				end
+
+
+				dx, dy = GetGearVelocity(CurrentHedgehog)
+
+				--WriteLnToConsole("I just got the velocity of currenthedgehog. It is dx: " .. dx .. "; dy: " .. dy)
+				--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+				if boosterOn == true then
+					tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtDust, 0, false)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+					SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, 1, g9, GetClanColor(GetHogClan(CurrentHedgehog)) )
+					dxlimit = 0.8*fMod
+					dylimit = 0.8*fMod
+				else
+					dxlimit = 0.4*fMod
+					dylimit = 0.4*fMod
+				end
+
+				if dx > dxlimit then
+					dx = dxlimit
+				end
+				if dy > dylimit then
+					dy = dylimit
+				end
+				if dx < -dxlimit then
+					dx = -dxlimit
+				end
+				if dy < -dylimit then
+					dy = -dylimit
+				end
+
+
+				if leftOn == true then
+					dx = dx - 0.1*fMod
+				end
+				if rightOn == true then
+					dx = dx + 0.1*fMod
+				end
+
+				if upOn == true then
+					dy = dy - 0.1*fMod
+				end
+				if downOn == true then
+					dy = dy + 0.1*fMod
+				end
+
+				SetGearVelocity(CurrentHedgehog, dx, dy)
+
+				--WriteLnToConsole("I just SET the velocity of currenthedgehog. It is now dx: " .. dx .. "; dy: " .. dy)
+				--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+				--nw WriteLnToConsole("End of Player MoveTimer")
+
+			end
+
+
+			HandleFlameThrower()
+
+
+		end -- new end I put here to check if he's still alive or not
+
+	end
+
+	--WriteLnToConsole("End of GameTick")
+
+end
+
+function onGearDamage(gear, damage)
+	if GetGearType(gear) == gtHedgehog then
+		if (fierceComp == false) and (damage >= 60) and (GetHogClan(gear) ~= GetHogClan(CurrentHedgehog)) then
+			fierceComp = true
+			AddCaption(loc("Fierce Competition!") .. " +8 " .. loc("points") .. "!",0xffba00ff,capgrpGameState)
+			AwardPoints(8)
+		end
+	end
+end
+
+function onGearResurrect(gear)
+
+	-- did I fall into the water? well, that was a stupid thing to do
+	if gear == CurrentHedgehog then
+		TimeLeft = 0
+		--WriteLnToConsole("Current hedgehog just drowned himself")
+		--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+	end
+
+end
+
+function onGearAdd(gear)
+
+	if isATrackedGear(gear) then
+		trackGear(gear)
+		setNewGearValues(gear)
+	end
+
+	--if GetGearType(gear) == gtBall then
+	--	SetTimer(gear, 5000)
+	--end
+
+	if GetGearType(gear) == gtHedgehog then
+		SetEffect(gear, heResurrectable, true)
+
+		-----------
+		-- control
+		hhs[numhhs] = gear
+		numhhs = numhhs + 1
+		-----------
+	end
+
+end
+
+function onGearDelete(gear)
+
+
+	--[[if GetGearType(gear) == gtShell then
+		--nw WriteLnToConsole("on GearDelete call. Shell ID: " .. getGearValue(gear,"ID"))
+		--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+		--if CurrentHedgehog ~= nil then
+		--	WriteLnToConsole("As it happens, player is at: " .. GetX(CurrentHedgehog) .. "; " .. GetY(CurrentHedgehog))
+		--end
+	elseif GetGearType(gear) == gtExplosives then
+		--nw WriteLnToConsole("on GearDelete call. Explosives ID: " .. getGearValue(gear,"ID"))
+		--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+		--if CurrentHedgehog ~= nil then
+		--	WriteLnToConsole("As it happens, player is at: " .. GetX(CurrentHedgehog) .. "; " .. GetY(CurrentHedgehog))
+		--end
+	elseif GetGearType(gear) == gtFlame then
+		--WriteLnToConsole("on GearDelete flame")
+	end]]
+
+	if isATrackedGear(gear) then
+		trackDeletion(gear)
+	end
+
+	if CurrentHedgehog ~= nil then
+		FollowGear(CurrentHedgehog)
+	end
+
+end
+
+
+
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+-- FLOATY THINGS
+-- "I'll make this into a generic library and code properly
+-- when I have more time and feel less lazy"
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+
+function DoHorribleThings(cUID)
+
+	-- work out the distance to the target
+	g1X, g1Y = GetGearPosition(CurrentHedgehog)
+	g2X, g2Y = vCircX[cUID], vCircY[cUID]
+	q = g1X - g2X
+	w = g1Y - g2Y
+	r = math.sqrt( (q*q) + (w*w) )	--alternate
+
+	opp = w
+	if opp < 0 then
+		opp = opp*-1
+	end
+
+	-- work out the angle (theta) to the target
+	t = math.deg ( math.asin(opp / r) )
+
+	-- based on the radius of the radar, calculate what x/y displacement should be
+	NR = 150 -- radius at which to draw circs
+	NX = math.cos( math.rad(t) ) * NR
+	NY = math.sin( math.rad(t) ) * NR
+
+	-- displace xy based on where this thing actually is
+
+	if r < NR then
+		rCircX[cUID] = g2X
+	elseif q > 0 then
+		rCircX[cUID] = g1X - NX
+	else
+		rCircX[cUID] = g1X + NX
+	end
+
+	if r < NR then
+		rCircY[cUID] = g2Y
+	elseif w > 0 then
+		rCircY[cUID] = g1Y - NY
+	else
+		rCircY[cUID] = g1Y + NY
+	end
+
+end
+
+function PlayerIsFine()
+	return (playerIsFine)
+end
+
+function GetDistFromXYtoXY(a, b, c, d)
+	q = a - c
+	w = b - d
+	return ( (q*q) + (w*w) )
+end
+
+function GetDistFromGearToGear(gear, gear2)
+
+	g1X, g1Y = GetGearPosition(gear)
+	g2X, g2Y = GetGearPosition(gear2)
+	q = g1X - g2X
+	w = g1Y - g2Y
+
+
+	--[[
+	WriteLnToConsole("I just got the position of two gears and calculated the distance betwen them")
+	if gear == CurrentHedgehog then
+		WriteLnToConsole("Gear 1 is CurrentHedgehog.")
+	end
+	if gear2 == CurrentHedgehog then
+		WriteLnToConsole("Gear 2 is CurrentHedgehog.")
+	end
+	WriteLnToConsole("G1X: " .. g1X .. "; G1Y: " .. g1Y)
+	WriteLnToConsole("G2X: " .. g2X .. "; G2Y: " .. g2Y)
+	WriteLnToConsole("Their distance is " .. (q*q) + (w*w) )
+	WriteLnToConsole("The above events occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+]]
+
+
+	return ( (q*q) + (w*w) )
+
+end
+
+function GetDistFromGearToXY(gear, g2X, g2Y)
+
+	g1X, g1Y = GetGearPosition(gear)
+	q = g1X - g2X
+	w = g1Y - g2Y
+
+
+	--[[WriteLnToConsole("I just got the position of a gear and calculated the distance betwen it and another xy")
+	if gear == CurrentHedgehog then
+		WriteLnToConsole("Gear 1 is CurrentHedgehog.")
+	end
+
+	WriteLnToConsole("G1X: " .. g1X .. "; G1Y: " .. g1Y)
+	WriteLnToConsole("Other X: " .. g2X .. "; Other Y: " .. g2Y)
+	WriteLnToConsole("Their distance is " .. (q*q) + (w*w) )
+	WriteLnToConsole("The above events occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+]]
+
+
+	return ( (q*q) + (w*w) )
+
+
+end
+
+function CreateMeSomeCircles()
+
+	for i = 0, 7 do
+		vCCount = vCCount +1
+		vCirc[i] = AddVisualGear(0,0,vgtCircle,0,true)
+
+		rCirc[i] = AddVisualGear(0,0,vgtCircle,0,true)
+		rCircX[i] = 0
+		rCircY[i] = 0
+
+		vCircDX[i] = 0
+		vCircDY[i] = 0
+
+		vType[i] = "generic"
+		vCounter[i] = 0
+		vCounterLim[i] = 150
+		vCircScore[i] = 0
+		vCircHealth[i] = 1
+
+		vCircMinA[i] = 80	--80 --20
+		vCircMaxA[i] = 255
+		vCircType[i] = 1	--1
+		vCircPulse[i] = 10
+		vCircFuckAll[i] = 0
+		vCircRadius[i] = 0
+		vCircWidth[i] = 3 --5
+
+		vCircRadMax[i] = 0
+		vCircRadMin[i] = 0
+		vCircRadDir[i] = -1
+		vCircRadCounter[i] = 0
+
+		vCircX[i], vCircY[i] = 0,0
+
+		vCircCol[i] = 0xff00ffff
+
+		SetVisualGearValues(vCirc[i], vCircX[i], vCircY[i], vCircMinA[i], vCircMaxA[i], vCircType[i], vCircPulse[i], vCircFuckAll[i], vCircRadius[i], vCircWidth[i], vCircCol[i])
+
+		SetVisualGearValues(rCirc[i], 0, 0, 100, 255, 1, 10, 0, 40, 3, vCircCol[i])
+
+	end
+
+	pShield = AddVisualGear(0,0,vgtCircle,0,true)
+	--SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), 80, 200, 1, 10, 0, 200, 5, 0xff00ffff)
+
+
+	shockwave = AddVisualGear(0,0,vgtCircle,0,true)
+
+end
+
+function IGotMeASafeXYValue(i)
+
+	acceptibleDistance = 800
+
+	-- put this in here to thwart attempts at repositioning and test sanity limit
+	--vCircX[i] = GetX(CurrentHedgehog)+250
+	--vCircY[i] = GetY(CurrentHedgehog)+250
+
+	vCircX[i] = GetRandom(5000)
+	vCircY[i] = GetRandom(2000)
+	dist = GetDistFromGearToXY(CurrentHedgehog, vCircX[i], vCircY[i])
+	if dist > acceptibleDistance*acceptibleDistance then
+		return(true)
+	else
+		return(false)
+	end
+
+end
+
+function CircleDamaged(i)
+
+	res = ""
+	vCircHealth[i] = vCircHealth[i] -1
+
+	if vCircHealth[i] <= 0 then
+	-- circle is dead, do death effects/consequences
+
+		vCircActive[i] = false
+
+		if (vType[i] == "drone") then
+			PlaySound(sndHellishImpact4)
+			TimeLeft = TimeLeft + 4
+			AddCaption(loc("Time Extended!") .. "+" .. 4 .. loc("sec"), 0xff0000ff,capgrpMessage )
+			DrawTag(0)
+
+			morte = AddGear(vCircX[i], vCircY[i], gtExplosives, 0, 0, 0, 1)
+			SetHealth(morte, 0)
+
+			RK = RK + 1
+			if RK == 5 then
+				RK = 0
+				AddCaption(loc("Drone Hunter!") .. " +10 " .. loc("points") .. "!",0xffba00ff,capgrpMessage2)
+				AwardPoints(10)
+			end
+
+		elseif (vType[i] == "ammo") then
+			AddVisualGear(vCircX[i], vCircY[i], vgtExplosion, 0, false)
+			PlaySound(sndExplosion)
+			PlaySound(sndShotgunReload)
+			wepAmmo[0] = wepAmmo[0] +3
+			--primShotsLeft = primShotsLeft + 3
+			AddCaption("+" .. 3 .. " " .. loc("Ammo"), 0x00ff00ff,capgrpMessage)
+			DrawTag(1)
+
+			GK = GK + 1
+			if GK == 3 then
+				GK = 0
+				AddCaption(loc("Ammo Maniac!") .. " +5 " .. loc("points") .. "!",0xffba00ff,capgrpMessage2)
+				AwardPoints(5)
+			end
+
+		elseif (vType[i] == "bonus") then
+
+			AddVisualGear(vCircX[i], vCircY[i], vgtExplosion, 0, false)
+			PlaySound(sndExplosion)
+
+			AddVisualGear(vCircX[i], vCircY[i], vgtFire, 0, false)
+			AddVisualGear(vCircX[i], vCircY[i], vgtFire, 0, false)
+			AddVisualGear(vCircX[i], vCircY[i], vgtFire, 0, false)
+			AddVisualGear(vCircX[i], vCircY[i], vgtFire, 0, false)
+			AddVisualGear(vCircX[i], vCircY[i], vgtFire, 0, false)
+			AddVisualGear(vCircX[i], vCircY[i], vgtSmoke, 0, false)
+
+			PlaySound(sndVaporize)
+			--sndWarp sndMineTick --sndSwitchHog --sndSuddenDeath
+
+			shieldHealth = shieldHealth + 30
+			AddCaption(loc("Shield boosted! +30 power"), 0xa800ffff,capgrpMessage)
+			if shieldHealth >= 250 then
+				shieldHealth = 250
+				AddCaption(loc("Shield is fully recharged!"),0xa800ffff,capgrpMessage)
+			end
+			DrawTag(2)
+
+			OK = OK + 1
+			if OK == 3 then
+				OK = 0
+				AddCaption(loc("Shield Seeker!") .. " + 10 " .. loc("points") .. "!",0xffba00ff,capgrpMessage2)
+				AwardPoints(10)
+			end
+
+		elseif (vType[i] == "blueboss") then
+			PlaySound(sndHellishImpact3)
+			AddCaption(loc("Boss defeated!") .. " +30 " .. loc("points") .. "!", 0x0050ffff,capgrpMessage)
+
+			morte = AddGear(vCircX[i], vCircY[i], gtExplosives, 0, 0, 0, 1)
+			SetHealth(morte, 0)
+
+			BK = BK + 1
+			if BK == 2 then
+				BK = 0
+				AddCaption(loc("Boss Slayer!") .. " +25 " .. loc("points") .. "!",0xffba00ff,capgrpMessage2)
+				AwardPoints(25)
+			end
+
+		end
+
+		AwardPoints(vCircScore[i])
+		AwardKills()
+		SetUpCircle(i)
+		res = "fatal"
+
+		chainCounter = 3000
+		chainLength = chainLength + 1
+		if chainLength > 1 then
+			AddCaption( chainLength .. "-" .. loc("Hit Combo!") .. " +" .. chainLength*2 .. " " .. loc("points") .. "!",0xffba00ff,capgrpAmmostate)
+			AwardPoints(chainLength*2)
+		end
+
+	else
+	-- circle is merely damaged
+	-- do damage effects/sounds
+		AddVisualGear(vCircX[i], vCircY[i], vgtSteam, 0, false)
+		r = GetRandom(4)
+		if r == 0 then
+			PlaySound(sndHellishImpact1)
+		elseif r == 1 then
+			PlaySound(sndHellishImpact2)
+		elseif r == 2 then
+			PlaySound(sndHellishImpact3)
+		elseif r == 3 then
+			PlaySound(sndHellishImpact4)
+		end
+		res = "non-fatal"
+
+	end
+
+	return(res)
+
+end
+
+function SetUpCircle(i)
+
+
+	r = GetRandom(10)
+	--r = 8
+	-- 80% of spawning either red/green
+	if r <= 7 then
+
+		--r = GetRandom(5)
+		r = GetRandom(2)
+		--r = 1
+		if r == 0 then
+		--if r <= 2 then
+			vCircCol[i] = 0xff0000ff -- red
+			vType[i] = "drone"
+			vCircRadMin[i] = 50	*5
+			vCircRadMax[i] = 90	*5
+			vCounterLim[i] = 150
+			vCircScore[i] = 10
+			vCircHealth[i] = 1
+		--else
+		elseif r == 1 then
+			vCircCol[i] = 0x00ff00ff -- green
+			vType[i] = "ammo"
+			vCircRadMin[i] = 25	*7
+			vCircRadMax[i] = 30	*7
+			vCircScore[i] = 3
+			vCircHealth[i] = 1
+		end
+
+	-- 20% chance of spawning boss or bonus
+	else
+		r = GetRandom(5)
+		--r = GetRandom(2)
+		--r = 0
+		if r <= 1 then
+		--if r == 0 then
+			vCircCol[i] = 0x0050ffff -- sexy blue
+			vType[i] = "blueboss"
+			vCircRadMin[i] = 100*5
+			vCircRadMax[i] = 180*5
+			vCircWidth[i] = 1
+			vCounterLim[i] = 100
+			vCircScore[i] = 30
+			vCircHealth[i] = 3
+		else
+		--elseif r == 1 then
+			--vCircCol[i] = 0xffae00ff -- orange
+			vCircCol[i] = 0xa800ffff -- purp
+			vType[i] = "bonus"
+			vCircRadMin[i] = 20 *7
+			vCircRadMax[i] = 40 *7
+			vCircScore[i] = 5
+			vCircHealth[i] = 1
+		end
+
+	end
+
+	-- regenerate circle xy if too close to player or until sanity limit kicks in
+	reN = 0
+	--zzz = 0
+	while (reN < 10) do
+		if IGotMeASafeXYValue(i) == false then
+			reN = reN + 1
+			--zzz = zzz + 1
+		else
+			reN = 15
+		end
+	end
+	--AddCaption("Took me this many retries: " .. zzz) -- number of times it took to work
+
+	vCircRadius[i] = vCircRadMax[i] - GetRandom(vCircRadMin[i])
+
+	g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vCirc[i])
+	SetVisualGearValues(vCirc[i], vCircX[i], vCircY[i], g3, g4, g5, g6, g7, vCircRadius[i], vCircWidth[i], vCircCol[i]-0x000000ff)
+	-- - -0x000000ff
+
+	g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(rCirc[i])
+	SetVisualGearValues(rCirc[i], 0, 0, g3, g4, g5, g6, g7, g8, g9, vCircCol[i]-0x000000ff)
+
+
+	vCircActive[i] = true -- new
+
+	--nw WriteLnToConsole("CIRC " .. i .. ": X: " .. vCircX[i] .. "; Y: " .. vCircY[i])
+	--nw WriteLnToConsole("CIRC " .. i .. ": dX: " .. vCircDX[i] .. "; dY: " .. vCircDY[i])
+	--nw WriteLnToConsole("CIRC " .. i .. ": RAD:" .. vCircRadius[i])
+
+end
+
+function SetMyCircles(s)
+
+	CirclesAreGo = s
+	playerIsFine = s
+
+	if s == true then
+		--nw WriteLnToConsole("About to set up all circles, old values are here:")
+		for i = 0,(vCCount-1) do
+			--nw WriteLnToConsole("CIRC " .. i .. ": X: " .. vCircX[i] .. "; Y: " .. vCircY[i])
+			--nw WriteLnToConsole("CIRC " .. i .. ": dX: " .. vCircDX[i] .. "; dY: " .. vCircDY[i])
+			--nw WriteLnToConsole("CIRC " .. i .. ": RAD:" .. vCircRadius[i])
+		end
+		--nw WriteLnToConsole("Old values given, new values to follow...")
+	end
+
+	for i = 0,(vCCount-1) do
+
+		if s == false then
+			--vCircCol[i] = 0xffffffff
+			vCircActive[i] = false
+		elseif s == true then
+			SetUpCircle(i)
+		end
+
+	end
+
+end
+
+function WellHeAintGonnaJumpNoMore(x,y)
+
+	AddVisualGear(x, y, vgtBigExplosion, 0, false)
+	playerIsFine = false
+	AddCaption(loc("GOTCHA!"))
+	PlaySound(sndExplosion)
+	PlaySound(sndHellish)
+
+	targetHit = true
+
+end
+
+--- collision detection for weapons fire
+function CheckVarious(gear)
+
+	--if (GetGearType(gear) == gtExplosives) then
+		--nw WriteLnToConsole("Start of CheckVarious(): Exp ID: " .. getGearValue(gear,"ID"))
+	--elseif (GetGearType(gear) == gtShell) then
+		--nw WriteLnToConsole("Start of CheckVarious(): Shell ID: " .. getGearValue(gear,"ID"))
+	--end
+
+	targetHit = false
+
+	-- if circle is hit by player fire
+	if (GetGearType(gear) == gtExplosives) then
+		circsHit = 0
+
+		for i = 0,(vCCount-1) do
+
+			--nw WriteLnToConsole("Is it neccessary to check for collision with circ " .. i)
+
+			--if (vCircActive[i] == true) and ( (vType[i] == "drone") ) then
+
+				--nw WriteLnToConsole("YES. about to calc distance between gtExplosives and circ " .. i)
+
+				dist = GetDistFromGearToXY(gear, vCircX[i], vCircY[i])
+
+				-- calculate my real radius if I am an aura
+				if vCircType[i] == 0 then
+					NR = vCircRadius[i]
+				else
+					NR = (48/100*vCircRadius[i])/2
+				end
+
+				if dist <= NR*NR then
+
+
+					--nw WriteLnToConsole("Collision confirmed. The gtExplosives is within the circ radius!")
+
+					dist = (GetDistFromXYtoXY(vCircX[i], vCircY[i], getGearValue(gear,"XP"), getGearValue(gear,"YP")) - (NR*NR))
+					--AddCaption(loc("Dist: ") .. dist .. "!",0xffba00ff,capgrpGameState)
+					if dist >= 1000000 then
+						sniperHits = sniperHits +1
+						AddCaption(loc("Sniper!") .. " +8 " .. loc("points") .. "!",0xffba00ff,capgrpGameState)
+						AwardPoints(8)
+						if sniperHits == 3 then
+							sniperHits = 0
+							AddCaption(loc("They Call Me Bullseye!") .. " +16 " .. loc("points") .. "!",0xffba00ff,capgrpGameState)
+							AwardPoints(15)
+						end
+					elseif dist <= 6000 then
+						pointBlankHits = pointBlankHits +1
+						if pointBlankHits == 3 then
+							pointBlankHits = 0
+							AddCaption(loc("Point Blank Combo!") .. " +5 " .. loc("points") .. "!",0xffba00ff,capgrpGameState)
+							AwardPoints(5)
+						end
+					end
+
+					AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+
+					targetHit = true
+					--DeleteGear(gear)
+					--SetHealth(gear,0)
+						--WriteLnToConsole("set " .. "Exp ID: " .. getGearValue(gear,"ID") .. " health to 0")
+						--WriteLnToConsole("targetHit set to true, explosive is at distance " .. dist .. "(within range " .. NR*NR.. ") of circ" )
+
+					CircleDamaged(i)
+
+					circsHit = circsHit + 1
+					if circsHit > 1 then
+						AddCaption(loc("Multi-shot!") .. " +15 " .. loc("points") .. "!",0xffba00ff,capgrpAmmostate)
+						AwardPoints(15)
+						circsHit = 0
+					end
+
+					shotsHit = shotsHit + 1
+
+
+
+				end
+
+			--end
+
+		end
+
+	-- if player is hit by circle bazooka
+	elseif (GetGearType(gear) == gtShell) and (CurrentHedgehog ~= nil) then --or (GetGearType(gear) == gtBall) then
+
+		dist = GetDistFromGearToGear(gear, CurrentHedgehog)
+
+		if beam == true then
+
+			if dist < 3000 then
+				tempE = AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, true)
+				g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+				SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, 0xff00ffff )
+				PlaySound(sndVaporize)
+				DeleteGear(gear)
+
+				SK = SK + 1
+				if SK == 5 then
+					SK = 0
+					AddCaption(loc("Shield Master!") .. " +10 " .. loc("points") .. "!",0xffba00ff,capgrpAmmoinfo)
+					AwardPoints(10)
+				end
+			end
+
+		elseif dist < 1600 then
+			WellHeAintGonnaJumpNoMore(GetX(gear), GetY(gear))
+		end
+
+		--[[if targetHit == true then
+			WriteLnToConsole("about to delete shell due to targetHit being set to true earlier")
+			DeleteGear(gear)
+			WriteLnToConsole("there, I deleted it")
+		end]]
+
+
+	end
+
+	if targetHit == true then
+			--nw WriteLnToConsole("about to delete something due to targetHit being set to true earlier")
+			DeleteGear(gear)
+			--nw WriteLnToConsole("there, I deleted it")
+	end
+
+	--nw WriteLnToConsole("End of CheckVarious()")
+
+end
+
+-- collision detection for player entering a circle
+function CheckDistances()
+
+	--nw WriteLnToConsole("Start of CheckDistances()")
+
+	for i = 0,(vCCount-1) do
+
+
+		--nw WriteLnToConsole("Attempting to calculate dist of circ " .. i)
+
+		g1X, g1Y = GetGearPosition(CurrentHedgehog)
+		g2X, g2Y = vCircX[i], vCircY[i]
+
+		g1X = g1X - g2X
+		g1Y = g1Y - g2Y
+		dist = (g1X*g1X) + (g1Y*g1Y)
+
+		--DoHorribleThings(i, g1X, g1Y, g2X, g2Y, dist)
+
+		--nw WriteLnToConsole("Calcs done. Dist to CurrentHedgehog is " .. dist)
+
+		-- calculate my real radius if I am an aura
+		if vCircType[i] == 0 then
+			NR = vCircRadius[i]
+		else
+			NR = (48/100*vCircRadius[i])/2
+		end
+
+		if dist <= NR*NR then
+
+			if 	(vCircActive[i] == true) and
+				((vType[i] == "ammo") or (vType[i] == "bonus") )
+			then
+
+				CircleDamaged(i)
+
+			elseif (vCircActive[i] == true) and
+					( (vType[i] == "drone") or (vType[i] == "blueboss") )
+			then
+
+				ss = CircleDamaged(i)
+				WellHeAintGonnaJumpNoMore(GetX(CurrentHedgehog),GetY(CurrentHedgehog))
+
+				if ss == "fatal" then
+
+					if (wepAmmo[0] == 0) and (TimeLeft <= 9) then
+					--if (primShotsLeft == 0) and (TimeLeft <= 9) then
+						AddCaption(loc("Kamikaze Expert!") .. " +15 " .. loc("points") .. "!",0xffba00ff,capgrpMessage)
+						AwardPoints(15)
+					elseif (wepAmmo[0] == 0) then
+						AddCaption(loc("Depleted Kamikaze!") .. " +5 " .. loc("points") .. "!",0xffba00ff,capgrpMessage)
+						AwardPoints(5)
+					elseif TimeLeft <= 9 then
+						AddCaption(loc("Timed Kamikaze!") .. " +10 " .. loc("points") .. "!",0xffba00ff,capgrpMessage)
+						AwardPoints(10)
+					end
+				end
+
+			end
+
+
+		end
+
+	end
+
+	--nw WriteLnToConsole("End of CheckDistances()")
+
+end
+
+function HandleCircles()
+
+	--[[if CirclesAreGo == true then
+
+		--CheckDistances()
+		--runOnGears(CheckVarious)	-- used to be in handletracking for some bizarre reason
+
+		--pTimer = pTimer + 1
+		--if pTimer == 100 then
+		--	pTimer = 0
+		--	runOnGears(ProjectileTrack)
+		--end
+
+	end]]
+
+
+	if rAlpha ~= 255 then
+
+		if GameTime%100 == 0 then
+
+			rAlpha = rAlpha + 5
+			if rAlpha >= 255 then
+				rAlpha = 255
+			end
+		end
+
+	end
+
+	for i = 0,(vCCount-1) do
+
+		--if (vCircActive[i] == true) then
+			SetVisualGearValues(rCirc[i], rCircX[i], rCircY[i], 100, 255, 1, 10, 0, 40, 3, vCircCol[i]-rAlpha)
+		--end
+
+
+
+		vCounter[i] = vCounter[i] + 1
+		if vCounter[i] >= vCounterLim[i] then
+
+			vCounter[i] = 0
+
+			if 	((vType[i] == "drone") or (vType[i] == "blueboss") ) and
+				(vCircActive[i] == true) then
+				AddGear(vCircX[i], vCircY[i], gtShell, 0, 0, 0, 1)
+
+				--WriteLnToConsole("Circle " .. i .. " just fired/added a gtShell")
+				--WriteLnToConsole("The above event occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+
+			--elseif (vType[i] == "bluebottle") and (vCircActive[i] == true) then
+			--	AddGear(vCircX[i], vCircY[i]-vCircRadius[i], gtBall, 0, 0, 0, 1)
+			--	AddGear(vCircX[i], vCircY[i]+vCircRadius[i], gtBall, 0, 0, 0, 1)
+			--	AddGear(vCircX[i]-vCircRadius[i], vCircY[i], gtBall, 0, 0, 0, 1)
+			--	AddGear(vCircX[i]+vCircRadius[i], vCircY[i], gtBall, 0, 0, 0, 1)
+			end
+
+		end
+
+		if (vCircActive[i] == true) then
+
+			vCircRadCounter[i] = vCircRadCounter[i] + 1
+			if vCircRadCounter[i] == 100 then
+
+				vCircRadCounter[i] = 0
+
+				-- make my radius increase/decrease faster if I am an aura
+				if vCircType[i] == 0 then
+					M = 1
+				else
+					M = 10
+				end
+
+				vCircRadius[i] = vCircRadius[i] + vCircRadDir[i]
+				if vCircRadius[i] > vCircRadMax[i] then
+					vCircRadDir[i] = -M
+				elseif vCircRadius[i] < vCircRadMin[i] then
+					vCircRadDir[i] = M
+				end
+
+
+				-- random effect test
+				-- maybe use this to tell the difference between circs
+				-- you can kill by shooting or not
+				--vgtSmoke vgtSmokeWhite
+				--vgtSteam -- nice long trail
+				--vgtDust -- short trail on earthrise
+				--vgtSmokeTrace
+				if vType[i] == "ammo" then
+
+					tempE = AddVisualGear(vCircX[i], vCircY[i], vgtSmoke, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)	--0xff00ffff	--0x00ff00ff
+					SetVisualGearValues(tempE, vCircX[i], vCircY[i], g3, g4, g5, g6, g7, g8, g9, vCircCol[i] )
+
+					--AddVisualGear(vCircX[i], vCircY[i], vgtDust, 0, true)
+
+				elseif vType[i] == "bonus" then
+
+					tempE = AddVisualGear(vCircX[i], vCircY[i], vgtDust, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)	--0xff00ffff	--0x00ff00ff --vCircCol[i]
+					SetVisualGearValues(tempE, vCircX[i], vCircY[i], g3, g4, g5, g6, g7, 1, g9, 0xff00ffff )
+
+
+				elseif vType[i] == "blueboss" then
+
+					k = 25
+					g = vgtSteam
+					trailColour = 0xae00ffff
+
+					-- 0xffae00ff -- orange
+					-- 0xae00ffff -- purp
+
+					tempE = AddVisualGear(vCircX[i], vCircY[i], g, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)	--0xff00ffff	--0x00ff00ff
+					SetVisualGearValues(tempE, vCircX[i], vCircY[i]+k, g3, g4, g5, g6, g7, g8, g9, trailColour-75 )
+
+					tempE = AddVisualGear(vCircX[i], vCircY[i], g, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)	--0xff00ffff	--0x00ff00ff
+					SetVisualGearValues(tempE, vCircX[i]+k, vCircY[i]-k, g3, g4, g5, g6, g7, g8, g9, trailColour-75 )
+
+					tempE = AddVisualGear(vCircX[i], vCircY[i], g, 0, true)
+					g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)	--0xff00ffff	--0x00ff00ff
+					SetVisualGearValues(tempE, vCircX[i]-k, vCircY[i]-k, g3, g4, g5, g6, g7, g8, g9, trailColour-75 )
+
+
+				end
+
+
+			end
+
+		end
+
+
+	end
+
+	-- alter the circles velocities
+	if GameTime%2000 == 0 then
+
+		for i = 0,(vCCount-1) do
+
+			-- bounce the circles off the edges if they go too far
+			-- or make them move in random directions
+
+			if vCircX[i] > 5500 then
+				vCircDX[i] = -4	--5 circmovchange
+			elseif vCircX[i] < -1500 then
+				vCircDX[i] = 4	--5 circmovchange
+			else
+
+				z = GetRandom(2)
+				if z == 1 then
+					z = 1
+				else
+					z = -1
+				end
+				vCircDX[i] = vCircDX[i] + GetRandom(3)*z	--3 circmovchange
+			end
+
+			if vCircY[i] > 1500 then
+				vCircDY[i] = -4	--5 circmovchange
+			elseif vCircY[i] < -2900 then
+				vCircDY[i] = 4	--5 circmovchange
+			else
+				z = GetRandom(2)
+				if z == 1 then
+					z = 1
+				else
+					z = -1
+				end
+				vCircDY[i] = vCircDY[i] + GetRandom(3)*z	--3 circmovchange
+			end
+
+		end
+
+	end
+
+	-- move the circles according to their current velocities
+	--m2Count = m2Count + 1
+	--if m2Count == 25 then	--25 circmovchange
+
+	--	m2Count = 0
+		for i = 0,(vCCount-1) do
+			vCircX[i] = vCircX[i] + vCircDX[i]
+			vCircY[i] = vCircY[i] + vCircDY[i]
+
+			if (CurrentHedgehog ~= nil) and (rAlpha ~= 255) then
+				DoHorribleThings(i)--(i, g1X, g1Y, g2X, g2Y, dist)
+			end
+
+		end
+
+		if (TimeLeft == 0) and (tumbleStarted == true) then
+
+			FadeAlpha = FadeAlpha + 1
+			if FadeAlpha >= 255 then
+				FadeAlpha = 255
+			end
+
+			--new
+			--if FadeAlpha == 1 then
+			--	AddCaption("GOT IT")
+			--	for i = 0,(vCCount-1) do
+			--		g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vCirc[i])
+			--		vCircCol[i] = g10
+			--	end
+			--end
+
+		end
+
+
+		-- derp
+		if shockwaveHealth > 0 then
+			shockwaveHealth = shockwaveHealth - 1
+			shockwaveRad = shockwaveRad + 80
+
+			--mrm = ((48/100*shockwaveRad)/2)
+			--AddVisualGear(GetX(CurrentHedgehog)-mrm+GetRandom(mrm*2),GetY(CurrentHedgehog)-mrm+GetRandom(mrm*2), vgtSmoke, 0, false)
+		end
+
+
+
+	--end
+
+	for i = 0,(vCCount-1) do
+		g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vCirc[i])		-- vCircCol[i] g10
+		SetVisualGearValues(vCirc[i], vCircX[i], vCircY[i], g3, g4, g5, g6, g7, vCircRadius[i], g9, g10)
+	end
+
+	if 	(TimeLeft == 0) or
+		((tumbleStarted == false)) then
+		for i = 0,(vCCount-1) do
+			g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vCirc[i])		-- vCircCol[i] g10
+			SetVisualGearValues(vCirc[i], vCircX[i], vCircY[i], g3, g4, g5, g6, g7, vCircRadius[i], g9, (vCircCol[i]-FadeAlpha))
+		end
+	end
+
+
+	if (CurrentHedgehog ~= nil) then
+		if beam == true then
+			g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(pShield)
+			--SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), g3, g4, g5, g6, g7, 200, g9, g10 )
+			SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), g3, g4, g5, g6, g7, 200, g9, 0xa800ffff-0x000000ff - -shieldHealth )
+			DrawTag(2)
+		else
+			SetVisualGearValues(pShield, GetX(CurrentHedgehog), GetY(CurrentHedgehog), g3, g4, g5, g6, g7, 0, g9, g10 )
+		end
+
+		if shockwaveHealth > 0 then
+			g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(shockwave)
+			SetVisualGearValues(shockwave, GetX(CurrentHedgehog), GetY(CurrentHedgehog), g3, g4, g5, g6, g7, shockwaveRad, g9, 0xff3300ff-0x000000ff - -shockwaveHealth )
+		else
+			SetVisualGearValues(shockwave, GetX(CurrentHedgehog), GetY(CurrentHedgehog), g3, g4, g5, g6, g7, 0, g9, g10 )
+		end
+
+	end
+
+
+end
+
+function ProjectileTrack(gear)
+
+	if (GetGearType(gear) == gtShell) then
+
+		--nw WriteLnToConsole("ProjectileTrack() for Shell ID: " .. getGearValue(gear,"ID"))
+
+		-- newnew
+		if (GetGearType(gear) == gtShell) then
+			turningSpeed = 0.1*fMod
+		--elseif (GetGearType(gear) == gtBall) then
+		--	turningSpeed = 0.2*fMod
+		end
+
+		dx, dy = GetGearVelocity(gear)
+
+		--WriteLnToConsole("I'm trying to track currenthedge with shell ID: " .. getGearValue(gear,"ID"))
+		--WriteLnToConsole("I just got the velocity of the shell. It is dx: " .. dx .. "; dy: " .. dy)
+		--WriteLnToConsole("CurrentHedgehog is at X: " .. GetX(CurrentHedgehog) .. "; Y: " .. GetY(CurrentHedgehog) )
+
+        if CurrentHedgehog ~= nil then
+            if GetX(gear) > GetX(CurrentHedgehog) then
+                dx = dx - turningSpeed--0.1
+            else
+                dx = dx + turningSpeed--0.1
+            end
+
+            if GetY(gear) > GetY(CurrentHedgehog) then
+                dy = dy - turningSpeed--0.1
+            else
+                dy = dy + turningSpeed--0.1
+            end
+        end
+
+
+		if (GetGearType(gear) == gtShell) then
+			dxlimit = 0.4*fMod
+			dylimit = 0.4*fMod
+		--elseif (GetGearType(gear) == gtBall) then
+		--	dxlimit = 0.5	--  0.5 is about the same
+		--	dylimit = 0.5 -- 0.6 is faster than player
+		end
+
+		if dx > dxlimit then
+			dx = dxlimit
+		end
+		if dy > dylimit then
+			dy = dylimit
+		end
+		if dx < -dxlimit then
+			dx = -dxlimit
+		end
+		if dy < -dylimit then
+			dy = -dylimit
+		end
+
+		SetGearVelocity(gear, dx, dy)
+
+		--WriteLnToConsole("I just SET the velocity of shell towards currenthegdge. It is now dx: " .. dx .. "; dy: " .. dy)
+		--WriteLnToConsole("The above events occured game Time: " .. GameTime .. "; luaTicks: " .. luaGameTicks)
+		--nw WriteLnToConsole("ProjectileTrack() finished successfully")
+
+	end
+
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/The_Specialists.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/The_Specialists.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,291 @@
+----------------------------------
+-- THE SPECIALISTS MODE 0.7
+-- by mikade
+----------------------------------
+
+-- version history
+-----------------
+-- version 0.1
+-----------------
+-- concept test
+
+----------------
+-- version 0.2
+----------------
+-- added gfRandomOrder to gameflags
+-- removed some deprecated variables/methods
+-- fixed lack of portal reset
+
+----------------
+-- version 0.3
+----------------
+-- added switching on start
+-- removed switch from engineer weaponset
+
+----------------
+-- version 0.4
+----------------
+-- Attempted to:
+-- fix potential switch explit
+-- improve user feedback on start
+
+----------------
+-- version 0.5
+----------------
+-- provision for variable minetimer / demo mines set to 5000ms
+-- don't autoswitch if player only has 1 hog on his team
+
+----------------
+-- version 0.6
+----------------
+-- for the meanwhile, don't drop any crates except health crates
+
+----------------
+-- version 0.7
+----------------
+-- perhogadmsdf :D :D :D :D
+
+--------------------
+--TO DO
+--------------------
+
+-- balance hog health, maybe
+-- add proper gameflag checking, maybe (so that we can throw in a .cfg and let the users break everything)
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+local numhhs = 0
+local hhs = {}
+
+local currName
+local lastName
+local started = false
+local switchStage = 0
+
+local hogCounter
+
+function CountHog(gear)
+	hogCounter = hogCounter +1
+end
+
+function onNewAmmoStore(groupIndex, hogIndex)
+
+	SetAmmo(amSkip, 9, 0, 0, 0)
+
+	if hogIndex == 0 then
+		SetAmmo(amBazooka, 1, 0, 0, 0)
+		SetAmmo(amGrenade, 1, 0, 0, 0)
+		SetAmmo(amShotgun, 1, 0, 0, 0)
+	elseif hogIndex == 1 then
+		SetAmmo(amGirder, 2, 0, 0, 0)
+		SetAmmo(amBlowTorch, 1, 0, 0, 0)
+		SetAmmo(amPickHammer, 1, 0, 0, 0)
+	elseif hogIndex == 2 then
+		SetAmmo(amRope, 9, 0, 0, 0)
+		SetAmmo(amParachute, 9, 0, 0, 0)
+		SetAmmo(amFirePunch, 1, 0, 0, 0)
+	elseif hogIndex == 3 then
+		SetAmmo(amDynamite, 1, 0, 0, 0)
+		SetAmmo(amMine, 1, 0, 0, 0)
+		SetAmmo(amDrill, 1, 0, 0, 0)
+	elseif hogIndex == 4 then
+		SetAmmo(amSniperRifle, 1, 0, 0, 0)
+		SetAmmo(amDEagle, 1, 0, 0, 0)
+		SetAmmo(amPortalGun, 2, 0, 0, 0)
+	elseif hogIndex == 5 then
+		SetAmmo(amSeduction, 9, 0, 0, 0)
+		SetAmmo(amResurrector, 1, 0, 0, 0)
+		SetAmmo(amInvulnerable, 1, 0, 0, 0)
+	elseif hogIndex == 6 then
+		SetAmmo(amFlamethrower, 1, 0, 0, 0)
+		SetAmmo(amMolotov, 1, 0, 0, 0)
+		SetAmmo(amNapalm, 1, 0, 0, 0)
+	elseif hogIndex == 7 then
+		SetAmmo(amBaseballBat, 1, 0, 0, 0)
+		SetAmmo(amGasBomb, 1, 0, 0, 0)
+		SetAmmo(amKamikaze, 1, 0, 0, 0)
+	end
+
+end
+
+function CreateTeam()
+
+	currTeam = ""
+	lastTeam = ""
+	z = 0
+
+	for i = 0, (numhhs-1) do
+
+			currTeam = GetHogTeamName(hhs[i])
+
+			if currTeam == lastTeam then
+					z = z + 1
+			else
+					z = 1
+			end
+
+			if z == 1 then
+
+					SetHogName(hhs[i],"Soldier")
+					SetHogHat(hhs[i], "sf_vega")
+					SetHealth(hhs[i],200)
+
+			elseif z == 2 then
+
+					SetHogHat(hhs[i], "Glasses")
+					SetHogName(hhs[i],"Engineer")
+
+			elseif z == 3 then
+
+					SetHogName(hhs[i],"Ninja")
+					SetHogHat(hhs[i], "NinjaFull")
+					SetHealth(hhs[i],80)
+
+			elseif z == 4 then
+
+					SetHogName(hhs[i],"Demo")
+					SetHogHat(hhs[i], "Skull")
+					SetHealth(hhs[i],200)
+
+			elseif z == 5 then
+
+					SetHogName(hhs[i],"Sniper")
+					SetHogHat(hhs[i], "Sniper")
+					SetHealth(hhs[i],120)
+
+			elseif z == 6 then
+
+					SetHogName(hhs[i],"Saint")
+					SetHogHat(hhs[i], "angel")
+					SetHealth(hhs[i],300)
+
+			elseif z == 7 then
+
+					SetHogName(hhs[i],"Pyro")
+					SetHogHat(hhs[i], "Gasmask")
+					SetHealth(hhs[i],150)
+
+			elseif z == 8 then
+
+					SetHogName(hhs[i],"Loon")
+					SetHogHat(hhs[i], "clown")
+					SetHealth(hhs[i],100)
+
+			end
+
+			lastTeam = GetHogTeamName(hhs[i])
+
+	end
+
+end
+
+function onGameInit()
+	GameFlags = gfRandomOrder + gfResetWeps + gfInfAttack + gfPlaceHog +gfPerHogAmmo
+	Delay = 10
+	HealthCaseProb = 100
+end
+
+function onGameStart()
+
+	CreateTeam()
+
+	ShowMission     (
+                                loc("THE SPECIALISTS"),
+                                loc("a Hedgewars mini-game"),
+
+                                loc("Eliminate the enemy specialists.") .. "|" ..
+                                " " .. "|" ..
+
+                                loc("Game Modifiers: ") .. "|" ..
+                                loc("Per-Hog Ammo") .. "|" ..
+                                loc("Weapons Reset") .. "|" ..
+                                loc("Unlimited Attacks") .. "|" ..
+
+                                "", 4, 4000
+                                )
+
+	trackTeams()
+
+end
+
+
+function onNewTurn()
+	currName = GetHogName(CurrentHedgehog)
+	lastName = GetHogName(CurrentHedgehog)
+	started = true
+	switchStage = 0
+end
+
+function onGameTick20()
+
+	if (CurrentHedgehog ~= nil) then
+
+		currName = GetHogName(CurrentHedgehog)
+
+		if (currName ~= lastName) and (switchStage > 5) then
+			AddCaption(loc("Switched to ") .. currName .. "!")
+		end
+
+		if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) and (switchStage < 5) then
+
+			AddCaption(loc("Prepare yourself") .. ", " .. currName .. "!")
+
+			hogCounter = 0
+			runOnHogsInTeam(CountHog, GetHogTeamName(CurrentHedgehog) )
+
+			if hogCounter > 1 then
+
+				switchStage = switchStage + 1
+
+				if switchStage == 1 then
+					AddAmmo(CurrentHedgehog, amSwitch, 1)
+
+				elseif switchStage == 2 then
+					ParseCommand("setweap " .. string.char(amSwitch))
+				elseif switchStage == 3 then
+					SetGearMessage(CurrentHedgehog,gmAttack)
+				elseif switchStage == 4 then
+					switchStage = 6
+					AddAmmo(CurrentHedgehog, amSwitch, 0)
+				end
+
+			else
+				switchStage = 6
+			end
+
+
+		end
+
+		lastName = currName
+
+	end
+
+end
+
+function onGearAdd(gear)
+
+    if GetGearType(gear) == gtHedgehog then
+		hhs[numhhs] = gear
+		numhhs = numhhs + 1
+	elseif (GetGearType(gear) == gtMine) and (started == true) then
+		SetTimer(gear,5000)
+	end
+
+	if (GetGearType(gear) == gtHedgehog) or (GetGearType(gear) == gtResurrector) then
+		trackGear(gear)
+	end
+
+
+end
+
+function onGearDelete(gear)
+	if (GetGearType(gear) == gtHedgehog) or (GetGearType(gear) == gtResurrector) then
+		trackDeletion(gear)
+	end
+end
+
+function onAmmoStoreInit()
+--
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Tumbler.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Default
+Default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/Tumbler.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,786 @@
+------------------------------------
+-- TUMBLER
+-- v.0.7.1
+------------------------------------
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+
+local fMod = 1000000 -- use this for dev and .16+ games
+
+local leftOn = false
+local rightOn = false
+local upOn = false
+local downOn = false
+local preciseOn = false
+
+local wep = {}
+local wepAmmo = {}
+local wepCol = {}
+local wepIndex = 0
+local wepCount = 0
+local fGears = 0
+
+local mineSpawn
+local barrelSpawn
+
+local roundKills = 0
+local barrelsEaten = 0
+local minesEaten = 0
+
+local moveTimer = 0
+local fireTimer = 0
+local TimeLeftCounter = 0
+local TimeLeft = 0
+local stopMovement = false
+local tumbleStarted = false
+
+local vTag = {}
+
+------------------------
+-- version 0.4
+------------------------
+
+-- removed some old code/comments
+-- removed both shell and mortar as the primary and secondary weapons
+-- the primary weapon is now an explosive(barrel)
+
+-- added support for picking up barrels scattered about the map (backspace)
+-- added support for dragging around mines (enter toggles on/off)
+-- added support for primary fire being onAttackUp
+-- added a trail to indicate when the player has 5s or less left to tumble
+-- updated showmission to reflect changed controls and options
+
+------------------------
+-- version 0.5
+------------------------
+
+-- changed some of the user feedback
+-- i can't remember??
+-- substituted onAttackUp for onPrecise()
+-- brought in line with new velocity changes
+
+------------------------
+-- version 0.6
+------------------------
+
+-- reduced starting "ammo"
+-- randomly spawn new barrels/mines on new turn
+-- updated user feedback
+-- better locs and coloured addcaptions
+-- added tag for turntime
+-- removed tractor beam
+-- added two new weapons and changed ammo handling
+-- health crates now give tumbler time, and wep/utility give flamer ammo
+-- explosives AND mines can be picked up to increase their relative ammo
+-- replaced "no weapon" selected message that hw serves
+-- modified crate frequencies a bit
+-- added some simple kill-based achievements, i think
+
+------------------------
+-- version 0.7
+------------------------
+
+-- a few code optimisations/performance tweaks
+-- removed some deprecated code
+-- fix a potential spawn bug
+
+-- improved HUD (now shows ammo counts)
+-- improved user feedback (less generic messages)
+-- colour-coded addcaptions to match hud :)
+
+-- base tumbling time now equals scheme turntime
+-- tumbling time extension is now based on the amount of health contained in crate
+-- new mines per turn based on minesnum
+-- new barrels per turn based on explosives
+
+-- added 2 more achievements: barrel eater and mine eater (like kills, don't do anything atm)
+-- slightly increased grab distance for explosives/mines
+-- slightly increased flamer velocity
+-- slightly decreased flamer volume
+-- added a flame vaporiser (based on number of flame gears?)
+-- give tumblers an extra 47 health on the start of their tumble to counter the grenade (exp)
+-- refocus camera on tumbler on newturn (not on crates, barrels etc)
+-- increase delay: yes, yes, eat your hearts out
+
+-- commit log
+-- Better HUD
+-- Allow more user customization
+-- Bugfix for new gear spawns
+-- Performance tweaks
+-- Variety of small gameplay changes
+
+------------------------
+-- version 0.7.1
+------------------------
+
+-- redraw HUD on screen resolution change
+
+---------------------------
+-- some other ideas/things
+---------------------------
+--[[
+-- add better gameflag handling
+-- fix flamer "shots remaining" message on start or choose a standard versus %
+-- add more sounds
+-- better barrel/minespawn effects
+-- separate grab distance for mines/barrels
+-- [probably not] make barrels always explode?
+-- [probably not] persistent ammo?
+-- [probably not] dont hurt tumblers and restore their health at turn end?
+]]
+
+
+----------------------------------------------------------------
+----------------------------------------------------------------
+
+local flames = {}
+local fGearValues = {}
+
+function runOnflames(func)
+    for k, gear in ipairs(flames) do
+        func(gear)
+    end
+end
+
+function trackFGear(gear)
+    table.insert(flames, gear)
+end
+
+function trackFGearDeletion(gear)
+    fGearValues[gear] = nil
+    for k, g in ipairs(flames) do
+        if g == gear then
+            table.remove(flames, k)
+            break
+        end
+    end
+end
+
+function getFGearValue(gear, key)
+    if fGearValues[gear] ~= nil then
+        return fGearValues[gear][key]
+    end
+    return nil
+end
+
+function setFGearValue(gear, key, value)
+    found = false
+    for id, values in pairs(fGearValues) do
+        if id == gear then
+            values[key] = value
+            found = true
+        end
+    end
+    if not found then
+        fGearValues[gear] = { [key] = value }
+    end
+end
+
+function decreaseFGearValue(gear, key)
+    for id, values in pairs(fGearValues) do
+        if id == gear then
+            values[key] = values[key] - 1
+        end
+    end
+end
+
+function HandleLife(gear)
+
+	decreaseFGearValue(gear, "L")
+	if getFGearValue(gear, "L") == 0 then
+		AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
+		DeleteGear(gear)
+	end
+
+end
+
+----------------------------------------------------------------
+----------------------------------------------------------------
+
+function HideTags()
+
+	for i = 0, 3 do
+		SetVisualGearValues(vTag[i],0,0,0,0,0,1,0, 0, 240000, 0xffffff00)
+	end
+
+end
+
+function DrawTag(i)
+
+	zoomL = 1.3
+
+	xOffset = 40
+
+	if i == 0 then
+		yOffset = 40
+		tCol = 0xffba00ff --0xffed09ff --0xffba00ff
+		tValue = TimeLeft
+	elseif i == 1 then
+		zoomL = 1.1
+		yOffset = 70
+		tCol = wepCol[0]
+		tValue = wepAmmo[0]
+	elseif i == 2 then
+		zoomL = 1.1
+		xOffset = 40 + 35
+		yOffset = 70
+		tCol = wepCol[1]
+		tValue = wepAmmo[1]
+	elseif i == 3 then
+		zoomL = 1.1
+		xOffset = 40 + 70
+		yOffset = 70
+		tCol = wepCol[2]
+		tValue = wepAmmo[2]
+	end
+
+	DeleteVisualGear(vTag[i])
+	vTag[i] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
+	g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vTag[i])
+	SetVisualGearValues	(
+				vTag[i], 		--id
+				-(ScreenWidth/2) + xOffset,	--xoffset
+				ScreenHeight - yOffset, --yoffset
+				0, 			--dx
+				0, 			--dy
+				zoomL, 			--zoom
+				1, 			--~= 0 means align to screen
+				g7, 			--frameticks
+				tValue, 		--value
+				240000, 		--timer
+				tCol		--GetClanColor( GetHogClan(CurrentHedgehog) )
+				)
+
+end
+
+function GetGearDistance(gear)
+
+	g1X, g1Y = GetGearPosition(gear)
+	g2X, g2Y = GetGearPosition(CurrentHedgehog)
+
+	q = g1X - g2X
+	w = g1Y - g2Y
+	return( (q*q) + (w*w) )
+
+end
+
+-- add to your ammo ***WHEN YOU PUSH A KEY*** near them
+-- yes that was my justification for a non generic method
+function CheckProximityToExplosives(gear)
+
+	if (GetGearDistance(gear) < 1400) then
+
+		if (GetGearType(gear) == gtExplosives) then
+
+			wepAmmo[0] = wepAmmo[0] + 1
+			PlaySound(sndShotgunReload)
+			DeleteGear(gear)
+			AddCaption(wep[0] .. " " .. loc("ammo extended!"), wepCol[0], capgrpAmmoinfo )
+			DrawTag(1)
+
+			barrelsEaten = barrelsEaten + 1
+			if barrelsEaten == 5 then
+				AddCaption(loc("Achievement Unlocked") .. ": " .. loc("Barrel Eater!"),0xffba00ff,capgrpMessage2)
+			end
+
+		elseif (GetGearType(gear) == gtMine) then
+			wepAmmo[1] = wepAmmo[1] + 1
+			PlaySound(sndShotgunReload)
+			DeleteGear(gear)
+			AddCaption(wep[1] .. " " .. loc("ammo extended!"), wepCol[1], capgrpAmmoinfo )
+			DrawTag(2)
+
+			minesEaten = minesEaten + 1
+			if minesEaten == 5 then
+				AddCaption(loc("Achievement Unlocked") .. ": " .. loc("Mine Eater!"),0xffba00ff,capgrpMessage2)
+			end
+
+		end
+
+	else
+		--AddCaption("There is nothing here...")
+	end
+
+end
+
+-- check proximity on crates
+function CheckProximity(gear)
+
+	dist = GetGearDistance(gear)
+
+	if (dist < 1600) and (GetGearType(gear) == gtCase) then
+
+		if GetHealth(gear) > 0 then
+
+			AddCaption(loc("Tumbling Time Extended!"), 0xffba00ff, capgrpMessage2 )
+
+			TimeLeft = TimeLeft + HealthCaseAmount  --5 --5s
+			DrawTag(0)
+			--PlaySound(sndShotgunReload)
+		else
+			wepAmmo[2] = wepAmmo[2] + 800
+			PlaySound(sndShotgunReload)
+			AddCaption(wep[2] .. " " .. loc("fuel extended!"), wepCol[2], capgrpAmmoinfo )
+			DrawTag(3)
+		end
+
+		DeleteGear(gear)
+
+	end
+
+end
+
+function ChangeWeapon()
+
+	wepIndex = wepIndex + 1
+	if wepIndex == wepCount then
+		wepIndex = 0
+	end
+
+	AddCaption(wep[wepIndex] .. " " .. loc("selected!"), wepCol[wepIndex],capgrpAmmoinfo )
+	AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
+
+end
+
+---------------
+-- action keys
+---------------
+
+function onPrecise()
+
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) and (wepAmmo[wepIndex] > 0) then
+
+		wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
+		AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
+
+		if wep[wepIndex] == loc("Barrel Launcher") then
+			morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtExplosives, 0, 0, 0, 1)
+			CopyPV(CurrentHedgehog, morte) -- new addition
+			x,y = GetGearVelocity(morte)
+			x = x*2
+			y = y*2
+			SetGearVelocity(morte, x, y)
+			DrawTag(1)
+
+		elseif wep[wepIndex] == loc("Mine Deployer") then
+			morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtMine, 0, 0, 0, 0)
+			SetTimer(morte, 1000)
+			DrawTag(2)
+		end
+
+	end
+
+	preciseOn = true
+
+end
+
+function onPreciseUp()
+	preciseOn = false
+end
+
+function onHJump()
+	-- pick up explosives/mines if nearby them
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
+		runOnGears(CheckProximityToExplosives)
+	end
+end
+
+function onLJump()
+	ChangeWeapon()
+end
+
+-----------------
+-- movement keys
+-----------------
+
+function onLeft()
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) then
+		leftOn = true
+	end
+end
+
+function onRight()
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) then
+		rightOn = true
+	end
+end
+
+function onUp()
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) then
+		upOn = true
+	end
+end
+
+function onDown()
+	if (CurrentHedgehog ~= nil) and (stopMovement == false) then
+		downOn = true
+	end
+end
+
+function onDownUp()
+	downOn = false
+end
+function onUpUp()
+	upOn = false
+end
+function onLeftUp()
+	leftOn = false
+end
+function onRightUp()
+	rightOn = false
+end
+
+--------------------------
+-- other event handlers
+--------------------------
+
+function onGameInit()
+	CaseFreq = 0
+	HealthCaseProb = 0
+	Delay = 1000
+
+	mineSpawn = MinesNum
+	if mineSpawn > 4 then
+		mineSpawn = 4
+	end
+
+	barrelSpawn = Explosives
+	if barrelSpawn > 4 then
+		barrelSpawn = 4
+	end
+
+	--MinesNum = 0
+	--Explosives = 0
+
+	for i = 0, 3 do
+		vTag[0] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
+	end
+
+	HideTags()
+
+	wep[0] = loc("Barrel Launcher")
+	wep[1] = loc("Mine Deployer")
+	wep[2] = loc("Flamer")
+
+	wepCol[0] = 0x78818eff
+	wepCol[1] = 0xa12a77ff
+	wepCol[2] = 0xf49318ff
+
+	wepCount = 3
+
+end
+
+function onGameStart()
+
+	ShowMission	(
+			"TUMBLER",
+			loc("a Hedgewars mini-game"),
+			loc("Eliminate the enemy hogs to win.") .. "|" ..
+			" " .. "|" ..
+
+			loc("New Mines Per Turn") .. ": " .. (mineSpawn) .. "|" ..
+			loc("New Barrels Per Turn") .. ": " .. (barrelSpawn) .. "|" ..
+			loc("Time Extension") .. ": " .. (HealthCaseAmount) .. loc("sec") .. "|" ..
+			" " .. "|" ..
+
+			loc("Movement: [Up], [Down], [Left], [Right]") .. "|" ..
+			loc("Fire") .. ": " .. loc("[Left Shift]") .. "|" ..
+			loc("Change Weapon") .. ": " .. loc("[Enter]") .. "|" ..
+			loc("Grab Mines/Explosives") .. ": " .. loc("[Backspace]") .. "|" ..
+
+			" " .. "|" ..
+
+			loc("Health crates extend your time.") .. "|" ..
+			loc("Ammo is reset at the end of your turn.") .. "|" ..
+
+			"", 4, 4000
+			)
+
+end
+
+function onScreenResize()
+
+	-- redraw Tags so that their screen locations are updated
+	if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
+		for i = 0, 3 do
+			DrawTag(i)
+		end
+	end
+
+end
+
+function onNewTurn()
+
+	stopMovement = false
+	tumbleStarted = false
+
+	-- randomly create new barrels mines on the map every turn (can be disabled by setting mine/barrels to 0 in scheme)
+	for i = 0, barrelSpawn-1 do
+		gear = AddGear(100, 100, gtExplosives, 0, 0, 0, 0)
+		SetHealth(gear, 100)
+		if FindPlace(gear, false, 0, LAND_WIDTH, false) ~= nil then
+			tempE = AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+		end
+	end
+	for i = 0, mineSpawn-1 do
+		gear = AddGear(100, 100, gtMine, 0, 0, 0, 0)
+		if FindPlace(gear, false, 0, LAND_WIDTH, false) ~= nil then
+			tempE = AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
+		end
+	end
+
+	-- randomly spawn time extension crates / flamer fuel on the map
+	r = GetRandom(100)
+	if r > 50 then
+		gear = SpawnHealthCrate(0, 0)
+	end
+	r = GetRandom(100)
+	if r > 70 then
+		gear = SpawnAmmoCrate(0, 0, amSkip)
+	end
+
+	HideTags()
+
+	--reset ammo counts
+	wepAmmo[0] = 2
+	wepAmmo[1] = 1
+	wepAmmo[2] = 50 -- 50000 -- 50
+	wepIndex = 2
+	ChangeWeapon()
+
+	roundKills = 0
+	barrelsEaten = 0
+	minesEaten = 0
+
+	FollowGear(CurrentHedgehog)
+
+end
+
+
+function DisableTumbler()
+	stopMovement = true
+	upOn = false
+	down = false
+	leftOn = false
+	rightOn = false
+	HideTags()
+end
+
+function onGameTick()
+
+	-- start the player tumbling with a boom once their turn has actually begun
+	if tumbleStarted == false then
+		if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then
+			--AddCaption(loc("Good to go!"))
+			tumbleStarted = true
+			TimeLeft = (TurnTime/1000)
+			AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1)
+			SetHealth(CurrentHedgehog, GetHealth(CurrentHedgehog) + 47) -- new
+			for i = 0, 3 do
+				DrawTag(i)
+			end
+		end
+	end
+
+	if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
+
+		runOnGears(CheckProximity) -- crates
+
+		-- Calculate and display turn time
+		TimeLeftCounter = TimeLeftCounter + 1
+		if TimeLeftCounter == 1000 then
+			TimeLeftCounter = 0
+			TimeLeft = TimeLeft - 1
+
+			if TimeLeft >= 0 then
+				DrawTag(0)
+			end
+
+		end
+
+		if TimeLeft == 0 then
+			DisableTumbler()
+		end
+
+		-- handle movement based on IO
+		moveTimer = moveTimer + 1
+		if moveTimer == 100 then -- 100
+			moveTimer = 0
+
+			runOnflames(HandleLife)
+
+			---------------
+			-- new trail code
+			---------------
+			-- the trail lets you know you have 5s left to pilot, akin to birdy feathers
+			if (TimeLeft <= 5) and (TimeLeft > 0) then
+				tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
+				g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+				SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, GetClanColor(GetHogClan(CurrentHedgehog)) )
+			end
+			--------------
+
+			dx, dy = GetGearVelocity(CurrentHedgehog)
+
+			dxlimit = 0.4*fMod
+			dylimit = 0.4*fMod
+
+			if dx > dxlimit then
+				dx = dxlimit
+			end
+			if dy > dylimit then
+				dy = dylimit
+			end
+			if dx < -dxlimit then
+				dx = -dxlimit
+			end
+			if dy < -dylimit then
+				dy = -dylimit
+			end
+
+
+			if leftOn == true then
+				dx = dx - 0.1*fMod
+			end
+			if rightOn == true then
+				dx = dx + 0.1*fMod
+			end
+
+			if upOn == true then
+				dy = dy - 0.1*fMod
+			end
+			if downOn == true then
+				dy = dy + 0.1*fMod
+			end
+
+			SetGearVelocity(CurrentHedgehog, dx, dy)
+
+		end
+
+		--
+		--flamer
+		--
+		fireTimer = fireTimer + 1
+		if fireTimer == 6 then	-- 5 --10
+			fireTimer = 0
+
+			if (wep[wepIndex] == loc("Flamer") ) and (preciseOn == true) and (wepAmmo[wepIndex] > 0) and (stopMovement == false) and (tumbleStarted == true) then
+
+				wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
+				AddCaption(
+						loc("Flamer") .. ": " ..
+						(wepAmmo[wepIndex]/800*100) - (wepAmmo[wepIndex]/800*100)%2 .. "%",
+						wepCol[2],
+						capgrpMessage2
+						)
+				DrawTag(3)
+
+				dx, dy = GetGearVelocity(CurrentHedgehog)
+				shell = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtFlame, 0, 0, 0, 0)
+
+				xdev = 1 + GetRandom(25)	--15
+				xdev = xdev / 100
+
+				r = GetRandom(2)
+				if r == 1 then
+					xdev = xdev*-1
+				end
+
+				ydev = 1 + GetRandom(25)	--15
+				ydev = ydev / 100
+
+				r = GetRandom(2)
+				if r == 1 then
+					ydev = ydev*-1
+				end
+
+				--*13	--8	*-4
+				SetGearVelocity(shell, (dx*4.5)+(xdev*fMod), (dy*4.5)+(ydev*fMod))	--10
+
+			end
+
+		end
+		--
+
+	end
+
+
+end
+
+function isATrackedGear(gear)
+	if 	(GetGearType(gear) == gtExplosives) or
+		(GetGearType(gear) == gtMine) or
+		(GetGearType(gear) == gtCase)
+	then
+		return(true)
+	else
+		return(false)
+	end
+end
+
+--[[function onGearDamage(gear, damage)
+	if gear == CurrentHedgehog then
+		-- You are now tumbling
+	end
+end]]
+
+function onGearAdd(gear)
+
+	if GetGearType(gear) == gtFlame then
+
+		trackFGear(gear)
+
+		fGears = fGears +1
+
+		if fGears < 80 then
+			setFGearValue(gear,"L",30)
+		else
+			setFGearValue(gear,"L",5) --3
+		end
+
+	elseif isATrackedGear(gear) then
+		trackGear(gear)
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if GetGearType(gear) == gtFlame then
+		trackFGearDeletion(gear)
+		fGears = fGears -1
+
+	elseif isATrackedGear(gear) then
+		trackDeletion(gear)
+
+	-- achievements? prototype
+	elseif GetGearType(gear) == gtHedgehog then
+
+		if GetHogTeamName(gear) ~= GetHogTeamName(CurrentHedgehog) then
+
+			roundKills = roundKills + 1
+			if roundKills == 2 then
+				AddCaption(loc("Double Kill!"),0xffba00ff,capgrpMessage2)
+			elseif roundKills == 3 then
+				AddCaption(loc("Killing spree!"),0xffba00ff,capgrpMessage2)
+			elseif roundKills >= 4 then
+				AddCaption(loc("Unstoppable!"),0xffba00ff,capgrpMessage2)
+			end
+
+		elseif gear == CurrentHedgehog then
+			DisableTumbler()
+
+		elseif gear ~= CurrentHedgehog then
+			AddCaption(loc("Friendly Fire!"),0xffba00ff,capgrpMessage2)
+		end
+
+	end
+
+	if CurrentHedgehog ~= nil then
+		FollowGear(CurrentHedgehog)
+	end
+
+end
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/WxW.cfg	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+Shoppa
+Shoppa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Multiplayer/WxW.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,751 @@
+
+----------------------
+-- WALL TO WALL 0.4
+----------------------
+-- a shoppa minigame
+-- by mikade
+
+-- feel free to add map specific walls to LoadConfig, or post additional
+-- wall suggestions on our forum at: http://www.hedgewars.org/forum
+
+----------------
+--0.1
+----------------
+-- concept test
+
+----------------
+--0.2
+----------------
+-- unhardcoded turntimeleft, now uses shoppa default of 45s
+-- changed some things behind the scenes
+-- fixed oooooold radar bug
+-- added radar / script support for multiple crates
+-- tweaked weapons tables
+-- added surfing and changed crate spawn requirements a bit
+
+----------------
+--0.3
+----------------
+-- stuffed dirty clothes into cupboard
+-- improved user feedback
+-- added/improved experimental config system, input masks included :D
+
+----------------
+--0.4
+----------------
+-- for version 0.9.18, now detects border in correct location
+-- fix 0.3 config constraint
+-- remove unnecessary vars
+-- oops, remove hardcoding of minesnum,explosives
+-- ... and unhardcode turntime (again)... man, 30s is hard :(
+-- move some initialisations around
+-- numerous improvements to user feedback
+-- walls disappear after being touched
+-- added backwards compatibility with 0.9.17
+
+----------------
+--TO DO
+----------------
+-- achievements / try detect shoppa moves? :|
+-- maybe add ability for the user to place zones like in Racer?
+-- add more hard-coded values for specific maps
+
+-----------------------------
+-- GO PONIES, GO PONIES, GO!
+-----------------------------
+
+loadfile(GetDataPath() .. "Scripts/Locale.lua")()
+loadfile(GetDataPath() .. "Scripts/Tracker.lua")()
+loadfile(GetDataPath() .. "Scripts/Utils.lua")()
+
+-- experimental menu stuff
+local menuIndex = 1
+local menu = {}
+local preMenuCfg
+local postMenuCfg
+local roundN = 0
+
+-- config and wall variables
+local AFR = false
+local allowCrazyWeps = false
+local requireSurfer = true
+local wX = {}
+local wY = {}
+local wWidth = {}
+local wHeight = {}
+local wTouched = {}
+--local margin
+local wallsLeft = 0
+
+local highestY = 0
+local surferTimer = 0
+local hasSurfed = false
+local allWallsHit = false
+
+local gTimer = 1
+local effectTimer = 1
+
+local ropeG = nil
+local crateG = nil
+local allowCrate = true
+
+-- crate radar vars
+local rCirc = {}
+local rAlpha = 255
+local rPingTimer = 0
+local m2Count = 0
+
+local weapons = {}
+
+--[[local unlisted = {amTardis, amLandGun,amExtraTime,amExtraDamage,
+				amVampiric, amSwitch, amInvulnerable, amGirder, amJetpack,
+				amPortalGun, amTeleport, amResurrector, amLaserSight, amLowGravity,
+				amAirAttack, amNapalm, amMineStrike, amDrillStrike,
+				amKamikaze, amSnowball, amSeduction}]]
+
+local crazyWeps = {amWatermelon, amHellishBomb, amBallgun, amRCPlane}
+
+local groundWeps = 	{amBee, amShotgun,amDEagle,amFirePunch, amWhip,
+				amPickHammer, amBaseballBat, amCake,amBallgun,
+				amRCPlane, amSniperRifle, amBirdy, amBlowTorch, amGasBomb,
+				amFlamethrower, amSMine, amMortar, amHammer}
+
+local ropeWeps = {amGrenade, amClusterBomb, amBazooka, amMine, amDynamite,
+				amWatermelon, amHellishBomb, amDrill, amMolotov}
+
+-- 0.9.18+ extra custom data for preset maps
+local MapList =
+	{
+	--name,      						surfer, roof, 	LRwalls
+	{"Atlantis Shoppa", 			    true, 	false, true},
+	{"BambooPlinko", 				    true,	false, true},
+	{"BrickShoppa", 				    false, 	false, true},
+	{"BubbleFlow",   					true, 	false, true},
+	{"Cave",       						false, 	false, true},
+	{"Glass Shoppa",      				true, 	false, true},
+	{"HardIce",      					false, 	false, true},
+	{"Industrial",       				false,	false, true},
+	{"Islands",       					true, 	false, true},
+	{"Hedgelove",       				true, 	false, true},
+	{"NeonStyle",       				false, 	false, true},
+	{"Octorama",       					false, 	false, true},
+	{"red vs blue - Castle",     		true, 	false, true},
+	{"red vs blue - castle2",     		true, 	false, true},
+	{"red vs blue - True Shoppa Sky",   true, 	false, true},
+	{"Ropes",       					false, 	false, true},
+	{"Ropes Rearranged",      			false, 	false, true},
+	{"RopesRevenge Flipped",    		true, 	false, true},
+	{"Ropes Three",      				false, 	false, true},
+	{"RopesTwo",      					false, 	false, true},
+	{"ShapeShoppa1.0",     				true, 	false, true},
+	{"ShappeShoppa Darkhow",      		true, 	false, true},
+	{"ShoppaCave2",      				true, 	false, true},
+	{"ShoppaFun",      					true, 	false, true},
+	{"ShoppaGolf",      				false, 	false,  true},
+	{"ShoppaHell",      				false, 	true,  false},
+	{"ShoppaKing",       				false, 	false, false},
+	{"ShoppaNeon",       				false, 	false, true},
+	{"ShoppaSky",       				false, 	false, true},
+	{"Shoppawall",       				false, 	false, true},
+	{"SkatePark",       				false, 	false, true},
+	{"SloppyShoppa",      				false, 	false, true},
+	{"Sticks",       					true, 	false, true},
+	{"Symmetrical Ropes ",       		false, 	false, true},
+	{"Tetris",       					false, 	false, true},
+	{"TransRopes2",      				false, 	false, true},
+	{"Wildmap",      					false, 	false, true},
+	{"Winter Shoppa",      				false, 	false, true},
+	{"2Cshoppa",      					true, 	false, true}
+	}
+
+function BoolToCfgTxt(p)
+	if p == false then
+		return("Disabled")
+	else
+		return("Enabled")
+	end
+end
+
+function LoadConfig(p)
+
+	margin = 20
+	mapID = nil
+
+	-- 0.9.17
+	if Map == "CHANGE_ME" then
+		AddCaption(loc("For improved features/stability, play 0.9.18+"))
+		--AddWall(10,10,4085,margin)
+		AddWall(10,10,margin,2025)
+		AddWall(4085-margin,10,margin,2025)
+	end
+
+	--0.9.18+
+	for i = 1, #MapList do
+		if Map == MapList[i][1] then
+			mapID = i
+			--AddCaption(MapList[i][1] .. " found. reqSurf is " .. BoolToCfgTxt(MapList[i][2]))
+		end
+	end
+
+	if (p == 1) and (mapID ~= nil) then
+		requireSurfer = MapList[mapID][2]
+	end
+
+	if mapID ~= nil then
+
+		-- add a wall to the roof
+		if MapList[mapID][3] == true then
+			AddWall(LeftX+10,TopY+10,RightX-LeftX-20,margin)
+		end
+
+		-- add walls on the left and right border
+		if MapList[mapID][4] == true then
+			AddWall(LeftX+10,TopY+10,margin,WaterLine)
+			AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
+		end
+
+		-- add map specific walls
+		if Map == "Ropes" then
+			AddWall(1092,934,54,262)
+			AddWall(2822,323,33,137)
+		elseif Map == "ShoppaKing" then
+			AddWall(3777,1520,50,196)
+			AddWall(1658,338,46,670)
+		elseif Map == "ShoppaHell" then
+			AddWall(2035,831,30,263)
+			AddWall(3968,1668,31,383)
+		elseif Map == "ShoppaNeon" then
+			AddWall(980,400,20,300)
+			AddWall(1940,400,20,300)
+			AddWall(3088,565,26,284)
+			AddWall(187,270,28,266)
+		end
+
+	-- if map is unrecognized, add two walls on the side borders
+	-- also, if version of hw is not 0.9.17 or lower
+	elseif Map ~= "CHANGE_ME" then
+		AddWall(LeftX+10,TopY+10,margin,WaterLine)
+		AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
+	end
+
+
+end
+
+function AddWall(zXMin,zYMin, zWidth, zHeight)
+
+	table.insert(wX, zXMin)
+	table.insert(wY, zYMin)
+	table.insert(wWidth, zWidth)
+	table.insert(wHeight, zHeight)
+	table.insert(wTouched, false)
+
+end
+
+function DrawBlip(gear)
+	SetVisualGearValues(getGearValue(gear,"CIRC"), getGearValue(gear,"RX"), getGearValue(gear,"RY"), 100, 255, 1, 10, 0, 40, 3, GetClanColor(GetHogClan(CurrentHedgehog))-rAlpha)
+end
+
+function TrackRadarBlip(gear)
+
+	-- work out the distance to the target
+	g1X, g1Y = GetGearPosition(CurrentHedgehog)
+	g2X, g2Y = GetX(gear), GetY(gear)
+	q = g1X - g2X
+	w = g1Y - g2Y
+	r = math.sqrt( (q*q) + (w*w) )	--alternate
+
+	RCX = getGearValue(gear,"RX")
+	RCY = getGearValue(gear,"RY")
+
+	rCircDistance = r -- distance to circle
+
+	opp = w
+	if opp < 0 then
+		opp = opp*-1
+	end
+
+	-- work out the angle (theta) to the target
+	t = math.deg ( math.asin(opp / r) )
+
+	-- based on the radius of the radar, calculate what x/y displacement should be
+	NR = 150 -- radius at which to draw circs
+	NX = math.cos( math.rad(t) ) * NR
+	NY = math.sin( math.rad(t) ) * NR
+
+	if rCircDistance < NR then
+		RCX = g2X
+	elseif q > 0 then
+		RCX = g1X - NX
+	else
+		RCX = g1X + NX
+	end
+
+	if rCircDistance < NR then
+		RCY = g2Y
+	elseif w > 0 then
+		RCY = g1Y - NY
+	else
+		RCY = g1Y + NY
+	end
+
+	setGearValue(gear, "RX", RCX)
+	setGearValue(gear, "RY", RCY)
+
+end
+
+
+function HandleCircles()
+
+	-- enable this if you want the radar to only show for a few seconds
+	-- after you spawn the crate
+	--[[if rAlpha ~= 255 then
+
+		rPingTimer = rPingTimer + 1
+		if rPingTimer == 100 then
+			rPingTimer = 0
+
+			rAlpha = rAlpha + 5
+			if rAlpha >= 255 then
+				rAlpha = 255
+			end
+		end
+
+	end]]
+
+	runOnGears(DrawBlip)
+
+	m2Count = m2Count + 1
+	if m2Count == 25 then
+		m2Count = 0
+
+		if (CurrentHedgehog ~= nil) and (rAlpha ~= 255) then
+			runOnGears(TrackRadarBlip)
+		end
+
+	end
+
+end
+
+
+function CheckCrateConditions()
+
+	crateSpawn = true
+
+	if requireSurfer == true then
+		if hasSurfed == false then
+			crateSpawn = false
+		end
+	end
+
+	if #wTouched > 0 then
+		if allWallsHit == false then
+			crateSpawn = false
+		end
+	end
+
+	if crateSpawn == true then
+		if allowCrate == true then
+		--if (crateG == nil) and (allowCrate == true) then
+			--AddCaption("")
+			SpawnAmmoCrate(0, 0, weapons[1+GetRandom(#weapons)] )
+			rPingTimer = 0
+			rAlpha = 0
+			PlaySound(sndWarp)
+		end
+	end
+
+end
+
+function CheckSurfer()
+
+	if GetY(CurrentHedgehog) > highestY then
+		highestY = GetY(CurrentHedgehog)
+	end
+
+	if (highestY == (WaterLine-8)) and (hasSurfed == false)  then
+
+		surferTimer = surferTimer +1
+		if (surferTimer == 40) then
+			hasSurfed = true
+			AddCaption(loc("Surfer!"),0xffba00ff,capgrpMessage2)
+		end
+	end
+
+end
+
+
+
+function WallHit(id, zXMin,zYMin, zWidth, zHeight)
+
+	if wTouched[id] == false then
+		tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtBigExplosion, 0, false)
+		PlaySound(sndExplosion)
+		wallsLeft = wallsLeft - 1
+
+		if wallsLeft == 0 then
+			AddCaption(loc("All walls touched!"))
+			allWallsHit = true
+			if (requireSurfer == true) and (hasSurfed == false) then
+				AddCaption(loc("Go surf!"),0xffba00ff,capgrpMessage2)
+			end
+		else
+			AddCaption(loc("Walls Left") .. ": " .. wallsLeft)
+		end
+
+	end
+
+	wTouched[id] = true
+	tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
+	--PlaySound(sndVaporize) -- yeah, this is just annoying as shit
+
+end
+
+function CheckForWallCollision()
+
+	for i = 1, #wTouched do
+		if gearIsInBox(CurrentHedgehog, wX[i],wY[i],wWidth[i],wHeight[i]) then
+			WallHit(i, wX[i],wY[i],wWidth[i],wHeight[i])
+		end
+	end
+
+end
+
+function BorderSpark(zXMin,zYMin, zWidth, zHeight, bCol)
+
+	eX = zXMin + GetRandom(zWidth+10)
+	eY = zYMin + GetRandom(zHeight+10)
+
+	tempE = AddVisualGear(eX, eY, vgtDust, 0, false)
+	if tempE ~= 0 then
+		g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
+		SetVisualGearValues(tempE, eX, eY, g3, g4, g5, g6, g7, 1, g9, bCol )
+	end
+
+end
+
+
+function HandleBorderEffects()
+
+	effectTimer = effectTimer + 1
+	if effectTimer > 15 then --25
+
+		effectTimer = 1
+
+		for i = 1, #wTouched do
+			if wTouched[i] == true then
+				--bCol = GetClanColor(GetHogClan(CurrentHedgehog))
+			else
+				--bCol = 0xFFFFFFFF
+				bCol = GetClanColor(GetHogClan(CurrentHedgehog))
+				BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
+			end
+			--BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
+		end
+
+	end
+
+end
+
+function onLJump()
+	if roundN < 2 then
+		roundN = 100
+		SetInputMask(0xFFFFFFFF)
+		TurnTimeLeft = 1
+		AddCaption(loc("Configuration accepted."),0xffba00ff,capgrpMessage)
+		HideMission()
+	end
+end
+
+function onAttack()
+
+	if roundN < 2 then
+
+		if menuIndex == 1 then
+
+			if #wTouched > 0 then
+				for i = 1, #wTouched do
+					wTouched[i] = nil
+					wX[i] = nil
+					wY[i] = nil
+					wWidth[i] = nil
+					wHeight[i] = nil
+				end
+			else
+				LoadConfig(2)
+			end
+
+		elseif menuIndex == 2 then
+			requireSurfer = not(requireSurfer)
+		elseif menuIndex == 3 then
+			AFR = not(AFR)
+		elseif menuIndex == 4 then
+			allowCrazyWeps = not(allowCrazyWeps)
+		end
+
+		UpdateMenu()
+		configureWeapons()
+		HandleStartingStage()
+
+	elseif (AFR == true) then
+
+		if (GetCurAmmoType() ~= amRope) and
+			(GetCurAmmoType() ~= amSkip) and
+			(GetCurAmmoType() ~= amNothing)
+		then
+			AddCaption(loc("You may only attack from a rope!"),0xffba00ff,capgrpMessage2)
+		end
+
+	end
+
+end
+
+function onDown()
+	if roundN < 2 then
+		menuIndex = menuIndex +1
+		if menuIndex > #menu then
+			menuIndex = 1
+		end
+		HandleStartingStage()
+	end
+end
+
+function onUp()
+	if roundN < 2 then
+		menuIndex = menuIndex -1
+		if 	menuIndex == 0 then
+			menuIndex = #menu
+		end
+		HandleStartingStage()
+	end
+end
+
+function onGameInit()
+
+	GameFlags = gfRandomOrder + gfBorder + gfSolidLand --+ gfInfAttack
+	HealthCaseProb = 0
+	CaseFreq = 0
+
+end
+
+function configureWeapons()
+
+	-- reset wep array
+	for i = 1, #weapons do
+		weapons[i] = nil
+	end
+
+	-- add rope weps
+	for i, w in pairs(ropeWeps) do
+        table.insert(weapons, w)
+	end
+
+	-- add ground weps
+	for i, w in pairs(groundWeps) do
+        table.insert(weapons, w)
+	end
+
+	-- remove ground weps if attacking from rope is mandatory
+	if AFR == true then
+		for i = 1, #weapons do
+			for w = 1, #groundWeps do
+				if groundWeps[w] == weapons[i] then
+					table.remove(weapons, i)
+				end
+			end
+		end
+	end
+
+	-- remove crazy weps is crazy weps aren't allowed
+	if allowCrazyWeps == false then
+		for i = 1, #weapons do
+			for w = 1, #crazyWeps do
+				if crazyWeps[w] == weapons[i] then
+					table.remove(weapons, i)
+				end
+			end
+		end
+	end
+
+end
+
+function onGameStart()
+
+	LoadConfig(1)
+	configureWeapons()
+	UpdateMenu()
+	HandleStartingStage()
+
+end
+
+function onNewTurn()
+
+	wallsLeft = #wTouched
+
+	for i = 1, #wTouched do
+		wTouched[i] = false
+	end
+
+	allowCrate = true
+
+	surferTimer = 0
+	hasSurfed = false
+	allWallsHit = false
+	highestY = 0
+
+	crateG = nil
+
+	-- new config stuff
+	roundN = roundN + 1
+	if roundN < 2 then
+		TurnTimeLeft = -1
+		SetInputMask(0)
+		allowCrate = false
+		HandleStartingStage() -- new
+	end
+
+end
+
+function UpdateMenu()
+
+	preMenuCfg = loc("Spawn the crate, and attack!") .. "|"
+	postMenuCfg = loc("Press [Enter] to accept this configuration.")
+
+	menu = 	{
+			loc("Walls Required") .. ": " .. #wTouched .. "|",
+			loc("Surf Before Crate") .. ": " .. BoolToCfgTxt(requireSurfer) .. "|",
+			loc("Attack From Rope") .. ": " .. BoolToCfgTxt(AFR) .. "|",
+			loc("Super Weapons") .. ": " .. BoolToCfgTxt(allowCrazyWeps) .. "|"
+			}
+end
+
+function HandleStartingStage()
+
+	temp = menu[menuIndex]
+	menu[menuIndex] = "--> " .. menu[menuIndex]
+
+	missionComment = ""
+	for i = 1, #menu do
+		missionComment = missionComment .. menu[i]
+	end
+
+	ShowMission	(
+				loc("WALL TO WALL") .. " 0.4",
+				loc("a shoppa minigame"),
+				preMenuCfg..
+				missionComment ..
+				postMenuCfg ..
+				--" " .. "|" ..
+				"", 4, 300000
+				)
+
+	menu[menuIndex] = temp
+
+end
+
+function onGameTick()
+
+	if CurrentHedgehog ~= nil then
+
+		--AddCaption(Map)
+		--AddCaption(RightX ..";" .. GetX(CurrentHedgehog))
+
+		CheckSurfer()
+
+		gTimer = gTimer + 1
+		if gTimer == 25 then
+			gTimer = 1
+
+			CheckForWallCollision()
+			CheckCrateConditions()
+
+			if (crateG == GetFollowGear()) and (crateG ~= nil) then
+				FollowGear(CurrentHedgehog)
+			end
+
+			-- if attackfromrope is set, forbid firing unless using rope
+			if (AFR == true) and (roundN >= 2) then
+				if (GetCurAmmoType() == amRope) or
+					(GetCurAmmoType() == amSkip) or
+					(GetCurAmmoType() == amNothing)
+				then
+					SetInputMask(0xFFFFFFFF)
+				elseif ropeG == nil then
+					SetInputMask(bnot(gmAttack))
+				end
+			end
+
+		end
+
+		HandleBorderEffects()
+		HandleCircles()
+
+	end
+
+end
+
+function onGearAdd(gear)
+
+	if GetGearType(gear) == gtRope then
+		ropeG = gear
+	elseif GetGearType(gear) == gtCase then
+
+		crateG = gear
+		trackGear(gear)
+
+		table.insert(rCirc, AddVisualGear(0,0,vgtCircle,0,true) )
+		setGearValue(gear,"CIRC",rCirc[#rCirc])
+		setGearValue(gear,"RX",0)
+		setGearValue(gear,"RY",0)
+		SetVisualGearValues(rCirc[#rCirc], 0, 0, 100, 255, 1, 10, 0, 40, 3, 0xff00ffff)
+
+		allowCrate = false
+
+		rPingTimer = 0
+		rAlpha = 0
+
+	end
+
+end
+
+function onGearDelete(gear)
+
+	if gear == ropeG then
+		ropeG = nil
+	elseif GetGearType(gear) == gtCase then
+
+		if gear == crateG then
+			crateG = nil
+		--	rAlpha = 255
+		end
+
+		for i = 1, #rCirc do
+			if rCirc[i] == getGearValue(gear,"CIRC") then
+				DeleteVisualGear(rCirc[i])
+				table.remove(rCirc, i)
+			end
+		end
+
+		trackDeletion(gear)
+
+	end
+
+end
+
+function onAmmoStoreInit()
+
+	for i, w in pairs(ropeWeps) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+    for i, w in pairs(groundWeps) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+    for i, w in pairs(crazyWeps) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+
+	SetAmmo(amRope, 9, 0, 0, 0)
+	SetAmmo(amSkip, 9, 0, 0, 0)
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Tracker.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,295 @@
+-- Library for keeping track of gears in the game
+-- and running functions on them
+-- also keeps track of clans and teams
+
+local trackingTeams = false
+local resurrecting = false
+local gears = {}
+local teams = {}
+local clans = {}
+local resurrectedHogs = {}
+local gearValues = {}
+local teamValues = {}
+local clanValues = {}
+
+-- Registers when a gear is added
+function trackGear(gear)
+    table.insert(gears, gear)
+    if trackingTeams and GetGearType(gear) == gtResurrector then
+        resurrecting = true
+    elseif resurrecting and GetGearType(gear) == gtHedgehog then
+        table.insert(resurrectedHogs, gear)
+    end
+end
+
+-- Registers when a gear is deleted
+function trackDeletion(gear)
+    gearValues[gear] = nil
+    for k, g in ipairs(gears) do
+        if g == gear then
+            table.remove(gears, k)
+            break
+        end
+    end
+    if trackingTeams and GetGearType(gear) == gtHedgehog then
+    	hogs = teams[GetHogTeamName(gear)]
+        if hogs ~= nil then
+            if table.maxn(hogs) == 1 then
+                hogs = nil
+            else
+				for k, hog in ipairs(hogs) do
+                    if hog == gear then
+                        table.remove(hogs, k)
+                        break
+                    end
+                end
+            end
+        end
+    elseif resurrecting and GetGearType(gear) == gtResurrector then
+        for k, gear in ipairs(resurrectedHogs) do
+            team = GetHogTeamName(gear)
+            if teams[team] == nil then
+                teams[team] = {}
+            end
+            table.insert(teams[team], gear)
+        end
+        resurrecting = false
+        resurrectedHogs = {}
+    end
+end
+
+-- Start to keep track of teams
+function trackTeams()
+    if not trackingTeams then
+        trackingTeams = true
+        for k, gear in ipairs(gears) do
+            if GetGearType(gear) == gtHedgehog then
+                team = GetHogTeamName(gear)
+                if teams[team] == nil then
+                    teams[team] = { gear }
+                    clans[team] = GetHogClan(gear)
+                else
+                    table.insert(teams[team], gear)
+                end
+            end
+        end
+    end
+end
+
+-- Registers when a hog is hidden
+function trackHiding(gear)
+    for k, g in ipairs(gears) do
+        if g == gear then
+            table.remove(gears, k)
+            break
+        end
+    end
+	
+    if trackingTeams then
+    	hogs = teams[GetHogTeamName(gear)]
+    	
+        if hogs ~= nil then
+            if table.maxn(hogs) == 1 then
+                hogs = nil
+            else
+                for k, hog in ipairs(hogs) do
+                    if hog == gear then
+                        table.remove(hogs, k)
+                        break
+                    end
+                end
+            end
+        end
+    end
+end
+
+-- Registers when a hog is restored
+function trackRestoring(gear)
+	table.insert(gears, gear)
+
+    if trackingTeams then
+        team = GetHogTeamName(gear)
+        if teams[team] == nil then
+            teams[team] = {}
+        end
+        table.insert(teams[team], gear)
+    end
+end
+
+-- Get a value for a specific gear
+function getGearValue(gear, key)
+    if gearValues[gear] ~= nil then
+        return gearValues[gear][key]
+    end
+    return nil
+end
+
+-- Set a value for a specific gear
+function setGearValue(gear, key, value)
+    found = false
+    for id, values in pairs(gearValues) do
+        if id == gear then
+            values[key] = value
+            found = true
+        end
+    end
+    if not found then
+        gearValues[gear] = { [key] = value }
+    end
+end
+
+-- Increase a value for a specific gear
+function increaseGearValue(gear, key)
+    for id, values in pairs(gearValues) do
+        if id == gear then
+            values[key] = values[key] + 1
+        end
+    end
+end
+
+-- Decrease a value for a specific gear
+function decreaseGearValue(gear, key)
+    for id, values in pairs(gearValues) do
+        if id == gear then
+            values[key] = values[key] - 1
+        end
+    end
+end
+
+-- Get a value for a specific team
+function getTeamValue(team, key)
+    if teamValues[team] ~= nil then
+        return teamValues[team][key]
+    end
+    return nil
+end
+
+-- Set a value for a specific team
+function setTeamValue(team, key, value)
+    found = false
+    for name, values in pairs(teamValues) do
+        if name == team then
+            values[key] = value
+            found = true
+        end
+    end
+    if not found then
+        teamValues[team] = { [key] = value }
+    end
+end
+
+-- Increase a value for a specific team
+function increaseTeamValue(team, key)
+    for name, values in pairs(teamValues) do
+        if name == team then
+            values[key] = values[key] + 1
+        end
+    end
+end
+
+-- Decrease a value for a specific team
+function decreaseTeamValue(team, key)
+    for name, values in pairs(teamValues) do
+        if name == team then
+            values[key] = values[key] - 1
+        end
+    end
+end
+
+-- Get a value for a specific clan
+function getClanValue(clan, key)
+    if clanValues[clan] ~= nil then
+        return clanValues[clan][key]
+    end
+    return nil
+end
+
+-- Set a value for a specific clan
+function setClanValue(clan, key, value)
+    found = false
+    for num, values in ipairs(clanValues) do
+        if num == clan then
+            values[key] = value
+            found = true
+        end
+    end
+    if not found then
+        clanValues[clan] = { [key] = value }
+    end
+end
+
+-- Increase a value for a specific clan
+function increaseClanValue(clan, key)
+    for num, values in ipairs(clanValues) do
+        if num == clan then
+            values[key] = values[key] + 1
+        end
+    end
+end
+
+-- Decrease a value for a specific clan
+function decreaseClanValue(clan, key)
+    for num, values in ipairs(clanValues) do
+        if num == clan then
+            values[key] = values[key] - 1
+        end
+    end
+end
+
+-- Run a function on all tracked gears
+function runOnGears(func)
+    for k, gear in ipairs(gears) do
+        func(gear)
+    end
+end
+
+-- Run a function on all tracked hogs
+function runOnHogs(func)
+    for k, hogs in pairs(teams) do
+        for m, hog in ipairs(hogs) do
+            func(hog)
+        end
+    end
+end
+
+-- Run a function on hogs in a team
+function runOnHogsInTeam(func, team)
+    if teams[team] ~= nil then
+        for k, hog in ipairs(teams[team]) do
+            func(hog)
+        end
+    end
+end
+
+-- Run a function on hogs in other teams
+function runOnHogsInOtherTeams(func, team)
+    for k, hogs in pairs(teams) do
+        if k ~= team then
+            for m, hog in ipairs(hogs) do
+                func(hog)
+            end
+        end
+    end
+end
+
+-- Run a function on hogs in a clan
+function runOnHogsInClan(func, clan)
+    for i = 1, table.maxn(clans) do
+        if clans[i] == clan then
+            for k, hog in ipairs(teams[i]) do
+                func(hog)
+            end
+        end
+    end
+end
+
+-- Run a function on hogs in other clans
+function runOnHogsInOtherClans(func, clan)
+    for i = 1, table.maxn(clans) do
+        if clans[i] ~= clan then
+            for k, hog in ipairs(teams[i]) do
+                func(hog)
+            end
+        end
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/Data/Scripts/Utils.lua	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,22 @@
+-- Library for miscellaneous utilitiy functions
+
+-- Check if a gear is inside a box
+function gearIsInBox(gear, x, y, w, h)
+    gx, gy = GetGearPosition(gear)
+    if gx >= x and gy >= y and gx <= x + w and gy <= y + h then
+        return true
+    end
+    return false
+end
+
+-- Check if a gear is inside a circle
+function gearIsInCircle(gear, x, y, r, useRadius)
+    gx, gy = GetGearPosition(gear)
+    if useRadius then
+        r = r + GetGearRadius(gear)
+    end
+    if r ^ 2 >= (x - gx) ^ 2 + (y - gy) ^ 2 then
+        return true
+    end
+    return false
+end
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/TARDIS.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/beep.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/beewater.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Amazing.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Brilliant.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Bugger.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Bungee.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Cutitout.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Drat.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Excellent.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Fire.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Gonnagetyou.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Grenade.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Hmm.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Justyouwait.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Leavemealone.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Ohdear.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Ouch.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Perfect.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Revenge.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Runaway.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Solong.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Thisoneismine.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Watchthis.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Whatthe.ogg has changed
Binary file project_files/Android-build/SDL-android-project/assets/Data/Sounds/voices/Classic/Whoopsee.ogg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/assets/assetsversion.txt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,1 @@
+8
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/jni/Android.mk	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/jni/Android.mk	Mon Aug 27 17:40:16 2012 +0200
@@ -6,5 +6,6 @@
 
 include $(CLEAR_VARS)
 include $(JNI_DIR)/../../../../misc/Android.mk
+include $(JNI_DIR)/../../../frontlib/Android.mk
 
 
--- a/project_files/Android-build/SDL-android-project/jni/SDL/src/main/android/SDL_android_main.cpp	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/jni/SDL/src/main/android/SDL_android_main.cpp	Mon Aug 27 17:40:16 2012 +0200
@@ -27,23 +27,20 @@
     char *argv[argc];
     jstring jstringArgv[argc];
     for(int i = 0; i < argc; i++){
-        jstringArgv[i] = (jstring)env->GetObjectArrayElement(strArray, i);  //get the element
-	argv[i] = (char*)malloc(sizeof(char) * env->GetStringLength(jstringArgv[i]));
-	strcpy(argv[i], env->GetStringUTFChars(jstringArgv[i], JNI_FALSE)); //copy it to a mutable location
-	//Don't release memory the JAVA GC will take care of it
-        //env->ReleaseStringChars(jstringArgv[i], (jchar*)argv[i]);           
+		jstringArgv[i] = (jstring)env->GetObjectArrayElement(strArray, i);  //get the element
+		argv[i] = (char*)malloc(env->GetStringUTFLength(jstringArgv[i]) + 1);
+		const char *str = env->GetStringUTFChars(jstringArgv[i], NULL);
+		strcpy(argv[i], str); //copy it to a mutable location
+        env->ReleaseStringUTFChars(jstringArgv[i], str);           
     }
     
     /* Run the application code! */
-    int status;
-    status = SDL_main(argc, argv);
+    int status = SDL_main(argc, argv);
 
     //Clean up argv
     for(int i = 0; i < argc; i++){
+		free(argv[i]);
     }
-
-    /* We exit here for consistency with other platforms. */
-    //exit(status); Xeli: Or lets not crash the entire app.....
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
Binary file project_files/Android-build/SDL-android-project/libs/android-support-v13.jar has changed
Binary file project_files/Android-build/SDL-android-project/libs/armeabi/libjnidispatch.so has changed
Binary file project_files/Android-build/SDL-android-project/libs/jna.jar has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-hdpi/button_local_play.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-hdpi/button_network_play.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-large-mdpi/button_local_play.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-large-mdpi/button_network_play.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/backbutton.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/button.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/button_focused" />
+    <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/button_focused_disabled" />
+    <item android:state_focused="false" android:state_enabled="true" android:drawable="@drawable/button_normal" />
+    <item android:state_focused="false" android:state_enabled="false" android:drawable="@drawable/button_disabled" />
+</selector>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/button_disabled.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/button_focused.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/button_focused_disabled.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/button_normal.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown.9.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/dropdown_focused" />
+    <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/dropdown_focused_disabled" />
+    <item android:state_focused="false" android:state_enabled="true" android:drawable="@drawable/dropdown_normal" />
+    <item android:state_focused="false" android:state_enabled="false" android:drawable="@drawable/dropdown_disabled" />
+</selector>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown_disabled.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown_focused.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown_focused_disabled.9.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/dropdown_normal.9.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,10 @@
+ <level-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:maxLevel="1" android:drawable="@drawable/hogcount1" />
+  <item android:maxLevel="2" android:drawable="@drawable/hogcount2" />
+  <item android:maxLevel="3" android:drawable="@drawable/hogcount3" />
+  <item android:maxLevel="4" android:drawable="@drawable/hogcount4" />
+  <item android:maxLevel="5" android:drawable="@drawable/hogcount5" />
+  <item android:maxLevel="6" android:drawable="@drawable/hogcount6" />
+  <item android:maxLevel="7" android:drawable="@drawable/hogcount7" />
+  <item android:maxLevel="8" android:drawable="@drawable/hogcount8" />
+ </level-list>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount1.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount2.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount3.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount4.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount5.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount6.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount7.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/hogcount8.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/lightbulb_off.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/lightbulb_on.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/playerlist_player.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_ingame.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/roomlist_preparing.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/savebutton.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_local_by_level.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:maxLevel="0" android:drawable="@drawable/human" />
+	<item android:maxLevel="1" android:drawable="@drawable/bot5" />
+	<item android:maxLevel="2" android:drawable="@drawable/bot4" />
+	<item android:maxLevel="3" android:drawable="@drawable/bot3" />
+	<item android:maxLevel="4" android:drawable="@drawable/bot2" />
+	<item android:maxLevel="5" android:drawable="@drawable/bot1" />
+</level-list>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot1.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot2.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot3.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot4.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_bot5.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_by_level.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:maxLevel="0" android:drawable="@drawable/team_net_human" />
+	<item android:maxLevel="1" android:drawable="@drawable/team_net_bot5" />
+	<item android:maxLevel="2" android:drawable="@drawable/team_net_bot4" />
+	<item android:maxLevel="3" android:drawable="@drawable/team_net_bot3" />
+	<item android:maxLevel="4" android:drawable="@drawable/team_net_bot2" />
+	<item android:maxLevel="5" android:drawable="@drawable/team_net_bot1" />
+</level-list>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/team_net_human.png has changed
--- a/project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
- <level-list xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:maxLevel="0" android:drawable="@drawable/teams_number0" />
-  <item android:maxLevel="1" android:drawable="@drawable/teams_number1" />
-  <item android:maxLevel="2" android:drawable="@drawable/teams_number2" />
-  <item android:maxLevel="3" android:drawable="@drawable/teams_number3" />
-  <item android:maxLevel="4" android:drawable="@drawable/teams_number4" />
-  <item android:maxLevel="5" android:drawable="@drawable/teams_number5" />
-  <item android:maxLevel="6" android:drawable="@drawable/teams_number6" />
-  <item android:maxLevel="7" android:drawable="@drawable/teams_number7" />
-  <item android:maxLevel="8" android:drawable="@drawable/teams_number8" />
-  <item android:maxLevel="9" android:drawable="@drawable/teams_number9" />
- </level-list>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount0.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount1.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount2.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount3.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount4.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount5.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount6.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount7.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount8.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-mdpi/teamcount9.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/drawable-mdpi/teams_number.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,12 @@
+ <level-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:maxLevel="0" android:drawable="@drawable/teams_number0" />
+  <item android:maxLevel="1" android:drawable="@drawable/teams_number1" />
+  <item android:maxLevel="2" android:drawable="@drawable/teams_number2" />
+  <item android:maxLevel="3" android:drawable="@drawable/teams_number3" />
+  <item android:maxLevel="4" android:drawable="@drawable/teams_number4" />
+  <item android:maxLevel="5" android:drawable="@drawable/teams_number5" />
+  <item android:maxLevel="6" android:drawable="@drawable/teams_number6" />
+  <item android:maxLevel="7" android:drawable="@drawable/teams_number7" />
+  <item android:maxLevel="8" android:drawable="@drawable/teams_number8" />
+  <item android:maxLevel="9" android:drawable="@drawable/teams_number9" />
+ </level-list>
\ No newline at end of file
Binary file project_files/Android-build/SDL-android-project/res/drawable-small-hdpi/button_local_play.png has changed
Binary file project_files/Android-build/SDL-android-project/res/drawable-small-hdpi/button_network_play.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-large/activity_lobby.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+  	android:layout_width="fill_parent"
+	android:layout_height="fill_parent">
+	<include layout="@layout/background"/>
+
+	<LinearLayout
+	    android:layout_width="match_parent"
+	    android:layout_height="match_parent"
+	    android:orientation="vertical"
+	    android:padding="5dp" >
+	
+	    <FrameLayout
+	        android:layout_width="fill_parent"
+	        android:layout_height="0dp"
+	        android:layout_marginBottom="10dp"
+	        android:layout_weight="0.4"
+	        android:background="@drawable/box" >
+	
+	        <fragment
+	            android:id="@+id/roomListFragment"
+	            android:layout_width="fill_parent"
+	            android:layout_height="fill_parent"
+	            class="org.hedgewars.hedgeroid.RoomlistFragment"
+	            tools:layout="@layout/fragment_roomlist" />
+	    </FrameLayout>
+	
+	    <RelativeLayout
+	        android:layout_width="fill_parent"
+	        android:layout_height="0dp"
+	        android:layout_weight="0.6"
+	        android:baselineAligned="false"
+	        android:orientation="horizontal" >
+	
+	        <FrameLayout
+	            android:id="@+id/playerFrame"
+	            android:layout_width="250dp"
+	            android:layout_height="fill_parent"
+	            android:layout_alignParentRight="true"
+	            android:background="@drawable/box" >
+	
+	            <fragment
+	                android:id="@+id/playerListFragment"
+	                android:layout_width="fill_parent"
+	                android:layout_height="fill_parent"
+	                class="org.hedgewars.hedgeroid.LobbyPlayerlistFragment"
+	                tools:layout="@layout/fragment_playerlist" />
+	        </FrameLayout>
+	        
+	        <FrameLayout
+	            android:layout_width="0dp"
+	            android:layout_height="fill_parent"
+	            android:layout_alignParentLeft="true"
+	            android:layout_toLeftOf="@id/playerFrame"
+	            android:layout_marginRight="10dp"
+	            android:background="@drawable/box" >
+	
+	            <fragment
+	                android:id="@+id/chatFragment"
+	                android:layout_width="fill_parent"
+	                android:layout_height="fill_parent"
+	                class="org.hedgewars.hedgeroid.ChatFragment"
+	                tools:layout="@layout/fragment_chat" />
+	        </FrameLayout>
+	    </RelativeLayout>
+	
+	</LinearLayout>
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-large/activity_localroom.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:padding="2dp" >
+
+        <LinearLayout
+            android:id="@+id/upperFrame"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginBottom="4dp"
+            android:layout_above="@+id/startGame"
+            android:baselineAligned="false"
+            android:minHeight="200dp" >
+
+            <FrameLayout
+                android:id="@+id/mapFrame"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_marginRight="4dp"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/mapFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.MapFragment"
+                    tools:layout="@layout/fragment_map" />
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/settingsFrame"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_marginRight="4dp"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/settingsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.SettingsFragment"
+                    tools:layout="@layout/fragment_settings" />
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/teamsFrame"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/teamsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.TeamlistFragment"
+                    tools:layout="@layout/fragment_teamlist" />
+            </FrameLayout>
+        </LinearLayout>
+
+        <Button
+            android:id="@id/startGame"
+            android:layout_width="200dp"
+            android:layout_height="67dp"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:background="@drawable/startgamebutton" />
+    </RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-large/activity_netroom.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:padding="2dp" >
+
+        <LinearLayout
+            android:id="@+id/upperFrame"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginBottom="4dp"
+            android:baselineAligned="false"
+            android:minHeight="200dp" >
+            
+            <FrameLayout
+                android:id="@+id/mapFrame"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="4dp"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/mapFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.MapFragment"
+                    tools:layout="@layout/fragment_map" />
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/settingsFrame"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="4dp"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/settingsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.SettingsFragment"
+                    tools:layout="@layout/fragment_settings" />
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/teamsFrame"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:background="@drawable/box" >
+
+                <fragment
+                    android:id="@+id/teamsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.TeamlistFragment"
+                    tools:layout="@layout/fragment_teamlist" />
+            </FrameLayout>
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/playerFrame"
+            android:layout_width="200dp"
+            android:layout_height="fill_parent"
+            android:layout_above="@+id/startGame"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/upperFrame"
+            android:background="@drawable/box" >
+
+            <fragment
+                android:id="@+id/playerListFragment"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                class="org.hedgewars.hedgeroid.RoomPlayerlistFragment"
+                tools:layout="@layout/fragment_playerlist" />
+        </FrameLayout>
+
+        <FrameLayout
+            android:layout_width="0dp"
+            android:layout_height="fill_parent"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentLeft="true"
+            android:layout_below="@id/upperFrame"
+            android:layout_marginRight="4dp"
+            android:layout_toLeftOf="@id/playerFrame"
+            android:background="@drawable/box" >
+
+            <fragment
+                android:id="@+id/chatFragment"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                class="org.hedgewars.hedgeroid.ChatFragment"
+                tools:layout="@layout/fragment_chat" />
+        </FrameLayout>
+
+        <Button
+            android:id="@id/startGame"
+            android:layout_width="200dp"
+            android:layout_height="67dp"
+            android:layout_marginTop="4dp"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentRight="true"
+            android:background="@drawable/startgamebutton" />
+
+    </RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-large/fragment_roomlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <FrameLayout
+        android:id="@+id/listHeader"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" >
+	    <include layout="@layout/listview_room_header" />
+    </FrameLayout>
+
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_alignParentBottom="true"
+        android:layout_below="@+id/listHeader"
+        android:cacheColorHint="@android:color/transparent"
+        android:drawSelectorOnTop="false"
+        tools:listitem="@layout/listview_room" />
+
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:text="@string/no_rooms_in_list" />
+
+</RelativeLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-large/listview_room.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="fill_parent"
+	android:layout_height="wrap_content" 
+	android:paddingTop="4dp"
+	android:paddingBottom="4dp">
+	
+<TextView
+    android:id="@+id/roomname"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_vertical"
+    android:layout_weight="1.5"
+    android:padding="3dp"
+    android:drawablePadding="5dp"
+    android:gravity="left|center_vertical"
+    android:singleLine="true"
+    android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+<include layout="@layout/roomlist_player_team_count" />
+
+<TextView
+    android:id="@+id/owner"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="left"
+	android:layout_gravity="center_vertical"
+	android:singleLine="true"/>
+
+<TextView
+    android:id="@+id/map"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="left"
+	android:layout_gravity="center_vertical"
+	android:singleLine="true"/>
+
+<TextView
+    android:id="@+id/scheme"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="left"
+	android:layout_gravity="center_vertical"
+	android:singleLine="true"/>
+
+<TextView
+    android:id="@+id/weapons"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="left"
+	android:layout_gravity="center_vertical"
+	android:singleLine="true"/>
+
+</LinearLayout>
+    
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-xlarge/roomlist_player_team_count.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    
+	<TextView
+	    android:id="@+id/playercount"
+		android:layout_width="20dp"
+		android:layout_height="wrap_content"
+		android:padding="3dp"
+		android:gravity="center" 
+		android:layout_gravity="center_vertical"
+		android:singleLine="true"/>
+	
+	<TextView
+	    android:id="@+id/teamcount"
+		android:layout_width="20dp"
+		android:layout_height="wrap_content"
+		android:padding="3dp"
+		android:gravity="center"
+		android:layout_gravity="center_vertical"
+		android:singleLine="true"/>
+</merge>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout-xlarge/roomlist_player_team_count_header.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    
+	<TextView
+	    android:id="@+id/playercount"
+		android:layout_width="20dp"
+		android:layout_height="wrap_content"
+		android:padding="3dp"
+		android:gravity="center" 
+		android:singleLine="true"
+		android:textAppearance="?android:attr/textAppearanceMedium"
+		android:text="@string/roomlist_header_clients"/>
+	
+	<TextView
+	    android:id="@+id/teamcount"
+		android:layout_width="20dp"
+		android:layout_height="wrap_content"
+		android:padding="3dp"
+		android:gravity="center"
+		android:singleLine="true"
+		android:textAppearance="?android:attr/textAppearanceMedium"
+		android:text="@string/roomlist_header_teams" />
+</merge>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_lobby.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <TabHost
+        android:id="@android:id/tabhost"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal" >
+
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_weight="0" />
+
+            <FrameLayout
+                android:id="@android:id/tabcontent"
+                android:layout_width="0dip"
+                android:layout_height="match_parent"
+                android:layout_weight="1" >
+
+                <fragment
+                    android:id="@+id/roomListFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.RoomlistFragment"
+                    tools:layout="@layout/fragment_roomlist" />
+
+                <fragment
+                    android:id="@+id/chatFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.ChatFragment"
+                    tools:layout="@layout/fragment_chat" />
+
+                <fragment
+                    android:id="@+id/playerListFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.LobbyPlayerlistFragment"
+                    tools:layout="@layout/fragment_playerlist" />
+            </FrameLayout>
+        </LinearLayout>
+    </TabHost>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_localroom.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <TabHost
+        android:id="@android:id/tabhost"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal" >
+
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_weight="0" />
+
+            <FrameLayout
+                android:id="@android:id/tabcontent"
+                android:layout_width="0dip"
+                android:layout_height="match_parent"
+                android:layout_weight="1" >
+
+                <fragment
+                    android:id="@+id/mapFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.MapFragment"
+                    tools:layout="@layout/fragment_map" />
+
+                <fragment
+                    android:id="@+id/settingsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.SettingsFragment"
+                    tools:layout="@layout/fragment_settings" />
+
+                
+                <LinearLayout
+                    android:id="@+id/teamlistContainer"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    android:orientation="vertical" >
+	                <fragment
+	                    android:id="@+id/teamlistFragment"
+	                    android:layout_width="fill_parent"
+	                    android:layout_height="0dp"
+	                    android:layout_weight="1"
+	                    class="org.hedgewars.hedgeroid.TeamlistFragment"
+	                    tools:layout="@layout/fragment_teamlist" />
+
+                    <Button
+                        android:id="@+id/startGame"
+                        android:layout_width="120dp"
+                        android:layout_height="40dp"
+                        android:layout_gravity="right"
+                        android:layout_marginTop="4dp"
+                        android:background="@drawable/startgamebutton" />
+                </LinearLayout>
+            </FrameLayout>
+        </LinearLayout>
+    </TabHost>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_main.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" >
+
+        <View
+            android:id="@+id/placeholder"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_centerInParent="true" />
+
+        <FrameLayout
+            android:id="@+id/frameLayout1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_toLeftOf="@id/placeholder" >
+
+            <Button
+                android:id="@+id/startGame"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:drawableTop="@drawable/button_local_play"
+                android:text="@string/main_button_localplay" />
+        </FrameLayout>
+
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentTop="true"
+            android:layout_toRightOf="@id/placeholder" >
+
+            <Button
+                android:id="@+id/joinLobby"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:drawableTop="@drawable/button_network_play"
+                android:text="@string/main_button_netplay" />
+        </FrameLayout>
+    </RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_netroom.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <include layout="@layout/background" />
+
+    <TabHost
+        android:id="@android:id/tabhost"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal" >
+
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_weight="0" />
+
+            <FrameLayout
+                android:id="@android:id/tabcontent"
+                android:layout_width="0dip"
+                android:layout_height="match_parent"
+                android:layout_weight="1" >
+
+                <fragment
+                    android:id="@+id/mapFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.MapFragment"
+                    tools:layout="@layout/fragment_map" />
+
+                <fragment
+                    android:id="@+id/settingsFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.SettingsFragment"
+                    tools:layout="@layout/fragment_settings" />
+
+                <fragment
+                    android:id="@+id/teamlistFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.TeamlistFragment"
+                    tools:layout="@layout/fragment_teamlist" />
+
+                <fragment
+                    android:id="@+id/chatFragment"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    class="org.hedgewars.hedgeroid.ChatFragment"
+                    tools:layout="@layout/fragment_chat" />
+
+                <LinearLayout
+                    android:id="@+id/playerListContainer"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    android:orientation="vertical" >
+
+                    <fragment
+                        android:id="@+id/playerListFragment"
+                        android:layout_width="fill_parent"
+                        android:layout_height="0dp"
+                        android:layout_weight="1"
+                        class="org.hedgewars.hedgeroid.RoomPlayerlistFragment"
+                        tools:layout="@layout/fragment_playerlist" />
+
+                    <Button
+                        android:id="@+id/startGame"
+                        android:layout_width="120dp"
+                        android:layout_height="40dp"
+                        android:layout_gravity="right"
+                        android:layout_marginTop="4dp"
+                        android:background="@drawable/startgamebutton" />
+                </LinearLayout>
+            </FrameLayout>
+        </LinearLayout>
+    </TabHost>
+
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_schemelist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <ListView 
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" 
+        android:cacheColorHint="@android:color/transparent" />
+
+	<Button
+        android:id="@+id/addButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/schemelist_add_button_text"
+        android:background="@drawable/button" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_teamlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="fill_parent"
+  android:layout_height="fill_parent">
+  
+  <include layout="@layout/background"/>
+
+  <TextView
+    android:id="@android:id/empty"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:text="@string/teamlist_empty" />
+  
+  <ListView
+  	android:id="@android:id/list"
+  	android:layout_height="fill_parent"
+  	android:layout_width="wrap_content"
+  	android:layout_margin="3dp"
+  	android:background="@drawable/box" />
+
+  <ImageButton
+   	android:id="@+id/btnAdd"
+   	android:layout_width="wrap_content"
+   	android:layout_height="50dip"
+   	android:layout_alignParentBottom="true"
+   	android:layout_alignParentRight="true"
+   	android:adjustViewBounds="true"
+   	android:scaleType="centerInside"
+   	android:background="@android:color/transparent"
+   	android:src="@drawable/settings"
+   	android:contentDescription="@string/teamlist_add_content_description"/>
+
+</RelativeLayout>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/activity_weaponsetlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="@string/weaponsetlist_empty" />
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:cacheColorHint="@android:color/transparent" />
+
+    <Button
+        android:id="@+id/addButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/button"
+        android:text="@string/weaponsetlist_add_button_text" />
+
+</LinearLayout>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/backbutton.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <ImageButton
-    	android:id="@+id/btnBack"
-    	android:layout_width="120dip"
-    	android:layout_height="40dip"
-    	android:layout_alignParentBottom="true"
-    	android:layout_alignParentLeft="true"
-    	android:adjustViewBounds="true"
-    	android:scaleType="centerInside"
-    	android:background="@android:color/transparent"
-    	android:src="@drawable/backbutton"/>
-</merge>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/config.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
-    	
-    <ListView
-    	android:id="@+id/listView"
-    	android:layout_width="wrap_content"
-    	android:layout_height="fill_parent"/>
-    	
-</RelativeLayout>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_chat.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <ListView 
+        android:id="@+id/chatConsole"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" 
+        android:clickable="false"
+        android:cacheColorHint="@android:color/transparent"
+        android:transcriptMode="normal"
+        android:focusableInTouchMode="false"
+        android:focusable="false"
+        android:longClickable="false"
+        android:stackFromBottom="true"
+        />
+
+	<EditText
+        android:id="@+id/chatInput"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/chat_hint"
+        android:imeOptions="actionSend"
+        android:inputType="text" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_map.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="3dp"
+    android:paddingLeft="5dp"
+    android:paddingRight="3dp"
+    android:paddingTop="3dp" >
+
+    <ImageView
+        android:id="@+id/mapPreview"
+        android:layout_width="256dip"
+        android:layout_height="128dip"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:background="@drawable/box"
+        android:scaleType="fitCenter"
+        android:src="@drawable/roomlist_preparing" />
+
+    <TableLayout
+        android:id="@+id/gameOptions"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/mapPreview"
+        android:stretchColumns="1" >
+
+        <TableRow android:layout_marginTop="5dip" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/map_gen" />
+
+            <Spinner
+                android:id="@+id/spinMapType"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft = "5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <TableRow
+            android:id="@+id/rowMapName"
+            android:layout_marginTop="5dip" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/map_name" />
+
+            <Spinner
+                android:id="@+id/spinMapName"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft = "5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <TableRow
+            android:id="@+id/rowTemplateFilter"
+            android:layout_marginTop="5dip"
+            android:visibility="gone" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/map_template" />
+
+            <Spinner
+                android:id="@+id/spinTemplateFilter"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft = "5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <TableRow
+            android:id="@+id/rowMazeSize"
+            android:layout_marginTop="5dip"
+            android:visibility="gone" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/map_maze_size" />
+
+            <Spinner
+                android:id="@+id/spinMazeSize"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft = "5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <Button
+            android:id="@+id/btnEditDrawnMap"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="5dip"
+            android:background="@drawable/button"
+            android:enabled="false"
+            android:text="@string/map_button_editdrawnmap"
+            android:visibility="gone" />
+    </TableLayout>
+
+</RelativeLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_playerlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        tools:listitem="@layout/listview_player" />
+
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="@string/no_players_in_list" />
+</FrameLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_roomlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingLeft="8dp"
+    android:paddingRight="8dp" >
+
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        tools:listitem="@layout/listview_room" />
+
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/no_rooms_in_list" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_settings.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingBottom="3dp"
+    android:paddingLeft="5dp"
+    android:paddingRight="3dp"
+    android:paddingTop="3dp" >
+
+    <TableLayout
+        android:id="@+id/gameOptions"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:stretchColumns="1" >
+
+        <TableRow>
+
+            <TextView
+                android:id="@+id/txtGameplay"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/start_gameplay" />
+
+            <Spinner
+                android:id="@+id/spinGameplay"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <TableRow android:layout_marginTop="5dip" >
+
+            <TextView
+                android:id="@+id/txtGamescheme"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/start_gamescheme" />
+
+            <Spinner
+                android:id="@+id/spinGamescheme"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+
+        <TableRow android:layout_marginTop="5dip" >
+
+            <TextView
+                android:id="@+id/txtweapons"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/start_weapons" />
+
+            <Spinner
+                android:id="@+id/spinweapons"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="5dip"
+                android:background="@drawable/dropdown" />
+        </TableRow>
+    </TableLayout>
+
+    <ImageView
+        android:id="@+id/imgTheme"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignBottom="@+id/spinTheme"
+        android:layout_alignLeft="@id/gameOptions"
+        android:layout_alignTop="@id/spinTheme"
+        android:adjustViewBounds="true" />
+
+    <Spinner
+        android:id="@id/spinTheme"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_below="@id/gameOptions"
+        android:layout_marginTop="5dip"
+        android:layout_toRightOf="@+id/imgTheme"
+        android:background="@drawable/dropdown" />
+
+</RelativeLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/fragment_teamlist.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="3dp"
+    android:paddingLeft="5dp"
+    android:paddingRight="3dp"
+    android:paddingTop="3dp" >
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:cacheColorHint="@android:color/transparent" />
+
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:text="@string/no_teams_in_list" />
+    
+    <Button
+        android:id="@+id/addTeamButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/button"
+        android:text="@string/teamlist_addteam" />
+
+</LinearLayout>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/listview_item.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/listview_item.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
 	android:layout_width="fill_parent"
 	android:layout_height="wrap_content"
 	android:textSize="10dip"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/listview_player.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="4dp"
+	android:paddingBottom="4dp"
+    android:drawablePadding="5dp"
+    android:drawableLeft="@drawable/playerlist_player"
+    android:gravity="center_vertical|left"
+    android:singleLine="true"
+    android:textAppearance="?android:attr/textAppearanceMedium" />
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/listview_room.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:mode="twoLine"
+    android:paddingTop="4dp"
+	android:paddingBottom="4dp">
+
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:drawablePadding="5dp"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <TextView
+        android:id="@android:id/text2"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignLeft="@android:id/text1"
+        android:layout_below="@android:id/text1"
+        android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</TwoLineListItem>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/listview_room_header.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="fill_parent"
+	android:layout_height="wrap_content" >
+	
+<TextView
+    android:id="@+id/roomname"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_vertical"
+    android:layout_weight="1.5"
+    android:padding="3dp"
+    android:gravity="center"
+    android:singleLine="true"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:text="@string/roomlist_header_roomname" />
+
+<include layout="@layout/roomlist_player_team_count_header" />
+
+<TextView
+    android:id="@+id/owner"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="center"
+	android:singleLine="true"
+	android:textAppearance="?android:attr/textAppearanceMedium"
+	android:text="@string/roomlist_header_owner" />
+
+<TextView
+    android:id="@+id/map"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="center"
+	android:singleLine="true"
+	android:textAppearance="?android:attr/textAppearanceMedium"
+	android:text="@string/roomlist_header_map" />
+
+<TextView
+    android:id="@+id/scheme"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="center"
+	android:singleLine="true"
+	android:textAppearance="?android:attr/textAppearanceMedium"
+	android:text="@string/roomlist_header_scheme" />
+
+<TextView
+    android:id="@+id/weapons"
+	android:layout_width="0dp"
+	android:layout_height="wrap_content"
+	android:layout_weight="0.5"
+	android:padding="3dp"
+	android:gravity="center"
+	android:singleLine="true"
+	android:textAppearance="?android:attr/textAppearanceMedium"
+	android:text="@string/roomlist_header_weapons" />
+
+</LinearLayout>
+    
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/listview_team.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="4dp"
+	android:paddingBottom="4dp" >
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical|left"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:drawablePadding="5dp"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <ImageButton
+        android:id="@+id/colorButton"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_gravity="center_vertical|right"
+        android:src="#fff"
+        android:padding="8dp"
+        android:contentDescription="@string/teamlist_color_button_description" />
+    
+    <ImageButton
+        android:id="@+id/hogCountButton"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_gravity="center_vertical|right"
+        android:src="@drawable/hogcount"
+        android:scaleType="centerCrop"
+        android:padding="0dp"
+        android:contentDescription="@string/teamlist_hogcount_button_description" />
+</LinearLayout>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/main.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-	xmlns:android="http://schemas.android.com/apk/res/android"
-  	android:layout_width="fill_parent"
-	android:layout_height="fill_parent">
-	<include layout="@layout/background"/>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    >    
-    <Button
-    	android:id="@+id/downloader"
-    	android:layout_width="wrap_content"
-    	android:layout_height="wrap_content"
-    	android:text="downloader"/>
-    	
-    <Button
-    	android:id="@+id/startGame"
-    	android:layout_width="wrap_content"
-    	android:layout_height="wrap_content"
-    	android:text="startgame"/>
-    	
-</LinearLayout>
-</FrameLayout>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/savebutton.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <ImageButton
-    	android:id="@+id/btnSave"
-    	android:layout_width="120dip"
-    	android:layout_height="40dip"
-    	android:layout_alignParentBottom="true"
-    	android:layout_alignParentRight="true"
-    	android:adjustViewBounds="true"
-    	android:scaleType="centerInside"
-    	android:background="@android:color/transparent"
-    	android:src="@drawable/savebutton"/>
-</merge>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/starting_game.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
-    <include
-    	layout="@layout/background"/>
-     
-    <ImageView
-    	android:id="@+id/mapPreview"
-    	android:layout_width="256dip"
-    	android:layout_height="128dip"
-    	android:layout_margin="5dip"
-    	android:scaleType="fitXY"
-    	android:background="@drawable/box"
-    	android:src="@drawable/backbutton"/>
-    
-    <Spinner 
-       	android:id="@+id/spinMaps"
-       	android:layout_height="wrap_content"
-       	android:layout_width="wrap_content"    
-       	android:layout_below="@id/mapPreview"
-       	android:layout_alignRight="@id/mapPreview"
-       	android:layout_toRightOf="@+id/txtMap"
-       	android:background="@drawable/dropdown"/>
-    <TextView
-		android:id="@id/txtMap"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:text="@string/start_map"
-		android:layout_alignTop="@id/spinMaps"
-		android:layout_alignBottom="@id/spinMaps"
-		android:layout_alignLeft="@id/mapPreview"
-		android:gravity="center"/>	
-   	
-    <TableLayout 
-       	android:id="@+id/gameOptions" 
-       	android:layout_height="wrap_content" 
-       	android:layout_width="wrap_content"
-       	android:layout_centerHorizontal="true"
-       	android:layout_toRightOf="@id/mapPreview" 
-       	android:layout_alignParentRight="true"
-       	android:padding="3dip"
-       	android:layout_margin="5dip"
-       	android:background="@drawable/box"
-       	android:stretchColumns="0,2"
-       	android:shrinkColumns="1">
-	       	
-       	<TableRow>
-        	 <TextView 
-		        android:id="@+id/txtGameplay"
-		        android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:text="@string/start_gameplay"/>
-        	<Spinner
-	        	android:id="@+id/spinGameplay"
-	        	android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:background="@drawable/dropdown"
-		        />
-		</TableRow>  
-		<TableRow>
-		    <TextView 
-		        android:id="@+id/txtGamescheme"
-		        android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:text="@string/start_gamescheme"/>
-		    <Spinner
-		       	android:id="@+id/spinGamescheme"
-		       	android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:background="@drawable/dropdown"/>
-		    <ImageButton
-		    	android:id="@+id/btnGamescheme"
-		    	android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:background="@drawable/edit"
-		        android:adjustViewBounds="true"
-		        android:scaleType="centerInside"
-		        android:layout_gravity="center"
-		        android:padding="3dip"/>
-		 </TableRow>
-		 <TableRow>    
-		     <TextView 
-		        android:id="@+id/txtweapons"
-		        android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:layout_below="@id/txtGamescheme"
-		        android:layout_marginTop="5dip"
-		        android:text="@string/start_weapons"/>
-	        
-	        <Spinner
-	        	android:id="@+id/spinweapons"
-	        	android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:background="@drawable/dropdown"/>
-		    
-		    <ImageButton
-		    	android:id="@+id/btnweapons"
-		    	android:layout_height="wrap_content"
-		        android:layout_width="wrap_content"
-		        android:background="@drawable/edit"
-		        android:adjustViewBounds="true"
-		        android:scaleType="centerInside"
-		        android:layout_gravity="center"
-		        android:padding="3dip"/>
-        </TableRow>	
-    </TableLayout>
-        
-    <ImageView 
-       	android:id="@+id/imgTheme"
-       	android:layout_height="wrap_content" 
-       	android:layout_width="wrap_content"
-       	android:layout_alignTop="@+id/spinTheme"
-       	android:layout_alignBottom="@id/spinTheme"
-       	android:layout_alignLeft="@id/gameOptions"
-       	android:adjustViewBounds="true"/>
-       
-    <Spinner
-        android:id="@id/spinTheme"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content" 
-        android:layout_toRightOf="@+id/imgTheme"
-        android:layout_alignParentRight="true"
-       	android:layout_below="@id/gameOptions"
-       	android:background="@drawable/dropdown"/>
-        
-	<include layout="@layout/backbutton"/>
-    
-    <LinearLayout
-    	android:layout_width="wrap_content"
-    	android:layout_height="wrap_content"
-    	android:layout_alignParentBottom="true"
-    	android:layout_centerHorizontal="true"
-    	android:orientation="horizontal">
-    <ImageButton
-    	android:id="@+id/btnTeams"
-    	android:layout_width="120dip"
-    	android:layout_height="40dip"
-    	android:adjustViewBounds="true"
-    	android:scaleType="centerInside"
-    	android:background="@android:color/transparent"
-    	android:src="@drawable/teams"/>
-    <ImageView
-    	android:id="@+id/imgTeamsCount"
-    	android:layout_width="40dip"
-    	android:layout_height="40dip"
-    	android:adjustViewBounds="true"
-    	android:scaleType="centerInside"
-    	android:background="@android:color/transparent"
-    	android:src="@drawable/teamcount"/>
-        
-    </LinearLayout>
-
-    <ImageButton
-    	android:id="@+id/btnStart"
-    	android:layout_width="120dip"
-    	android:layout_height="40dip"
-    	android:layout_alignParentBottom="true"
-    	android:layout_alignParentRight="true"
-    	android:adjustViewBounds="true"
-    	android:scaleType="centerInside"
-    	android:background="@android:color/transparent"
-    	android:src="@drawable/startgamebutton"/>
-
-</RelativeLayout>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/layout/tab_indicator_vertical.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,23 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="0dp"
+    android:layout_weight="1"
+    android:orientation="vertical"
+    android:background="@drawable/box">
+
+    <ImageView android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+    />
+
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:scrollHorizontally="false"
+        android:padding="4dp"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        style="?android:attr/tabWidgetStyle"
+    />
+</RelativeLayout>
--- a/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -15,8 +15,6 @@
 	  	android:layout_width="fill_parent"
 	  	android:layout_height="fill_parent"
 	  	android:layout_weight="1">
-	  	<include layout="@layout/backbutton"/>
-	  	<include layout="@layout/savebutton"/>
 	  	<ScrollView
 		  	android:layout_width="fill_parent"
 		  	android:layout_height="fill_parent"
--- a/project_files/Android-build/SDL-android-project/res/layout/team_selection_dialog.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:orientation="vertical"
-  android:layout_width="wrap_content"
-  android:layout_height="wrap_content">
-  <TextView
-  	android:id="@+id/team_selection_dialog_select"
-  	android:layout_width="wrap_content"
-  	android:layout_height="wrap_content"
-  	android:text="@string/select"/>
-  <TextView
-  	android:id="@+id/team_selection_dialog_edit"
-  	android:layout_width="wrap_content"
-  	android:layout_height="wrap_content"
-  	android:text="@string/edit"/>
-  <TextView
-  	android:id="@+id/team_selection_dialog_delete"
-  	android:layout_width="wrap_content"
-  	android:layout_height="wrap_content"
-  	android:text="@string/delete"/>
-</LinearLayout>
--- a/project_files/Android-build/SDL-android-project/res/layout/team_selection_entry.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/team_selection_entry.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -31,7 +31,7 @@
   	android:layout_alignBottom="@id/imgDifficulty"
   	android:adjustViewBounds="true"
   	android:scaleType="centerInside"
-  	android:src="@drawable/teamcount7"/>
+  	android:src="@drawable/hogcount"/>
   <TextView
   	android:id="@+id/txtName"
   	android:layout_height="fill_parent"
--- a/project_files/Android-build/SDL-android-project/res/layout/team_selector.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-  
-  <include layout="@layout/background"/>
- 
-  <include layout="@layout/backbutton"/>
-
-  <ImageButton
-   	android:id="@+id/btnAdd"
-   	android:layout_width="wrap_content"
-   	android:layout_height="50dip"
-   	android:layout_alignParentBottom="true"
-   	android:layout_alignParentRight="true"
-   	android:adjustViewBounds="true"
-   	android:scaleType="centerInside"
-   	android:background="@android:color/transparent"
-   	android:src="@drawable/settings"/>
- <TextView
-  	android:id="@+id/txtInfo"
-  	android:layout_height="wrap_content"
-  	android:layout_width="fill_parent"
-  	android:layout_alignParentBottom="true"
-  	android:layout_toRightOf="@id/btnBack"
-  	android:layout_toLeftOf="@id/btnAdd"
-  	android:layout_alignTop="@id/btnBack"
-  	android:layout_margin="3dp"
-  	android:gravity="center"
-  	android:background="@drawable/box"/>
-  	
-  
-
-  <LinearLayout
-  	android:orientation="horizontal"
-  	android:layout_width="fill_parent"
-  	android:layout_height="fill_parent"
-  	android:layout_above="@id/txtInfo"
-  	android:layout_margin="3dp">
-  	
-	  <ListView
-	  	android:id="@+id/selectedTeams"
-	  	android:layout_height="fill_parent"
-	  	android:layout_width="wrap_content"
-	  	android:layout_margin="3dp"
-	  	android:background="@drawable/box"
-	  	android:layout_weight="1"/>
-	  	
-	  <ListView
-	  	android:id="@+id/availableTeams"
-	  	android:layout_height="fill_parent"
-	  	android:layout_width="wrap_content"
-	  	android:layout_margin="3dp"
-	  	android:background="@drawable/box"
-	  	android:layout_weight="1"/>
-  </LinearLayout>
-</RelativeLayout>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/menu/lobby_options.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,10 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/room_create"
+        android:title="@string/lobby_roomlistmenu_create"
+        android:showAsAction="ifRoom" />
+    <item
+        android:id="@+id/disconnect"
+        android:title="@string/lobby_menu_disconnect"
+        android:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/menu/lobby_playerlist_context.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,10 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/player_info"
+        android:title="@string/lobby_playerlist_contextmenu_info">
+    </item>
+    <item
+        android:id="@+id/player_follow"
+        android:title="@string/lobby_playerlist_contextmenu_follow">
+    </item>
+</menu>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/menu/main_options.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,20 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/download"
+        android:title="@string/main_menu_downloader"
+        android:icon="@android:drawable/ic_menu_save"
+        android:showAsAction="ifRoom|withText" />
+    <item
+        android:id="@+id/preferences"
+        android:title="@string/main_menu_preferences"
+        android:icon="@android:drawable/ic_menu_preferences"
+        android:showAsAction="ifRoom|withText" />
+    <item
+        android:id="@+id/edit_teams"
+        android:title="@string/edit_teams_menu"
+        android:showAsAction="ifRoom|withText" />
+    <item
+        android:id="@+id/edit_weaponsets"
+        android:title="@string/edit_weaponsets_menu"
+        android:showAsAction="ifRoom|withText" />
+</menu>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/menu/room_playerlist_chief_context.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/player_kick"
+        android:title="@string/playerlist_contextmenu_kick">
+    </item>
+</menu>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/menu/room_playerlist_context.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/player_info"
+        android:title="@string/lobby_playerlist_contextmenu_info">
+    </item>
+</menu>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/basicflags.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,390 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<basicflags>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$damagepct</string>
-        </command>
-        <default>
-            <integer>100</integer>
-        </default>
-        <image>
-            <string>Damage</string>
-        </image>
-        <max>
-            <integer>300</integer>
-        </max>
-        <min>
-            <integer>10</integer>
-        </min>
-        <title>
-            <string>Damage Modifier</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>true</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>true</boolean>
-        </times1000>
-        <command>
-            <string>e$turntime</string>
-        </command>
-        <default>
-            <integer>45</integer>
-        </default>
-        <image>
-            <string>Time</string>
-        </image>
-        <max>
-            <integer>100</integer>
-        </max>
-        <min>
-            <integer>1</integer>
-        </min>
-        <title>
-            <string>Turn Time</string>
-        </title>
-    </flag>
-    <flag>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>inithealth</string>
-        </command>
-        <default>
-            <integer>200</integer>
-        </default>
-        <image>
-            <string>Health</string>
-        </image>
-        <max>
-            <integer>200</integer>
-        </max>
-        <min>
-            <integer>50</integer>
-        </min>
-        <title>
-            <string>Initial Health</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>true</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$sd_turns</string>
-        </command>
-        <default>
-            <integer>15</integer>
-        </default>
-        <image>
-            <string>SuddenDeath</string>
-        </image>
-        <max>
-            <integer>50</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Sudden Death Timeout</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$casefreq</string>
-        </command>
-        <default>
-            <integer>5</integer>
-        </default>
-        <image>
-            <string>Box</string>
-        </image>
-        <max>
-            <integer>9</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Crate Drop Turns</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>true</boolean>
-        </times1000>
-        <command>
-            <string>e$minestime</string>
-        </command>
-        <default>
-            <integer>3</integer>
-        </default>
-        <image>
-            <string>Time</string>
-        </image>
-        <max>
-            <integer>5</integer>
-        </max>
-        <min>
-            <integer>-1</integer>
-        </min>
-        <title>
-            <string>Mines Time</string>
-        </title>
-    </flag>
-   <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$minesnum</string>
-        </command>
-        <default>
-            <integer>4</integer>
-        </default>
-        <image>
-            <string>Mine</string>
-        </image>
-        <max>
-            <integer>80</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Mines Number</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$minedudpct</string>
-        </command>
-        <default>
-            <integer>0</integer>
-        </default>
-        <image>
-            <string>Dud</string>
-        </image>
-        <max>
-            <integer>100</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Dud Mines Probability (%)</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$explosives</string>
-        </command>
-        <default>
-            <integer>2</integer>
-        </default>
-        <image>
-            <string>Damage</string>
-        </image>
-        <max>
-            <integer>40</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Explosives</string>
-        </title>
-    </flag>
-        <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$healthprob</string>
-        </command>
-        <default>
-            <integer>35</integer>
-        </default>
-        <image>
-            <string>Health</string>
-        </image>
-        <max>
-            <integer>100</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Health Kit Probability (%)</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$hcaseamount</string>
-        </command>
-        <default>
-            <integer>25</integer>
-        </default>
-        <image>
-            <string>Health</string>
-        </image>
-        <max>
-            <integer>200</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Health Amount in Kit</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$waterrise</string>
-        </command>
-        <default>
-            <integer>47</integer>
-        </default>
-        <image>
-            <string>SuddenDeath</string>
-        </image>
-        <max>
-            <integer>100</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Water Rise Amount</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$healthdec</string>
-        </command>
-        <default>
-            <integer>5</integer>
-        </default>
-        <image>
-            <string>SuddenDeath</string>
-        </image>
-        <max>
-            <integer>100</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Health Decrease</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$ropepct</string>
-        </command>
-        <default>
-            <integer>100</integer>
-        </default>
-        <image>
-            <string>Rope</string>
-        </image>
-        <max>
-            <integer>999</integer>
-        </max>
-        <min>
-            <integer>25</integer>
-        </min>
-        <title>
-            <string>Rope Length (%)</string>
-        </title>
-    </flag>
-    <flag>
-        <checkOverMax>
-            <boolean>false</boolean>
-        </checkOverMax>
-        <times1000>
-            <boolean>false</boolean>
-        </times1000>
-        <command>
-            <string>e$getawaytime</string>
-        </command>
-        <default>
-            <integer>100</integer>
-        </default>
-        <image>
-            <string>Time</string>
-        </image>
-        <max>
-            <integer>999</integer>
-        </max>
-        <min>
-            <integer>0</integer>
-        </min>
-        <title>
-            <string>Get Away Time (%)</string>
-        </title>
-    </flag>
-</basicflags>
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_barrelmayhem.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Barrel Mayhem</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>30</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>80</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_cleanslate.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Clean Slate</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>45</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>4</integer>
-		<integer>0</integer>
-		<integer>2</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_default_scheme.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Default</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>45</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>4</integer>
-		<integer>0</integer>
-		<integer>2</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_fortmode.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Fort Mode</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>45</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_kingmode.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>King Mode</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>45</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>4</integer>
-		<integer>0</integer>
-		<integer>2</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_minefield.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Minefield</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>30</integer>
-		<integer>50</integer>
-		<integer>15</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>80</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-        <integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_promode.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Pro Mode</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>0</integer>
-		<integer>3</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>2</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_shoppa.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Shoppa</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>30</integer>
-		<integer>100</integer>
-		<integer>50</integer>
-		<integer>1</integer>
-		<integer>3</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_thinkingwithportals.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Thinking with Portals</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>45</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>2</integer>
-		<integer>3</integer>
-		<integer>5</integer>
-		<integer>0</integer>
-		<integer>5</integer>
-		<integer>25</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_timeless.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Timeless</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>9999</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>5</integer>
-		<integer>10</integer>
-		<integer>2</integer>
-		<integer>35</integer>
-		<integer>30</integer>
-		<integer>0</integer>
-		<integer>0</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_tunnelhogs.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scheme>
-	<name>Tunnelhogs</name>
-	<basicflags>
-		<integer>100</integer>
-		<integer>30</integer>
-		<integer>100</integer>
-		<integer>15</integer>
-		<integer>5</integer>
-		<integer>3</integer>
-		<integer>10</integer>
-		<integer>10</integer>
-		<integer>10</integer>
-		<integer>35</integer>
-		<integer>25</integer>
-		<integer>47</integer>
-		<integer>5</integer>
-		<integer>100</integer>
-		<integer>100</integer>
-	</basicflags>
-	<gamemod>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<true/>
-		<false/>
-		<false/>
-		<true/>
-		<true/>
-		<true/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-		<false/>
-	</gamemod>
-</scheme>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/schemes_builtin.ini	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,456 @@
+
+[schemes]
+size                           = 11
+1\name                         = Default
+1\fortsmode                    = false
+1\divteams                     = false
+1\solidland                    = false
+1\border                       = false
+1\lowgrav                      = false
+1\laser                        = false
+1\invulnerability              = false
+1\resethealth                  = false
+1\vampiric                     = false
+1\karma                        = false
+1\artillery                    = false
+1\randomorder                  = false
+1\king                         = false
+1\placehog                     = false
+1\sharedammo                   = false
+1\disablegirders               = false
+1\disablelandobjects           = false
+1\aisurvival                   = false
+1\infattack                    = false
+1\resetweps                    = false
+1\perhogammo                   = false
+1\disablewind                  = false
+1\morewind                     = false
+1\tagteam                      = false
+1\bottomborder                 = false
+1\damagefactor                 = 100
+1\turntime                     = 45
+1\health                       = 100
+1\suddendeath                  = 15
+1\caseprobability              = 5
+1\minestime                    = 3
+1\minesnum                     = 4
+1\minedudpct                   = 0
+1\explosives                   = 2
+1\healthprobability            = 35
+1\healthcaseamount             = 25
+1\waterrise                    = 47
+1\healthdecrease               = 5
+1\ropepct                      = 100
+1\getawaytime                  = 100
+2\name                         = Pro Mode
+2\fortsmode                    = false
+2\divteams                     = false
+2\solidland                    = false
+2\border                       = false
+2\lowgrav                      = false
+2\laser                        = false
+2\invulnerability              = false
+2\resethealth                  = false
+2\vampiric                     = false
+2\karma                        = false
+2\artillery                    = false
+2\randomorder                  = true
+2\king                         = false
+2\placehog                     = false
+2\sharedammo                   = false
+2\disablegirders               = false
+2\disablelandobjects           = false
+2\aisurvival                   = false
+2\infattack                    = false
+2\resetweps                    = false
+2\perhogammo                   = false
+2\disablewind                  = false
+2\morewind                     = false
+2\tagteam                      = false
+2\bottomborder                 = false
+2\damagefactor                 = 100
+2\turntime                     = 45
+2\health                       = 100
+2\suddendeath                  = 15
+2\caseprobability              = 5
+2\minestime                    = 3
+2\minesnum                     = 4
+2\minedudpct                   = 0
+2\explosives                   = 2
+2\healthprobability            = 35
+2\healthcaseamount             = 25
+2\waterrise                    = 47
+2\healthdecrease               = 5
+2\ropepct                      = 100
+2\getawaytime                  = 100
+3\name                         = Shoppa
+3\fortsmode                    = false
+3\divteams                     = false
+3\solidland                    = false
+3\border                       = false
+3\lowgrav                      = false
+3\laser                        = false
+3\invulnerability              = false
+3\resethealth                  = false
+3\vampiric                     = false
+3\karma                        = false
+3\artillery                    = false
+3\randomorder                  = true
+3\king                         = false
+3\placehog                     = false
+3\sharedammo                   = true
+3\disablegirders               = false
+3\disablelandobjects           = false
+3\aisurvival                   = false
+3\infattack                    = false
+3\resetweps                    = false
+3\perhogammo                   = false
+3\disablewind                  = false
+3\morewind                     = false
+3\tagteam                      = false
+3\bottomborder                 = false
+3\damagefactor                 = 100
+3\turntime                     = 15
+3\health                       = 100
+3\suddendeath                  = 15
+3\caseprobability              = 0
+3\minestime                    = 3
+3\minesnum                     = 0
+3\minedudpct                   = 0
+3\explosives                   = 2
+3\healthprobability            = 35
+3\healthcaseamount             = 25
+3\waterrise                    = 47
+3\healthdecrease               = 5
+3\ropepct                      = 100
+3\getawaytime                  = 100
+4\name                         = Clean Slate
+4\fortsmode                    = false
+4\divteams                     = false
+4\solidland                    = true
+4\border                       = true
+4\lowgrav                      = false
+4\laser                        = false
+4\invulnerability              = false
+4\resethealth                  = false
+4\vampiric                     = false
+4\karma                        = false
+4\artillery                    = false
+4\randomorder                  = true
+4\king                         = false
+4\placehog                     = false
+4\sharedammo                   = true
+4\disablegirders               = true
+4\disablelandobjects           = false
+4\aisurvival                   = false
+4\infattack                    = false
+4\resetweps                    = true
+4\perhogammo                   = false
+4\disablewind                  = false
+4\morewind                     = false
+4\tagteam                      = false
+4\bottomborder                 = false
+4\damagefactor                 = 100
+4\turntime                     = 30
+4\health                       = 100
+4\suddendeath                  = 50
+4\caseprobability              = 1
+4\minestime                    = 3
+4\minesnum                     = 0
+4\minedudpct                   = 0
+4\explosives                   = 0
+4\healthprobability            = 0
+4\healthcaseamount             = 25
+4\waterrise                    = 47
+4\healthdecrease               = 5
+4\ropepct                      = 100
+4\getawaytime                  = 100
+5\name                         = Minefield
+5\fortsmode                    = false
+5\divteams                     = false
+5\solidland                    = false
+5\border                       = false
+5\lowgrav                      = false
+5\laser                        = false
+5\invulnerability              = false
+5\resethealth                  = true
+5\vampiric                     = false
+5\karma                        = false
+5\artillery                    = false
+5\randomorder                  = true
+5\king                         = false
+5\placehog                     = false
+5\sharedammo                   = false
+5\disablegirders               = false
+5\disablelandobjects           = false
+5\aisurvival                   = false
+5\infattack                    = true
+5\resetweps                    = true
+5\perhogammo                   = false
+5\disablewind                  = false
+5\morewind                     = false
+5\tagteam                      = false
+5\bottomborder                 = false
+5\damagefactor                 = 100
+5\turntime                     = 45
+5\health                       = 100
+5\suddendeath                  = 15
+5\caseprobability              = 5
+5\minestime                    = 3
+5\minesnum                     = 4
+5\minedudpct                   = 0
+5\explosives                   = 2
+5\healthprobability            = 35
+5\healthcaseamount             = 25
+5\waterrise                    = 47
+5\healthdecrease               = 5
+5\ropepct                      = 100
+5\getawaytime                  = 100
+6\name                         = Barrel Mayhem
+6\fortsmode                    = false
+6\divteams                     = false
+6\solidland                    = false
+6\border                       = false
+6\lowgrav                      = false
+6\laser                        = false
+6\invulnerability              = false
+6\resethealth                  = false
+6\vampiric                     = false
+6\karma                        = false
+6\artillery                    = false
+6\randomorder                  = true
+6\king                         = false
+6\placehog                     = false
+6\sharedammo                   = true
+6\disablegirders               = true
+6\disablelandobjects           = false
+6\aisurvival                   = false
+6\infattack                    = false
+6\resetweps                    = false
+6\perhogammo                   = false
+6\disablewind                  = false
+6\morewind                     = false
+6\tagteam                      = false
+6\bottomborder                 = false
+6\damagefactor                 = 100
+6\turntime                     = 30
+6\health                       = 50
+6\suddendeath                  = 15
+6\caseprobability              = 0
+6\minestime                    = 0
+6\minesnum                     = 80
+6\minedudpct                   = 0
+6\explosives                   = 0
+6\healthprobability            = 35
+6\healthcaseamount             = 25
+6\waterrise                    = 47
+6\healthdecrease               = 5
+6\ropepct                      = 100
+6\getawaytime                  = 100
+7\name                         = Tunnel Hogs
+7\fortsmode                    = false
+7\divteams                     = false
+7\solidland                    = false
+7\border                       = false
+7\lowgrav                      = false
+7\laser                        = false
+7\invulnerability              = false
+7\resethealth                  = false
+7\vampiric                     = false
+7\karma                        = false
+7\artillery                    = false
+7\randomorder                  = true
+7\king                         = false
+7\placehog                     = false
+7\sharedammo                   = true
+7\disablegirders               = false
+7\disablelandobjects           = false
+7\aisurvival                   = false
+7\infattack                    = false
+7\resetweps                    = false
+7\perhogammo                   = false
+7\disablewind                  = false
+7\morewind                     = false
+7\tagteam                      = false
+7\bottomborder                 = false
+7\damagefactor                 = 100
+7\turntime                     = 30
+7\health                       = 100
+7\suddendeath                  = 15
+7\caseprobability              = 0
+7\minestime                    = 0
+7\minesnum                     = 0
+7\minedudpct                   = 0
+7\explosives                   = 80
+7\healthprobability            = 35
+7\healthcaseamount             = 25
+7\waterrise                    = 47
+7\healthdecrease               = 5
+7\ropepct                      = 100
+7\getawaytime                  = 100
+8\name                         = Fort Mode
+8\fortsmode                    = false
+8\divteams                     = false
+8\solidland                    = false
+8\border                       = true
+8\lowgrav                      = false
+8\laser                        = false
+8\invulnerability              = false
+8\resethealth                  = false
+8\vampiric                     = false
+8\karma                        = false
+8\artillery                    = false
+8\randomorder                  = true
+8\king                         = false
+8\placehog                     = false
+8\sharedammo                   = true
+8\disablegirders               = true
+8\disablelandobjects           = true
+8\aisurvival                   = false
+8\infattack                    = false
+8\resetweps                    = false
+8\perhogammo                   = false
+8\disablewind                  = false
+8\morewind                     = false
+8\tagteam                      = false
+8\bottomborder                 = false
+8\damagefactor                 = 100
+8\turntime                     = 30
+8\health                       = 100
+8\suddendeath                  = 15
+8\caseprobability              = 5
+8\minestime                    = 3
+8\minesnum                     = 10
+8\minedudpct                   = 10
+8\explosives                   = 10
+8\healthprobability            = 35
+8\healthcaseamount             = 25
+8\waterrise                    = 47
+8\healthdecrease               = 5
+8\ropepct                      = 100
+8\getawaytime                  = 100
+9\name                         = Timeless
+9\fortsmode                    = true
+9\divteams                     = true
+9\solidland                    = false
+9\border                       = false
+9\lowgrav                      = true
+9\laser                        = false
+9\invulnerability              = false
+9\resethealth                  = false
+9\vampiric                     = false
+9\karma                        = false
+9\artillery                    = false
+9\randomorder                  = true
+9\king                         = false
+9\placehog                     = false
+9\sharedammo                   = false
+9\disablegirders               = false
+9\disablelandobjects           = false
+9\aisurvival                   = false
+9\infattack                    = false
+9\resetweps                    = false
+9\perhogammo                   = false
+9\disablewind                  = false
+9\morewind                     = false
+9\tagteam                      = false
+9\bottomborder                 = false
+9\damagefactor                 = 100
+9\turntime                     = 45
+9\health                       = 100
+9\suddendeath                  = 15
+9\caseprobability              = 5
+9\minestime                    = 3
+9\minesnum                     = 0
+9\minedudpct                   = 0
+9\explosives                   = 0
+9\healthprobability            = 35
+9\healthcaseamount             = 25
+9\waterrise                    = 47
+9\healthdecrease               = 5
+9\ropepct                      = 100
+9\getawaytime                  = 100
+10\name                        = Thinking with Portals
+10\fortsmode                   = false
+10\divteams                    = false
+10\solidland                   = false
+10\border                      = false
+10\lowgrav                     = false
+10\laser                       = false
+10\invulnerability             = false
+10\resethealth                 = false
+10\vampiric                    = false
+10\karma                       = false
+10\artillery                   = false
+10\randomorder                 = true
+10\king                        = false
+10\placehog                    = false
+10\sharedammo                  = false
+10\disablegirders              = false
+10\disablelandobjects          = false
+10\aisurvival                  = false
+10\infattack                   = false
+10\resetweps                   = false
+10\perhogammo                  = true
+10\disablewind                 = false
+10\morewind                    = false
+10\tagteam                     = false
+10\bottomborder                = false
+10\damagefactor                = 100
+10\turntime                    = 9999
+10\health                      = 100
+10\suddendeath                 = 15
+10\caseprobability             = 5
+10\minestime                   = 3
+10\minesnum                    = 5
+10\minedudpct                  = 10
+10\explosives                  = 2
+10\healthprobability           = 35
+10\healthcaseamount            = 30
+10\waterrise                   = 0
+10\healthdecrease              = 0
+10\ropepct                     = 100
+10\getawaytime                 = 100
+11\name                        = King Mode
+11\fortsmode                   = false
+11\divteams                    = false
+11\solidland                   = false
+11\border                      = false
+11\lowgrav                     = false
+11\laser                       = false
+11\invulnerability             = false
+11\resethealth                 = false
+11\vampiric                    = false
+11\karma                       = false
+11\artillery                   = true
+11\randomorder                 = true
+11\king                        = false
+11\placehog                    = false
+11\sharedammo                  = false
+11\disablegirders              = false
+11\disablelandobjects          = false
+11\aisurvival                  = false
+11\infattack                   = false
+11\resetweps                   = false
+11\perhogammo                  = false
+11\disablewind                 = false
+11\morewind                    = false
+11\tagteam                     = false
+11\bottomborder                = false
+11\damagefactor                = 100
+11\turntime                    = 45
+11\health                      = 100
+11\suddendeath                 = 15
+11\caseprobability             = 2
+11\minestime                   = 3
+11\minesnum                    = 5
+11\minedudpct                  = 0
+11\explosives                  = 5
+11\healthprobability           = 25
+11\healthcaseamount            = 25
+11\waterrise                   = 47
+11\healthdecrease              = 5
+11\ropepct                     = 100
+11\getawaytime                 = 100
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/team_one.hwt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,74 @@
+[Team]
+Name=Team 1
+Grave=Bone
+Fort=Lego
+Voicepack=Classic
+Flag=hedgewars
+Difficulty=0
+Rounds=0
+Wins=0
+CampaignProgress=0
+
+[Hedgehog0]
+Name=Leonidas
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog1]
+Name=Pipo
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog2]
+Name=Sonic
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog3]
+Name=Xin
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog4]
+Name=Arnold
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog5]
+Name=Jack
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog6]
+Name=Tom
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog7]
+Name=Goldie
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
--- a/project_files/Android-build/SDL-android-project/res/raw/team_one.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
-<team>
-  <name>Team 1</name>
-  <flag>hedgewars</flag>
-  <fort>Lego</fort>
-  <grave>Bone</grave>
-  <voice>Classic</voice>
-  <hash>0</hash>
-  <hog>
-    <name>Leonidas</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Pipo</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Sonic</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Xin</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Arnold</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Jack</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Tom</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-  <hog>
-    <name>Goldie</name>
-    <hat>NoHat</hat>
-    <level>0</level>
-  </hog>
-</team>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/team_two.hwt	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,74 @@
+[Team]
+Name=Team 2
+Grave=Bone
+Fort=Lego
+Voicepack=Classic
+Flag=cm_binary
+Difficulty=2
+Rounds=0
+Wins=0
+CampaignProgress=0
+
+[Hedgehog0]
+Name=Paris
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog1]
+Name=Knut
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog2]
+Name=Ash
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog3]
+Name=Woad
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog4]
+Name=Bob
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog5]
+Name=Corky
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog6]
+Name=Bea
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
+
+[Hedgehog7]
+Name=Silvia
+Hat=NoHat
+Rounds=0
+Kills=0
+Deaths=0
+Suicides=0
--- a/project_files/Android-build/SDL-android-project/res/raw/team_two.xml	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
-<team>
-  <name>Team 2</name>
-  <flag>cm_binary</flag>
-  <fort>Lego</fort>
-  <grave>Bone</grave>
-  <voice>Classic</voice>
-  <hash>0</hash>
-  <hog>
-    <name>Paris</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Knut</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Ash</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Woad</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Bob</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Corky</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Bea</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-  <hog>
-    <name>Silvia</name>
-    <hat>NoHat</hat>
-    <level>2</level>
-  </hog>
-</team>
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_clean	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Clean</name>
-    <QT>
-    101000900001000001100000000000000000000000000000100000
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_crazy	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Crazy</name>
-    <QT>
-    999999999999999999299999999999999929999999990999999229
-    </QT>
-    <probability>
-    111111011111111111111111111111111111111111110111111111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111010111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_default	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Default</name>
-    <QT>
-    939192942219912103223511100120100000021111010101111991
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000022000000060000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_mines	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Mines</name>
-    <QT>
-    000000990009000000030000000000000000000000000000000000
-    </QT>
-    <probability>
-    000000000000000000000000000000000000000000000000000000
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000060000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_portals	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Portals</name>
-    <QT>
-    900000900200000000210000000000000011000009000000000000
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000060000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_promode	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Pro Mode</name>
-    <QT>
-    909000900000000000000900000000000000000000000000000000
-    </QT>
-    <probability>
-    000000000000000000000000000000000000000000000000000000
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000000000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111110010111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_shoppa	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Shoppa</name>
-    <QT>
-    000000990000000000000000000000000000000000000000000000
-    </QT>
-    <probability>
-    444441004424440221011212122242200000000200040001001111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111110110111111111
-    </crate>
-</weapon>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/weapons_builtin.ini	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+[General]
+%44efault=9391929422199121032235111001201000000211110101011111011040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002200000006000001311110312111111123114111111111111111211111101111111010
+%43razy=9999999999999999992999999999999999299999999909999992099111111011111111111111111111111111111111111110111111101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211110101111111011
+%50ro%20%4dode=9090009000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000000000001111111111111111111111111111111111111111100101111111011
+%53hoppa=0000009900000000000000000000000000000000000000000000000444441004424440221011212122242200000000200040001001100000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111101101111111001
+%43lean%20%53late=1010009000010000011000000000000000000000000000001000000040504054160065554655446477657666666615551010111541101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211111101111111011
+%4dinefield=0000009900090000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000006000001111111111111111111111111111111111111111111101111111011
+%54hinking%20with%20%50ortals=9000009002000000002100000000000000110000090000000000000040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002000000006000001311110312111111123114111111111111111211111101111111011
--- a/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -1,34 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-
-<array name="schemes">
-	<item>@raw/basicflags</item>
-	<item>@raw/scheme_default_scheme</item>
-	<item>@raw/scheme_barrelmayhem</item>
-	<item>@raw/scheme_cleanslate</item>
-	<item>@raw/scheme_fortmode</item>
-	<item>@raw/scheme_kingmode</item>
-	<item>@raw/scheme_minefield</item>
-	<item>@raw/scheme_promode</item>
-	<item>@raw/scheme_shoppa</item>
-	<item>@raw/scheme_thinkingwithportals</item>
-	<item>@raw/scheme_timeless</item>
-	<item>@raw/scheme_tunnelhogs</item>
-</array>
-
-<array name="weapons">
-    <item>@raw/weapon_default</item>
-    <item>@raw/weapon_clean</item>
-    <item>@raw/weapon_crazy</item>
-    <item>@raw/weapon_mines</item>
-    <item>@raw/weapon_portals</item>
-    <item>@raw/weapon_promode</item>
-    <item>@raw/weapon_shoppa</item>
-</array>
-
 <array name="teams">
 	<item>@raw/team_one</item>
 	<item>@raw/team_two</item>
-
+</array>
+<array name="teamFilenames">
+	<item>Team 1.hwt</item>
+	<item>Team 2.hwt</item>
 </array>
 </resources>
--- a/project_files/Android-build/SDL-android-project/res/values/strings.xml	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml	Mon Aug 27 17:40:16 2012 +0200
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
+
     <string name="app_name">Hedgewars</string>
-    
     <string name="select">Select</string>
     <string name="edit">Edit</string>
     <string name="delete">Delete</string>
     <string name="saved">Saved succesfully</string>
-    
+
     <!-- SDCARD -->
     <string name="sdcard_not_mounted_title">Sorry! Could not find the SDCard</string>
     <string name="sdcard_not_mounted">There\'s been an error when accessing the SDcard. Please check if there is an SDcard present in the device (internal or external) and if the SDcard is not mounted (via usb to your computer for example). Hedgewars for Android will now quit</string>
-        
+
     <!-- Notification -->
-    <string name="notification_title">Downloading hedgewars files...</string>
+    <string name="notification_title">Downloading hedgewars files&#8230;</string>
     <string name="notification_done">Successfully downloaded: </string>
-    
+
     <!-- Download Activity -->
     <string name="download_background">Continue in background</string>
     <string name="download_cancel">Cancel</string>
@@ -22,28 +22,31 @@
     <string name="download_back">Back to main menu</string>
     <string name="download_tryagain">Try again</string>
     <string name="download_failed">The download has failed because of: </string>
-    <string name="download_userexplain">Before starting the game we must download some extra files...</string>
-    
+    <string name="download_userexplain">Before starting the game we must download some extra files&#8230;</string>
     <string name="download_areyousure">Are you sure you want to download this package?</string>
     <string name="download_alreadydownloaded">You\'ve already downloaded this package, are you sure you want to download it again?</string>
     <string name="download_downloadnow">Download now!</string>
-    
     <string name="download_queued">This download has been queued</string>
+
+    <!-- main activity -->
+    <string name="main_button_localplay">Local Game</string>
+    <string name="main_button_netplay">Network Game</string>
+    <string name="main_menu_downloader">Downloader</string>
+    <string name="main_menu_preferences">Preferences</string>
     
     <!-- start game -->
-    
     <string name="start_gameplay">Style</string>
-    <string name="start_gamescheme">Game scheme</string>
+    <string name="start_gamescheme">Scheme</string>
     <string name="start_weapons">Weapons</string>
     <string name="start_map">Map</string>
     <string name="start_filter">Filter</string>
-    <string name="start_themes">Themes</string>
-    
-    
+
+    <!-- Teams -->
+    <string name="not_enough_teams">You need at least two teams.</string>
+    <string name="not_enough_clans">You need at least two different team colors (clans).</string>
+    <string name="teamlist_empty">No teams</string>
+    <string name="teamlist_add_content_description">New team</string>
     
-    <!-- Teams -->
-    <string name="not_enough_teams">Not enough teams</string>
-    <string name="teams_info_template">Selected teams = %d</string>
     <!-- Settings -->
     <string name="name">Name</string>
     <string name="name_default">Unnamed</string>
@@ -52,7 +55,7 @@
     <string name="flag">Flag</string>
     <string name="voice">Voice</string>
     <string name="fort">Fort</string>
-    
+
     <!-- Difficulty levels -->
     <string name="human">Human</string>
     <string name="bot5">Level 5</string>
@@ -60,5 +63,131 @@
     <string name="bot3">Level 3</string>
     <string name="bot2">Level 2</string>
     <string name="bot1">Level 1</string>
+
+    <string name="title_activity_lobby">Hedgewars Server Lobby</string>
+    <string name="title_activity_room">Room</string>
+    <string name="title_activity_weaponset_list">User-defined Weaponsets</string>
+    <string name="title_activity_weaponset_creator">Weaponset Editor</string>
+    <string name="title_activity_scheme_list">User-defined Schemes</string>
+    <string name="title_activity_scheme_creator">Scheme Editor</string>
     
+    <string name="chat_hint">Type here to chat</string>
+
+    <!-- Map settings -->
+    <string name="map_gen">Map</string>
+    <string name="map_name">Name</string>
+    <string name="map_template">Type</string>
+    <string name="map_maze_size">Type</string>
+    <string name="map_mission_prefix">Mission: </string>
+    <string name="map_button_editdrawnmap">Edit drawn map</string>
+    <string-array name="map_types">
+        <item>Generated map</item>
+        <item>Generated maze</item>
+        <item>Hand-drawn map</item>
+        <item>Map file</item>
+    </string-array>
+    <string-array name="map_templates">
+        <item>Random</item>
+        <item>Small</item>
+        <item>Medium</item>
+        <item>Large</item>
+        <item>Cavern</item>
+        <item>Wacky</item>
+    </string-array>
+    <string-array name="map_maze_sizes">
+        <item>Small tunnels</item>
+        <item>Medium tunnels</item>
+        <item>Large tunnels</item>
+        <item>Small floating islands</item>
+        <item>Medium floating islands</item>
+        <item>Large floating islands</item>
+    </string-array>
+    
+    <!-- Player list -->
+    <string name="no_players_in_list">No players</string>
+    
+    <!-- Teamlist -->
+    <string name="teamlist_addteam">Add team</string>
+    <string name="teamlist_color_button_description">Team color</string>
+    <string name="teamlist_hogcount_button_description">Hog count</string>
+    <string name="no_teams_in_list">No teams</string>
+    <string name="edit_teams_menu">Edit Teams</string>
+    
+    <!-- Roomlist -->
+    <string name="roomlist_header_roomname">Room Name</string>
+    <string name="roomlist_header_clients">C</string>
+    <string name="roomlist_header_teams">T</string>
+    <string name="roomlist_header_owner">Owner</string>
+    <string name="roomlist_header_map">Map</string>
+    <string name="roomlist_header_scheme">Rules</string>
+    <string name="roomlist_header_weapons">Weapons</string>
+    <string name="no_rooms_in_list">No rooms</string>
+    
+    <string name="roomlist_owner">by %1$s</string>
+    <string name="roomlist_map">Map: %1$s</string>
+    <string name="roomlist_scheme">Scheme: %1$s</string>
+    <string name="map_regular">Random map</string>
+    <string name="map_maze">Random maze</string>
+    <string name="map_drawn">Drawn map</string>
+    
+    <!-- Chatlog messages -->
+    <string name="log_player_join">%1$s has joined.</string>
+    <string name="log_player_leave">%1$s has left.</string>
+    <string name="log_player_leave_with_msg">%1$s has left (%2$s).</string>
+    
+    <!-- Start netgame dialog -->
+    <string name="start_netgame_dialog_title">Connect</string>
+    <string name="start_netgame_dialog_message">Please select a username.</string>
+    <string name="start_netgame_dialog_playername_hint">Username</string>
+    
+    <string name="playerlist_contextmenu_kick">Kick</string>
+    <string name="lobby_playerlist_contextmenu_info">Info (shown in chat)</string>
+    <string name="lobby_playerlist_contextmenu_follow">Follow</string>
+    <string name="lobby_roomlistmenu_create">Create room</string>
+    <string name="lobby_roomlistmenu_refresh">Refresh</string>
+    <string name="lobby_menu_disconnect">Disconnect</string>
+    <string name="lobby_tab_rooms">Rooms</string>
+    <string name="lobby_tab_chat">Chat</string>
+    <string name="lobby_tab_players">Users</string>
+    
+    <string name="not_implemented_yet">Sorry, not implemented yet. :(</string>
+    
+    <!-- Errors -->
+    <string name="error_connection_failed">Unable to connect to the server.</string>
+    <string name="error_unexpected">An unexpected error has occurred: %1$s</string>
+    <string name="error_server_too_old">The server you tried to connect to is using an incompatible protocol.</string>
+    <string name="error_auth_failed">Unable to authenticate for your username.</string>
+    <string name="error_connection_lost">The connection to the server was lost.</string>
+    <string name="error_save_failed">Saving has failed.</string>
+    <string name="error_missing_sdcard_or_files">Error: Either the sdcard is not available, or files are missing from the Data directory.</string>
+    <string name="error_team_attribute_not_found">Error: This team uses assets which we do not have. Try downloading the big data package from the main menu.</string>
+    
+    <!-- Dialogs -->
+    <string name="dialog_connecting_title">Please wait</string>
+    <string name="dialog_connecting_message">Connecting to the server&#8230;</string>
+    <string name="dialog_password_title">Password required</string>
+    <string name="dialog_password_message">The server has requested a password to connect as "%1$s".</string>
+    <string name="dialog_password_hint">Password</string>
+    <string name="dialog_password_remember">remember password</string>
+    <string name="dialog_create_room_hint">Room name</string>
+    <string name="dialog_create_room_title">Create new room</string>
+    <string name="dialog_addteam_title">Add team</string>
+    
+    <string name="toast_disconnected">Disconnected: %1$s</string>
+    <string name="toast_room_abandoned">The room was closed because the owner left.</string>
+    <string name="toast_kicked">You were kicked from the room.</string>
+    
+    <!-- Weaponset editor -->
+    <string name="weaponsetlist_add_button_text">New Weaponset</string>
+    <string name="weaponsetlist_empty">No weaponsets</string>
+    <string name="edit_weaponsets_menu">Edit Weaponsets</string>
+    
+    <string name="schemelist_add_button_text">New Scheme</string>
+    
+    <!-- Room activity tabs -->
+    <string name="room_tab_map">Map</string>
+    <string name="room_tab_settings">Game</string>
+    <string name="room_tab_teams">Teams</string>
+    <string name="room_tab_chat">Chat</string>
+    <string name="room_tab_players">Users</string>
 </resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/BasicRoomState.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,161 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import static org.hedgewars.hedgeroid.util.ObjectUtils.equal;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.hedgewars.hedgeroid.RoomStateManager;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+
+/**
+ * Common base implementation for a roomstate that will call listeners on every
+ * change. The derived classes have to coordinate how state is changed to
+ * complete the implementation of the RoomStateManager interface.
+ * 
+ * See {@link RoomStateManager} for a description of what this is for.
+ */
+public abstract class BasicRoomState implements RoomStateManager {
+	private final List<RoomStateManager.Listener> observers = new LinkedList<RoomStateManager.Listener>();
+	
+	private boolean chief;
+	private String gameStyle;
+	private Scheme scheme;
+	private MapRecipe map;
+	private Weaponset weaponset;
+	private Map<String, TeamInGame> teams = Collections.emptyMap();
+	
+	public final MapRecipe getMapRecipe() {
+		return map;
+	}
+
+	public final boolean getChiefStatus() {
+		return chief;
+	}
+
+	public final Scheme getScheme() {
+		return scheme;
+	}
+
+	public final String getGameStyle() {
+		return gameStyle;
+	}
+
+	public final Weaponset getWeaponset() {
+		return weaponset;
+	}
+	
+	public final Map<String, TeamInGame> getTeams() {
+		return teams;
+	}
+	
+	public final void setWeaponset(Weaponset weaponset) {
+		if(!equal(weaponset, this.weaponset)) {
+			this.weaponset = weaponset;
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onWeaponsetChanged(weaponset);
+			}
+		}
+	}
+	
+	public final void setMapRecipe(MapRecipe map) {
+		if(!equal(map, this.map)) { 
+			this.map = map;
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onMapChanged(map);
+			}
+		}
+	}
+	
+	public final void setGameStyle(String gameStyle) {
+		if(!equal(gameStyle, this.gameStyle)) {
+			this.gameStyle = gameStyle;
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onGameStyleChanged(gameStyle);
+			}
+		}
+	}
+	
+	public final void setScheme(Scheme scheme) {
+		if(!equal(scheme, this.scheme)) {
+			this.scheme = scheme;
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onSchemeChanged(scheme);
+			}
+		}
+	}
+	
+	public final void setChief(boolean chief) {
+		if(chief != this.chief) {
+			this.chief = chief;
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onChiefStatusChanged(chief);
+			}
+		}
+	}
+	
+	public final void putTeam(TeamInGame team) {
+		TeamInGame oldEntry = teams.get(team.team.name);
+		if(!equal(team, oldEntry)) {
+			Map<String, TeamInGame> changedMap = new TreeMap<String, TeamInGame>(teams);
+			changedMap.put(team.team.name, team);
+			teams = Collections.unmodifiableMap(changedMap);
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onTeamsChanged(teams);
+			}
+		}
+	}
+	
+	public final void removeTeam(String teamname) {
+		if(teams.containsKey(teamname)) {
+			Map<String, TeamInGame> changedMap = new TreeMap<String, TeamInGame>(teams);
+			changedMap.remove(teamname);
+			teams = Collections.unmodifiableMap(changedMap);
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onTeamsChanged(teams);
+			}
+		}
+	}
+	
+	public final void setTeams(Map<String, TeamInGame> newTeams) {
+		if(!newTeams.equals(teams)) {
+			teams = Collections.unmodifiableMap(new TreeMap<String, TeamInGame>(newTeams));
+			for(RoomStateManager.Listener observer : observers) {
+				observer.onTeamsChanged(teams);
+			}
+		}
+	}
+	
+	public final void addListener(Listener observer) {
+		observers.add(observer);
+	}
+
+	public final void removeListener(Listener observer) {
+		observers.remove(observer);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,99 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.netplay.MessageLog;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * This fragment displays a chatlog and text input field for chatting in either
+ * the lobby or a room.
+ */
+public class ChatFragment extends Fragment {
+	private ChatlogAdapter adapter;
+	private Netplay netplay;
+	private MessageLog messageLog;
+	private boolean inRoom;
+	
+	public void setInRoom(boolean inRoom) {
+		this.inRoom = inRoom;
+	}
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		netplay = Netplay.getAppInstance(getActivity().getApplicationContext());
+		adapter = new ChatlogAdapter(getActivity());
+	}
+	
+	@Override
+	public void onStart() {
+		super.onStart();
+		messageLog = inRoom ? netplay.roomChatlog : netplay.lobbyChatlog;
+    	adapter.setLog(messageLog.getLog());
+    	messageLog.addListener(adapter);
+	}
+	
+	@Override
+	public void onStop() {
+		super.onStop();
+		messageLog.removeListener(adapter);
+	}
+	
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		View view = inflater.inflate(R.layout.fragment_chat, container, false);
+		
+		ListView listView = (ListView) view.findViewById(R.id.chatConsole);
+		listView.setAdapter(adapter);
+		listView.setDivider(null);
+		listView.setDividerHeight(0);
+		listView.setVerticalFadingEdgeEnabled(true);
+		
+		EditText editText = (EditText) view.findViewById(R.id.chatInput);
+        editText.setOnEditorActionListener(new ChatSendListener());
+        
+		return view;
+	}
+	
+	private final class ChatSendListener implements OnEditorActionListener {
+		public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+			String text = v.getText().toString();
+			if(text.length()>0) {
+				v.setText("");
+				netplay.sendChat(text);
+			}
+			return true;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ChatlogAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,121 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.netplay.MessageLog;
+
+import android.content.Context;
+import android.text.method.LinkMovementMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.LayoutParams;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+/**
+ * Optimization: ListView is smart enough to try re-using the same view for an item
+ * with the same ID, but it still calls getView for those items when the list changes.
+ * Since lines with a given ID never change in our chatlog, we can avoid the effort
+ * of TextView.setText in many cases by checking if the view is already set up for the
+ * line with the right ID - but to do that, the view needs to remember the ID it's
+ * holding the text for. That's what the LoglineView does. 
+ */
+class LoglineView extends TextView {
+	long chatlogId = -1;
+	
+	public LoglineView(Context context) {
+		super(context);
+	}
+}
+
+/**
+ * For performance reasons, the chatlog is implemented as ListView instead of a
+ * single TextView (although I later learned that TextView might also have
+ * facilities for efficient appending with limited backlog... oh well, this
+ * works). Every chat line is a line in the ListView, and this adapter prepares
+ * the textviews from a messagelog in an efficient way.
+ */
+public class ChatlogAdapter extends BaseAdapter implements MessageLog.Listener {
+	long idOffset = 0;
+	private List<CharSequence> log = new ArrayList<CharSequence>();
+	private Context context;
+	
+	public ChatlogAdapter(Context context) {
+		this.context = context;
+	}
+	
+	public int getCount() {
+		return log.size();
+	}
+
+	public Object getItem(int position) {
+		return log.get(position);
+	}
+
+	public long getItemId(int position) {
+		return position+idOffset;
+	}
+
+	public boolean hasStableIds() {
+		return true;
+	}
+
+	public void clear() {
+		idOffset += log.size();
+		log.clear();
+		notifyDataSetChanged();
+	}
+	
+	public void lineAdded(CharSequence text) {
+		log.add(text);
+		notifyDataSetChanged();
+	}
+	
+	public void lineRemoved() {
+		log.remove(0);
+		idOffset += 1;
+		notifyDataSetChanged();
+	}
+	
+	public void setLog(Collection<CharSequence> log) {
+		idOffset += log.size();
+		this.log = new ArrayList<CharSequence>(log);
+		notifyDataSetChanged();
+	}
+	
+	public View getView(int position, View convertView, ViewGroup parent) {
+		LoglineView v = (LoglineView)convertView;
+		if (v == null) {
+			v = new LoglineView(context);
+			v.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
+			v.setMovementMethod(LinkMovementMethod.getInstance());
+		}
+		long id = getItemId(position);
+		if(id != v.chatlogId) {
+			v.setText(log.get(position));
+			v.chatlogId = id;
+		}
+		return v;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ConnectingDialog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,94 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.widget.Toast;
+
+/**
+ * Indeterminate progress dialog that is shown in the MainActivity while trying
+ * to connect to the server. If the connection fails (disconnect before we reach
+ * lobby state), an error toast with the disconnect message is shown.
+ * 
+ */
+public class ConnectingDialog extends ConnectionDependendDialogFragment {
+	@Override
+	public void onStart() {
+		super.onStart();
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).registerReceiver(connectedReceiver, new IntentFilter(Netplay.ACTION_CONNECTED));
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).registerReceiver(disconnectedReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED));
+		
+		if(Netplay.getAppInstance(getActivity().getApplicationContext()).getState() != State.CONNECTING) {
+			dismiss();
+		}
+	}
+	
+	@Override
+	public void onStop() {
+		super.onStop();
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(connectedReceiver);
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(disconnectedReceiver);
+	}
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		ProgressDialog dialog = new ProgressDialog(getActivity());
+		dialog.setIndeterminate(true);
+		dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+		dialog.setTitle(R.string.dialog_connecting_title);
+		dialog.setMessage(getString(R.string.dialog_connecting_message));
+		return dialog;
+	}
+	
+	private BroadcastReceiver connectedReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			Dialog dialog = getDialog();
+			if(dialog != null) {
+				dialog.dismiss();
+			} else {
+				dismiss();
+			}
+		}
+	};
+	
+	private BroadcastReceiver disconnectedReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			Toast.makeText(getActivity(), intent.getExtras().getString(Netplay.EXTRA_MESSAGE), Toast.LENGTH_LONG).show();
+		}
+	};
+	
+	public void onCancel(DialogInterface dialog) {
+		super.onCancel(dialog);
+		Netplay.getAppInstance(getActivity().getApplicationContext()).disconnect();
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/ConnectionDependendDialogFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,65 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.content.LocalBroadcastManager;
+
+/**
+ * Helper class for DialogFragments that are supposed to be dismissed when the
+ * network connection is lost. This is used for some dialog fragments that
+ * appear during connecting (e.g. username input)
+ */
+public class ConnectionDependendDialogFragment extends DialogFragment {
+	@Override
+	public void onStart() {
+		super.onStart();
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).registerReceiver(dismissReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED));
+		if(Netplay.getAppInstance(getActivity().getApplicationContext()).getState() == State.NOT_CONNECTED) {
+			dismiss();
+		}
+	}
+	
+	@Override
+	public void onStop() {
+		super.onStop();
+		LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(dismissReceiver);
+	}
+	
+	private BroadcastReceiver dismissReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			Dialog dialog = getDialog();
+			if(dialog != null) {
+				dialog.dismiss();
+			} else {
+				dismiss();
+			}
+		}
+	};
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,12 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,95 +15,89 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-
 package org.hedgewars.hedgeroid.Datastructures;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.hedgewars.hedgeroid.R;
-import org.hedgewars.hedgeroid.Utils;
-import org.hedgewars.hedgeroid.Datastructures.Map.MapType;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import java.nio.ByteBuffer;
 
 public class FrontendDataUtils {
 
-
-	public static ArrayList<Map> getMaps(Context c){
-		File[] files = Utils.getFilesFromRelativeDir(c,"Maps");
-		ArrayList<Map> ret = new ArrayList<Map>();
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Maps directory doesn't exist
+	 */
+	public static List<MapFile> getMaps(Context c) throws FileNotFoundException {
+		File[] files = FileUtils.getFilesFromRelativeDir(c,"Maps");
+		List<MapFile> ret = new ArrayList<MapFile>();
 
-		for(File f : files){
-			if(Utils.hasFileWithSuffix(f, ".lua")){
-				ret.add(new Map(f,MapType.TYPE_MISSION, c));
-			}else{
-				ret.add(new Map(f, MapType.TYPE_DEFAULT,c));
-			}
+		for(File f : files) {
+			boolean isMission = FileUtils.hasFileWithSuffix(f, ".lua");
+			ret.add(new MapFile(f.getName(), isMission));
 		}
-		Collections.sort(ret);
 
 		return ret;
 	}
 
-	public static List<String> getGameplay(Context c){
-		String[] files = Utils.getFileNamesFromRelativeDir(c, "Scripts/Multiplayer");
-		ArrayList<String> ret = new ArrayList<String>();
-		
-		for(int i = 0; i < files.length; i++){
-			if(files[i].endsWith(".lua")){
-				ret.add(files[i].replace('_', ' ').substring(0, files[i].length()-4)); //replace _ by a space and removed the last four characters (.lua)
+	/**
+	 * Returns a list of all multiplayer scripts (game styles)
+	 * @throws FileNotFoundException if the sdcard isn't available or the Scripts/Multiplayer directory doesn't exist
+	 */
+	public static List<String> getGameStyles(Context c) throws FileNotFoundException {
+		File[] files = FileUtils.getFilesFromRelativeDir(c, "Scripts/Multiplayer");
+		List<String> ret = new ArrayList<String>();
+		/*
+		 * Caution: It is important that the "empty" style has this exact name, because
+		 * it will be interpreted as "don't load a script" by the frontlib, and also by
+		 * the QtFrontend in a netgame. This should probably be improved some time
+		 * (maybe TODO add a dummy script called "Normal" to the MP scripts?) 
+		 */
+		ret.add("Normal");
+		for(int i = 0; i < files.length; i++) {
+			String name = files[i].getName();
+			if(name.endsWith(".lua")){
+				//replace _ by a space and removed the last four characters (.lua)
+				ret.add(name.replace('_', ' ').substring(0, name.length()-4));
 			}
 		}
-		ret.add(0,"None");
-		Collections.sort(ret);
-		return ret;	
+		return ret;
 	}
 
-	public static List<String> getThemes(Context c){
-		List<String> list = Utils.getDirsWithFileSuffix(c, "Themes", "icon.png");
-		Collections.sort(list);
-		return list;
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Themes directory doesn't exist
+	 */
+	public static List<String> getThemes(Context c) throws FileNotFoundException {
+		return FileUtils.getDirsWithFileSuffix(c, "Themes", "icon.png");
 	}
 
-	public static List<Scheme> getSchemes(Context c){
-		List<Scheme> list = Scheme.getSchemes(c);
-		Collections.sort(list);
-		return list;
-	}
-
-	public static List<Weapon> getWeapons(Context c){
-		List<Weapon> list = Weapon.getWeapons(c);
-		Collections.sort(list);
-		return list;
-	}
-
-	public static ArrayList<HashMap<String, ?>> getGraves(Context c){
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Graves/";
-		ArrayList<String> names = Utils.getFilesFromDirWithSuffix(c,"Graphics/Graves", ".png", true);
-		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size());
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist
+	 */
+	public static List<Map<String, ?>> getGraves(Context c) throws FileNotFoundException {
+		File gravePath = FileUtils.getDataPathFile(c, "Graphics", "Graves");
+		List<String> names = FileUtils.getFileNamesFromDirWithSuffix(c,"Graphics/Graves", ".png", true);
+		List<Map<String, ?>> data = new ArrayList<Map<String, ?>>(names.size());
 
 		for(String s : names){
 			HashMap<String, Object> map = new HashMap<String, Object>();
 			map.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(gravePath, s + ".png").getAbsolutePath());
 			int width = b.getWidth();
-			if(b.getHeight() > width){//some pictures contain more 'frames' underneath each other, if so we only use the first frame
-                                Bitmap tmp = Bitmap.createBitmap(width, width, b.getConfig());
-                                int[] pixels = new int[width * width];
-                                b.getPixels(pixels, 0,width,0,0,width,width);
-				tmp.setPixels(pixels,0,width,0,0,width,width);
-                                b.recycle();
-				b = tmp;
+			if(b.getHeight() > width){
+				// some pictures contain more 'frames' underneath each other, if so we only use the first frame
+				b = Bitmap.createBitmap(b, 0, 0, width, width);
 			}
 			map.put("img", b);
 			data.add(map);
@@ -109,24 +105,30 @@
 		return data;
 	}
 
-	public static ArrayList<HashMap<String, ?>> getFlags(Context c){
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Flags/";
-		ArrayList<String> names = Utils.getFilesFromDirWithSuffix(c, "Graphics/Flags", ".png", true);
-		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size());
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist
+	 */
+	public static List<Map<String, ?>> getFlags(Context c) throws FileNotFoundException {
+		File flagsPath = FileUtils.getDataPathFile(c, "Graphics", "Flags");
+		List<String> names = FileUtils.getFileNamesFromDirWithSuffix(c, "Graphics/Flags", ".png", true);
+		List<Map<String, ?>> data = new ArrayList<Map<String, ?>>(names.size());
 
 		for(String s : names){
-			HashMap<String, Object> map = new HashMap<String, Object>();
+			Map<String, Object> map = new HashMap<String, Object>();
 			map.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(flagsPath, s + ".png").getAbsolutePath());
 			map.put("img", b);
 			data.add(map);
 		}
 		return data;
 	}
 
-	public static ArrayList<String> getVoices(Context c){
-		File[] files = Utils.getFilesFromRelativeDir(c, "Sounds/voices");
-		ArrayList<String> ret = new ArrayList<String>();
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Sounds/voices directory doesn't exist
+	 */
+	public static List<String> getVoices(Context c) throws FileNotFoundException {
+		File[] files = FileUtils.getFilesFromRelativeDir(c, "Sounds/voices");
+		List<String> ret = new ArrayList<String>();
 
 		for(File f : files){
 			if(f.isDirectory()) ret.add(f.getName());
@@ -134,35 +136,43 @@
 		return ret;
 	}
 
-	public static ArrayList<String> getForts(Context c){
-		return Utils.getFilesFromDirWithSuffix(c,"Forts", "L.png", true);
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Forts directory doesn't exist
+	 */
+	public static List<String> getForts(Context c) throws FileNotFoundException {
+		return FileUtils.getFileNamesFromDirWithSuffix(c,"Forts", "L.png", true);
 	}
-	public static ArrayList<HashMap<String, ?>> getTypes(Context c){
-		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(6);
+	
+	public static List<Map<String, ?>> getTypes(Context c){
+		List<Map<String, ?>> data = new ArrayList<Map<String, ?>>(6);
 		String[] levels = {c.getString(R.string.human), c.getString(R.string.bot5), c.getString(R.string.bot4), c.getString(R.string.bot3), c.getString(R.string.bot2), c.getString(R.string.bot1)};
 		int[] images = {R.drawable.human, R.drawable.bot5, R.drawable.bot4, R.drawable.bot3, R.drawable.bot2, R.drawable.bot1};
 
 		for(int i = 0; i < levels.length; i++){
-			HashMap<String, Object> map = new HashMap<String, Object>();
+			Map<String, Object> map = new HashMap<String, Object>();
 			map.put("txt", levels[i]);
 			map.put("img", images[i]);
+			map.put("level", i);
+			
 			data.add(map);
 		}
 
 		return data;
 	}
 
-	public static ArrayList<HashMap<String, ?>> getHats(Context c){
-		ArrayList<String> files = Utils.getFilesFromDirWithSuffix(c,"Graphics/Hats", ".png", true);
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Hats/";
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Hats directory doesn't exist
+	 */
+	public static List<Map<String, ?>> getHats(Context c) throws FileNotFoundException {
+		List<String> files = FileUtils.getFileNamesFromDirWithSuffix(c,"Graphics/Hats", ".png", true);
+		File hatsPath = FileUtils.getDataPathFile(c, "Graphics", "Hats");
 		int size = files.size();
-		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(size);
+		List<Map<String, ?>> data = new ArrayList<Map<String, ?>>(size);
 
-		HashMap<String, Object> hashmap; 
 		for(String s : files){
-			hashmap = new HashMap<String, Object>();
+			Map<String, Object> hashmap = new HashMap<String, Object>();
 			hashmap.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(hatsPath, s + ".png").getAbsolutePath());
 			b = Bitmap.createBitmap(b, 0,0,b.getWidth()/2, b.getWidth()/2);
 			hashmap.put("img", b);
 			data.add(hashmap);
@@ -171,50 +181,21 @@
 		return data;
 	}
 
-	public static List<HashMap<String, Object>> getTeams(Context c){
-		List<HashMap<String, Object>> ret = new ArrayList<HashMap<String, Object>>();
-
-		File teamsDir = new File(c.getFilesDir().getAbsolutePath() + '/' + Team.DIRECTORY_TEAMS);
+	public static List<Team> getTeams(Context c) {
+		List<Team> ret = new ArrayList<Team>();
+		
+		File teamsDir = new File(c.getFilesDir(), Team.DIRECTORY_TEAMS);
 		File[] teamFileNames = teamsDir.listFiles();
 		if(teamFileNames != null){
-			for(File s : teamFileNames){
-				Team t = Team.getTeamFromXml(s.getAbsolutePath());
-				if(t != null){
-					t.file = s.getName();
-					ret.add(teamToMap(t));
+			for(File file : teamFileNames){
+				if(file.getName().endsWith(".hwt")) {
+					Team team = Team.load(file);
+					if(team != null){
+						ret.add(team);
+					}
 				}
 			}
 		}
 		return ret;
 	}
-
-	public static HashMap<String, Object> teamToMap(Team t){
-		HashMap<String, Object> hashmap = new HashMap<String, Object>();
-		hashmap.put("team", t);
-		hashmap.put("txt", t.name);
-		hashmap.put("color", t.color);
-		hashmap.put("count", t.hogCount);
-		switch(t.levels[0]){
-		case 0:
-			hashmap.put("img", R.drawable.human);
-			break;
-		case 1:
-			hashmap.put("img", R.drawable.bot5);
-			break;
-		case 2:
-			hashmap.put("img", R.drawable.bot4);
-			break;
-		case 3:
-			hashmap.put("img", R.drawable.bot3);
-			break;
-		case 4:
-			hashmap.put("img", R.drawable.bot2);
-			break;
-		default:
-		case 5:
-			hashmap.put("img", R.drawable.bot1);
-			break;
-		}
-		return hashmap;
-	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,62 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Game configuration from the point of view of the UI. This differs slightly from the
+ * frontlib view, because the engine allows setting a separate initial health and weapon set
+ * for each hog, while the Android UI currently only supports both attributes on a per-game
+ * basis (initial health is contained in the scheme).
+ * 
+ * This difference means that creating a GameConfig object from a frontlib object can, in
+ * theory, lose information. This does not cause problems at the moment because for now
+ * weaponset and initial health are always per-game, but that might change in the future.
+ */
+public final class GameConfig {
+	public static final String DEFAULT_STYLE = "Normal";
+	public static final String DEFAULT_SCHEME = "Default";
+	public static final String DEFAULT_WEAPONSET = "Default";
+	public static final String DEFAULT_THEME = "Bamboo";
+	
+	public final String style;
+	public final Scheme scheme;
+	public final MapRecipe map;
+	public final List<TeamInGame> teams;
+	public final Weaponset weaponset;
+	
+	public GameConfig(String style, Scheme scheme, MapRecipe map, List<TeamInGame> teams, Weaponset weaponset) {
+		this.style = style;
+		this.scheme = scheme;
+		this.map = map;
+		this.teams = Collections.unmodifiableList(new ArrayList<TeamInGame>(teams));
+		this.weaponset = weaponset;
+	}
+
+	@Override
+	public String toString() {
+		return "GameConfig [style=" + style + ", scheme=" + scheme + ", map="
+				+ map + ", teams=" + teams + ", weaponset=" + weaponset + "]";
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameMode.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-public enum GameMode {
-		MODE_LOCAL, MODE_DEMO, MODE_NET, MODE_SAVE
-}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Grave.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-public class Grave{
-
-	public final String name;
-	public final String path;
-	
-	public Grave(String _name, String _path) {
-		name = _name;
-		path = _path;
-	}
-
-	public String toString(){
-		return name;
-	}
-	
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Hog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,36 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+public final class Hog {
+	public final String name, hat;
+	public final int level;
+	
+	public Hog(String name, String hat, int level) {
+		this.name = name;
+		this.hat = hat;
+		this.level = level;
+	}
+
+	@Override
+	public String toString() {
+		return "Hog [name=" + name + ", hat=" + hat + ", level=" + level + "]";
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Map.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class Map implements Comparable<Map>, Parcelable{
-
-	private static final String MISSION_PREFIX = "Mission: ";
-
-	private String name;
-	private String path;
-	private String previewPath;
-	private MapType type;
-
-	public Map(File mapDir, MapType _type, Context c){
-		type = _type;
-
-		name = mapDir.getName();
-		path = mapDir.getAbsolutePath();
-		previewPath = path + "/preview.png";
-		
-		/*switch(type){
-		case TYPE_DEFAULT:
-			
-			break;
-		case TYPE_GENERATED:
-			//TODO
-			break;
-		case TYPE_MISSION:
-			name = MISSION_PREFIX + mapDir.getName();
-			path = mapDir.getAbsolutePath();
-			break;
-		}*/
-
-		
-	}
-	
-	public Map(Parcel in){
-		readFromParcel(in);
-	}
-
-	public String toString(){
-		switch(type){
-		default:
-		case TYPE_DEFAULT:
-			return name;
-		case TYPE_GENERATED:
-			return "bla";
-		case TYPE_MISSION:
-			return MISSION_PREFIX + name;
-		}
-	}
-	
-	public void sendToEngine(EngineProtocolNetwork epn) throws IOException{
-		epn.sendToEngine(String.format("emap %s",name));
-	}
-	
-	public MapType getType(){
-		return type;
-	}
-
-	public Drawable getDrawable(){
-		switch(type){
-		case TYPE_MISSION:
-		case TYPE_DEFAULT:
-			return Drawable.createFromPath(previewPath);
-		case TYPE_GENERATED:
-
-		default:
-			return null;
-		}
-	}
-
-	public int compareTo(Map another) {
-		switch(type){
-		case TYPE_GENERATED:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return name.compareTo(another.name);
-			case TYPE_MISSION:
-				return -1;
-			case TYPE_DEFAULT:
-				return -1;
-			}
-		case TYPE_MISSION:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return 1;
-			case TYPE_MISSION:
-				return name.compareTo(another.name);
-			case TYPE_DEFAULT:
-				return -1;
-			}
-		case TYPE_DEFAULT:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return 1;
-			case TYPE_MISSION:
-				return 1;
-			case TYPE_DEFAULT:
-				return name.compareTo(another.name);
-			}
-		}
-		return 0;//default case this should never happen
-	}
-
-	public enum MapType{
-		TYPE_DEFAULT, TYPE_MISSION, TYPE_GENERATED
-	}
-
-	public int describeContents() {
-		return 0;
-	}
-	
-	public void writeToParcel(Parcel dest, int flags) {
-		dest.writeString(name);
-		dest.writeString(path);
-		dest.writeString(previewPath);
-		dest.writeString(type.name());
-	}
-	
-	private void readFromParcel(Parcel src){
-		name = src.readString();
-		path = src.readString();
-		previewPath = src.readString();
-		type = MapType.valueOf(src.readString());
-	}
-	public static final Parcelable.Creator<Map> CREATOR = new Parcelable.Creator<Map>() {
-		public Map createFromParcel(Parcel source) {
-			return new Map(source);
-		}
-		public Map[] newArray(int size) {
-			return new Map[size];
-		}
-		
-	};
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,86 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.util.FileUtils;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+/**
+ * Represents a map from the data directory
+ */
+public final class MapFile {
+	public static final String MAP_DIRECTORY = "Maps";
+	
+	public final String name;
+	public final boolean isMission;
+	
+	public MapFile(String name, boolean isMission) {
+		this.name = name;
+		this.isMission = isMission;
+	}
+	
+	/**
+	 * @throws FileNotFoundException if the sdcard is not available. Does NOT throw if the requested map file does not exist.
+	 */
+	public static File getFileForMapname(Context ctx, String mapname) throws FileNotFoundException {
+		return FileUtils.getDataPathFile(ctx, MAP_DIRECTORY, mapname);
+	}
+	
+	public static final Comparator<MapFile> MISSIONS_FIRST_NAME_ORDER = new Comparator<MapFile>() {
+		public int compare(MapFile lhs, MapFile rhs) {
+			if(lhs.isMission != rhs.isMission) {
+				return lhs.isMission && !rhs.isMission ? -1 : 1;
+			} else {
+				return lhs.name.compareToIgnoreCase(rhs.name);
+			}
+		}
+	};
+	
+	@Override
+	public String toString() {
+		return "MapFile [name=" + name + ", isMission=" + isMission + "]";
+	}
+
+	public File getPreviewFile(Context c) throws FileNotFoundException {
+		return getPreviewFile(c, name);
+	}
+	
+	public static File getPreviewFile(Context c, String mapName) throws FileNotFoundException {
+		return FileUtils.getDataPathFile(c, MAP_DIRECTORY, mapName, "preview.png");
+	}
+	
+	public static List<String> toDisplayNameList(List<MapFile> mapFiles, Resources res) {
+		String missionPrefix = res.getString(R.string.map_mission_prefix);
+		List<String> result = new ArrayList<String>();
+		for(MapFile mapFile : mapFiles) {
+			result.add((mapFile.isMission ? missionPrefix : "") + mapFile.name);
+		}
+		return result;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,203 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.Arrays;
+import java.util.UUID;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+
+import android.content.res.Resources;
+
+public final class MapRecipe {
+	public static final String MAPNAME_REGULAR = "+rnd+";
+	public static final String MAPNAME_MAZE = "+maze+";
+	public static final String MAPNAME_DRAWN = "+drawn+";
+
+	public final int mapgen;			// Frontlib.MAPGEN_xxx
+	public final int templateFilter;	// Frontlib.TEMPLATEFILTER_xxx, only used when mapgen==MAPGEN_REGULAR
+	public final int mazeSize;			// Frontlib.MAZE_SIZE_xxx, only used when mapgen==MAPGEN_MAZE
+	public final String name, seed, theme;
+	
+	private final byte[] drawData;		// For drawn maps, can be null.
+
+	public MapRecipe(int mapgen, int templateFilter, int mazeSize, String name, String seed, String theme, byte[] drawData) {
+		this.mapgen = mapgen;
+		this.templateFilter = templateFilter;
+		this.mazeSize = mazeSize;
+		this.name = name;
+		this.seed = seed;
+		this.theme = theme;
+		this.drawData = drawData==null ? null : drawData.clone();
+	}
+	
+	public static MapRecipe makeMap(String name, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_NAMED, 0, 0, name, seed, theme, null);
+	}
+	
+	public static MapRecipe makeRandomMap(int templateFilter, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_REGULAR, templateFilter, 0, MAPNAME_REGULAR, seed, theme, null);
+	}
+	
+	public static MapRecipe makeRandomMaze(int mazeSize, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_MAZE, 0, mazeSize, MAPNAME_MAZE, seed, theme, null);
+	}
+	
+	public static MapRecipe makeDrawnMap(String seed, String theme, byte[] drawData) {
+		return new MapRecipe(Frontlib.MAPGEN_DRAWN, 0, 0, MAPNAME_DRAWN, seed, theme, drawData);
+	}
+	
+	public byte[] getDrawData() {
+		return drawData==null ? null : drawData.clone();
+	}
+	
+	public MapRecipe withMapgen(int mapgen) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withTemplateFilter(int templateFilter) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withMazeSize(int mazeSize) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withName(String name) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withSeed(String seed) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withTheme(String theme) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public MapRecipe withDrawData(byte[] drawData) {
+		return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, drawData);
+	}
+	
+	public static String formatMapName(Resources res, String map) {
+		if(map.charAt(0)=='+') {
+			if(map.equals(MAPNAME_REGULAR)) {
+				return res.getString(R.string.map_regular);
+			} else if(map.equals(MAPNAME_MAZE)) {
+				return res.getString(R.string.map_maze);
+			} else if(map.equals(MAPNAME_DRAWN)) {
+				return res.getString(R.string.map_drawn);
+			}
+		}
+		return map;
+	}
+
+	/**
+	 * Returns the mapname corresponding to the map generator (e.g. "+rnd+" for regular maps)
+	 * If the mapgen does not have a unique name (MAPGEN_NAMED) or is not known, the def
+	 * value is returned.
+	 */
+	public static String mapnameForGenerator(int mapgen, String def) {
+		switch(mapgen) {
+		case Frontlib.MAPGEN_REGULAR: return MAPNAME_REGULAR;
+		case Frontlib.MAPGEN_MAZE: return MAPNAME_MAZE;
+		case Frontlib.MAPGEN_DRAWN: return MAPNAME_DRAWN;
+		default: return def;
+		}
+	}
+	
+	/**
+	 * In a sense this is the inverse of mapnameForGenerator. Returns the mapgen that uses
+	 * mapName as special identifier, or MAPGEN_NAMED if there is none.
+	 */
+	public static int generatorForMapname(String mapName) {
+		if(MapRecipe.MAPNAME_REGULAR.equals(mapName)) {
+			return Frontlib.MAPGEN_REGULAR;
+		} else if(MapRecipe.MAPNAME_MAZE.equals(mapName)) {
+			return Frontlib.MAPGEN_MAZE;
+		} else if(MapRecipe.MAPNAME_DRAWN.equals(mapName)) {
+			return Frontlib.MAPGEN_DRAWN;
+		} else {
+			return Frontlib.MAPGEN_NAMED;
+		}
+	}
+	
+	@Override
+	public String toString() {
+		return "MapRecipe [mapgen=" + mapgen + ", templateFilter="
+				+ templateFilter + ", mazeSize=" + mazeSize + ", name=" + name
+				+ ", seed=" + seed + ", theme=" + theme + ", drawData="
+				+ Arrays.toString(drawData) + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + Arrays.hashCode(drawData);
+		result = prime * result + mapgen;
+		result = prime * result + mazeSize;
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		result = prime * result + ((seed == null) ? 0 : seed.hashCode());
+		result = prime * result + templateFilter;
+		result = prime * result + ((theme == null) ? 0 : theme.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		MapRecipe other = (MapRecipe) obj;
+		if (!Arrays.equals(drawData, other.drawData))
+			return false;
+		if (mapgen != other.mapgen)
+			return false;
+		if (mazeSize != other.mazeSize)
+			return false;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		if (seed == null) {
+			if (other.seed != null)
+				return false;
+		} else if (!seed.equals(other.seed))
+			return false;
+		if (templateFilter != other.templateFilter)
+			return false;
+		if (theme == null) {
+			if (other.theme != null)
+				return false;
+		} else if (!theme.equals(other.theme))
+			return false;
+		return true;
+	}
+
+	public static String makeRandomSeed() {
+		return "{"+UUID.randomUUID().toString()+"}";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MetaScheme.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+
+public final class MetaScheme {
+	public static final MetaScheme INSTANCE = Flib.INSTANCE.flib_get_metascheme().deref();
+	
+	public static final class Mod {
+		public final String name;
+		public final int bitmaskIndex;
+		
+		public Mod(String name, int bitmaskIndex) {
+			this.name = name;
+			this.bitmaskIndex = bitmaskIndex;
+		}
+
+		@Override
+		public String toString() {
+			return "MetaScheme$Mod [name=" + name + ", bitmaskIndex=" + bitmaskIndex + "]";
+		}
+	}
+	
+	public static final class Setting {
+		public final String name, engineCommand;
+		public final boolean maxMeansInfinity, times1000;
+		public final int min, max, def;
+		
+		public Setting(String name, String engineCommand, boolean maxMeansInfinity, boolean times1000, int min, int max, int def) {
+			this.name = name;
+			this.engineCommand = engineCommand;
+			this.maxMeansInfinity = maxMeansInfinity;
+			this.times1000 = times1000;
+			this.min = min;
+			this.max = max;
+			this.def = def;
+		}
+
+		@Override
+		public String toString() {
+			return "MetaScheme$Setting [name=" + name + ", engineCommand=" + engineCommand
+					+ ", maxMeansInfinite=" + maxMeansInfinity + ", times1000="
+					+ times1000 + ", min=" + min + ", max=" + max + ", def="
+					+ def + "]";
+		}
+	}
+	
+	public final List<Mod> mods;
+	public final List<Setting> settings;
+	
+	public MetaScheme(List<Mod> mods, List<Setting> settings) {
+		this.mods = Collections.unmodifiableList(new ArrayList<Mod>(mods));
+		this.settings = Collections.unmodifiableList(new ArrayList<Setting>(settings));
+	}
+
+	@Override
+	public String toString() {
+		return "MetaScheme [\nmods=" + mods + ", \nsettings=" + settings + "\n]";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.Comparator;
+
+/**
+ * Basic information about a player on a server.
+ */
+public final class Player {
+	public final String name;
+	public final boolean registered, admin;
+	
+	public Player(String name, boolean registered, boolean admin) {
+		this.name = name;
+		this.registered = registered;
+		this.admin = admin;
+	}
+
+	@Override
+	public String toString() {
+		return "Player [name=" + name + ", registered=" + registered
+				+ ", admin=" + admin + "]";
+	}
+
+	public static Comparator<Player> NAME_ORDER = new Comparator<Player>() {
+		public int compare(Player lhs, Player rhs) {
+			return lhs.name.compareToIgnoreCase(rhs.name);
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,35 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+public final class PlayerInRoom {
+	public final Player player;
+	public final boolean ready;
+	
+	public PlayerInRoom(Player player, boolean ready) {
+		this.player = player;
+		this.ready = ready;
+	}
+
+	@Override
+	public String toString() {
+		return "PlayerInRoom [player=" + player + ", ready=" + ready + "]";
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Room.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,55 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import android.content.res.Resources;
+
+/**
+ * A room as presented in the roomlist in a server lobby.
+ */
+public final class Room {
+	public final String name, map, scheme, weapons, owner;
+	public final int playerCount, teamCount;
+	public final boolean inProgress;
+	
+	public Room(String name, String map, String scheme, String weapons,
+			String owner, int playerCount, int teamCount, boolean inProgress) {
+		this.name = name;
+		this.map = map;
+		this.scheme = scheme;
+		this.weapons = weapons;
+		this.owner = owner;
+		this.playerCount = playerCount;
+		this.teamCount = teamCount;
+		this.inProgress = inProgress;
+	}
+	
+	public String formatMapName(Resources res) {
+		return MapRecipe.formatMapName(res, map);
+	}
+
+	@Override
+	public String toString() {
+		return "RoomlistRoom [name=" + name + ", map=" + map + ", scheme="
+				+ scheme + ", weapons=" + weapons + ", owner=" + owner
+				+ ", playerCount=" + playerCount + ", teamCount=" + teamCount
+				+ ", inProgress=" + inProgress + "]";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomWithId.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,43 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.Comparator;
+
+public final class RoomWithId {
+	public final Room room;
+	public final long id;
+	
+	public RoomWithId(Room room, long id) {
+		this.room = room;
+		this.id = id;
+	}
+
+	@Override
+	public String toString() {
+		return "RoomWithId [room=" + room + ", id=" + id + "]";
+	}
+	
+	public static final Comparator<RoomWithId> NEWEST_FIRST_ORDER = new Comparator<RoomWithId>() {
+		public int compare(RoomWithId lhs, RoomWithId rhs) {
+			return rhs.id<lhs.id ? -1 : rhs.id>lhs.id ? 1 : 0;
+		}
+	};
+}
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,11 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,356 +14,93 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 package org.hedgewars.hedgeroid.Datastructures;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-
-import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
 
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class Scheme implements Parcelable, Comparable<Scheme>{
-
-	public static final String DIRECTORY_SCHEME = "schemes";
-
-	private String name;
-	//private ArrayList<Integer> basic;
-	private Integer gamemod;
-	private ArrayList<Integer> basic;;
-	private static ArrayList<LinkedHashMap<String, ?>> basicflags = new ArrayList<LinkedHashMap<String, ?>>();//TODO why is it static?
-	public int health;
-	
-	public Scheme(String _name, ArrayList<Integer> _basic, int _gamemod){
-		name = _name;
-		gamemod = _gamemod;
-		basic = _basic;
+public final class Scheme {
+	public final String name;
+	public final Map<String, Integer> settings;
+	public final Map<String, Boolean> mods;
+		
+	public Scheme(String name, Map<String, Integer> settings, Map<String, Boolean> mods) {
+		this.name = name;
+		this.settings = Collections.unmodifiableMap(new HashMap<String, Integer>(settings));
+		this.mods = Collections.unmodifiableMap(new HashMap<String, Boolean>(mods));
 	}
 	
-	public Scheme(Parcel in){
-		readFromParcel(in);
-	}
-
-	public void sendToEngine(EngineProtocolNetwork epn)throws IOException{ 
-		epn.sendToEngine(String.format("e$gmflags %d", gamemod));
-
-		for(int pos = 0; pos < basic.size(); pos++){
-			LinkedHashMap<String, ?> basicflag = basicflags.get(pos);
-			
-			String command = (String)basicflag.get("command");
-			Integer value = basic.get(pos);
-			
-			if(command.equals("inithealth")){//Health is a special case, it doesn't need to be send 				                             
-				health = value;              //to the engine yet, we'll do that with the other HH info
-				continue;
-			}
-			
-			Boolean checkOverMax = (Boolean) basicflag.get("checkOverMax");
-			Boolean times1000 = (Boolean) basicflag.get("times1000");
-			Integer max = (Integer) basicflag.get("max");
-			
-			if(checkOverMax && value >= max) value = max;
-			if(times1000) value *= 1000;
-			
-			epn.sendToEngine(String.format("%s %d", command, value));
-		}
-	}
-	public String toString(){
-		return name;
+	public int getHealth() {
+		Integer health = settings.get("health");
+		return health==null ? 100 : health.intValue();
 	}
 
-
-	public static final int STATE_START = 0;
-	public static final int STATE_ROOT = 1;
-	public static final int STATE_NAME = 2;
-	public static final int STATE_BASICFLAGS = 3;
-	public static final int STATE_GAMEMOD = 4;
-	public static final int STATE_BASICFLAG_INTEGER = 5;
-	public static final int STATE_GAMEMOD_TRUE = 6;
-	public static final int STATE_GAMEMOD_FALSE = 7;
-
-	public static ArrayList<Scheme> getSchemes(Context c) throws IllegalArgumentException{
-		String dir = c.getFilesDir().getAbsolutePath() + '/' + DIRECTORY_SCHEME + '/';
-		String[] files = new File(dir).list(fnf);
-		if(files == null) files = new String[]{};
-		Arrays.sort(files);
-		ArrayList<Scheme> schemes = new ArrayList<Scheme>();
-
-		try {
-			XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
-			XmlPullParser xmlPuller = xmlPullFactory.newPullParser();
-
-			for(String file : files){
-				BufferedReader br = new BufferedReader(new FileReader(dir + file), 1024);
-				xmlPuller.setInput(br);
-				String name = null;
-				ArrayList<Integer> basic = new ArrayList<Integer>();
-				Integer gamemod = 0;
-				int health = 0;
-				int mask = 0x000000004;
-
-				int eventType = xmlPuller.getEventType();
-				int state = STATE_START;
-				while(eventType != XmlPullParser.END_DOCUMENT){
-					switch(state){
-					case STATE_START:
-						if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().equals("scheme")) state = STATE_ROOT;
-						else if(eventType != XmlPullParser.START_DOCUMENT) throwException(file, eventType);
-						break;
-					case STATE_ROOT:
-						if(eventType == XmlPullParser.START_TAG){
-							if(xmlPuller.getName().equals("basicflags")) state = STATE_BASICFLAGS;
-							else if(xmlPuller.getName().toLowerCase().equals("gamemod")) state = STATE_GAMEMOD;
-							else if(xmlPuller.getName().toLowerCase().equals("name")) state = STATE_NAME;
-							else throwException(file, eventType);
-						}else if(eventType == XmlPullParser.END_TAG) state = STATE_START;
-						else throwException(xmlPuller.getText(), eventType);
-						break;
-					case STATE_BASICFLAGS:
-						if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().toLowerCase().equals("integer")) state = STATE_BASICFLAG_INTEGER;
-						else if(eventType == XmlPullParser.END_TAG)	state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_GAMEMOD:
-						if(eventType == XmlPullParser.START_TAG){
-							if(xmlPuller.getName().toLowerCase().equals("true")) state = STATE_GAMEMOD_TRUE;
-							else if(xmlPuller.getName().toLowerCase().equals("false")) state = STATE_GAMEMOD_FALSE;
-							else throwException(file, eventType);
-						}else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_NAME:
-						if(eventType == XmlPullParser.TEXT) name = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_BASICFLAG_INTEGER:
-						if(eventType == XmlPullParser.TEXT) basic.add(Integer.parseInt(xmlPuller.getText().trim()));
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_BASICFLAGS;
-						else throwException(file, eventType);
-						break;
-					case STATE_GAMEMOD_FALSE:
-						if(eventType == XmlPullParser.TEXT) gamemod <<= 1;
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_GAMEMOD;
-						else throwException(file, eventType);
-						break;
-					case STATE_GAMEMOD_TRUE:
-						if(eventType == XmlPullParser.TEXT){
-							gamemod |= mask;
-							gamemod <<= 1;
-						}else if(eventType == XmlPullParser.END_TAG) state = STATE_GAMEMOD;
-						else throwException(file, eventType);
-						break;
-					}
-					eventType = getEventType(xmlPuller);
-				}//end while(eventtype != END_DOCUMENT
-				schemes.add(new Scheme(name, basic, gamemod));
-			}//end for(string file : files
-			return schemes;
-		} catch (XmlPullParserException e) {
-			e.printStackTrace();
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
+	public static Scheme createDefaultScheme(MetaScheme meta) {
+		String name = GameConfig.DEFAULT_SCHEME;
+		Map<String, Integer> settings = new TreeMap<String, Integer>();
+		Map<String, Boolean> mods = new TreeMap<String, Boolean>();
+		for(MetaScheme.Setting setting : meta.settings) {
+			settings.put(setting.name, setting.def);
 		}
-		return new ArrayList<Scheme>();//TODO handle correctly
+		for(MetaScheme.Mod mod : meta.mods) {
+			mods.put(mod.name, Boolean.FALSE);
+		}
+		return new Scheme(name, settings, mods);
+	}
+	
+	@Override
+	public String toString() {
+		return "Scheme [name=" + name + ", settings=" + settings + ", mods="
+				+ mods + "]";
 	}
 	
-	private static FilenameFilter fnf = new FilenameFilter(){
-		public boolean accept(File dir, String filename) {
-			return filename.toLowerCase().startsWith("scheme_");
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		result = prime * result + ((mods == null) ? 0 : mods.hashCode());
+		result = prime * result
+				+ ((settings == null) ? 0 : settings.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Scheme other = (Scheme) obj;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		if (mods == null) {
+			if (other.mods != null)
+				return false;
+		} else if (!mods.equals(other.mods))
+			return false;
+		if (settings == null) {
+			if (other.settings != null)
+				return false;
+		} else if (!settings.equals(other.settings))
+			return false;
+		return true;
+	}
+
+	public static final Comparator<Scheme> NAME_ORDER = new Comparator<Scheme>() {
+		public int compare(Scheme lhs, Scheme rhs) {
+			return String.CASE_INSENSITIVE_ORDER.compare(lhs.name, rhs.name);
 		}
 	};
-
-	/**
-	 * This method will parse the basic flags from a prespecified xml file.
-	 * I use a raw xml file rather than one parsed by aatp at compile time
-	 * to keep it generic with other frontends, ie in the future we could 
-	 * use one provided by the Data folder.
-	 */
-	public static void parseBasicFlags(Context c){
-		String filename = String.format("%s/%s/basicflags", c.getFilesDir().getAbsolutePath(), DIRECTORY_SCHEME);
-
-		XmlPullParser xmlPuller = null;
-		BufferedReader br = null;
-		try {
-			XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
-			xmlPuller = xmlPullFactory.newPullParser();
-			br = new BufferedReader(new FileReader(filename), 1024);
-			xmlPuller.setInput(br);
-
-			int eventType = getEventType(xmlPuller);
-			boolean continueParsing = true;
-			do{
-				switch(eventType){
-				
-				case XmlPullParser.START_TAG:
-					if(xmlPuller.getName().toLowerCase().equals("flag")){
-						basicflags.add(parseFlag(xmlPuller));
-					}else if(xmlPuller.getName().toLowerCase().equals("basicflags")){
-						eventType = getEventType(xmlPuller);
-					}else{
-						skipCurrentTag(xmlPuller);
-						eventType = getEventType(xmlPuller);
-					}
-					break;
-				case XmlPullParser.START_DOCUMENT://ignore all tags not being "flag"
-				case XmlPullParser.END_TAG:
-				case XmlPullParser.TEXT:
-				default:
-					continueParsing = true;
-				case XmlPullParser.END_DOCUMENT:
-					continueParsing = false;
-				}
-			}while(continueParsing);
-
-		}catch(IOException e){
-			e.printStackTrace();
-		}catch (XmlPullParserException e) {
-			e.printStackTrace();
-		}finally{
-			if(br != null)
-				try {
-					br.close();
-				} catch (IOException e) {}
-		}
-
-	}
-
-	/*
-	 * * Parses a Tag structure from xml as example we use
-	 *<flag>
-	 *   <checkOverMax>
-	 *       <boolean>false</boolean>
-	 *   </checkOverMax>
-	 *</flag>
-	 *
-	 * It returns a LinkedHashMap with key/value pairs
-	 */
-	private static LinkedHashMap<String, Object> parseFlag(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
-		LinkedHashMap<String, Object> hash = new LinkedHashMap<String, Object>();
-
-		int eventType = xmlPuller.getEventType();//Get the event type which triggered this method
-		if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().toLowerCase().equals("flag")){//valid start of flag tag
-			String lcKey = null;
-			String lcType = null;
-			String value = null;
-
-			eventType = getEventType(xmlPuller);//<checkOverMax>
-			while(eventType == XmlPullParser.START_TAG){
-				lcKey = xmlPuller.getName();//checkOverMax
-				if(getEventType(xmlPuller) == XmlPullParser.START_TAG){//<boolean>
-					lcType = xmlPuller.getName().toLowerCase();
-					if(getEventType(xmlPuller) == XmlPullParser.TEXT){
-						value = xmlPuller.getText();
-						if(getEventType(xmlPuller) == XmlPullParser.END_TAG && //</boolean> 
-								getEventType(xmlPuller) == XmlPullParser.END_TAG){//</checkOverMax>
-							if(lcType.equals("boolean")) hash.put(lcKey, new Boolean(value));
-							else if(lcType.equals("string"))hash.put(lcKey, value);							
-							else if(lcType.equals("integer")){
-								try{
-									hash.put(lcKey, new Integer(value));
-								}catch (NumberFormatException e){
-									throw new XmlPullParserException("Wrong integer value in xml file");
-								}
-							}else{
-								throwException("basicflags", eventType);
-							}
-						}//</boolean> / </checkOverMax>
-					}//if TEXT
-				}//if boolean
-				eventType = getEventType(xmlPuller);//start new loop
-			}
-			eventType = getEventType(xmlPuller);//</flag>
-		}
-
-		return hash;
-	}
-
-	private static void skipCurrentTag(XmlPullParser xmlPuller) throws XmlPullParserException, IOException{
-		int eventType = xmlPuller.getEventType();
-		if(eventType != XmlPullParser.START_TAG)return;
-		String tag = xmlPuller.getName().toLowerCase();
-
-		while(true){
-			eventType = getEventType(xmlPuller);//getNext()
-			switch(eventType){
-			case XmlPullParser.START_DOCUMENT://we're inside of a start tag so START_ or END_DOCUMENT is just wrong
-			case XmlPullParser.END_DOCUMENT:
-				throw new XmlPullParserException("invalid xml file");
-			case XmlPullParser.START_TAG://if we get a new tag recursively handle it
-				skipCurrentTag(xmlPuller);
-				break;
-			case XmlPullParser.TEXT:
-				break;
-			case XmlPullParser.END_TAG:
-				if(!xmlPuller.getName().toLowerCase().equals(tag)){//if the end tag doesn't match the start tag
-					throw new XmlPullParserException("invalid xml file");
-				}else{
-					return;//skip completed	
-				}
-
-			}
-		}
-	}
-
-	/**
-	 * Skips whitespaces..
-	 */
-	private static int getEventType(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
-		int eventType = xmlPuller.next();
-		while(eventType == XmlPullParser.TEXT && xmlPuller.isWhitespace()){
-			eventType = xmlPuller.next();
-		}
-		return eventType;
-	}
-	private static void throwException(String file, int eventType){
-		throw new IllegalArgumentException(String.format("Xml file: %s malformed with error: %d.", file, eventType));
-	}
-
-	public int describeContents() {
-		return 0;
-	}
-
-	public void writeToParcel(Parcel dest, int flags) {
-		dest.writeString(name);
-		dest.writeInt(gamemod);
-		dest.writeList(basic);
-	}
-	
-	public void readFromParcel(Parcel src){
-		name = src.readString();
-		gamemod = src.readInt();
-		basic = src.readArrayList(ArrayList.class.getClassLoader());
-	}
-
-	public static final Parcelable.Creator<Scheme> CREATOR = new Parcelable.Creator<Scheme>() {
-		public Scheme createFromParcel(Parcel source) {
-			return new Scheme(source);
-		}
-		public Scheme[] newArray(int size) {
-			return new Scheme[size];
-		}
-		
-	};
-
-	public int compareTo(Scheme another) {
-		return name.compareTo(another.name);
-	}
-}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,75 @@
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemelistPtr;
+
+import android.content.Context;
+
+/**
+ * Functions for handling the persistent list of schemes.
+ * Schemes in that list are identified by name (case sensitive).
+ */
+public final class Schemes {
+	private Schemes() {
+		throw new AssertionError("This class is not meant to be instantiated");
+	}
+	
+	public static File getUserSchemesFile(Context c) {
+		return new File(c.getFilesDir(), "schemes_user.ini");
+	}
+	
+	public static File getBuiltinSchemesFile(Context c) {
+		return new File(c.getFilesDir(), "schemes_builtin.ini");
+	}
+	
+	public static List<Scheme> loadAllSchemes(Context c) throws IOException {
+		List<Scheme> result = loadBuiltinSchemes(c);
+		result.addAll(loadUserSchemes(c));
+		return result;
+	}
+	
+	public static List<Scheme> loadUserSchemes(Context c) throws IOException {
+		return loadSchemes(c, getUserSchemesFile(c));
+	}
+	
+	public static List<Scheme> loadBuiltinSchemes(Context c) throws IOException {
+		return loadSchemes(c, getBuiltinSchemesFile(c));
+	}
+	
+	public static List<Scheme> loadSchemes(Context c, File schemeFile) throws IOException {
+		if(!schemeFile.isFile()) {
+			// No schemes file == no schemes, no error
+			return new ArrayList<Scheme>();
+		}
+		SchemelistPtr schemeListPtr = null;
+		try {
+			schemeListPtr = Flib.INSTANCE.flib_schemelist_from_ini(schemeFile.getAbsolutePath());
+			if(schemeListPtr == null) {
+				throw new IOException("Unable to read schemelist");
+			}
+			return schemeListPtr.deref();
+		} finally {
+			if(schemeListPtr != null) {
+				Flib.INSTANCE.flib_schemelist_destroy(schemeListPtr);
+			}
+		}
+	}
+	
+	public static void saveUserSchemes(Context c, List<Scheme> schemes) throws IOException {
+		SchemelistPtr ptr = SchemelistPtr.createJavaOwned(schemes);
+		Flib.INSTANCE.flib_schemelist_to_ini(getUserSchemesFile(c).getAbsolutePath(), ptr);
+	}
+	
+	public static List<String> toNameList(List<Scheme> schemes) {
+		List<String> result = new ArrayList<String>();
+		for(Scheme scheme : schemes) {
+			result.add(scheme.name);
+		}
+		return result;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,12 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,356 +15,77 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-
 package org.hedgewars.hedgeroid.Datastructures;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
-import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-import org.xmlpull.v1.XmlSerializer;
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
 import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Xml;
 
-public class Team implements Parcelable{
-
+public final class Team {
 	public static final String DIRECTORY_TEAMS = "teams";
-	private static final Integer[] TEAM_COLORS = {
-		0xd12b42, /* red    */ 
-		0x4980c1, /* blue   */ 
-		0x6ab530, /* green  */ 
-		0xbc64c4, /* purple */ 
-		0xe76d14, /* orange */ 
-		0x3fb6e6, /* cyan   */ 
-		0xe3e90c, /* yellow */ 
-		0x61d4ac, /* mint   */ 
-		0xf1c3e1, /* pink   */ 
-		/* add new colors here */
-	};
+
+	public static final int HEDGEHOGS_PER_TEAM = Flib.INSTANCE.flib_get_hedgehogs_per_team();
+	public static final int maxNumberOfTeams = PascalExports.HWgetMaxNumberOfTeams();
+
+	public final String name, grave, flag, voice, fort;
+	public final List<Hog> hogs;
 
-//	private static final Integer[] TEAM_COLORS = {
-//		0xff0000, /* red    */ 
-//		0x00ff00, /* blue   */ 
-//		0x0000ff, /* green  */ 
-//	};
-
-	private static final int STATE_START = 0;
-	private static final int STATE_ROOT = 1;
-	private static final int STATE_HOG_ROOT = 2;
-
-	public String name, grave, flag, voice, fort, hash;
-	public String file = null;
-
-	public static int maxNumberOfHogs = 0;
-	public static int maxNumberOfTeams = 0;
-
-	static{
-		maxNumberOfHogs = PascalExports.HWgetMaxNumberOfHogs();
-		maxNumberOfTeams = PascalExports.HWgetMaxNumberOfTeams();
-	}
-	public String[] hats = new String[maxNumberOfHogs];
-	public String[] hogNames = new String[maxNumberOfHogs];
-	public int[] levels = new int[maxNumberOfHogs];
-
-	public int hogCount = 4;
-	public int color = TEAM_COLORS[0];
-
-	public Team(){
+	public Team(String name, String grave, String flag, String voice, String fort, List<Hog> hogs) {
+		if(hogs.size() != HEDGEHOGS_PER_TEAM) {
+			throw new IllegalArgumentException("A team must consist of "+HEDGEHOGS_PER_TEAM+" hogs.");
+		}
+		this.name = name;
+		this.grave = grave;
+		this.flag = flag;
+		this.voice = voice;
+		this.fort = fort;
+		this.hogs = Collections.unmodifiableList(new ArrayList<Hog>(hogs));
 	}
 
-	public Team(Parcel in){
-		readFromParcel(in);
-	}
-
-	public boolean equals(Object o){
-		if(super.equals(o)) return true;
-		else if(o instanceof Team){
-			Team t = (Team)o;
-			boolean ret = name.equals(t.name);
-			ret &= grave.equals(t.grave);
-			ret &= flag.equals(t.flag);
-			ret &= voice.equals(t.voice);
-			ret &= fort.equals(t.fort);
-			ret &= hash.equals(t.hash);
-			return ret;
-		}else{
-			return false;
+	public void save(File f) throws IOException {
+		TeamPtr teamPtr = TeamPtr.createJavaOwned(this);
+		if(Flib.INSTANCE.flib_team_to_ini(f.getAbsolutePath(), teamPtr) != 0) {
+			throw new IOException("Error saving team "+name);
 		}
 	}
 
-	public void setRandomColor(int[] illegalcolors){
-		Integer[] colorsToPickFrom = TEAM_COLORS;
-		if(illegalcolors != null){
-			ArrayList<Integer> colors = new ArrayList<Integer>();
-			for(int color : TEAM_COLORS){
-				boolean validColor = true;
-				for(int illegal : illegalcolors){
-					if(color == illegal) validColor = false;
-				}
-				if(validColor) colors.add(color);
-			}
-			if(colors.size() != 0) colorsToPickFrom = colors.toArray(new Integer[1]);
-		}
-		int index = (int)Math.round(Math.random()*(colorsToPickFrom.length-1));
-		color = colorsToPickFrom[index];
-	}
-
-
-	public void sendToEngine(EngineProtocolNetwork epn, int hogCount, int health) throws IOException{
-		epn.sendToEngine(String.format("eaddteam %s %d %s", hash, color, name));
-		epn.sendToEngine(String.format("egrave %s", grave));
-		epn.sendToEngine(String.format("efort %s", fort));
-		epn.sendToEngine(String.format("evoicepack %s", voice));
-		epn.sendToEngine(String.format("eflag %s", flag));
-
-		for(int i = 0; i < hogCount; i++){
-			epn.sendToEngine(String.format("eaddhh %d %d %s", levels[i], health, hogNames[i]));
-			epn.sendToEngine(String.format("ehat %s", hats[i]));
-		}
-	}
-
-	public void setFileName(Context c){
-		if(file == null){
-		  	file = validFileName(c, name);
-		}
-	}
-	private String validFileName(Context c, String fileName){
-		String absolutePath = String.format("%s/%s", c.getFilesDir(), fileName);
-		File f = new File(absolutePath);
-		if(f.exists()){
-			String newFileName = fileName + (int)(Math.random()*10);
-			return validFileName(c, newFileName);
-		}else{
-			return fileName;
+	public static Team load(File f) {
+		TeamPtr teamPtr = Flib.INSTANCE.flib_team_from_ini(f.getAbsolutePath());
+		if(teamPtr != null) {
+			Team team = teamPtr.deref().team;
+			Flib.INSTANCE.flib_team_destroy(teamPtr);
+			return team;
+		} else {
+			return null;
 		}
 	}
 	
-	/*
-	 * XML METHODS
-	 */
-
-	/**
-	 * Read the xml file path and convert it to a Team object
-	 * @param path absolute path to the xml file
-	 * @return
-	 */
-	public static Team getTeamFromXml(String path){
-		try {
-			XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
-			XmlPullParser xmlPuller = xmlPullFactory.newPullParser();
-
-			BufferedReader br = new BufferedReader(new FileReader(path), 1024);
-			xmlPuller.setInput(br);
-			Team team = new Team();
-			int hogCounter = 0;
-
-			int eventType = xmlPuller.getEventType();
-			int state = STATE_START;
-			while(eventType != XmlPullParser.END_DOCUMENT){
-				switch(state){
-				case STATE_START:
-					if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().equals("team")) state = STATE_ROOT;
-					else if(eventType != XmlPullParser.START_DOCUMENT) throwException(path, eventType);
-					break;
-				case STATE_ROOT:
-					if(eventType == XmlPullParser.START_TAG){
-						if(xmlPuller.getName().toLowerCase().equals("name")){
-							team.name = getXmlText(xmlPuller, "name");
-						}else if(xmlPuller.getName().toLowerCase().equals("flag")){
-							team.flag= getXmlText(xmlPuller, "flag");
-						}else if(xmlPuller.getName().toLowerCase().equals("voice")){
-							team.voice = getXmlText(xmlPuller, "voice");
-						}else if(xmlPuller.getName().toLowerCase().equals("grave")){
-							team.grave = getXmlText(xmlPuller, "grave");
-						}else if(xmlPuller.getName().toLowerCase().equals("fort")){
-							team.fort = getXmlText(xmlPuller, "fort");
-						}else if(xmlPuller.getName().toLowerCase().equals("hash")){
-							team.hash = getXmlText(xmlPuller, "hash");
-						}else if(xmlPuller.getName().toLowerCase().equals("hog")){
-							state = STATE_HOG_ROOT;
-						}else throwException(xmlPuller.getName(), eventType);
-					}else if(eventType == XmlPullParser.END_TAG) state = STATE_START;
-					else throwException(xmlPuller.getText(), eventType);
-					break;
-				case STATE_HOG_ROOT:
-					if(eventType == XmlPullParser.START_TAG){
-						if(xmlPuller.getName().toLowerCase().equals("name")){
-							team.hogNames[hogCounter] = getXmlText(xmlPuller, "name");
-						}else if(xmlPuller.getName().toLowerCase().equals("hat")){
-							team.hats[hogCounter] = getXmlText(xmlPuller, "hat");
-						}else if(xmlPuller.getName().toLowerCase().equals("level")){
-							team.levels[hogCounter] = Integer.parseInt(getXmlText(xmlPuller, "level"));
-						}else throwException(xmlPuller.getText(), eventType);
-					}else if(eventType == XmlPullParser.END_TAG){
-						hogCounter++;
-						state = STATE_ROOT;
-					}else throwException(xmlPuller.getText(), eventType);
-					break;
-				}
-				eventType = getEventType(xmlPuller);
-			}//end while(eventtype != END_DOCUMENT
-			return team;
-		} catch (NumberFormatException e){
-			e.printStackTrace();
-		} catch (XmlPullParserException e) {
-			e.printStackTrace();
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		return null;
-	}
-
-	private static String getXmlText(XmlPullParser xmlPuller, String parentTag)throws XmlPullParserException, IOException{
-		if(getEventType(xmlPuller) == XmlPullParser.TEXT){
-			String txt = xmlPuller.getText();
-			if(getEventType(xmlPuller) == XmlPullParser.END_TAG && xmlPuller.getName().toLowerCase().equals(parentTag)){
-				return txt;
-			}
-		}
-		throw new XmlPullParserException("malformed xml file on string read from tag: " + parentTag);
-	}
-
-	/**
-	 * Skips whitespaces..
-	 */
-	private static int getEventType(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
-		int eventType = xmlPuller.next();
-		while(eventType == XmlPullParser.TEXT && xmlPuller.isWhitespace()){
-			eventType = xmlPuller.next();
-		}
-		return eventType;
-	}
-
-	private static void throwException(String file, int eventType){
-		throw new IllegalArgumentException(String.format("Xml file: %s malformed with error: %d.", file, eventType));
+	public static File getTeamfileByName(Context c, String teamName) {
+		return new File(new File(c.getFilesDir(), DIRECTORY_TEAMS), FileUtils.replaceBadChars(teamName)+".hwt");
 	}
-
-	public void writeToXml(OutputStream os){
-		XmlSerializer serializer = Xml.newSerializer();
-		try{
-			serializer.setOutput(os, "UTF-8");	
-			serializer.startDocument("UTF-8", true);
-			serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
-			serializer.startTag(null, "team");
-			serializer.startTag(null, "name");
-			serializer.text(name);
-			serializer.endTag(null, "name");
-			serializer.startTag(null, "flag");
-			serializer.text(flag);
-			serializer.endTag(null, "flag");
-			serializer.startTag(null, "fort");
-			serializer.text(fort);
-			serializer.endTag(null, "fort");
-			serializer.startTag(null, "grave");
-			serializer.text(grave);
-			serializer.endTag(null, "grave");
-			serializer.startTag(null, "voice");
-			serializer.text(voice);
-			serializer.endTag(null, "voice");
-			serializer.startTag(null, "hash");
-			serializer.text(hash);
-			serializer.endTag(null, "hash");
-
-			for(int i = 0; i < maxNumberOfHogs; i++){
-				serializer.startTag(null, "hog");
-				serializer.startTag(null, "name");
-				serializer.text(hogNames[i]);
-				serializer.endTag(null, "name");
-				serializer.startTag(null, "hat");
-				serializer.text(hats[i]);
-				serializer.endTag(null, "hat");
-				serializer.startTag(null, "level");
-				serializer.text(String.valueOf(levels[i]));
-				serializer.endTag(null, "level");
-
-				serializer.endTag(null, "hog");
-			}
-			serializer.endTag(null, "team");
-			serializer.endDocument();
-			serializer.flush();
-
-		} catch (IOException e) {
-			e.printStackTrace();
-		}finally{
-			try {
-				os.close();
-			} catch (IOException e) {}
-		}
+	
+	@Override
+	public String toString() {
+		return "Team [name=" + name + ", grave=" + grave + ", flag=" + flag
+				+ ", voice=" + voice + ", fort=" + fort + ", hogs=" + hogs
+				+ "]";
 	}
-	/*
-	 * END XML METHODS
-	 */
-
-
-
-	/*
-	 * PARCABLE METHODS
-	 */
-
-	public int describeContents() {
-		return 0;
-	}
-
-	public void writeToParcel(Parcel dest, int flags) {
-		dest.writeString(name);
-		dest.writeString(grave);
-		dest.writeString(flag);
-		dest.writeString(voice);
-		dest.writeString(fort);
-		dest.writeString(hash);
-		dest.writeStringArray(hats);
-		dest.writeStringArray(hogNames);
-		dest.writeIntArray(levels);
-		dest.writeInt(color);
-		dest.writeInt(hogCount);
-		dest.writeString(file);
-	}
-
-
-	public void readFromParcel(Parcel src){
-		name = src.readString();
-		grave = src.readString();
-		flag = src.readString();
-		voice = src.readString();
-		fort = src.readString();
-		hash = src.readString();
-		src.readStringArray(hats);
-		src.readStringArray(hogNames);
-		src.readIntArray(levels);
-		color = src.readInt();
-		hogCount = src.readInt();
-		file = src.readString();
-	}
-
-	public static final Parcelable.Creator<Team> CREATOR = new Parcelable.Creator<Team>() {
-		public Team createFromParcel(Parcel source) {
-			return new Team(source);
+	
+	public static Comparator<Team> NAME_ORDER = new Comparator<Team>() {
+		public int compare(Team lhs, Team rhs) {
+			return lhs.name.compareToIgnoreCase(rhs.name);
 		}
-		public Team[] newArray(int size) {
-			return new Team[size];
-		}
-
 	};
-
-	/*
-	 * END PARCABLE METHODS
-	 */
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+/**
+ * A team with per-game configuration. This is similar to the frontlib "team" structure,
+ * except that it does not include weaponset and initial health, which are handled on a
+ * per-game basis in the UI, but per-hog in the frontlib.
+ */
+public final class TeamInGame {
+	public final Team team;
+	public final TeamIngameAttributes ingameAttribs;
+	
+	public TeamInGame(Team team, TeamIngameAttributes ingameAttribs) {
+		this.team = team;
+		this.ingameAttribs = ingameAttribs;
+	}
+	
+	public TeamInGame withAttribs(TeamIngameAttributes attribs) {
+		return new TeamInGame(team, attribs);
+	}
+	
+	public static int getUnusedOrRandomColorIndex(Collection<TeamInGame> teams) {
+		int[] illegalColors = new int[teams.size()];
+		int i=0;
+		for(TeamInGame team : teams) {
+			illegalColors[i] = team.ingameAttribs.colorIndex;
+			i++;
+		}
+		return TeamIngameAttributes.randomColorIndex(illegalColors);
+	}
+	
+	public static Comparator<TeamInGame> NAME_ORDER = new Comparator<TeamInGame>() {
+		public int compare(TeamInGame lhs, TeamInGame rhs) {
+			return Team.NAME_ORDER.compare(lhs.team, rhs.team);
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,80 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+
+public final class TeamIngameAttributes {
+	public static final int DEFAULT_HOG_COUNT = 4;
+	public static final int[] TEAM_COLORS;
+	
+	static {
+		int[] teamColors = new int[Flib.INSTANCE.flib_get_teamcolor_count()];
+		for(int i=0; i<teamColors.length; i++) {
+			teamColors[i] = Flib.INSTANCE.flib_get_teamcolor(i);
+		}
+		TEAM_COLORS = teamColors;
+	}
+	
+	public final String ownerName;
+	public final int colorIndex, hogCount;
+	public final boolean remoteDriven;
+	
+	public TeamIngameAttributes(String ownerName, int colorIndex, int hogCount, boolean remoteDriven) {
+		this.ownerName = ownerName;
+		this.colorIndex = colorIndex;
+		this.hogCount = hogCount;
+		this.remoteDriven = remoteDriven;
+	}
+	
+	public static int randomColorIndex(int[] illegalColors) {
+		Random rnd = new Random();
+		ArrayList<Integer> legalcolors = new ArrayList<Integer>();
+		for(int i=0; i<TEAM_COLORS.length; i++) {
+			legalcolors.add(i);
+		}
+		for(int illegalColor : illegalColors) {
+			legalcolors.remove(Integer.valueOf(illegalColor));
+		}
+		if(legalcolors.isEmpty()) {
+			return rnd.nextInt(TEAM_COLORS.length);
+		} else {
+			return legalcolors.get(rnd.nextInt(legalcolors.size()));
+		}
+	}
+	
+	public TeamIngameAttributes withColorIndex(int colorIndex) {
+		return new TeamIngameAttributes(ownerName, colorIndex, hogCount, remoteDriven);
+	}
+	
+	public TeamIngameAttributes withHogCount(int hogCount) {
+		return new TeamIngameAttributes(ownerName, colorIndex, hogCount, remoteDriven);
+	}
+
+	@Override
+	public String toString() {
+		return "TeamIngameAttributes [ownerName=" + ownerName + ", colorIndex="
+				+ colorIndex + ", hogCount=" + hogCount + ", remoteDriven="
+				+ remoteDriven + "]";
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weapon.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-
-import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class Weapon implements Parcelable, Comparable<Weapon>{
-
-	public static final String DIRECTORY_WEAPON = "weapons";
-	
-	private String name;
-	private String QT;
-	private String prob;
-	private String delay;
-	private String crate;
-	private static int maxWeapons;
-	
-	static{
-		maxWeapons = PascalExports.HWgetNumberOfWeapons();
-	}
-	
-	public Weapon(String _name, String _QT, String _prob, String _delay, String _crate){
-		name = _name;
-		
-		//Incase there's a newer ammoStore which is bigger we append with zeros
-		StringBuffer sb = new StringBuffer();
-		while(_QT.length() + sb.length() < maxWeapons){
-			sb.append('0');
-		}
-		
-		QT = String.format("e%s %s%s", "ammloadt", _QT, sb);
-		prob = String.format("e%s %s%s", "ammprob", _prob, sb);
-		delay = String.format("e%s %s%s", "ammdelay", _delay, sb);
-		crate = String.format("e%s %s%s", "ammreinf", _crate, sb);
-	}
-	
-	public Weapon(Parcel in){
-		readFromParcel(in);
-	}
-	
-	public String toString(){
-		return name;
-	}
-	
-	public void sendToEngine(EngineProtocolNetwork epn, int teamsCount) throws IOException{
-		epn.sendToEngine(QT);//command prefix is already in string 
-		epn.sendToEngine(prob);
-		epn.sendToEngine(delay);
-		epn.sendToEngine(crate);
-		
-		for(int i = 0; i < teamsCount; i++){
-			epn.sendToEngine("eammstore");
-		}
-	}
-	
-	public static final int STATE_START = 0;
-	public static final int STATE_ROOT = 1;
-	public static final int STATE_NAME = 2;
-	public static final int STATE_QT = 3;
-	public static final int STATE_PROBABILITY = 4;
-	public static final int STATE_DELAY = 5;
-	public static final int STATE_CRATE = 6;
-	
-	public static ArrayList<Weapon> getWeapons(Context c) throws IllegalArgumentException{
-		String dir = c.getFilesDir().getAbsolutePath() + '/' + DIRECTORY_WEAPON + '/';
-		String[] files = new File(dir).list();
-		if(files == null) files = new String[]{};
-		
-		ArrayList<Weapon> weapons = new ArrayList<Weapon>();
-
-		try {
-			XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
-			XmlPullParser xmlPuller = xmlPullFactory.newPullParser();
-			
-			for(String file : files){
-				BufferedReader br = new BufferedReader(new FileReader(dir + file), 1024);
-				xmlPuller.setInput(br);
-				String name = null;
-				String qt = null;
-				String prob = null;
-				String delay = null;
-				String crate = null;
-				
-				int eventType = xmlPuller.getEventType();
-				int state = STATE_START;
-				while(eventType != XmlPullParser.END_DOCUMENT){
-					switch(state){
-					case STATE_START:
-						if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().equals("weapon")) state = STATE_ROOT;
-						else if(eventType != XmlPullParser.START_DOCUMENT) throwException(file, eventType);
-						break;
-					case STATE_ROOT:
-						if(eventType == XmlPullParser.START_TAG){
-							if(xmlPuller.getName().toLowerCase().equals("qt")) state = STATE_QT;
-							else if(xmlPuller.getName().toLowerCase().equals("name")) state = STATE_NAME;
-							else if(xmlPuller.getName().toLowerCase().equals("probability")) state = STATE_PROBABILITY;
-							else if(xmlPuller.getName().toLowerCase().equals("delay")) state = STATE_DELAY;
-							else if(xmlPuller.getName().toLowerCase().equals("crate")) state = STATE_CRATE;
-							else throwException(file, eventType);
-						}else if(eventType == XmlPullParser.END_TAG) state = STATE_START;
-						else throwException(xmlPuller.getText(), eventType);
-						break;
-					case STATE_NAME:
-						if(eventType == XmlPullParser.TEXT) name = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_QT:
-						if(eventType == XmlPullParser.TEXT) qt = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_PROBABILITY:
-						if(eventType == XmlPullParser.TEXT) prob = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_DELAY:
-						if(eventType == XmlPullParser.TEXT) delay = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					case STATE_CRATE:
-						if(eventType == XmlPullParser.TEXT) crate = xmlPuller.getText().trim();
-						else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
-						else throwException(file, eventType);
-						break;
-					}
-					eventType = xmlPuller.next();
-					while(eventType == XmlPullParser.TEXT && xmlPuller.isWhitespace()){//Skip whitespaces
-						eventType = xmlPuller.next();
-					}
-				}//end while(eventtype != END_DOCUMENT
-				weapons.add(new Weapon(name, qt, prob, delay, crate));
-			}//end for(string file : files
-			return weapons;
-			
-		} catch (XmlPullParserException e) {
-			e.printStackTrace();
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		return new ArrayList<Weapon>();//TODO handle correctly
-	}
-	
-	private static void throwException(String file, int eventType){
-		throw new IllegalArgumentException(String.format("Xml file: %s malformed with eventType: %d.", file, eventType));
-	}
-
-	public int describeContents() {
-		return 0;
-	}
-
-	public void writeToParcel(Parcel dest, int flags) {
-		dest.writeString(name);
-		dest.writeString(QT);
-		dest.writeString(prob);
-		dest.writeString(delay);
-		dest.writeString(crate);
-	}
-	
-	private void readFromParcel(Parcel src){
-		name = src.readString();
-		QT = src.readString();
-		prob = src.readString();
-		delay = src.readString();
-		crate = src.readString();
-	}
-	
-	public static final Parcelable.Creator<Weapon> CREATOR = new Parcelable.Creator<Weapon>() {
-		public Weapon createFromParcel(Parcel source) {
-			return new Weapon(source);
-		}
-		public Weapon[] newArray(int size) {
-			return new Weapon[size];
-		}
-		
-	};
-
-	public int compareTo(Weapon another) {
-		return name.compareTo(another.name);
-	}
-	
-	
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,102 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.Comparator;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+
+public final class Weaponset {
+	public static final int WEAPONS_COUNT = Flib.INSTANCE.flib_get_weapons_count();
+	
+	public final String name, loadout, crateProb, crateAmmo, delay;
+	
+	public Weaponset(String name, String loadout, String crateProb, String crateAmmo, String delay) {
+		this.name = name;
+		this.loadout = loadout;
+		this.crateProb = crateProb;
+		this.crateAmmo = crateAmmo;
+		this.delay = delay;
+	}
+
+	@Override
+	public String toString() {
+		return "Weaponset [name=" + name + ", loadout=" + loadout
+				+ ", crateProb=" + crateProb + ", crateAmmo=" + crateAmmo
+				+ ", delay=" + delay + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((crateAmmo == null) ? 0 : crateAmmo.hashCode());
+		result = prime * result
+				+ ((crateProb == null) ? 0 : crateProb.hashCode());
+		result = prime * result + ((delay == null) ? 0 : delay.hashCode());
+		result = prime * result + ((loadout == null) ? 0 : loadout.hashCode());
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Weaponset other = (Weaponset) obj;
+		if (crateAmmo == null) {
+			if (other.crateAmmo != null)
+				return false;
+		} else if (!crateAmmo.equals(other.crateAmmo))
+			return false;
+		if (crateProb == null) {
+			if (other.crateProb != null)
+				return false;
+		} else if (!crateProb.equals(other.crateProb))
+			return false;
+		if (delay == null) {
+			if (other.delay != null)
+				return false;
+		} else if (!delay.equals(other.delay))
+			return false;
+		if (loadout == null) {
+			if (other.loadout != null)
+				return false;
+		} else if (!loadout.equals(other.loadout))
+			return false;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		return true;
+	}
+
+	public static Comparator<Weaponset> NAME_ORDER = new Comparator<Weaponset>() {
+		public int compare(Weaponset lhs, Weaponset rhs) {
+			return String.CASE_INSENSITIVE_ORDER.compare(lhs.name, rhs.name);
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,103 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetListPtr;
+
+import android.content.Context;
+
+public final class Weaponsets {
+	private Weaponsets() {
+		throw new AssertionError("This class is not meant to be instantiated");
+	}
+	
+	public static File getUserWeaponsetsFile(Context c) {
+		return new File(c.getFilesDir(), "weapons_user.ini");
+	}
+	
+	public static File getBuiltinWeaponsetsFile(Context c) {
+		return new File(c.getFilesDir(), "weapons_builtin.ini");
+	}
+	
+	public static List<Weaponset> loadAllWeaponsets(Context c) throws IOException {
+		List<Weaponset> result = loadBuiltinWeaponsets(c);
+		result.addAll(loadUserWeaponsets(c));
+		return result;
+	}
+	
+	public static List<Weaponset> loadUserWeaponsets(Context c) throws IOException {
+		return loadWeaponsets(c, getUserWeaponsetsFile(c));
+	}
+	
+	public static List<Weaponset> loadBuiltinWeaponsets(Context c) throws IOException {
+		return loadWeaponsets(c, getBuiltinWeaponsetsFile(c));
+	}
+	
+	public static List<Weaponset> loadWeaponsets(Context c, File weaponsetFile) throws IOException {
+		if(!weaponsetFile.isFile()) {
+			// No file == no weaponsets, no error
+			return new ArrayList<Weaponset>();
+		}
+		WeaponsetListPtr weaponsetListPtr = null;
+		try {
+			weaponsetListPtr = Flib.INSTANCE.flib_weaponsetlist_from_ini(weaponsetFile.getAbsolutePath());
+			if(weaponsetListPtr == null) {
+				throw new IOException("Unable to read weaponsets from "+weaponsetFile);
+			}
+			return weaponsetListPtr.deref();
+		} finally {
+			if(weaponsetListPtr != null) {
+				Flib.INSTANCE.flib_weaponsetlist_destroy(weaponsetListPtr);
+			}
+		}
+	}
+	
+	public static void saveUserWeaponsets(Context c, List<Weaponset> weaponsets) throws IOException {
+		WeaponsetListPtr ptr = WeaponsetListPtr.createJavaOwned(weaponsets);
+		Flib.INSTANCE.flib_weaponsetlist_to_ini(getUserWeaponsetsFile(c).getAbsolutePath(), ptr);
+	}
+	
+	public static void deleteUserWeaponset(Context c, String setToDelete) throws IOException {
+		List<Weaponset> userWeaponsets = loadUserWeaponsets(c);
+		for(Iterator<Weaponset> iter = userWeaponsets.iterator(); iter.hasNext();) {
+			Weaponset set = iter.next();
+			if(set.name.equals(setToDelete)) {
+				iter.remove();
+				break;
+			}
+		}
+		saveUserWeaponsets(c, userWeaponsets);
+	}
+	
+	public static List<String> toNameList(List<Weaponset> weaponsets) {
+		List<String> result = new ArrayList<String>();
+		for(Weaponset weaponset : weaponsets) {
+			result.add(weaponset.name);
+		}
+		return result;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,100 +1,83 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid.Downloader;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 
 import org.hedgewars.hedgeroid.MainActivity;
 import org.hedgewars.hedgeroid.R;
-import org.hedgewars.hedgeroid.Utils;
-import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Schemes;
 import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
-import android.content.Context;
 import android.content.res.AssetManager;
 import android.os.AsyncTask;
 import android.util.Log;
 
-public class DownloadAssets extends AsyncTask<Object, Long, Long>{
-	
-	private MainActivity act;
-	private static byte[] buffer = null;
-	
-	public DownloadAssets(MainActivity _act){
-		act = _act;
-	}
+public class DownloadAssets extends AsyncTask<Object, Long, Boolean> {
+	private static final String VERSION_FILENAME = "assetsversion.txt";
+	private final MainActivity act;
 	
-	public static Long copyFileOrDir(Context c, String path) {
-	    AssetManager assetManager = c.getAssets();
-	    String assets[] = null;
-	    try {
-	        assets = assetManager.list(path);
-	        if (assets.length == 0) {
-	            return DownloadAssets.copyFile(c, path);
-	        } else {
-	            String fullPath = Utils.getCachePath(c) + path;
-	            File dir = new File(fullPath);
-	            if (!dir.exists())
-	                dir.mkdir();
-	            for (int i = 0; i < assets.length; ++i) {
-	                Long result = DownloadAssets.copyFileOrDir(c, path + "/" + assets[i]);
-	                if(result > 0) return 1l;
-	            }
-	        }
-	    } catch (IOException ex) {
-	    	ex.printStackTrace();
-	        Log.e("tag", "I/O Exception", ex);
-	        return 1l;
-	    }
-	    return 0l;
+	public DownloadAssets(MainActivity act){
+		this.act = act;
 	}
 	
-	private static Long copyFile(Context c, String filename) {
-	    AssetManager assetManager = c.getAssets();
-
-	    InputStream in = null;
-	    OutputStream out = null;
-	    try {
-	        in = assetManager.open(filename);
-	        in = new BufferedInputStream(in, 8192);
-	        
-	        String newFileName = Utils.getCachePath(c) + filename;
-	        out = new FileOutputStream(newFileName);
-	        out = new BufferedOutputStream(out, 8192);
-
-	        int read;
-	        while ((read = in.read(buffer)) != -1) {
-	            out.write(buffer, 0, read);
-	        }
-	        in.close();
-	        in = null;
-	        out.flush();
-	        out.close();
-	        out = null;
-	    } catch (Exception e) {
-	    	e.printStackTrace();
-	        Log.e("tag", e.getMessage());
-	        return 1l;
-	    }
-	    return 0l;
-
-	}
-
-	protected Long doInBackground(Object... params) {
-		Utils.resRawToFilesDir(act,R.array.schemes, Scheme.DIRECTORY_SCHEME);
-		Utils.resRawToFilesDir(act, R.array.weapons, Weapon.DIRECTORY_WEAPON);
-		Utils.resRawToFilesDir(act, R.array.teams, Team.DIRECTORY_TEAMS);
-		buffer = new byte[8192];//allocate the buffer
-		return DownloadAssets.copyFileOrDir(act, "Data");
+	private void copyFileOrDir(AssetManager assetManager, File target, String assetPath) throws IOException {
+		try {
+			FileUtils.writeStreamToFile(assetManager.open(assetPath), target);
+		} catch(FileNotFoundException e) {
+			/*
+			 * I can't find a better way to figure out whether an asset entry is
+			 * a file or a directory. Checking if assetManager.list(assetPath)
+			 * is empty is a bit cleaner, but SLOW.
+			 */
+			if (!target.isDirectory() && !target.mkdir()) {
+				throw new IOException("Unable to create directory "+target);
+			}
+			for (String asset : assetManager.list(assetPath)) {
+				copyFileOrDir(assetManager, new File(target, asset), assetPath + "/" + asset);
+			}
+		}
 	}
 	
-	protected void onPostExecute(Long result){
-		act.onAssetsDownloaded(result == 0);
-		buffer = null;
+	@Override
+	protected Boolean doInBackground(Object... params) {
+		try {
+			FileUtils.writeStreamToFile(act.getResources().openRawResource(R.raw.schemes_builtin), Schemes.getBuiltinSchemesFile(act));
+			FileUtils.writeStreamToFile(act.getResources().openRawResource(R.raw.weapons_builtin), Weaponsets.getBuiltinWeaponsetsFile(act));
+			FileUtils.resRawToFilesDir(act, R.array.teams, R.array.teamFilenames, Team.DIRECTORY_TEAMS);
+			copyFileOrDir(act.getAssets(), FileUtils.getDataPathFile(act), "Data");
+			copyFileOrDir(act.getAssets(), new File(FileUtils.getCachePath(act), VERSION_FILENAME), VERSION_FILENAME);
+			return Boolean.TRUE;
+		} catch(IOException e) {
+			Log.e("DownloadAssets", e.getMessage(), e);
+			return Boolean.FALSE;
+		}
+	}
+	
+	@Override
+	protected void onPostExecute(Boolean result){
+		act.onAssetsDownloaded(result);
 	}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 
@@ -107,7 +108,7 @@
 				entry = input.getNextEntry();	
 			}catch(IOException e){
 				e.printStackTrace();
-				if(conn != null) conn.disconnect();
+				conn.disconnect();
 				return EXIT_CONNERROR;
 			}
 
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadDialogFragment.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadDialogFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,3 +1,22 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid.Downloader;
 
 import org.hedgewars.hedgeroid.R;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadFragment.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,10 +14,9 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-
 package org.hedgewars.hedgeroid.Downloader;
 
 import org.hedgewars.hedgeroid.R;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,3 +1,22 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid.Downloader;
 
 import org.hedgewars.hedgeroid.R;
@@ -6,7 +25,6 @@
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.OnClickListener;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListFragment.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,3 +1,22 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid.Downloader;
 
 import java.io.BufferedReader;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,14 +14,14 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 package org.hedgewars.hedgeroid.Downloader;
 
 import java.io.IOException;
 
-import org.hedgewars.hedgeroid.Utils;
+import org.hedgewars.hedgeroid.util.FileUtils;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -29,7 +30,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.preference.PreferenceManager;
-import android.util.Log;
 
 public class DownloadPackage implements Parcelable{
 	private String url_without_suffix;
@@ -137,7 +137,7 @@
 							version = -1;
 						}
 					}else if(name.equals("path")){
-						path = Utils.getDataPath(c) + text;
+						path = FileUtils.getDataPathFile(c, text).getAbsolutePath();
 					}else if(name.equals("representation")){
 						representation = text;
 					}else if(name.equals("description")){
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,10 +14,9 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-
 package org.hedgewars.hedgeroid.Downloader;
 
 import java.util.LinkedList;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/EngineProtocolNetwork.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid.EngineProtocol;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-public class EngineProtocolNetwork extends Thread{
-
-	public static final String GAMEMODE_LOCAL = "TL";
-	public static final String GAMEMODE_DEMO = "TD";
-	public static final String GAMEMODE_NET = "TN";
-	public static final String GAMEMODE_SAVE = "TS";
-	
-	public static final int BUFFER_SIZE = 255; //From iOS code which got it from the origional frontend
-	
-	public static final int MODE_GENLANDPREVIEW = 0;
-	public static final int MODE_GAME = 1;
-
-	private ServerSocket serverSocket;
-	private InputStream input;
-	private OutputStream output;
-	public int port;
-	private final GameConfig config;
-	private boolean clientQuit = false;
-
-	public EngineProtocolNetwork(GameConfig _config){
-		config = _config;
-		try {
-			serverSocket = new ServerSocket(0);
-			port = serverSocket.getLocalPort();
-			Thread ipcThread = new Thread(this, "IPC - Thread");			
-			ipcThread.start();
-		} catch (UnknownHostException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-	
-	public void run(){
-		//if(mode == MODE_GENLANDPREVIEW) genLandPreviewIPC();
-		/*else if (mode == MODE_GAME)*/ gameIPC();
-	}
-	
-	private void gameIPC(){
-		Socket sock = null;
-		try{
-			sock = serverSocket.accept();
-			input = sock.getInputStream();
-			output = sock.getOutputStream();
-			
-			int msgSize = 0;
-			byte[] buffer = new byte[BUFFER_SIZE];
-
-			while(!clientQuit){
-				msgSize = 0;
-
-				input.read(buffer, 0, 1);
-				msgSize = buffer[0];
-
-				input.read(buffer, 0, msgSize);
-				System.out.println("IPC" + (char)buffer[0] + " : " + new String(buffer, 1,msgSize-1, "US_ASCII"));
-				switch(buffer[0]){
-				case 'C'://game init
-					config.sendToEngine(this);
-					break;
-				case '?'://ping - pong
-					sendToEngine("!");
-					break;
-				case 'e'://Send protocol version
-					System.out.println(new String(buffer));
-					break;
-				case 'i'://game statistics
-					switch(buffer[1]){
-					case 'r'://winning team
-						break;
-					case 'D'://best shot
-						break;
-					case 'k'://best hedgehog
-						break;
-					case 'K'://# hogs killed
-						break;
-					case 'H'://team health graph
-						break;
-					case 'T':// local team stats
-						break;
-					case 'P'://teams ranking
-						break;
-					case 's'://self damage
-						break;
-					case 'S'://friendly fire
-						break;
-					case 'B'://turn skipped
-						break;
-					default:
-					}
-					break;
-				case 'E'://error - quits game
-					System.out.println(new String(buffer));
-					return;
-				case 'q'://game ended remove save file
-
-				    return;
-				case 'Q'://game ended but not finished
-
-					return;
-				}
-
-			}
-		}catch(IOException e){
-			e.printStackTrace();
-		}finally{
-			try {
-				if(sock != null) sock.close();
-			} catch (IOException e) {}
-			try{
-				if(serverSocket != null) serverSocket.close();
-			} catch (IOException e) {}
-		}
-	}
-
-	public void sendToEngine(String s){
-		int length = s.length();
-		
-		try {
-			output.write(length);
-			output.write(s.getBytes(), 0, length);
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-	
-	public void quitIPC(){
-		clientQuit = true;
-	}
-	
-}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.EngineProtocol;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.UUID;
-
-import org.hedgewars.hedgeroid.Datastructures.GameMode;
-import org.hedgewars.hedgeroid.Datastructures.Map;
-import org.hedgewars.hedgeroid.Datastructures.Scheme;
-import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-public class GameConfig implements Parcelable{
-	
-	public GameMode mode = GameMode.MODE_LOCAL;
-	public Map map = null;
-	public String theme = null;
-	public Scheme scheme = null;
-	public Weapon weapon = null;
-	
-	public String style = null;
-	public String training = null;
-	public String seed = null;
-	
-	public ArrayList<Team> teams = new ArrayList<Team>();
-	
-	public GameConfig(){
-		
-	}
-	
-	public GameConfig(Parcel in){
-		readFromParcel(in);	
-	}
-	
-
-	
-	public void sendToEngine(EngineProtocolNetwork epn) throws IOException{
-		Log.d("HW_Frontend", "Sending Gameconfig...");
-		int teamCount = 4;
-		epn.sendToEngine("TL"); //Write game mode
-		if(training != null) epn.sendToEngine(String.format("escript Scripts/Training/%s.lua", training));
-		else if(style != null) epn.sendToEngine(String.format("escript Scripts/Multiplayer/%s.lua", style));
-		
-		//seed info
-		epn.sendToEngine(String.format("eseed {%s}", UUID.randomUUID().toString()));
-		
-		map.sendToEngine(epn);
-		//dimensions of the map
-		//templatefilter_command
-		//mapgen_command
-		//mazesize_command
-		
-		epn.sendToEngine(String.format("etheme %s", theme));
-		
-		scheme.sendToEngine(epn);
-		
-		weapon.sendToEngine(epn, teamCount);
-		
-		for(Team t : teams){
-			if(t != null)t.sendToEngine(epn, teamCount, scheme.health);
-		}
-	}
-	
-	public int describeContents() {
-		return 0;
-	}
-
-	public void writeToParcel(Parcel dest, int flags) {
-		dest.writeString(mode.name());
-		dest.writeParcelable(map, flags);
-		dest.writeString(theme);
-		dest.writeParcelable(scheme, flags);
-		dest.writeParcelable(weapon, flags);
-		dest.writeString(style);
-		dest.writeString(training);
-		dest.writeString(seed);
-		dest.writeParcelableArray((Team[])teams.toArray(new Team[1]), 0);
-	}
-	
-	private void readFromParcel(Parcel src){
-		mode = GameMode.valueOf(src.readString());
-		map = src.readParcelable(Map.class.getClassLoader());
-		theme = src.readString();
-		scheme = src.readParcelable(Scheme.class.getClassLoader());
-		weapon = src.readParcelable(Weapon.class.getClassLoader());
-		style = src.readString();
-		training = src.readString();
-		seed = src.readString();
-		Parcelable[] parcelables = src.readParcelableArray(Team[].class.getClassLoader());
-		for(Parcelable team : parcelables){
-			teams.add((Team)team);
-		}
-		
-	}
-	
-	public static final Parcelable.Creator<GameConfig> CREATOR = new Parcelable.Creator<GameConfig>() {
-		public GameConfig createFromParcel(Parcel source) {
-			return new GameConfig(source);
-		}
-		public GameConfig[] newArray(int size) {
-			return new GameConfig[size];
-		}
-	};
-	
-}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,12 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,12 +15,13 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 package org.hedgewars.hedgeroid.EngineProtocol;
 
 public class PascalExports {
+	public static Object engineMutex = new Object();
 
 	static{
 		System.loadLibrary("SDL");
@@ -31,10 +34,13 @@
 		System.loadLibrary("hwengine");
 	}
 	
-	public static native int HWversionInfoNetProto();
-	public static native String HWversionInfoVersion();
-	public static native int HWgetNumberOfWeapons();
 	public static native int HWgetMaxNumberOfTeams();
-	public static native int HWgetMaxNumberOfHogs();
-        public static native int HWterminate(boolean b);	
+    private static native void HWGenLandPreview(int port);
+
+    public static void synchronizedGenLandPreview(int port) {
+    	synchronized(engineMutex) {
+    		HWGenLandPreview(port);
+    	}
+    }
+    
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/GameConnection.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,222 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.net.ConnectException;
+
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.ByteArrayPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
+import org.hedgewars.hedgeroid.frontlib.NativeSizeT;
+import org.hedgewars.hedgeroid.netplay.GameMessageListener;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.util.TickHandler;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+import com.sun.jna.Pointer;
+
+/**
+ * This class handles both talking to the engine (IPC) for running a game, and
+ * coordinating with the netconn if it is a netgame, using the frontlib for the
+ * actual IPC networking communication.
+ * 
+ * After creating the GameConnection object, it will communicate with the engine
+ * on its own thread. It shuts itself down as soon as the connection to the engine
+ * is lost.
+ */
+public final class GameConnection {
+	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+	
+	public final int port;
+	private final HandlerThread thread;
+	private final Handler handler;
+	private TickHandler tickHandler;
+	private final Netplay netplay; // ==null if not a netgame
+	private GameconnPtr conn;
+	
+	private GameConnection(GameconnPtr conn, Netplay netplay) {
+		this.conn = conn;
+		this.port = Flib.INSTANCE.flib_gameconn_getport(conn);
+		this.netplay = netplay;
+		this.thread = new HandlerThread("IPCThread");
+		thread.start();
+		this.handler = new Handler(thread.getLooper());
+	}
+	
+	private void setupConnection() {
+		tickHandler = new TickHandler(thread.getLooper(), 50, tickCb);
+		tickHandler.start();
+		
+		if(netplay != null) {
+			mainHandler.post(new Runnable() {
+				public void run() { 
+					netplay.registerGameMessageListener(gameMessageListener);
+				}
+			});
+			Flib.INSTANCE.flib_gameconn_onChat(conn, chatCb, null);
+			Flib.INSTANCE.flib_gameconn_onEngineMessage(conn, engineMessageCb, null);
+		}
+		Flib.INSTANCE.flib_gameconn_onConnect(conn, connectCb, null);
+		Flib.INSTANCE.flib_gameconn_onDisconnect(conn, disconnectCb, null);
+		Flib.INSTANCE.flib_gameconn_onErrorMessage(conn, errorMessageCb, null);
+	}
+	
+	/**
+	 * Start a new IPC server to communicate with the engine.
+	 * Performs networking operations, don't run on the UI thread.
+	 * @throws ConnectException if we can't set up the IPC server
+	 */
+	public static GameConnection forNetgame(final GameConfig config, Netplay netplay) throws ConnectException {
+		final String playerName = netplay.getPlayerName();
+		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create(playerName, GameSetupPtr.createJavaOwned(config), true);
+		if(conn == null) {
+			throw new ConnectException();
+		}
+		GameConnection result = new GameConnection(conn, netplay);
+		result.setupConnection();
+		return result;
+	}
+	
+	/**
+	 * Start a new IPC server to communicate with the engine.
+	 * Performs networking operations, don't run on the UI thread.
+	 * @throws ConnectException if we can't set up the IPC server
+	 */
+	public static GameConnection forLocalGame(final GameConfig config) throws ConnectException {
+		GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Player", GameSetupPtr.createJavaOwned(config), false);
+		if(conn == null) {
+			throw new ConnectException();
+		}
+		GameConnection result = new GameConnection(conn, null);
+		result.setupConnection();
+		return result;
+	}
+	
+	private final Runnable tickCb = new Runnable() {
+		public void run() {
+			Flib.INSTANCE.flib_gameconn_tick(conn);
+		}
+	};
+	
+	// runs on the IPCThread
+	private void shutdown() {
+		tickHandler.stop();
+		thread.quit();
+		Flib.INSTANCE.flib_gameconn_destroy(conn);
+		conn = null;
+		if(netplay != null) {
+			mainHandler.post(new Runnable() {
+				public void run() {
+					netplay.unregisterGameMessageListener(gameMessageListener);
+				}
+			});
+		}
+	}
+	
+	// runs on the IPCThread
+	private final StrBoolCallback chatCb = new StrBoolCallback() {
+		public void callback(Pointer context, String message, boolean teamChat) {
+			if(teamChat) {
+				netplay.sendTeamChat(message);
+			} else {
+				netplay.sendChat(message);
+			}
+		}
+	};
+	
+	// runs on the IPCThread
+	private final VoidCallback connectCb = new VoidCallback() {
+		public void callback(Pointer context) {
+			Log.i("GameConnection", "Connected");
+		}
+	};
+	
+	// runs on the IPCThread
+	private final IntCallback disconnectCb = new IntCallback() {
+		public void callback(Pointer context, int reason) {
+			if(netplay != null) {
+				netplay.sendRoundFinished(reason==Frontlib.GAME_END_FINISHED);
+			}
+			shutdown();
+		}
+	};
+	
+	// runs on the IPCThread
+	private final BytesCallback engineMessageCb = new BytesCallback() {
+		public void callback(Pointer context, ByteArrayPtr buffer, NativeSizeT size) {
+			netplay.sendEngineMessage(buffer.deref(size.intValue()));
+		}
+	};
+	
+	// runs on the IPCThread
+	private final StrCallback errorMessageCb = new StrCallback() {
+		public void callback(Pointer context, String message) {
+			Log.e("GameConnection", message);
+		}
+	};
+	
+	// runs on any thread
+	private final GameMessageListener gameMessageListener = new GameMessageListener() {
+		public void onNetDisconnected() {
+			handler.post(new Runnable() {
+				public void run() {
+					Flib.INSTANCE.flib_gameconn_send_quit(conn);
+				}
+			});
+		}
+		
+		public void onMessage(final int type, final String message) {
+			handler.post(new Runnable() {
+				public void run() {
+					Flib.INSTANCE.flib_gameconn_send_textmsg(conn, type, message);
+				}
+			});
+		}
+		
+		public void onEngineMessage(final byte[] em) {
+			handler.post(new Runnable() {
+				public void run() {
+					ByteArrayPtr ptr = ByteArrayPtr.createJavaOwned(em);
+					Flib.INSTANCE.flib_gameconn_send_enginemsg(conn, ptr, NativeSizeT.valueOf(em.length));
+				}
+			});
+		}
+		
+		public void onChatMessage(final String nick, final String message) {
+			handler.post(new Runnable() {
+				public void run() {
+					Flib.INSTANCE.flib_gameconn_send_chatmsg(conn, nick, message);
+				}
+			});
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,140 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.NetplayStateFragment.NetplayStateListener;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+import org.hedgewars.hedgeroid.util.TextInputDialog;
+import org.hedgewars.hedgeroid.util.TextInputDialog.TextInputDialogListener;
+import org.hedgewars.hedgeroid.util.UiUtils;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.LinearLayout;
+import android.widget.TabHost;
+
+/**
+ * Activity for the server lobby of a hedgewars server. Allows you to chat, join
+ * and create rooms and interact with a list of players.
+ * 
+ * Most of the functionality is handled by various fragments.
+ */
+public class LobbyActivity extends FragmentActivity implements TextInputDialogListener, NetplayStateListener {
+	private static final int DIALOG_CREATE_ROOM = 0;
+	
+    private TabHost tabHost;
+    private Netplay netplay;
+    
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        
+        setContentView(R.layout.activity_lobby);
+        ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment);
+        chatFragment.setInRoom(false);
+        
+        FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+        trans.add(new NetplayStateFragment(), "netplayFragment");
+        trans.commit();
+        
+        netplay = Netplay.getAppInstance(getApplicationContext());
+        
+        // Set up a tabbed UI for medium and small screens
+        tabHost = (TabHost)findViewById(android.R.id.tabhost);
+        if(tabHost != null) {
+	        tabHost.setup();
+	        tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL);
+
+	        tabHost.addTab(tabHost.newTabSpec("rooms").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.lobby_tab_rooms, R.drawable.roomlist_ingame)).setContent(R.id.roomListFragment));
+	        tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.lobby_tab_chat, R.drawable.edit)).setContent(R.id.chatFragment));
+	        tabHost.addTab(tabHost.newTabSpec("players").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.lobby_tab_players, R.drawable.human)).setContent(R.id.playerListFragment));
+	
+	        if (icicle != null) {
+	            tabHost.setCurrentTabByTag(icicle.getString("currentTab"));
+	        }
+        }
+    }
+    
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		super.onCreateOptionsMenu(menu);
+		getMenuInflater().inflate(R.menu.lobby_options, menu);
+		return true;
+	}
+	
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		switch(item.getItemId()) {
+		case R.id.room_create:
+	        TextInputDialog dialog = new TextInputDialog(DIALOG_CREATE_ROOM, R.string.dialog_create_room_title, 0, R.string.dialog_create_room_hint);
+	        dialog.show(getSupportFragmentManager(), "create_room_dialog");
+			return true;
+		case R.id.disconnect:
+			netplay.disconnect();
+			return true;
+		default:
+			return super.onOptionsItemSelected(item);
+		}
+	}
+	
+	@Override
+	public void onBackPressed() {
+		netplay.disconnect();
+	}
+	
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+        if(tabHost != null) {
+        	icicle.putString("currentTab", tabHost.getCurrentTabTag());
+        }
+    }
+    
+    public void onTextInputDialogSubmitted(int dialogId, String text) {
+    	if(text != null && text.length()>0) {
+    		netplay.sendCreateRoom(text);
+    	}
+    }
+    
+    public void onTextInputDialogCancelled(int dialogId) {
+    }
+    
+    public void onNetplayStateChanged(State newState) {
+    	switch(newState) {
+    	case CONNECTING:
+    	case NOT_CONNECTED:
+    		finish();
+    		break;
+    	case ROOM:
+    		startActivity(new Intent(getApplicationContext(), NetRoomActivity.class));
+    		break;
+    	case LOBBY:
+    		// Do nothing
+    		break;
+		default:
+			throw new IllegalStateException("Unknown connection state: "+newState);
+    	}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,54 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.Comparator;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Player;
+import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Simple adapter for displaying the list of players in the lobby.
+ */
+public class LobbyPlayerlistAdapter extends ObservableTreeMapAdapter<String, Player> {
+	@Override
+	protected Comparator<Player> getEntryOrder() {
+		return Player.NAME_ORDER;
+	}
+
+	public View getView(int position, View convertView, ViewGroup parent) {
+		View v = convertView;
+		if (v == null) {
+			LayoutInflater vi = LayoutInflater.from(parent.getContext());
+			v = vi.inflate(R.layout.listview_player, null);
+		}
+
+		String player = getItem(position).name;
+		TextView username = (TextView) v.findViewById(android.R.id.text1);
+		username.setText(player);
+		return v;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,98 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Player;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+/**
+ * Shows the list of players in the lobby and allows some interactions with them
+ * over the context menu.
+ */
+public class LobbyPlayerlistFragment extends ListFragment {
+	private Netplay netplay;
+	private LobbyPlayerlistAdapter adapter;
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		netplay = Netplay.getAppInstance(getActivity().getApplicationContext());
+		adapter = new LobbyPlayerlistAdapter();
+		adapter.setSource(netplay.lobbyPlayerlist);
+		setListAdapter(adapter);
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		adapter.invalidate();
+	}
+	
+	@Override
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
+		registerForContextMenu(getListView());
+	}
+
+	@Override
+	public void onCreateContextMenu(ContextMenu menu, View v,
+			ContextMenuInfo menuInfo) {
+		super.onCreateContextMenu(menu, v, menuInfo);
+		AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
+		MenuInflater inflater = getActivity().getMenuInflater();
+		inflater.inflate(R.menu.lobby_playerlist_context, menu);
+		menu.setHeaderIcon(R.drawable.human);
+		menu.setHeaderTitle(adapter.getItem(info.position).name);
+	}
+	
+	@Override
+	public boolean onContextItemSelected(MenuItem item) {
+		AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
+		Player player = adapter.getItem(info.position);
+		switch(item.getItemId()) {
+		case R.id.player_info:
+			netplay.sendPlayerInfoQuery(player.name);
+			return true;
+		case R.id.player_follow:
+			netplay.sendFollowPlayer(player.name);
+			return true;
+		default:
+			return super.onContextItemSelected(item);
+		}
+	}
+	
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		return inflater.inflate(R.layout.fragment_playerlist, container, false);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LocalRoomActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,121 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.util.UiUtils;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TabHost;
+import android.widget.Toast;
+
+/**
+ * This activity is used to set up and start a local game.
+ */
+public class LocalRoomActivity extends FragmentActivity implements RoomStateManager.Provider, TeamAddDialog.Listener {
+	private TabHost tabHost;
+	private RoomStateManager stateManager;
+	private Button startButton;
+	
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        // TODO find a better central location / way to set up the default scheme and weaponset
+        Netplay netplay = Netplay.getAppInstance(getApplicationContext());
+        stateManager = new LocalRoomStateManager(netplay.defaultScheme, netplay.defaultWeaponset);
+        
+        setContentView(R.layout.activity_localroom);
+        startButton = (Button)findViewById(R.id.startGame);
+        
+        startButton.setOnClickListener(startButtonClickListener);
+        
+        // Set up a tabbed UI for medium and small screens
+        tabHost = (TabHost)findViewById(android.R.id.tabhost);
+        if(tabHost != null) {
+	        tabHost.setup();
+	        tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL);
+
+	        tabHost.addTab(tabHost.newTabSpec("map").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_map, 0)).setContent(R.id.mapFragment));
+	        tabHost.addTab(tabHost.newTabSpec("settings").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_settings, 0)).setContent(R.id.settingsFragment));
+	        tabHost.addTab(tabHost.newTabSpec("teams").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_teams, 0)).setContent(R.id.teamlistContainer));
+	        
+	        if (icicle != null) {
+	            tabHost.setCurrentTabByTag(icicle.getString("currentTab"));
+	        }
+        }
+    }
+    
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+        if(tabHost != null) {
+        	icicle.putString("currentTab", tabHost.getCurrentTabTag());
+        }
+    }
+    
+	public void onTeamAddDialogSubmitted(Team newTeam) {
+		stateManager.requestAddTeam(newTeam, TeamInGame.getUnusedOrRandomColorIndex(stateManager.getTeams().values()));
+	}
+	
+	public RoomStateManager getRoomStateManager() {
+		return stateManager;
+	}
+
+	private final OnClickListener startButtonClickListener = new OnClickListener() {
+		public void onClick(View v) {
+			Map<String, TeamInGame> teams = stateManager.getTeams();
+			Set<Integer> clanColors = new TreeSet<Integer>();
+			for(TeamInGame t : teams.values()) {
+				clanColors.add(t.ingameAttribs.colorIndex);
+			}
+			if(clanColors.size()<2) {
+				if(tabHost != null) {
+					tabHost.setCurrentTabByTag("teams");
+				}
+				int errortext = teams.size()<2 ? R.string.not_enough_teams : R.string.not_enough_clans;
+				Toast.makeText(getApplicationContext(), errortext, Toast.LENGTH_SHORT).show();
+				return;
+			}
+			
+			SDLActivity.startNetgame = false;
+			SDLActivity.startConfig = new GameConfig(
+					stateManager.getGameStyle(),
+					stateManager.getScheme(),
+					stateManager.getMapRecipe(),
+					new ArrayList<TeamInGame>(stateManager.getTeams().values()),
+					stateManager.getWeaponset());
+			startActivity(new Intent(LocalRoomActivity.this, SDLActivity.class));
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LocalRoomStateManager.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,114 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+
+import android.util.Log;
+
+/**
+ * This RoomStateManager is responsible for keeping/changing the roomstate in local play.
+ * That is very straightforward, just react to every request by immediately changing the
+ * state.
+ */
+public class LocalRoomStateManager extends BasicRoomState {
+	private static final String TAG = LocalRoomStateManager.class.getSimpleName(); 
+
+	public LocalRoomStateManager(Scheme defaultScheme, Weaponset defaultWeaponset) {
+		setChief(true);
+		setGameStyle(GameConfig.DEFAULT_STYLE);
+		setMapRecipe(MapRecipe.makeRandomMap(0, MapRecipe.makeRandomSeed(), GameConfig.DEFAULT_THEME));
+		setScheme(defaultScheme);
+		setWeaponset(defaultWeaponset);
+	}
+	
+	public void changeMapRecipe(MapRecipe map) {
+		setMapRecipe(map);
+	}
+
+	public void changeMapTheme(String theme) {
+		setMapRecipe(getMapRecipe().withTheme(theme));
+	}
+
+	public void changeMapNameAndGenerator(String mapName) {
+		int newGenerator = MapRecipe.generatorForMapname(mapName);
+		setMapRecipe(getMapRecipe().withName(mapName).withMapgen(newGenerator));
+	}
+
+	public void changeMapTemplate(int template) {
+		setMapRecipe(getMapRecipe().withTemplateFilter(template));
+	}
+
+	public void changeMazeSize(int mazeSize) {
+		setMapRecipe(getMapRecipe().withMazeSize(mazeSize));
+	}
+
+	public void changeMapSeed(String seed) {
+		setMapRecipe(getMapRecipe().withSeed(seed));
+	}
+
+	public void changeMapDrawdata(byte[] drawdata) {
+		setMapRecipe(getMapRecipe().withDrawData(drawdata));
+	}
+
+	public void changeScheme(Scheme scheme) {
+		setScheme(scheme);
+	}
+
+	public void changeGameStyle(String style) {
+		setGameStyle(style);
+	}
+
+	public void changeWeaponset(Weaponset weaponset) {
+		setWeaponset(weaponset);
+	}
+
+	public void requestAddTeam(Team team, int colorIndex) {
+		putTeam(new TeamInGame(team, new TeamIngameAttributes("Player", colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false)));
+	}
+
+	public void requestRemoveTeam(String teamname) {
+		removeTeam(teamname);
+	}
+
+	public void changeTeamColorIndex(String teamname, int colorIndex) {
+		TeamInGame oldTeam = getTeams().get(teamname);
+		if(oldTeam != null) {
+			putTeam(oldTeam.withAttribs(oldTeam.ingameAttribs.withColorIndex(colorIndex)));
+		} else {
+			Log.e(TAG, "Requested color change for unknown team "+ teamname);
+		}
+	}
+
+	public void changeTeamHogCount(String teamname, int hogcount) {
+		TeamInGame oldTeam = getTeams().get(teamname);
+		if(oldTeam != null) {
+			putTeam(oldTeam.withAttribs(oldTeam.ingameAttribs.withHogCount(hogcount)));
+		} else {
+			Log.e(TAG, "Requested hog count change for unknown team "+ teamname);
+		}
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,12 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,66 +15,137 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+package org.hedgewars.hedgeroid;
 
-package org.hedgewars.hedgeroid;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 
 import org.hedgewars.hedgeroid.Downloader.DownloadAssets;
 import org.hedgewars.hedgeroid.Downloader.DownloadListActivity;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.IntentFilter;
 import android.os.Bundle;
-import android.preference.PreferenceManager;
 import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.content.LocalBroadcastManager;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.Toast;
 
 public class MainActivity extends FragmentActivity {
-
-	private Button downloader, startGame;
+	private static final int DIALOG_NO_SDCARD = 0;
+	
+	private LocalBroadcastManager broadcastManager;
 	private ProgressDialog assetsDialog;
 
 	public void onCreate(Bundle sis){
 		super.onCreate(sis);
-		setContentView(R.layout.main);
+		setContentView(R.layout.activity_main);
 
-		downloader = (Button)findViewById(R.id.downloader);
-		startGame = (Button)findViewById(R.id.startGame);
+		broadcastManager = LocalBroadcastManager.getInstance(getApplicationContext());
+		Button startLocalGame = (Button)findViewById(R.id.startGame);
+		Button startNetGame = (Button)findViewById(R.id.joinLobby);
 
-		downloader.setOnClickListener(downloadClicker);
-		startGame.setOnClickListener(startGameClicker);
-
+		startLocalGame.setOnClickListener(startGameListener);
+		startNetGame.setOnClickListener(startNetGameListener);
 
-		String cacheDir = Utils.getCachePath(this);
-		if(cacheDir == null){
-			showDialog(0);
-		}else{
-			int versionCode = 0;
+		if(!FileUtils.isDataPathAvailable()){
+			showDialog(DIALOG_NO_SDCARD);
+		} else {
+			String existingVersion = "";
 			try {
-				versionCode = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionCode;
-			} catch (NameNotFoundException e) {
-
+				File versionFile = new File(FileUtils.getCachePath(this), "assetsversion.txt");
+				existingVersion = FileUtils.readToString(new FileInputStream(versionFile));
+			} catch(IOException e) {
 			}
-			boolean assetsCopied = PreferenceManager.getDefaultSharedPreferences(this).getInt("latestAssets", 0) >= versionCode;
-
-			if(!assetsCopied){
+			
+			String newVersion = "";
+			try {
+				newVersion = FileUtils.readToString(getAssets().open("assetsversion.txt"));
+			} catch(IOException e) {
+			}
+			
+			if(!existingVersion.equals(newVersion)) {
 				DownloadAssets assetsAsyncTask = new DownloadAssets(this);
-				assetsDialog = ProgressDialog.show(this, "Please wait a moment", "Moving assets...");
-				assetsAsyncTask.execute((Object[])null);
+				assetsDialog = ProgressDialog.show(this, "Please wait a moment", "Moving assets to SD card...");
+				assetsAsyncTask.execute();
 			}
 		}
 	}
 
+	@Override
+	protected void onResume() {
+		super.onResume();
+		broadcastManager.registerReceiver(connectedReceiver, new IntentFilter(Netplay.ACTION_CONNECTED));
+		broadcastManager.registerReceiver(connectionFailedReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED));
+		broadcastManager.registerReceiver(passwordRequestedReceiver, new IntentFilter(Netplay.ACTION_PASSWORD_REQUESTED));
+	}
+	
+	@Override
+	protected void onPause() {
+		super.onPause();
+		broadcastManager.unregisterReceiver(connectedReceiver);
+		broadcastManager.unregisterReceiver(connectionFailedReceiver);
+		broadcastManager.unregisterReceiver(passwordRequestedReceiver);
+		Netplay netplay = Netplay.getAppInstance(getApplicationContext());
+		if(netplay.getState() == State.CONNECTING) {
+			netplay.disconnect();
+		}
+	}
+	
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		super.onCreateOptionsMenu(menu);
+		getMenuInflater().inflate(R.menu.main_options, menu);
+		return true;
+	}
+	
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		switch(item.getItemId()) {
+		case R.id.download:
+			startActivityForResult(new Intent(this, DownloadListActivity.class), 0);
+			return true;
+		case R.id.preferences:
+			Toast.makeText(this, R.string.not_implemented_yet, Toast.LENGTH_SHORT).show();
+			return true;
+		case R.id.edit_weaponsets:
+			startActivity(new Intent(this, WeaponsetListActivity.class));
+			return true;
+		case R.id.edit_teams:
+			startActivity(new Intent(this, TeamListActivity.class));
+			return true;
+		default:
+			return super.onOptionsItemSelected(item);
+		}
+	}
+	
 	public Dialog onCreateDialog(int id, Bundle args){
+		switch(id) {
+		case DIALOG_NO_SDCARD:
+			return createNoSdcardDialog();
+		default:
+			throw new IndexOutOfBoundsException();
+		}
+	}
+
+	private Dialog createNoSdcardDialog() {
 		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 		builder.setTitle(R.string.sdcard_not_mounted_title);
 		builder.setMessage(R.string.sdcard_not_mounted);
@@ -84,30 +157,67 @@
 
 		return builder.create();
 	}
-
+	
 	public void onAssetsDownloaded(boolean result){
-		if(result){
-			try {
-				int versionCode = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionCode;
-				PreferenceManager.getDefaultSharedPreferences(this).edit().putInt("latestAssets", versionCode).commit();
-			} catch (NameNotFoundException e) {}
-			
-		}else{
-			Toast.makeText(this, R.string.download_failed, Toast.LENGTH_LONG);
+		if(!result){
+			Toast.makeText(this, R.string.download_failed, Toast.LENGTH_LONG).show();
 		}
 		assetsDialog.dismiss();
 	}
 
-	private OnClickListener downloadClicker = new OnClickListener(){
+	private final OnClickListener startGameListener = new OnClickListener(){
 		public void onClick(View v){
-			//startActivityForResult(new Intent(getApplicationContext(), DownloadActivity.class), 0);
-			startActivityForResult(new Intent(getApplicationContext(), DownloadListActivity.class), 0);
+			startActivity(new Intent(getApplicationContext(), LocalRoomActivity.class));
+		}
+	};
+	
+	private final OnClickListener startNetGameListener = new OnClickListener() {
+		public void onClick(View v) {
+			State state = Netplay.getAppInstance(getApplicationContext()).getState();
+			switch(state) {
+			case NOT_CONNECTED:
+		        FragmentManager fm = getSupportFragmentManager();
+		        StartNetgameDialog startNetgameDialog = new StartNetgameDialog();
+		        startNetgameDialog.show(fm, "start_netgame_dialog");
+				break;
+			case CONNECTING:
+				onNetConnectingStarted();
+				break;
+			default:
+				startActivity(new Intent(getApplicationContext(), LobbyActivity.class));
+				break;
+			}
+		}
+	};
+	
+	private BroadcastReceiver connectedReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			startActivity(new Intent(getApplicationContext(), LobbyActivity.class));
+		}
+	};
+	
+	private BroadcastReceiver connectionFailedReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			if(intent.getBooleanExtra(Netplay.EXTRA_HAS_ERROR, true)) {
+				Toast.makeText(getApplicationContext(), intent.getStringExtra(Netplay.EXTRA_MESSAGE), Toast.LENGTH_LONG).show();
+			}
+		}
+	};
+	
+	private BroadcastReceiver passwordRequestedReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+	        FragmentManager fm = getSupportFragmentManager();
+	        PasswordDialog passwordDialog = new PasswordDialog(intent.getStringExtra(Netplay.EXTRA_PLAYERNAME));
+	        passwordDialog.show(fm, "fragment_password_dialog");
 		}
 	};
 
-	private OnClickListener startGameClicker = new OnClickListener(){
-		public void onClick(View v){
-			startActivity(new Intent(getApplicationContext(), StartGameActivity.class));
-		}
-	};
+	public void onNetConnectingStarted() {
+        FragmentManager fm = getSupportFragmentManager();
+        ConnectingDialog connectingDialog = new ConnectingDialog();
+        connectingDialog.show(fm, "fragment_connecting_dialog");
+	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,274 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.MapFile;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+import org.hedgewars.hedgeroid.util.CalmDownHandler;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TableRow;
+import android.widget.Toast;
+
+/**
+ * Display a map preview, and configuration options for the map.
+ * 
+ * Mostly for layout reasons, this does not include the theme setting, which
+ * (arguably) is more a map setting than a general game setting.
+ */
+public class MapFragment extends Fragment {
+	private Spinner mapTypeSpinner, mapNameSpinner, templateSpinner, mazeSizeSpinner;
+	private TableRow nameRow, templateRow, mazeSizeRow;
+	private ImageView mapPreview;
+	
+	private List<MapFile> mapFiles;
+	private RoomStateManager stateManager;
+	private Random random = new Random();
+	private CalmDownHandler mapPreviewHandler;
+	
+	/*
+	 * Rendering the preview can take a few seconds on Android, so we want to prevent preview
+	 * requests from queueing up if maps are changed quickly. So if there is already a preview
+	 * being generated, we store our latest request in the newPreviewRequest variable instead.
+	 * Once the current preview is finished generating it will start on that one.
+	 */
+	private boolean previewGenerationInProgress;
+	private MapRecipe newPreviewRequest;
+	private MapRecipe currentMap; // kept for reference on every change to find out what changed
+	
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+		View v = inflater.inflate(R.layout.fragment_map, container, false);
+		final Context appContext = getActivity().getApplicationContext();
+
+		/*
+		 * This handler will start the map preview after none of the map settings
+		 * have been updated for a short time.
+		 */
+		mapPreviewHandler = new CalmDownHandler(getActivity().getMainLooper(), new Runnable() {
+			public void run() {
+				if(!previewGenerationInProgress) {
+					mapPreview.setImageResource(R.drawable.roomlist_preparing);
+					MapPreviewGenerator.startPreviewGeneration(appContext, stateManager.getMapRecipe(), mapPreviewListener);
+					previewGenerationInProgress = true;
+				} else {
+					newPreviewRequest = stateManager.getMapRecipe();
+				}
+			}
+		}, 250);
+		
+		nameRow = (TableRow) v.findViewById(R.id.rowMapName);
+		templateRow = (TableRow) v.findViewById(R.id.rowTemplateFilter);
+		mazeSizeRow = (TableRow) v.findViewById(R.id.rowMazeSize);
+		mapPreview = (ImageView) v.findViewById(R.id.mapPreview);
+		mapPreview.setImageDrawable(null);;
+		mapPreview.setOnClickListener(mapClickListener);
+		
+		try {
+			mapFiles = FrontendDataUtils.getMaps(getActivity());
+		} catch (IOException e) {
+			Toast.makeText(getActivity().getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
+			getActivity().finish();
+			return null;
+		}
+		Collections.sort(mapFiles, MapFile.MISSIONS_FIRST_NAME_ORDER);
+		
+		List<String> mapNames = MapFile.toDisplayNameList(mapFiles, getResources());
+		mapTypeSpinner = prepareSpinner(v, R.id.spinMapType, Arrays.asList(getResources().getStringArray(R.array.map_types)), mapTypeSelectedListener);
+		mapNameSpinner = prepareSpinner(v, R.id.spinMapName, mapNames, mapNameSelectedListener);
+		templateSpinner = prepareSpinner(v, R.id.spinTemplateFilter, Arrays.asList(getResources().getStringArray(R.array.map_templates)), mapTemplateSelectedListener);
+		mazeSizeSpinner = prepareSpinner(v, R.id.spinMazeSize, Arrays.asList(getResources().getStringArray(R.array.map_maze_sizes)), mazeSizeSelectedListener);
+
+		stateManager.addListener(roomStateChangeListener);
+		currentMap = stateManager.getMapRecipe();
+		if(currentMap != null) {
+			updateDisplay(currentMap);
+		}
+		setChiefState(stateManager.getChiefStatus());
+		mapPreviewHandler.activity();
+		return v;
+	}
+	
+	private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) {
+		Spinner spinner = (Spinner)v.findViewById(id);
+		ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items);
+		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+		spinner.setAdapter(adapter);
+		spinner.setOnItemSelectedListener(itemSelectedListener);
+		return spinner;
+	}
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		try {
+			stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager();
+		} catch(ClassCastException e) {
+			throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e);
+		}
+	}
+	
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		mapPreviewHandler.stop();
+		newPreviewRequest = null;
+		
+		stateManager.removeListener(roomStateChangeListener);
+	}
+	
+	private void setChiefState(boolean chiefState) {
+		mapTypeSpinner.setEnabled(chiefState);
+		mapNameSpinner.setEnabled(chiefState);
+		templateSpinner.setEnabled(chiefState);
+		mazeSizeSpinner.setEnabled(chiefState);
+		mapPreview.setEnabled(chiefState);
+		
+		if(chiefState) {
+			sendMapnameAndGenerator();
+			stateManager.changeMapTemplate(templateSpinner.getSelectedItemPosition());
+			stateManager.changeMazeSize(mazeSizeSpinner.getSelectedItemPosition());
+		}
+	}
+	
+	private void updateDisplay(MapRecipe map) {
+		nameRow.setVisibility(map.mapgen == Frontlib.MAPGEN_NAMED ? View.VISIBLE : View.GONE);
+		templateRow.setVisibility(map.mapgen == Frontlib.MAPGEN_REGULAR ? View.VISIBLE : View.GONE);
+		mazeSizeRow.setVisibility(map.mapgen == Frontlib.MAPGEN_MAZE ? View.VISIBLE : View.GONE);
+		
+		mapTypeSpinner.setSelection(map.mapgen);
+		int mapPosition = findMapPosition(mapFiles, map.name);
+		if(mapPosition >= 0) {
+			mapNameSpinner.setSelection(mapPosition);
+		}
+		templateSpinner.setSelection(map.templateFilter);
+		mazeSizeSpinner.setSelection(map.mazeSize);
+	}
+	
+	private static int findMapPosition(List<MapFile> mapFiles, String mapName) {
+		for(int i=0; i<mapFiles.size(); i++) {
+			if(mapName.equals(mapFiles.get(i).name)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	private void sendMapnameAndGenerator() {
+		int mapType = mapTypeSpinner.getSelectedItemPosition();
+		String mapName = mapFiles.get(mapNameSpinner.getSelectedItemPosition()).name;
+		stateManager.changeMapNameAndGenerator(MapRecipe.mapnameForGenerator(mapType, mapName));
+	}
+	
+	private final OnItemSelectedListener mapTypeSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			sendMapnameAndGenerator();
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener mapNameSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			sendMapnameAndGenerator();
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener mapTemplateSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeMapTemplate(position);
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener mazeSizeSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeMazeSize(position);
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnClickListener mapClickListener = new OnClickListener() {
+		public void onClick(View v) {
+			stateManager.changeMapSeed(MapRecipe.makeRandomSeed());
+			if(mapTypeSpinner.getSelectedItemPosition() == Frontlib.MAPGEN_NAMED) {
+				mapNameSpinner.setSelection(random.nextInt(mapNameSpinner.getCount()));
+			}
+		}
+	};
+	
+	private final RoomStateManager.Listener roomStateChangeListener = new RoomStateManager.ListenerAdapter() {
+		@Override
+		public void onChiefStatusChanged(boolean isChief) {
+			setChiefState(isChief);
+		};
+		
+		@Override
+		public void onMapChanged(MapRecipe recipe) {
+			// Only trigger a preview update if a relevant field changed (not theme)
+			if(currentMap==null
+					|| currentMap.mapgen != recipe.mapgen
+					|| currentMap.mazeSize != recipe.mazeSize
+					|| !currentMap.name.equals(recipe.name)
+					|| !currentMap.seed.equals(recipe.seed)
+					|| currentMap.templateFilter != recipe.templateFilter
+					|| !Arrays.equals(currentMap.getDrawData(), recipe.getDrawData())) {
+				mapPreviewHandler.activity();
+			}
+			updateDisplay(recipe);
+			currentMap = recipe;
+		};
+	};
+	
+	private MapPreviewGenerator.Listener mapPreviewListener = new MapPreviewGenerator.Listener() {
+		public void onMapPreviewResult(Drawable preview) {
+			if(newPreviewRequest != null) {
+				MapPreviewGenerator.startPreviewGeneration(getActivity().getApplicationContext(), newPreviewRequest, mapPreviewListener);
+				newPreviewRequest = null;
+			} else {
+				if(mapPreview != null) {
+					mapPreview.setImageDrawable(preview);
+				}
+				previewGenerationInProgress = false;
+			}
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapPreviewGenerator.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,223 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import org.hedgewars.hedgeroid.Datastructures.MapFile;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.ByteArrayPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.MapconnPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.MapimageCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
+import org.hedgewars.hedgeroid.util.FileUtils;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.sun.jna.Pointer;
+
+/**
+ * A class that asynchronously generates a map preview from a MapRecipe.
+ * 
+ * For named maps, this will load the preview image from the filesystem. For others,
+ * it will call the Hedgewars engine to generate a preview image. The result is sent
+ * back to a listener on the UI thread.
+ */
+public final class MapPreviewGenerator implements Runnable {
+	private static final String TAG = MapPreviewGenerator.class.getSimpleName();
+	private static final Handler mainHandler = new Handler(Looper.getMainLooper());
+	private static final long TIMEOUT_NS = 20l * 1000 * 1000 * 1000;
+
+	private final Context appContext;
+	private final MapRecipe map;
+	private final Listener listener;
+	
+	private boolean resultAvailable;
+	private Drawable result;
+	
+	public static interface Listener {
+		/**
+		 * This is called on the UI thread once the preview is ready or failed.
+		 * In case of failure, null is passed.
+		 */
+		void onMapPreviewResult(Drawable preview);
+	}
+
+	private MapPreviewGenerator(Context appContext, MapRecipe map, Listener listener) {
+		this.appContext = appContext;
+		this.map = map;
+		this.listener = listener;
+	}
+	
+	public void run() {
+		if (map.mapgen == Frontlib.MAPGEN_NAMED) {
+			postToListener(loadPreviewFromFile(appContext, map.name));
+		} else {
+			resultAvailable = false;
+			result = null;
+			MapconnPtr conn = Flib.INSTANCE.flib_mapconn_create(MapRecipePtr.createJavaOwned(map));
+			if (conn == null) {
+				postToListener(null);
+				return;
+			}
+			try {
+				int port = Flib.INSTANCE.flib_mapconn_getport(conn);
+				Flib.INSTANCE.flib_mapconn_onSuccess(conn, successCb, null);
+				Flib.INSTANCE.flib_mapconn_onFailure(conn, failureCb, null);
+	
+				String configPath;
+				try {
+					configPath = FileUtils.getCachePath(appContext).getAbsolutePath();
+				} catch(FileNotFoundException e) {
+					return;
+				}
+				
+				startEngine(configPath, port);
+				long startTime = System.nanoTime();
+				do {
+					Flib.INSTANCE.flib_mapconn_tick(conn);
+					try {
+						Thread.sleep(50);
+					} catch (InterruptedException e) {
+						// ignore
+					}
+					if(System.nanoTime()-startTime > TIMEOUT_NS) {
+						Log.w(TAG, "Error generating map preview: timeout");
+						resultAvailable = true;
+					}
+				} while(!resultAvailable); 
+			} finally {
+				Flib.INSTANCE.flib_mapconn_destroy(conn);
+				postToListener(result);
+			}
+		}
+	}
+	
+	public static void startPreviewGeneration(Context appContext, MapRecipe map, Listener listener) {
+		new Thread(new MapPreviewGenerator(appContext, map, listener)).start();
+	}
+	
+	private static Drawable loadPreviewFromFile(Context appContext, String mapName) {
+		if(!mapName.startsWith("+")) {
+			try {
+				File previewFile = MapFile.getPreviewFile(appContext, mapName);
+				return Drawable.createFromPath(previewFile.getAbsolutePath());
+			} catch (FileNotFoundException e) {
+				Log.w("MapPreviewGenerator", "Preview for map "+mapName+" not found.");
+			}
+		}
+		return null;
+	}
+	
+	private static void startEngine(final String configPath, final int port) {
+		new Thread(new Runnable() {
+			public void run() {
+				Log.d(TAG, "Starting engine "+port);
+				PascalExports.synchronizedGenLandPreview(port);
+				Log.d(TAG, "Engine finished");
+			}
+		}).start();
+	}
+	
+	private void postToListener(final Drawable result) {
+		mainHandler.post(new Runnable() {
+			public void run() {
+				listener.onMapPreviewResult(result);
+			}
+		});
+	}
+	
+	/**
+	 * Let's be extra nice here and clip off the left and right sides, so the preview is centered...
+	 * Since the image is present in bytes, we can save some effort by checking entire byte-columns first.
+	 */
+	private final MapimageCallback successCb = new MapimageCallback() {
+		public void callback(Pointer context, ByteArrayPtr buffer, int hedgehogCount) {
+			byte[] mapdata = buffer.deref(Frontlib.MAPIMAGE_BYTES);
+			
+			int leftmostPixel = Frontlib.MAPIMAGE_WIDTH;
+			int rightmostPixel = -1;
+			int bytesPerLine = Frontlib.MAPIMAGE_WIDTH/8;
+			
+			// Find the leftmost pixel
+			for(int xbyte=0; xbyte<bytesPerLine; xbyte++) {
+				for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) {
+					int b = 0xff&mapdata[xbyte+y*bytesPerLine];
+					if(b != 0) {
+						leftmostPixel = Math.min(leftmostPixel, Integer.numberOfLeadingZeros(b)-24+xbyte*8);
+					}
+				}
+				if(leftmostPixel!=Frontlib.MAPIMAGE_WIDTH) break;
+			}
+			
+			// Find the rightmost pixel
+			for(int xbyte=bytesPerLine-1; xbyte>=0; xbyte--) {
+				for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) {
+					int b = mapdata[xbyte+y*bytesPerLine];
+					if(b != 0) {
+						rightmostPixel = Math.max(rightmostPixel, xbyte*8+7-Integer.numberOfTrailingZeros(b));
+					}
+				}
+				if(rightmostPixel!=-1) break;
+			}
+		
+			// No pixel was set at all -> use default width
+			if(rightmostPixel==-1) {
+				leftmostPixel = 0;
+				rightmostPixel = Frontlib.MAPIMAGE_WIDTH-1;
+			}
+			
+			Bitmap bitmap = Bitmap.createBitmap(rightmostPixel-leftmostPixel+1, Frontlib.MAPIMAGE_HEIGHT, Config.ARGB_8888);
+			for(int y=0; y<Frontlib.MAPIMAGE_HEIGHT; y++) {
+				for(int x=0; x<bitmap.getWidth(); x++) {
+					bitmap.setPixel(x, y, isPixelSet(mapdata, x+leftmostPixel, y) ? Color.YELLOW : Color.TRANSPARENT);
+				}
+			}
+			result = new BitmapDrawable(bitmap);
+			resultAvailable = true;
+		}
+	};
+	
+	private static boolean isPixelSet(byte[] imgdata, int x, int y) {
+		int pixelnum = x+Frontlib.MAPIMAGE_WIDTH*y;
+		return (imgdata[pixelnum>>3] & (128>>(pixelnum&7))) != 0;
+	}
+	
+	private final StrCallback failureCb = new StrCallback() {
+		public void callback(Pointer context, String reason) {
+			Log.w(TAG, "Error generating map preview: "+reason);
+			result = null;
+			resultAvailable = true;
+		}
+	};
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/NetRoomActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,151 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.NetplayStateFragment.NetplayStateListener;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.RunGameListener;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+import org.hedgewars.hedgeroid.util.UiUtils;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TabHost;
+
+/**
+ * This activity is used to set up and start a game on the server.
+ */
+public class NetRoomActivity extends FragmentActivity implements NetplayStateListener, TeamAddDialog.Listener, RoomStateManager.Provider, RunGameListener {
+	private TabHost tabHost;
+	private Netplay netplay;
+	private RoomStateManager stateManager;
+	private Button startButton;
+	
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        netplay = Netplay.getAppInstance(getApplicationContext());
+        netplay.registerRunGameListener(this);
+        stateManager = netplay.getRoomStateManager();
+        stateManager.addListener(roomStateChangeListener);
+        
+        setContentView(R.layout.activity_netroom);
+        startButton = (Button)findViewById(R.id.startGame);
+        
+        ChatFragment chatFragment = (ChatFragment)getSupportFragmentManager().findFragmentById(R.id.chatFragment);
+        chatFragment.setInRoom(true);
+        
+        FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+        trans.add(new NetplayStateFragment(), "netplayFragment");
+        trans.commit();
+        
+        startButton.setVisibility(netplay.isChief() ? View.VISIBLE : View.GONE);
+        startButton.setOnClickListener(startButtonClickListener);
+        
+        // Set up a tabbed UI for medium and small screens
+        tabHost = (TabHost)findViewById(android.R.id.tabhost);
+        if(tabHost != null) {
+	        tabHost.setup();
+	        tabHost.getTabWidget().setOrientation(LinearLayout.VERTICAL);
+
+	        tabHost.addTab(tabHost.newTabSpec("map").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_map, 0)).setContent(R.id.mapFragment));
+	        tabHost.addTab(tabHost.newTabSpec("settings").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_settings, 0)).setContent(R.id.settingsFragment));
+	        tabHost.addTab(tabHost.newTabSpec("teams").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_teams, 0)).setContent(R.id.teamlistFragment));
+	        tabHost.addTab(tabHost.newTabSpec("chat").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_chat, 0)).setContent(R.id.chatFragment));
+	        tabHost.addTab(tabHost.newTabSpec("players").setIndicator(UiUtils.createVerticalTabIndicator(tabHost, R.string.room_tab_players, 0)).setContent(R.id.playerListContainer));
+	        
+	        if (icicle != null) {
+	            tabHost.setCurrentTabByTag(icicle.getString("currentTab"));
+	        }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+    	super.onDestroy();
+    	stateManager.removeListener(roomStateChangeListener);
+    	netplay.unregisterRunGameListener(this);
+    }
+    
+	@Override
+	public void onBackPressed() {
+		netplay.sendLeaveRoom(null);
+	}
+    
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+        if(tabHost != null) {
+        	icicle.putString("currentTab", tabHost.getCurrentTabTag());
+        }
+    }
+    
+    public void onNetplayStateChanged(State newState) {
+    	switch(newState) {
+    	case NOT_CONNECTED:
+    	case CONNECTING:
+    	case LOBBY:
+    		finish();
+    		break;
+    	case ROOM:
+    		// Do nothing
+    		break;
+		default:
+			throw new IllegalStateException("Unknown connection state: "+newState);
+    	}
+    }
+    
+	public void onTeamAddDialogSubmitted(Team newTeam) {
+		stateManager.requestAddTeam(newTeam, TeamInGame.getUnusedOrRandomColorIndex(stateManager.getTeams().values()));
+	}
+	
+	public RoomStateManager getRoomStateManager() {
+		return stateManager;
+	}
+
+	private final OnClickListener startButtonClickListener = new OnClickListener() {
+		public void onClick(View v) {
+			netplay.sendStartGame();
+		}
+	};
+	
+	private final RoomStateManager.Listener roomStateChangeListener = new RoomStateManager.ListenerAdapter() {
+		@Override
+		public void onChiefStatusChanged(boolean isChief) {
+			startButton.setVisibility(isChief ? View.VISIBLE : View.GONE);
+		}
+	};
+	
+	public void runGame(GameConfig config) {
+		SDLActivity.startConfig = config;
+		SDLActivity.startNetgame = true;
+		startActivity(new Intent(this, SDLActivity.class));
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/NetplayStateFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,137 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.netplay.Netplay.State;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.LocalBroadcastManager;
+import android.widget.Toast;
+
+/**
+ * Fragment for use by an activity that depends on the state of the network
+ * connection. The activity must implement the NetplayStateListener interface.
+ * 
+ * This fragment manages reacting to changes in the networking state by calling
+ * a callback method on the activity.
+ */
+public class NetplayStateFragment extends Fragment {
+    private Netplay netplay;
+    private Context appContext;
+    private LocalBroadcastManager broadcastManager;
+    private NetplayStateListener listener;
+    private State knownState;
+    
+    interface NetplayStateListener {
+    	/**
+    	 * This is called while the activity is running, and every time during resume, if
+    	 * a change in the networking state is detected. It is also called once
+    	 * with the initial state (which could be called a change from the "unknown" state).
+    	 */
+    	void onNetplayStateChanged(State newState);
+    } 
+    
+    @Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
+		try {
+			listener = (NetplayStateListener) activity;
+		} catch(ClassCastException e) {
+			throw new ClassCastException("Activity " + activity + " must implement NetplayStateListener to use NetplayStateFragment.");
+		}
+	}
+	
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		listener = null;
+	}
+	
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        appContext = getActivity().getApplicationContext();
+        broadcastManager = LocalBroadcastManager.getInstance(appContext);
+        netplay = Netplay.getAppInstance(appContext);
+    }    
+
+    @Override
+    public void onResume() {
+    	super.onResume();
+    	broadcastManager.registerReceiver(disconnectReceiver, new IntentFilter(Netplay.ACTION_DISCONNECTED));
+    	broadcastManager.registerReceiver(leaveRoomReceiver, new IntentFilter(Netplay.ACTION_LEFT_ROOM));
+    	broadcastManager.registerReceiver(stateChangeReceiver, new IntentFilter(Netplay.ACTION_STATE_CHANGED));
+    	
+    	State newState = netplay.getState();
+		if(knownState != newState) {
+    		listener.onNetplayStateChanged(newState);
+    		knownState = newState;
+    	}
+    }
+    
+    @Override
+    public void onPause() {
+    	super.onPause();
+    	broadcastManager.unregisterReceiver(disconnectReceiver);
+    	broadcastManager.unregisterReceiver(leaveRoomReceiver);
+    	broadcastManager.unregisterReceiver(stateChangeReceiver);
+    }
+
+	private final BroadcastReceiver disconnectReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			if(intent.getBooleanExtra(Netplay.EXTRA_HAS_ERROR, true)) {
+				String message = intent.getStringExtra(Netplay.EXTRA_MESSAGE);
+				String toastText = getString(R.string.toast_disconnected, message);
+				Toast.makeText(appContext, toastText, Toast.LENGTH_LONG).show();
+			}
+		}
+	};
+	
+	private final BroadcastReceiver leaveRoomReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			int reason = intent.getIntExtra(Netplay.EXTRA_REASON, -1);
+			if(reason == Frontlib.NETCONN_ROOMLEAVE_ABANDONED) {
+				Toast.makeText(appContext, R.string.toast_room_abandoned, Toast.LENGTH_LONG).show();
+			} else if(reason == Frontlib.NETCONN_ROOMLEAVE_KICKED) {
+				Toast.makeText(appContext, R.string.toast_kicked, Toast.LENGTH_LONG).show();
+			}
+		}
+	};
+	
+	private final BroadcastReceiver stateChangeReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			State newState = netplay.getState();
+			listener.onNetplayStateChanged(newState);
+			knownState = newState;
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/PasswordDialog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,82 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.text.method.PasswordTransformationMethod;
+import android.widget.EditText;
+
+/**
+ * Shown when connecting to the server, and the server requests a password.
+ */
+public class PasswordDialog extends ConnectionDependendDialogFragment {
+	String username;
+	
+	public PasswordDialog() {
+	}
+	
+	public PasswordDialog(String username) {
+		this.username = username;
+	}
+	
+	@Override
+	public void onSaveInstanceState(Bundle icicle) {
+		super.onSaveInstanceState(icicle);
+		icicle.putString("username", username);
+	}
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		if(savedInstanceState != null) {
+			username = savedInstanceState.getString("username");
+		}
+		final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		final EditText editText = new EditText(getActivity());
+		final Netplay netplay = Netplay.getAppInstance(getActivity().getApplicationContext());
+		
+		editText.setHint(R.string.dialog_password_hint);
+		editText.setId(android.R.id.text1);
+		editText.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+		editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+		builder.setView(editText);
+		builder.setTitle(R.string.dialog_password_title);
+		builder.setMessage(getString(R.string.dialog_password_message, username));
+		builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				String password = editText.getText().toString();
+				editText.setText("");
+				netplay.sendPassword(password);
+			}
+		});
+		return builder.create();
+	}
+	
+	@Override
+	public void onCancel(DialogInterface dialog) {
+		super.onCancel(dialog);
+		Netplay.getAppInstance(getActivity().getApplicationContext()).disconnect();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,60 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.Comparator;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
+import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class RoomPlayerlistAdapter extends ObservableTreeMapAdapter<String, PlayerInRoom> {
+	@Override
+	protected Comparator<PlayerInRoom> getEntryOrder() {
+		return AlphabeticalOrderComparator.INSTANCE;
+	}
+
+	public View getView(int position, View convertView, ViewGroup parent) {
+		View v = convertView;
+		if (v == null) {
+			LayoutInflater vi = LayoutInflater.from(parent.getContext());
+			v = vi.inflate(R.layout.listview_player, null);
+		}
+
+		PlayerInRoom player = getItem(position);
+		TextView username = (TextView) v.findViewById(android.R.id.text1);
+		username.setText(player.player.name);
+		int readyDrawable = player.ready ? R.drawable.lightbulb_on : R.drawable.lightbulb_off;
+		username.setCompoundDrawablesWithIntrinsicBounds(readyDrawable, 0, 0, 0);
+		return v;
+	}
+	
+	private static final class AlphabeticalOrderComparator implements Comparator<PlayerInRoom> {
+		public static final AlphabeticalOrderComparator INSTANCE = new AlphabeticalOrderComparator();
+		public int compare(PlayerInRoom lhs, PlayerInRoom rhs) {
+			return lhs.player.name.compareToIgnoreCase(rhs.player.name);
+		};
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomPlayerlistFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,110 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Player;
+import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class RoomPlayerlistFragment extends ListFragment implements OnItemClickListener {
+	private Netplay netplay;
+	private RoomPlayerlistAdapter adapter;
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		netplay = Netplay.getAppInstance(getActivity().getApplicationContext());
+		adapter = new RoomPlayerlistAdapter();
+		adapter.setSource(netplay.roomPlayerlist);
+		setListAdapter(adapter);
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		adapter.invalidate();
+	}
+	
+	@Override
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
+		registerForContextMenu(getListView());
+		getListView().setOnItemClickListener(this);
+	}
+
+	@Override
+	public void onCreateContextMenu(ContextMenu menu, View v,
+			ContextMenuInfo menuInfo) {
+		super.onCreateContextMenu(menu, v, menuInfo);
+		AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
+		String playerName = adapter.getItem(info.position).player.name;
+		
+		MenuInflater inflater = getActivity().getMenuInflater();
+		inflater.inflate(R.menu.room_playerlist_context, menu);
+		if(netplay.isChief() && !playerName.equals(netplay.getPlayerName())) {
+			inflater.inflate(R.menu.room_playerlist_chief_context, menu);
+		}
+		menu.setHeaderIcon(R.drawable.human);
+		menu.setHeaderTitle(playerName);
+	}
+	
+	@Override
+	public boolean onContextItemSelected(MenuItem item) {
+		AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
+		PlayerInRoom player = adapter.getItem(info.position);
+		switch(item.getItemId()) {
+		case R.id.player_info:
+			netplay.sendPlayerInfoQuery(player.player.name);
+			return true;
+		case R.id.player_kick:
+			netplay.sendKick(player.player.name);
+			return true;
+		default:
+			return super.onContextItemSelected(item);
+		}
+	}
+	
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		return inflater.inflate(R.layout.fragment_playerlist, container, false);
+	}
+	
+	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+		Player player = adapter.getItem(position).player;
+		if(player.name.equals(netplay.getPlayerName())) {
+			netplay.sendToggleReady();
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomStateManager.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,110 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+
+/**
+ * This interface is supposed to abstract the handling of room state for several
+ * fragments that can display and manipulate it. The purpose of this is to allow
+ * using these fragments both for setting up networked and local games, despite
+ * the fact that for local games the settings can be changed immediately in
+ * memory, while they have to be sent out to the server for networked games.
+ * 
+ * If/when the state changes as result of calling one of the "changeX" or
+ * "requestX" functions, that will also trigger the corresponding change
+ * listener method. There is no guarantee that calling a changeX method will
+ * actually change the setting (e.g. if you're not room chief).
+ * 
+ * For local games, getChiefStatus is always true.
+ * 
+ * Implementations of this interface are probably not thread safe and should
+ * only be used on the UI thread.
+ */
+public interface RoomStateManager {
+	// Query current state
+	MapRecipe getMapRecipe();
+	boolean getChiefStatus();
+	Scheme getScheme();
+	String getGameStyle();
+	Weaponset getWeaponset();
+	Map<String, TeamInGame> getTeams();
+	
+	// Manipulate state
+	void changeMapRecipe(MapRecipe map);
+	void changeMapTheme(String theme);
+
+	/**
+	 * This function sets both the map's name and generator. There is no function
+	 * to change them independendly since e.g. the QtFrontend relies on them being
+	 * consistent.
+	 * 
+	 * If the name parameter is equal to one of the MapRecipe.MAPNAME_REGULAR, MAPNAME_MAZE
+	 * or MAPNAME_DRAWN constants, the map generator is set accordingly. Otherwise, the
+	 * map generator is set to represent a mapfile. The map's name is always set to
+	 * the parameter.
+	 */
+	void changeMapNameAndGenerator(String mapName);
+	void changeMapTemplate(int template);
+	void changeMazeSize(int mazeSize);
+	void changeMapSeed(String seed);
+	void changeMapDrawdata(byte[] drawdata);
+	
+	void changeScheme(Scheme scheme);
+	void changeGameStyle(String style);
+	void changeWeaponset(Weaponset weaponset);
+	
+	void requestAddTeam(Team team, int colorIndex);
+	void requestRemoveTeam(String teamname);
+	void changeTeamColorIndex(String teamname, int colorIndex);
+	void changeTeamHogCount(String teamname, int hogcount);
+	
+	// Observe changes
+	void addListener(Listener observer);
+	void removeListener(Listener observer);
+	
+	public interface Listener {
+		void onMapChanged(MapRecipe recipe);
+		void onChiefStatusChanged(boolean isChief);
+		void onSchemeChanged(Scheme scheme);
+		void onGameStyleChanged(String gameStyle);
+		void onWeaponsetChanged(Weaponset weaponset);
+		void onTeamsChanged(Map<String, TeamInGame> teams);
+	}
+	
+	public static class ListenerAdapter implements Listener {
+		public void onMapChanged(MapRecipe recipe) {}
+		public void onChiefStatusChanged(boolean isChief) {}
+		public void onSchemeChanged(Scheme scheme) {}
+		public void onGameStyleChanged(String gameStyle) {}
+		public void onWeaponsetChanged(Weaponset weaponset) {}
+		public void onTeamsChanged(Map<String, TeamInGame> teams) {}
+	}
+	
+	public interface Provider {
+		RoomStateManager getRoomStateManager();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,113 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.Comparator;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Room;
+import org.hedgewars.hedgeroid.Datastructures.RoomWithId;
+import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Displays the list of all rooms in the lobby
+ */
+public class RoomlistAdapter extends ObservableTreeMapAdapter<String, RoomWithId> {
+	private Context context;
+	
+	public RoomlistAdapter(Context context) {
+		this.context = context;
+	}
+	
+	@Override
+	protected Comparator<RoomWithId> getEntryOrder() {
+		return RoomWithId.NEWEST_FIRST_ORDER;
+	}
+	
+	@Override
+	public long getItemId(int position) {
+		return getItem(position).id;
+	}
+	
+	@Override
+	public boolean hasStableIds() {
+		return true;
+	}
+	
+	private static CharSequence formatExtra(Resources res, Room room) {
+		String ownermsg = res.getString(R.string.roomlist_owner, room.owner);
+		String mapmsg = res.getString(R.string.roomlist_map, room.formatMapName(res));
+		String scheme = room.scheme.equals(room.weapons) ? room.scheme : room.scheme + " / " + room.weapons;
+		String schememsg = res.getString(R.string.roomlist_scheme, scheme);
+		return ownermsg + ". " + mapmsg + ", " + schememsg;
+	}
+	
+	public View getView(int position, View convertView, ViewGroup parent) {
+		View v = convertView;
+		if (v == null) {
+			LayoutInflater vi = LayoutInflater.from(context);
+			v = vi.inflate(R.layout.listview_room, null);
+		}
+		
+		Room room = getItem(position).room;
+		int iconRes = room.inProgress ? R.drawable.roomlist_ingame : R.drawable.roomlist_preparing;
+		
+		if(v.findViewById(android.R.id.text1) == null) {
+			// Tabular room list
+			TextView roomnameView = (TextView)v.findViewById(R.id.roomname);
+			TextView playerCountView = (TextView)v.findViewById(R.id.playercount);
+			TextView teamCountView = (TextView)v.findViewById(R.id.teamcount);
+			TextView ownerView = (TextView)v.findViewById(R.id.owner);
+			TextView mapView = (TextView)v.findViewById(R.id.map);
+			TextView schemeView = (TextView)v.findViewById(R.id.scheme);
+			TextView weaponView = (TextView)v.findViewById(R.id.weapons);
+			
+			roomnameView.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
+			roomnameView.setText(room.name);
+			if(playerCountView != null) {
+				playerCountView.setText(String.valueOf(room.playerCount));
+			}
+			if(teamCountView != null) {
+				teamCountView.setText(String.valueOf(room.teamCount));
+			}
+			ownerView.setText(room.owner);
+			mapView.setText(room.formatMapName(context.getResources()));
+			schemeView.setText(room.scheme);
+			weaponView.setText(room.weapons);
+		} else {
+			// Small room list
+			TextView v1 = (TextView)v.findViewById(android.R.id.text1);
+			TextView v2 = (TextView)v.findViewById(android.R.id.text2);
+			
+			v1.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
+			v1.setText(room.name);
+			v2.setText(formatExtra(context.getResources(), room));
+		}
+		
+		return v;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/RoomlistFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,94 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.support.v4.app.ListFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+
+/**
+ * Displays the list of all rooms in the lobby
+ */
+public class RoomlistFragment extends ListFragment implements OnItemClickListener {
+	private static final int AUTO_REFRESH_INTERVAL_MS = 15000;
+	
+	private Netplay netplay;
+	private RoomlistAdapter adapter;
+	private CountDownTimer autoRefreshTimer = new CountDownTimer(Long.MAX_VALUE, AUTO_REFRESH_INTERVAL_MS) {
+		@Override
+		public void onTick(long millisUntilFinished) {
+			netplay.sendRoomlistRequest();
+		}
+		
+		@Override
+		public void onFinish() { }
+	};
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		netplay = Netplay.getAppInstance(getActivity().getApplicationContext());
+		adapter = new RoomlistAdapter(getActivity());
+		adapter.setSource(netplay.roomList);
+		setListAdapter(adapter);
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		return inflater.inflate(R.layout.fragment_roomlist, container, false);
+	}
+	
+	@Override
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
+		getListView().setOnItemClickListener(this);
+	}
+	
+	@Override
+	public void onResume() {
+		super.onResume();
+		autoRefreshTimer.start();
+	}
+	
+	@Override
+	public void onPause() {
+		super.onPause();
+		autoRefreshTimer.cancel();
+	}
+	
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		adapter.invalidate();
+	}
+	
+	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+		netplay.sendJoinRoom(adapter.getItem(position).room.name);
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,14 +1,40 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package org.hedgewars.hedgeroid;
 
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.ConnectException;
+
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.egl.EGLSurface;
 
-import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-import org.hedgewars.hedgeroid.EngineProtocol.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
+import org.hedgewars.hedgeroid.netplay.Netplay;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
 import android.app.Activity;
 import android.content.Context;
@@ -22,8 +48,7 @@
 import android.media.AudioManager;
 import android.media.AudioTrack;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
+import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -37,12 +62,17 @@
     SDL Activity
  */
 public class SDLActivity extends Activity {
-
+	/**
+	 * Set startConfig to the desired config when starting this activity. This avoids having to parcel all
+	 * the config objects into the Intent. Not particularly elegant, but it's actually a recommended
+	 * way to do this (http://developer.android.com/guide/faq/framework.html#3)
+	 */
+	public static volatile GameConfig startConfig;
+	public static volatile boolean startNetgame;
+	
 	// Main components
 	public static SDLActivity mSingleton;
 	private static SDLSurface mSurface;
-
-	// This is what SDL runs in. It invokes SDL_main(), eventually
 	private static Thread mSDLThread;
 
 	// Audio
@@ -59,26 +89,20 @@
 	// Load the .so
 	static {
 		System.loadLibrary("SDL");
-		//System.loadLibrary("SDL_image");
-		//System.loadLibrary("SDL_mixer");
-		//System.loadLibrary("SDL_ttf");
 		System.loadLibrary("main");
 	}
 
 	// Setup
 	protected void onCreate(Bundle savedInstanceState) {
-		//Log.v("SDL", "onCreate()");
 		super.onCreate(savedInstanceState);
 
 		// So we can call stuff from static callbacks
 		mSingleton = this;
 
 		// Set up the surface
-		GameConfig config = getIntent().getParcelableExtra("config");
-
-		mSurface = new SDLSurface(getApplication(), config);
+		mSurface = new SDLSurface(getApplication(), startConfig, startNetgame);
+		startConfig = null;
 		setContentView(mSurface);
-		SurfaceHolder holder = mSurface.getHolder();
 	}
 
 	// Events
@@ -106,42 +130,26 @@
 		Log.v("SDL", "onDestroy()");
 		// Send a quit message to the application
 		SDLActivity.nativeQuit();
-
 		// Now wait for the SDL thread to quit
 		if (mSDLThread != null) {
 			try {
 				mSDLThread.join();
 			} catch(Exception e) {
-				Log.v("SDL", "Problem stopping thread: " + e);
+				Log.w("SDL", "Problem stopping thread: " + e);
 			}
 			mSDLThread = null;
-
-			//Log.v("SDL", "Finished waiting for SDL thread");
+		}
+		mSingleton = null;
+	}
+	
+	public static void synchronizedNativeInit(String...args) {
+		synchronized(PascalExports.engineMutex) {
+			nativeInit(args);
 		}
 	}
-
-	// Messages from the SDLMain thread
-	static int COMMAND_CHANGE_TITLE = 1;
-
-	// Handler for the messages
-	Handler commandHandler = new Handler() {
-		public void handleMessage(Message msg) {
-			if (msg.arg1 == COMMAND_CHANGE_TITLE) {
-				setTitle((String)msg.obj);
-			}
-		}
-	};
-
-	// Send a message from the SDLMain thread
-	void sendCommand(int command, Object data) {
-		Message msg = commandHandler.obtainMessage();
-		msg.arg1 = command;
-		msg.obj = data;
-		commandHandler.sendMessage(msg);
-	}
-
+	
 	// C functions we call
-	public static native void nativeInit(String...args);
+	private static native void nativeInit(String...args);
 	public static native void nativeQuit();
 	public static native void nativePause();
 	public static native void nativeResume();
@@ -165,22 +173,25 @@
 		flipEGL();
 	}
 
-	public static void setActivityTitle(String title) {
+	public static void setActivityTitle(final String title) {
 		// Called from SDLMain() thread and can't directly affect the view
-		mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
+		mSingleton.runOnUiThread(new Runnable() {
+			public void run() {
+				mSingleton.setTitle(title);
+			}
+		});
 	}
 
 	public static Context getContext() {
 		return mSingleton;
 	}
 
-	public static void startApp(int width, int height, GameConfig config) {
+	public static void startApp(final int width, final int height, GameConfig config, boolean netgame) {
 		// Start up the C app thread
 		if (mSDLThread == null) {
-			mSDLThread = new Thread(new SDLMain(width, height, config), "SDLThread");
+			mSDLThread = new Thread(new SDLMain(width, height, config, netgame));
 			mSDLThread.start();
-		}
-		else {
+		} else {
 			SDLActivity.nativeResume();
 		}
 	}
@@ -188,8 +199,6 @@
 	// EGL functions
 	public static boolean initEGL(int majorVersion, int minorVersion) {
 		if (SDLActivity.mEGLDisplay == null) {
-			//Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);
-
 			try {
 				EGL10 egl = (EGL10)EGLContext.getEGL();
 
@@ -207,7 +216,6 @@
 					renderableType = EGL_OPENGL_ES_BIT;
 				}
 				int[] configSpec = {
-						//EGL10.EGL_DEPTH_SIZE,   16,
 						EGL10.EGL_RENDERABLE_TYPE, renderableType,
 						EGL10.EGL_NONE
 				};
@@ -219,15 +227,6 @@
 				}
 				EGLConfig config = configs[0];
 
-				/*int EGL_CONTEXT_CLIENT_VERSION=0x3098;
-                int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, majorVersion, EGL10.EGL_NONE };
-                EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs);
-
-                if (ctx == EGL10.EGL_NO_CONTEXT) {
-                    Log.e("SDL", "Couldn't create context");
-                    return false;
-                }
-                SDLActivity.mEGLContext = ctx;*/
 				SDLActivity.mEGLDisplay = dpy;
 				SDLActivity.mEGLConfig = config;
 				SDLActivity.mGLMajor = majorVersion;
@@ -413,37 +412,74 @@
     Simple nativeInit() runnable
  */
 class SDLMain implements Runnable {
-
-	private int surfaceWidth, surfaceHeight;
-	private GameConfig config;
-
-	public SDLMain(int width, int height, GameConfig _config) {
-		config = _config;
+	public static final String TAG = "SDLMain";
+	
+    public static final int RQ_LOWRES            = 0x00000001; // use half land array
+    public static final int RQ_BLURRY_LAND       = 0x00000002; // downscaled terrain
+    public static final int RQ_NO_BACKGROUND     = 0x00000004; // don't draw background
+    public static final int RQ_SIMPLE_ROPE       = 0x00000008; // avoid drawing rope
+    public static final int RQ_2D_WATER          = 0x00000010; // disabe 3D water effect
+    public static final int RQ_SIMPLE_EXPLOSIONS = 0x00000020; // no fancy explosion effects
+    public static final int RQ_NO_FLAKES         = 0x00000040; // no flakes
+    public static final int RQ_NO_MENU_ANIM      = 0x00000080; // ammomenu appears with no animation
+    public static final int RQ_NO_DROPLETS       = 0x00000100; // no droplets
+    public static final int RQ_NO_CLAMPING       = 0x00000200; // don't clamp textures
+    public static final int RQ_NO_TOOLTIPS       = 0x00000400; // tooltips are not drawn
+    public static final int RQ_NO_VSYNC          = 0x00000800; // don't sync on vblank
+	
+	private final int surfaceWidth, surfaceHeight;
+	private final String playerName;
+	private final GameConfig config;
+	private final boolean netgame;
+	
+	public SDLMain(int width, int height, GameConfig config, boolean netgame) {
 		surfaceWidth = width;
 		surfaceHeight = height;
+		if(netgame) {
+			playerName = Netplay.getAppInstance(SDLActivity.getContext().getApplicationContext()).getPlayerName();
+		} else {
+			playerName = "Player";
+		}
+		this.config = config;
+		this.netgame = netgame;
 	}
 
 	public void run() {
 		//Set up the IPC socket server to communicate with the engine
-		EngineProtocolNetwork ipc = new EngineProtocolNetwork(config);
-
-		String path = Utils.getDataPath(SDLActivity.mSingleton);//This represents the data directory
-		path = path.substring(0, path.length()-1);//remove the trailing '/'
-
-
-		// Runs SDL_main() with added parameters
-		SDLActivity.nativeInit(new String[] { String.valueOf(ipc.port),
-				String.valueOf(surfaceWidth), String.valueOf(surfaceHeight),
-				"0", "en.txt", "xeli", "1", "1", "1", path, ""  });
-
+		GameConnection gameConn;
+		String path;
 		try {
-			ipc.quitIPC();
-			ipc.join();
-		} catch (InterruptedException e) {
-			e.printStackTrace();
+			if(netgame) {
+				Netplay netplay = Netplay.getAppInstance(SDLActivity.mSingleton.getApplicationContext());
+				gameConn = GameConnection.forNetgame(config, netplay);
+			} else {
+				gameConn = GameConnection.forLocalGame(config);
+			}
+			
+			path = FileUtils.getDataPathFile(SDLActivity.mSingleton).getAbsolutePath();
+			Log.d(TAG, "Starting engine");
+			// Runs SDL_main() with added parameters
+			try {
+				String pPort = String.valueOf(gameConn.port);
+				String pWidth = String.valueOf(surfaceWidth);
+				String pHeight = String.valueOf(surfaceHeight);
+				String pQuality = Integer.toString(RQ_NO_FLAKES|RQ_NO_DROPLETS|RQ_SIMPLE_EXPLOSIONS);
+				String pPlayerName = Base64.encodeToString(playerName.getBytes("UTF-8"), 0);
+				SDLActivity.synchronizedNativeInit(new String[] { pPort, pWidth, pHeight, pQuality, "en.txt", pPlayerName, "1", "1", "1", path, ""  });
+			} catch (UnsupportedEncodingException e) {
+				throw new AssertionError(e); // never happens
+			}
+			Log.d(TAG, "Engine stopped");
+		} catch(ConnectException e) {
+			Log.e(TAG, "Error starting IPC connection");
+		} catch (IOException e) {
+			Log.e(TAG, "Missing SDCard");
 		}
-		Log.v("SDL", "SDL thread terminated");
-		//Log.v("SDL", "SDL thread terminated");
+		SDLActivity.mSingleton.runOnUiThread(new Runnable() { public void run() {
+			if(SDLActivity.mSingleton != null) {
+				SDLActivity.mSingleton.finish();
+			}
+		}});
 	}
 }
 
@@ -458,12 +494,13 @@
 View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
 
 	private GameConfig config;
-
+	private boolean netgame;
+	
 	// Sensors
 	private static SensorManager mSensorManager;
 
 	// Startup    
-	public SDLSurface(Context context, GameConfig _config) {
+	public SDLSurface(Context context, GameConfig _config, boolean netgame) {
 		super(context);
 		getHolder().addCallback(this); 
 
@@ -475,6 +512,7 @@
 
 		mSensorManager = (SensorManager)context.getSystemService("sensor");
 		config = _config;
+		this.netgame = netgame;
 	}
 
 	// Called when we have a valid drawing surface
@@ -544,7 +582,7 @@
 		SDLActivity.onNativeResize(width, height, sdlFormat);
 		Log.v("SDL", "Window size:" + width + "x"+height);
 
-		SDLActivity.startApp(width, height, config);
+		SDLActivity.startApp(width, height, config, netgame);
 	}
 
 	// unused
@@ -557,8 +595,9 @@
 	public boolean onKey(View  v, int keyCode, KeyEvent event) {
 		switch(keyCode){
 		case KeyEvent.KEYCODE_BACK:
-		        PascalExports.HWterminate(true);
-                        return true;
+			Log.d("SDL", "KEYCODE_BACK");
+			SDLActivity.nativeQuit();
+            return true;
 		case KeyEvent.KEYCODE_VOLUME_DOWN:
 		case KeyEvent.KEYCODE_VOLUME_UP:
 		case KeyEvent.KEYCODE_VOLUME_MUTE:
@@ -580,34 +619,29 @@
 
 	// Touch events
 	public boolean onTouch(View v, MotionEvent event) {
-		{
-			final int touchDevId = event.getDeviceId();
-			final int pointerCount = event.getPointerCount();
-			// touchId, pointerId, action, x, y, pressure
-			int actionPointerIndex = event.getActionIndex();
-			int pointerFingerId = event.getPointerId(actionPointerIndex);
-			int action = event.getActionMasked();
-
-			float x = event.getX(actionPointerIndex);
-			float y = event.getY(actionPointerIndex);
-			float p = event.getPressure(actionPointerIndex);
+		final int action = event.getAction() & MotionEvent.ACTION_MASK;
+		final int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;		
 
-			if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) {
-				// TODO send motion to every pointer if its position has
-				// changed since prev event.
-				for (int i = 0; i < pointerCount; i++) {
-					pointerFingerId = event.getPointerId(i);
-					x = event.getX(i);
-					y = event.getY(i);
-					p = event.getPressure(i);
-					SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
-				}
-			} else {
-				SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+		if (action == MotionEvent.ACTION_MOVE) {
+			// TODO send motion to every pointer if its position has
+			// changed since prev event.
+			for (int i = 0; i < event.getPointerCount(); i++) {
+				sendNativeTouch(event, action, i);
 			}
+		} else {
+			sendNativeTouch(event, action, actionPointerIndex);
 		}
 		return true;
 	} 
+	
+	private static void sendNativeTouch(MotionEvent event, int action, int pointerIndex) {
+		int touchDevId = event.getDeviceId();
+		int pointerFingerId = event.getPointerId(pointerIndex);
+		float x = event.getX(pointerIndex);
+		float y = event.getY(pointerIndex);
+		float pressure = event.getPressure(pointerIndex);
+		SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, pressure);
+	}
 
 	// Sensor events
 	public void enableSensor(int sensortype, boolean enabled) {
@@ -633,6 +667,5 @@
 					event.values[2] / SensorManager.GRAVITY_EARTH);
 		}
 	}
-
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeCreatorActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+package org.hedgewars.hedgeroid;
+
+import android.app.Activity;
+
+// TODO stub for the actual scheme editor
+public class SchemeCreatorActivity extends Activity {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SchemeListActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+package org.hedgewars.hedgeroid;
+
+import android.app.Activity;
+
+// TODO
+public class SchemeListActivity extends Activity {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SettingsFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,227 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Schemes;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
+import org.hedgewars.hedgeroid.util.FileUtils;
+
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+public class SettingsFragment extends Fragment {
+	private Spinner styleSpinner, schemeSpinner, weaponsetSpinner, themeSpinner;
+	private ImageView themeIcon;
+	
+	private List<String> styles;
+	private List<Scheme> schemes;
+	private List<Weaponset> weaponsets;
+	private List<String> themes;
+	
+	private RoomStateManager stateManager;
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		View v = inflater.inflate(R.layout.fragment_settings, container, false);
+		themeIcon = (ImageView)v.findViewById(R.id.imgTheme);
+		
+		try {
+			styles = FrontendDataUtils.getGameStyles(getActivity());
+			schemes = Schemes.loadAllSchemes(getActivity());
+			weaponsets = Weaponsets.loadAllWeaponsets(getActivity());
+			themes = FrontendDataUtils.getThemes(getActivity());
+		} catch (IOException e) {
+			Toast.makeText(getActivity().getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
+			getActivity().finish();
+			return null;
+		}
+		
+		Collections.sort(styles, String.CASE_INSENSITIVE_ORDER);
+		Collections.sort(schemes, Scheme.NAME_ORDER);
+		Collections.sort(weaponsets, Weaponset.NAME_ORDER);
+		Collections.sort(themes, String.CASE_INSENSITIVE_ORDER);
+		
+		styleSpinner = prepareSpinner(v, R.id.spinGameplay, styles, styleSelectedListener);
+		schemeSpinner = prepareSpinner(v, R.id.spinGamescheme, Schemes.toNameList(schemes), schemeSelectedListener);
+		weaponsetSpinner = prepareSpinner(v, R.id.spinweapons, Weaponsets.toNameList(weaponsets), weaponsetSelectedListener);
+		themeSpinner = prepareSpinner(v, R.id.spinTheme, themes, themeSelectedListener);
+		
+		stateManager.addListener(roomStateChangeListener);
+
+		if(stateManager.getGameStyle() != null) {
+			styleSpinner.setSelection(styles.indexOf(stateManager.getGameStyle()), false);
+		}
+		if(stateManager.getScheme() != null) {
+			schemeSpinner.setSelection(getSchemePosition(schemes, stateManager.getScheme().name), false);
+		}
+		if(stateManager.getWeaponset() != null) {
+			weaponsetSpinner.setSelection(getWeaponsetPosition(weaponsets, stateManager.getWeaponset().name), false);
+		}
+		if(stateManager.getMapRecipe() != null) {
+			themeSpinner.setSelection(themes.indexOf(stateManager.getMapRecipe().theme), false);
+		}
+		
+		setChiefState(stateManager.getChiefStatus());
+		
+		return v;
+	}
+	
+	private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) {
+		Spinner spinner = (Spinner)v.findViewById(id);
+		ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items);
+		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+		spinner.setAdapter(adapter);
+		spinner.setOnItemSelectedListener(itemSelectedListener);
+		return spinner;
+	}
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		try {
+			stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager();
+		} catch(ClassCastException e) {
+			throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e);
+		}
+	}
+	
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		stateManager.removeListener(roomStateChangeListener);
+	}
+	
+	private static int getSchemePosition(List<Scheme> schemes, String scheme) {
+		for(int i=0; i<schemes.size(); i++) {
+			if(schemes.get(i).name.equals(scheme)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	private static int getWeaponsetPosition(List<Weaponset> weaponsets, String weaponset) {
+		for(int i=0; i<weaponsets.size(); i++) {
+			if(weaponsets.get(i).name.equals(weaponset)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	private void setChiefState(boolean chiefState) {
+		styleSpinner.setEnabled(chiefState);
+		schemeSpinner.setEnabled(chiefState);
+		weaponsetSpinner.setEnabled(chiefState);
+		themeSpinner.setEnabled(chiefState);
+		
+		if(chiefState) {
+			stateManager.changeGameStyle(styles.get(styleSpinner.getSelectedItemPosition()));
+			stateManager.changeScheme(schemes.get(schemeSpinner.getSelectedItemPosition()));
+			stateManager.changeWeaponset(weaponsets.get(weaponsetSpinner.getSelectedItemPosition()));
+			stateManager.changeMapTheme(themes.get(themeSpinner.getSelectedItemPosition()));
+		}
+	}
+	
+	private final OnItemSelectedListener styleSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeGameStyle(styles.get(position));
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener schemeSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeScheme(schemes.get(position));
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener weaponsetSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeWeaponset(weaponsets.get(position));
+		}
+		public void onNothingSelected(AdapterView<?> arg0) {}
+	};
+	
+	private final OnItemSelectedListener themeSelectedListener = new OnItemSelectedListener() {
+		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
+			stateManager.changeMapTheme(themes.get(position));
+			String theme = themes.get(position);
+			try {
+				File iconFile = FileUtils.getDataPathFile(getActivity(), "Themes", theme, "icon@2X.png");
+				Drawable themeIconDrawable = Drawable.createFromPath(iconFile.getAbsolutePath());
+				themeIcon.setImageDrawable(themeIconDrawable);
+			} catch (FileNotFoundException e) {
+				Log.e("SettingsFragment", "Unable to find preview for theme "+theme, e);
+			}
+		};
+		public void onNothingSelected(AdapterView<?> arg0) {};
+	};
+	
+	private final RoomStateManager.Listener roomStateChangeListener = new RoomStateManager.Listener() {
+		public void onWeaponsetChanged(Weaponset weaponset) {
+			weaponsetSpinner.setSelection(getWeaponsetPosition(weaponsets, weaponset.name));
+		}
+		
+		public void onTeamsChanged(Map<String, TeamInGame> teams) {}
+		
+		public void onSchemeChanged(Scheme scheme) {
+			schemeSpinner.setSelection(getSchemePosition(schemes, scheme.name));
+		}
+		
+		public void onMapChanged(MapRecipe recipe) {
+			themeSpinner.setSelection(themes.indexOf(recipe.theme));
+		}
+		
+		public void onGameStyleChanged(String gameStyle) {
+			styleSpinner.setSelection(styles.indexOf(gameStyle));
+		}
+		
+		public void onChiefStatusChanged(boolean isChief) {
+			setChiefState(isChief);
+		}
+	};
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,234 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid;
-
-import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
-import org.hedgewars.hedgeroid.Datastructures.Map;
-import org.hedgewars.hedgeroid.Datastructures.Map.MapType;
-import org.hedgewars.hedgeroid.Datastructures.Scheme;
-import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
-import org.hedgewars.hedgeroid.EngineProtocol.GameConfig;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.preference.PreferenceManager;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.Spinner;
-import android.widget.Toast;
-
-public class StartGameActivity extends Activity {
-
-	public static final int ACTIVITY_TEAM_SELECTOR = 0;
-
-	private GameConfig config = null;
-	private ImageButton start, back, team;
-	private Spinner maps, gameplay, gamescheme, weapons, themes;
-	private ImageView themeIcon, mapPreview, teamCount;
-
-	public void onCreate(Bundle savedInstanceState){
-		super.onCreate(savedInstanceState);
-
-		Scheme.parseBasicFlags(this);
-		config = new GameConfig();
-
-		setContentView(R.layout.starting_game);
-
-		back = (ImageButton) findViewById(R.id.btnBack);
-		team = (ImageButton) findViewById(R.id.btnTeams);
-		start = (ImageButton) findViewById(R.id.btnStart);
-
-		maps = (Spinner) findViewById(R.id.spinMaps);
-		gameplay = (Spinner) findViewById(R.id.spinGameplay);
-		gamescheme = (Spinner) findViewById(R.id.spinGamescheme);
-		weapons = (Spinner) findViewById(R.id.spinweapons);
-		themes = (Spinner) findViewById(R.id.spinTheme);
-
-		themeIcon = (ImageView) findViewById(R.id.imgTheme);
-		mapPreview = (ImageView) findViewById(R.id.mapPreview);
-		teamCount = (ImageView) findViewById(R.id.imgTeamsCount);
-
-		start.setOnClickListener(startClicker);
-		back.setOnClickListener(backClicker);
-		team.setOnClickListener(teamClicker);
-
-		ArrayAdapter<?> adapter = new ArrayAdapter<Map>(this, R.layout.listview_item, FrontendDataUtils.getMaps(this));
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		maps.setAdapter(adapter);
-		maps.setOnItemSelectedListener(mapsClicker);
-		//set to first nonmap
-		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Map)adapter.getItem(i)).getType() == MapType.TYPE_DEFAULT){
-				maps.setSelection(i, false);
-				break;
-			}
-		}
-
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, FrontendDataUtils.getGameplay(this));
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		gameplay.setAdapter(adapter);
-		gameplay.setOnItemSelectedListener(gameplayClicker);
-		//set to first nonmap
-		for(int i = 0; i < adapter.getCount(); i++){
-			if(((String)adapter.getItem(i)).equals("None")){
-				gameplay.setSelection(i, false);
-				break;
-			}
-		}
-
-		adapter = new ArrayAdapter<Scheme>(this, R.layout.listview_item, FrontendDataUtils.getSchemes(this));
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		gamescheme.setAdapter(adapter);
-		gamescheme.setOnItemSelectedListener(schemeClicker);
-		//set to first nonmap
-		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Scheme)adapter.getItem(i)).toString().equals("Default")){
-				gamescheme.setSelection(i, false);
-				break;
-			}
-		}
-		
-		
-		adapter = new ArrayAdapter<Weapon>(this, R.layout.listview_item, FrontendDataUtils.getWeapons(this));
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		weapons.setAdapter(adapter);
-		weapons.setOnItemSelectedListener(weaponClicker);
-		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Weapon)adapter.getItem(i)).toString().equals("Crazy")){
-				weapons.setSelection(i, false);
-				break;
-			}
-		}
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, FrontendDataUtils.getThemes(this));
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		themes.setAdapter(adapter);
-		themes.setOnItemSelectedListener(themesClicker);
-	}
-
-	private void startTeamsActivity(){
-		Intent i = new Intent(StartGameActivity.this, TeamSelectionActivity.class);
-		i.putParcelableArrayListExtra("teams", config.teams);
-		startActivityForResult(i, ACTIVITY_TEAM_SELECTOR);
-	}
-
-	public void onActivityResult(int requestCode, int resultCode, Intent data){
-		switch(requestCode){
-		case ACTIVITY_TEAM_SELECTOR:
-			if(resultCode == Activity.RESULT_OK){
-				Parcelable[] parcelables = (Parcelable[])data.getParcelableArrayExtra("teams");
-				config.teams.clear();
-				for(Parcelable t : parcelables){
-					config.teams.add((Team)t);
-				}
-				teamCount.getDrawable().setLevel(config.teams.size());
-			}
-			break;
-		}
-	}
-
-
-	private OnItemSelectedListener themesClicker = new OnItemSelectedListener(){
-
-		public void onItemSelected(AdapterView<?> arg0, View view, int position, long rowId) {
-			String themeName = (String) arg0.getAdapter().getItem(position);
-			Drawable themeIconDrawable = Drawable.createFromPath(Utils.getDataPath(StartGameActivity.this) + "Themes/" + themeName + "/icon@2X.png");
-			themeIcon.setImageDrawable(themeIconDrawable);
-			config.theme = themeName;
-		}
-
-		public void onNothingSelected(AdapterView<?> arg0) {
-		}
-
-	};
-
-	private OnItemSelectedListener mapsClicker = new OnItemSelectedListener(){
-
-		public void onItemSelected(AdapterView<?> arg0, View view, int position,long rowId) {
-			Map map = (Map)arg0.getAdapter().getItem(position);
-			mapPreview.setImageDrawable(map.getDrawable());
-			config.map = map;
-		}
-
-		public void onNothingSelected(AdapterView<?> arg0) {
-		}
-
-	};
-
-	private OnItemSelectedListener weaponClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			config.weapon = (Weapon)arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-	private OnItemSelectedListener schemeClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			config.scheme = (Scheme)arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-	private OnItemSelectedListener gameplayClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			//config = ()arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-
-	private OnClickListener startClicker = new OnClickListener(){
-		public void onClick(View v) {
-			if(config.teams.size() < 2){
-				Toast.makeText(StartGameActivity.this, R.string.not_enough_teams, Toast.LENGTH_LONG).show();
-				startTeamsActivity();
-			}
-			else{
-				Intent i = new Intent(StartGameActivity.this, SDLActivity.class);
-				i.putExtra("config", config);
-				startActivity(i);}
-		}
-	};
-
-	private OnClickListener backClicker = new OnClickListener(){
-		public void onClick(View v) {
-			finish();
-		}
-	};
-
-	private OnClickListener teamClicker = new OnClickListener(){
-		public void onClick(View v) {
-			startTeamsActivity();
-		}
-	};
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartNetgameDialog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,95 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import org.hedgewars.hedgeroid.netplay.Netplay;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+public class StartNetgameDialog extends DialogFragment {
+	private static final String PREF_PLAYERNAME = "playerName";
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		SharedPreferences prefs = getActivity().getSharedPreferences("settings", Context.MODE_PRIVATE);
+		final String playerName = prefs.getString(PREF_PLAYERNAME, "Player");
+		final EditText editText = new EditText(getActivity());
+		final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		
+		editText.setText(playerName);
+		editText.setHint(R.string.start_netgame_dialog_playername_hint);
+		editText.setId(android.R.id.text1);
+		editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
+		editText.setSingleLine();
+
+		builder.setTitle(R.string.start_netgame_dialog_title);
+		builder.setMessage(R.string.start_netgame_dialog_message);
+		builder.setView(editText);
+		builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				editText.setText(playerName);
+			}
+		});
+		
+		editText.setOnEditorActionListener(new OnEditorActionListener() {
+			public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+				boolean handled = false;
+				if(actionId == EditorInfo.IME_ACTION_DONE) {
+					startConnection(v.getText().toString());
+					handled = true;
+				}
+				return handled;
+			}
+		});
+		
+		builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				startConnection(editText.getText().toString());
+			}
+		});
+
+		return builder.create();
+	}
+	
+	private void startConnection(String username) {
+		if(username.length() > 0) {
+			SharedPreferences prefs = getActivity().getSharedPreferences("settings", Context.MODE_PRIVATE);
+			Editor edit = prefs.edit();
+			edit.putString(PREF_PLAYERNAME, username);
+			edit.commit();
+			
+			Netplay.getAppInstance(getActivity().getApplicationContext()).connectToDefaultServer(username);
+			getDialog().dismiss();
+			((MainActivity)getActivity()).onNetConnectingStarted();
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamAddDialog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,112 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class TeamAddDialog extends DialogFragment {
+	private static final String STATE_TEAMS_ALREADY_IN_GAME = "teamAlreadyInGame";
+	private ArrayList<String> teamsAlreadyInGame;
+	private List<Team> availableTeams;
+	private Listener listener;
+	
+	public static interface Listener {
+		void onTeamAddDialogSubmitted(Team newTeam);
+	}
+	
+	public TeamAddDialog() {
+		// Only for reflection-based instantiation by the framework
+	}
+	
+	TeamAddDialog(Collection<String> teamsAlreadyInGame) {
+		this.teamsAlreadyInGame = new ArrayList<String>(teamsAlreadyInGame);
+	}
+	
+	@Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
+		try {
+			listener = (Listener) activity;
+		} catch(ClassCastException e) {
+			throw new ClassCastException("Activity " + activity + " must implement TeamAddDialog.Listener to use TeamAddDialog.");
+		}
+	}
+	
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		listener = null;
+	}
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		if(savedInstanceState != null) {
+			teamsAlreadyInGame = savedInstanceState.getStringArrayList(STATE_TEAMS_ALREADY_IN_GAME);
+		}
+		availableTeams = new ArrayList<Team>();
+		List<Team> teams = FrontendDataUtils.getTeams(getActivity());
+		for(Team team : teams) {
+			if(!teamsAlreadyInGame.contains(team.name)) {
+				availableTeams.add(team);
+			}
+		}
+		Collections.sort(availableTeams, Team.NAME_ORDER);
+	}
+	
+	// TODO use icons for the teams (corresponding to botlevel) 
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		builder.setTitle(R.string.dialog_addteam_title);
+		builder.setIcon(R.drawable.human);
+		String[] teamNames = new String[availableTeams.size()];
+		for(int i=0; i<availableTeams.size(); i++) {
+			teamNames[i] = availableTeams.get(i).name;
+		}
+		builder.setItems(teamNames, new OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				listener.onTeamAddDialogSubmitted(availableTeams.get(which));
+			}
+		});
+		return builder.create();
+	}
+	
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		outState.putStringArrayList(STATE_TEAMS_ALREADY_IN_GAME, teamsAlreadyInGame);
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -1,10 +1,12 @@
 /*
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,31 +15,32 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-
 package org.hedgewars.hedgeroid;
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
 
 import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.Hog;
 import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.util.FileUtils;
 
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.media.MediaPlayer;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.ArrayAdapter;
@@ -49,33 +52,50 @@
 import android.widget.ScrollView;
 import android.widget.SimpleAdapter;
 import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import android.widget.Toast;
 
-public class TeamCreatorActivity extends Activity implements Runnable{
-
+/**
+ * Edit or create a team. If a team should be edited, it is supplied in the extras
+ * as parameter oldTeamName.
+ */
+public class TeamCreatorActivity extends Activity implements Runnable {
+	public static final String PARAMETER_EXISTING_TEAMNAME = "existingTeamName";
+	private static final String TAG = TeamCreatorActivity.class.getSimpleName();
+	
 	private TextView name;
 	private Spinner difficulty, grave, flag, voice, fort;
 	private ImageView imgFort;
 	private ArrayList<ImageButton> hogDice = new ArrayList<ImageButton>();
 	private ArrayList<Spinner> hogHat = new ArrayList<Spinner>();
 	private ArrayList<EditText> hogName = new ArrayList<EditText>();
-	private ImageButton back, save, voiceButton;
+	private ImageButton voiceButton;
 	private ScrollView scroller;
 	private MediaPlayer mp = null;
-	private boolean settingsChanged = false;
-	private boolean saved = false;
-	private String fileName = null;
+	private boolean initComplete = false;
+	
+	private String existingTeamName = null;
 
-	private final List<HashMap<String, ?>> flagsData = new ArrayList<HashMap<String, ?>>();
-	private final List<HashMap<String, ?>> typesData = new ArrayList<HashMap<String, ?>>();
-	private final List<HashMap<String, ?>> gravesData = new ArrayList<HashMap<String, ?>>();
-	private final List<HashMap<String, ?>> hatsData = new ArrayList<HashMap<String, ?>>();
+	private final List<Map<String, ?>> flagsData = new ArrayList<Map<String, ?>>();
+	private final List<Map<String, ?>> typesData = new ArrayList<Map<String, ?>>();
+	private final List<Map<String, ?>> gravesData = new ArrayList<Map<String, ?>>();
+	private final List<Map<String, ?>> hatsData = new ArrayList<Map<String, ?>>();
 	private final List<String> voicesData = new ArrayList<String>();
 	private final List<String> fortsData = new ArrayList<String>();
 
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
+		initComplete = false;
+		
+		// Restore state and read parameters 
+		if(savedInstanceState != null) {
+			existingTeamName = savedInstanceState.getString(PARAMETER_EXISTING_TEAMNAME);
+		} else {
+			existingTeamName = getIntent().getStringExtra(PARAMETER_EXISTING_TEAMNAME);
+		}
+		
+		// Set up view
 		setContentView(R.layout.team_creation);
 
 		name = (TextView) findViewById(R.id.txtName);
@@ -87,15 +107,11 @@
 
 		imgFort = (ImageView) findViewById(R.id.imgFort);
 
-		back = (ImageButton) findViewById(R.id.btnBack);
-		save = (ImageButton) findViewById(R.id.btnSave);
 		voiceButton = (ImageButton) findViewById(R.id.btnPlay);
 
 		scroller = (ScrollView) findViewById(R.id.scroller);
 
-		save.setOnClickListener(saveClicker);
-		back.setOnClickListener(backClicker);
-
+		// Wire view elements
 		LinearLayout ll = (LinearLayout) findViewById(R.id.HogsContainer);
 		for (int i = 0; i < ll.getChildCount(); i++) {
 			RelativeLayout team_creation_entry = (RelativeLayout) ll.getChildAt(i);
@@ -108,107 +124,116 @@
 					.findViewById(R.id.txtTeam1));
 		}
 
-		SimpleAdapter sa = new SimpleAdapter(this, gravesData,
-				R.layout.spinner_textimg_entry, new String[] { "txt", "img" },
-				new int[] { R.id.spinner_txt, R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
-		grave.setAdapter(sa);
-		grave.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, flagsData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
-		flag.setAdapter(sa);
-		flag.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, typesData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		difficulty.setAdapter(sa);
-		difficulty.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, hatsData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
+		grave.setAdapter(createMapSpinnerAdapter(gravesData));
+		flag.setAdapter(createMapSpinnerAdapter(flagsData));
+		difficulty.setAdapter(createMapSpinnerAdapter(typesData));
+		SpinnerAdapter hatAdapter = createMapSpinnerAdapter(hatsData);
 		for (Spinner spin : hogHat) {
-			spin.setAdapter(sa);
+			spin.setAdapter(hatAdapter);
 		}
 
-		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.listview_item, voicesData);
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		voice.setAdapter(adapter);
-		voice.setOnFocusChangeListener(focusser);
+
+		voice.setAdapter(createListSpinnerAdapter(voicesData));
 		voiceButton.setOnClickListener(voiceClicker);
 
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, fortsData);
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		fort.setAdapter(adapter);
+		fort.setAdapter(createListSpinnerAdapter(fortsData));
 		fort.setOnItemSelectedListener(fortSelector);
-		fort.setOnFocusChangeListener(focusser);
 
 		new Thread(this).start();
 	}
 
-	public void run(){
-		final ArrayList<HashMap<String, ?>> gravesDataNew = FrontendDataUtils.getGraves(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(gravesData, gravesDataNew);
-				((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> flagsDataNew = FrontendDataUtils.getFlags(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(flagsData, flagsDataNew);
-				((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> typesDataNew = FrontendDataUtils.getTypes(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(typesData, typesDataNew);
-				((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> hatsDataNew = FrontendDataUtils.getHats(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(hatsData, hatsDataNew);
-				((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<String> voicesDataNew = FrontendDataUtils.getVoices(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(voicesData, voicesDataNew);
-				((ArrayAdapter<String>)voice.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<String> fortsDataNew = FrontendDataUtils.getForts(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(fortsData, fortsDataNew);
-				((ArrayAdapter<String>)fort.getAdapter()).notifyDataSetChanged();
-			}
-		});
+	private SpinnerAdapter createMapSpinnerAdapter(List<? extends Map<String, ?>> data) {
+		SimpleAdapter sa = new SimpleAdapter(this, data,
+				R.layout.spinner_textimg_entry, new String[] { "txt", "img" },
+				new int[] { R.id.spinner_txt, R.id.spinner_img });
+		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
+		sa.setViewBinder(viewBinder);
+		return sa;
+	}
+	
+	private SpinnerAdapter createListSpinnerAdapter(List<String> data) {
+		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.listview_item, data);
+		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+		return adapter;
 	}
 	
-	private static <T> void copy(List<T> dest, List<T> src){
-		for(T t: src) dest.add(t);
+	public void run(){
+		try {
+			final List<Map<String, ?>> gravesDataNew = FrontendDataUtils.getGraves(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					gravesData.addAll(gravesDataNew);
+					((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final List<Map<String, ?>> flagsDataNew = FrontendDataUtils.getFlags(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					flagsData.addAll(flagsDataNew);
+					((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final List<Map<String, ?>> typesDataNew = FrontendDataUtils.getTypes(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					typesData.addAll(typesDataNew);
+					((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final List<Map<String, ?>> hatsDataNew = FrontendDataUtils.getHats(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					hatsData.addAll(hatsDataNew);
+					((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final List<String> voicesDataNew = FrontendDataUtils.getVoices(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					voicesData.addAll(voicesDataNew);
+					((ArrayAdapter<?>)voice.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final List<String> fortsDataNew = FrontendDataUtils.getForts(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					fortsData.addAll(fortsDataNew);
+					((ArrayAdapter<?>)fort.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			if(existingTeamName!=null) {
+				final Team loadedTeam = Team.load(Team.getTeamfileByName(getApplicationContext(), existingTeamName));
+				if(loadedTeam==null) {
+					existingTeamName = null;
+				} else {
+					runOnUiThread(new Runnable(){
+						public void run() {
+							setTeamValues(loadedTeam);
+						}
+					});
+				}
+			}
+			runOnUiThread(new Runnable(){
+				public void run() {
+					initComplete = true;
+				}
+			});
+		} catch(FileNotFoundException e) {
+			this.runOnUiThread(new Runnable(){
+				public void run() {
+					Toast.makeText(getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
+					finish();
+				}
+			});
+		}
 	}
-
+	
 	public void onDestroy() {
 		super.onDestroy();
 		if (mp != null) {
@@ -217,101 +242,70 @@
 		}
 	}
 
-	private OnFocusChangeListener focusser = new OnFocusChangeListener() {
-		public void onFocusChange(View v, boolean hasFocus) {
-			settingsChanged = true;
-		}
-
-	};
-
-	public void onBackPressed() {
-		onFinishing();
-		super.onBackPressed();
-
+	@Override
+	protected void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		outState.putString(PARAMETER_EXISTING_TEAMNAME, existingTeamName);
 	}
 
-	private OnClickListener backClicker = new OnClickListener() {
-		public void onClick(View v) {
-			onFinishing();
-			finish();
+	public void onBackPressed() {
+		if(initComplete) {
+			saveTeam();
 		}
-	};
-
-	private void onFinishing() {
-		if (settingsChanged) {
-			setResult(RESULT_OK);
-		} else {
-			setResult(RESULT_CANCELED);
-		}
+		setResult(RESULT_OK);
+		super.onBackPressed();
 	}
 
-	private OnClickListener saveClicker = new OnClickListener() {
-		public void onClick(View v) {
-			Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show();
-			saved = true;
-			Team team = new Team();
-			team.name = name.getText().toString();
-			HashMap<String, Object> hashmap = (HashMap<String, Object>) flag.getSelectedItem();
-
-			team.flag = (String) hashmap.get("txt");
-			team.fort = fort.getSelectedItem().toString();
-			hashmap = (HashMap<String, Object>) grave.getSelectedItem();
-			team.grave = hashmap.get("txt").toString();
-			team.hash = "0";
-			team.voice = voice.getSelectedItem().toString();
-			team.file = fileName;
-
-			hashmap = ((HashMap<String, Object>) difficulty.getSelectedItem());
-			String levelString = hashmap.get("txt").toString();
-			int levelInt;
-			if (levelString.equals(getString(R.string.human))) {
-				levelInt = 0;
-			} else if (levelString.equals(getString(R.string.bot5))) {
-				levelInt = 1;
-			} else if (levelString.equals(getString(R.string.bot4))) {
-				levelInt = 2;
-			} else if (levelString.equals(getString(R.string.bot3))) {
-				levelInt = 3;
-			} else if (levelString.equals(getString(R.string.bot2))) {
-				levelInt = 4;
-			} else {
-				levelInt = 5;
+	private void saveTeam() {
+		String teamName = name.getText().toString();
+		String teamFlag = (String)((Map<String, Object>) flag.getSelectedItem()).get("txt");
+		String teamFort = fort.getSelectedItem().toString();
+		String teamGrave = (String)((Map<String, Object>) grave.getSelectedItem()).get("txt");
+		String teamVoice = voice.getSelectedItem().toString();
+		int levelInt = (Integer)((Map<String, Object>) difficulty.getSelectedItem()).get("level");
+		
+		List<Hog> hogs = new ArrayList<Hog>();
+		for (int i = 0; i < hogName.size(); i++) {
+			String name = hogName.get(i).getText().toString();
+			String hat = ((Map<String, Object>) hogHat.get(i).getSelectedItem()).get("txt").toString();
+			hogs.add(new Hog(name, hat, levelInt));
+		}
+		
+		Team team = new Team(teamName, teamGrave, teamFlag, teamVoice, teamFort, hogs);
+		File teamsDir = new File(getFilesDir(), Team.DIRECTORY_TEAMS);
+		if (!teamsDir.exists()) teamsDir.mkdir();
+		
+		File newFile = Team.getTeamfileByName(this, teamName);
+		File oldFile = null;
+		if(existingTeamName != null) {
+			oldFile = Team.getTeamfileByName(this, existingTeamName);
+		}
+		try {
+			team.save(newFile);
+			// If the team was renamed, delete the old file.
+			if(oldFile != null && oldFile.isFile() && !oldFile.equals(newFile)) {
+				oldFile.delete();
 			}
-
-			for (int i = 0; i < hogName.size(); i++) {
-				team.hogNames[i] = hogName.get(i).getText().toString();
-				hashmap = (HashMap<String, Object>) hogHat.get(i).getSelectedItem();
-				team.hats[i] = hashmap.get("txt").toString();
-				team.levels[i] = levelInt;
-			}
-			try {
-				File teamsDir = new File(getFilesDir().getAbsolutePath() + '/' + Team.DIRECTORY_TEAMS);
-				if (!teamsDir.exists()) teamsDir.mkdir();
-				if(team.file == null){
-					team.setFileName(TeamCreatorActivity.this);
-				}
-				FileOutputStream fos = new FileOutputStream(String.format("%s/%s", teamsDir.getAbsolutePath(), team.file));
-				team.writeToXml(fos);
-			} catch (FileNotFoundException e) {
-				e.printStackTrace();
-			}
+			existingTeamName = teamName;
+		} catch(IOException e) {
+			Toast.makeText(getApplicationContext(), R.string.error_save_failed, Toast.LENGTH_SHORT).show();
 		}
-
 	};
 
 	private OnItemSelectedListener fortSelector = new OnItemSelectedListener() {
 		public void onItemSelected(AdapterView<?> arg0, View arg1,
 				int position, long arg3) {
-			settingsChanged = true;
 			String fortName = (String) arg0.getAdapter().getItem(position);
-			Drawable fortIconDrawable = Drawable.createFromPath(Utils
-					.getDataPath(TeamCreatorActivity.this)
-					+ "Forts/"
-					+ fortName + "L.png");
-			imgFort.setImageDrawable(fortIconDrawable);
+			try {
+				File fortImage = FileUtils.getDataPathFile(TeamCreatorActivity.this, "Forts", fortName, "L.png");
+				Drawable fortIconDrawable = Drawable.createFromPath(fortImage.getAbsolutePath());
+				imgFort.setImageDrawable(fortIconDrawable);
+			} catch(IOException e) {
+				Log.e(TAG, "Unable to show fort image", e);
+			}
 			scroller.fullScroll(ScrollView.FOCUS_DOWN);// Scroll the scrollview
 			// to the bottom, work
-			// around for scollview
+			// around for scrollview
 			// invalidation (scrolls
 			// back to top)
 		}
@@ -324,9 +318,7 @@
 	private OnClickListener voiceClicker = new OnClickListener() {
 		public void onClick(View v) {
 			try {
-				File dir = new File(String.format("%sSounds/voices/%s",
-						Utils.getDataPath(TeamCreatorActivity.this),
-						voice.getSelectedItem()));
+				File dir = FileUtils.getDataPathFile(TeamCreatorActivity.this, "Sounds", "voices", (String)voice.getSelectedItem());
 				String file = "";
 				File[] dirs = dir.listFiles();
 				File f = dirs[(int) Math.round(Math.random() * dirs.length)];
@@ -341,63 +333,57 @@
 				mp.prepare();
 				mp.start();
 			} catch (IllegalArgumentException e) {
-				e.printStackTrace();
+				Log.e(TAG, "Unable to play voice sample", e);
 			} catch (IllegalStateException e) {
-				e.printStackTrace();
+				Log.e(TAG, "Unable to play voice sample", e);
 			} catch (IOException e) {
-				e.printStackTrace();
+				Log.e(TAG, "Unable to play voice sample", e);
 			}
 		}
 	};
 
+	@SuppressWarnings("unchecked")
 	private void setTeamValues(Team t){
-
-		if (t != null) {
+		if (t == null) {
+			return;
+		}
+		
+		try {
 			name.setText(t.name);
-			int position = ((ArrayAdapter<String>) voice.getAdapter()).getPosition(t.voice);
-			voice.setSelection(position);
-
-			position = ((ArrayAdapter<String>) fort.getAdapter()).getPosition(t.fort);
-			fort.setSelection(position);
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : typesData) {
-				if (hashmap.get("txt").equals(t.levels[0])) {
-					difficulty.setSelection(position);
-					break;
-				}
+			voice.setSelection(findPosition((ArrayAdapter<String>) voice.getAdapter(), t.voice));
+			fort.setSelection(findPosition((ArrayAdapter<String>) fort.getAdapter(), t.fort));
+			difficulty.setSelection(findPosition(typesData, "level", Integer.valueOf(t.hogs.get(0).level)));
+			grave.setSelection(findPosition(gravesData, "txt", t.grave));
+			flag.setSelection(findPosition(flagsData, "txt", t.flag));
+	
+			for (int i = 0; i < Team.HEDGEHOGS_PER_TEAM; i++) {
+				hogHat.get(i).setSelection(findPosition(hatsData, "txt", t.hogs.get(i).hat));
+				hogName.get(i).setText(t.hogs.get(i).name);
 			}
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : gravesData) {
-				if (hashmap.get("txt").equals(t.grave)) {
-					grave.setSelection(position);
-					break;
-				}
-			}
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : typesData) {
-				if (hashmap.get("txt").equals(t.flag)) {
-					flag.setSelection(position);
-					break;
-				}
-			}
-
-			for (int i = 0; i < Team.maxNumberOfHogs; i++) {
-				position = 0;
-				for (HashMap<String, ?> hashmap : hatsData) {
-					if (hashmap.get("txt").equals(t.hats[i])) {
-						hogHat.get(i).setSelection(position);
-					}
-				}
-
-				hogName.get(i).setText(t.hogNames[i]);
-			}
-			this.fileName = t.file;
+		} catch(NoSuchElementException e) {
+			Toast.makeText(getApplicationContext(), R.string.error_team_attribute_not_found, Toast.LENGTH_LONG).show();
+			finish();
 		}
 	}
 
+	int findPosition(ArrayAdapter<String> adapter, String value) throws NoSuchElementException {
+		int position = adapter.getPosition(value);
+		if(position<0) {
+			throw new NoSuchElementException();
+		}
+		return position;
+	}
+	
+	int findPosition(List<? extends Map<String, ?>> data, String key, Object value) throws NoSuchElementException {
+		int position = 0;
+		for (Map<String, ?> map : data) {
+			if (map.get(key).equals(value)) {
+				return position;
+			}
+			position++;
+		}
+		throw new NoSuchElementException();
+	}
 
 	private SimpleAdapter.ViewBinder viewBinder = new SimpleAdapter.ViewBinder() {
 
@@ -412,5 +398,4 @@
 			}
 		}
 	};
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamListActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,129 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageButton;
+import android.widget.SimpleAdapter;
+
+public class TeamListActivity extends ListActivity implements OnItemClickListener {
+	private List<Team> teams;
+	private ImageButton addButton;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_teamlist);
+		addButton = (ImageButton)findViewById(R.id.btnAdd);
+		addButton.setOnClickListener(new OnClickListener() {
+			public void onClick(View v) {
+				editTeam(null);
+			}
+		});
+	}
+	
+	@Override
+	public void onResume() {
+		super.onResume();
+		updateList();
+		getListView().setOnItemClickListener(this);
+		registerForContextMenu(getListView());
+	}
+	
+	public void onItemClick(AdapterView<?> adapterView, View v, int position, long arg3) {
+		editTeam(teams.get(position).name);
+	}
+	
+	@Override
+	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuinfo){
+		menu.add(ContextMenu.NONE, 0, ContextMenu.NONE, R.string.edit);
+		menu.add(ContextMenu.NONE, 1, ContextMenu.NONE, R.string.delete);
+	}
+	
+	@Override
+	public boolean onContextItemSelected(MenuItem item){
+		AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
+		int position = menuInfo.position;
+		Team team = teams.get(position);
+		switch(item.getItemId()){
+		case 0:
+			editTeam(team.name);
+			return true;
+		case 1:
+			Team.getTeamfileByName(getApplicationContext(), team.name).delete();
+			updateList();
+			return true;
+		}
+		return false;
+	}
+	
+	private void updateList() {
+		teams = FrontendDataUtils.getTeams(getApplicationContext());
+		Collections.sort(teams, Team.NAME_ORDER);
+		SimpleAdapter adapter = new SimpleAdapter(this, teamsToMaps(teams), R.layout.team_selection_entry_simple, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty});
+		setListAdapter(adapter);
+	}
+	
+	private void editTeam(String teamName) {
+		Intent i = new Intent(this, TeamCreatorActivity.class);
+		i.putExtra(TeamCreatorActivity.PARAMETER_EXISTING_TEAMNAME, teamName);
+		startActivity(i);
+	}
+
+	private static final int[] botlevelDrawables = new int[] {
+		R.drawable.human, R.drawable.bot5, R.drawable.bot4, R.drawable.bot3, R.drawable.bot2, R.drawable.bot1
+	};
+	
+	private List<Map<String, ?>> teamsToMaps(List<Team> teams) {
+		List<Map<String, ?>> result = new ArrayList<Map<String,?>>();
+		for(Team t : teams) {
+			HashMap<String, Object> map = new HashMap<String, Object>();
+			map.put("team", t);
+			map.put("txt", t.name);
+			int botlevel = t.hogs.get(0).level;
+			if(botlevel<0 || botlevel>=botlevelDrawables.length) {
+				map.put("img", R.drawable.bot1);
+			} else {
+				map.put("img", botlevelDrawables[botlevel]);
+			}
+			result.add(map);
+		}
+		return result;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
-import org.hedgewars.hedgeroid.Datastructures.Team;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RelativeLayout;
-import android.widget.SimpleAdapter;
-import android.widget.SimpleAdapter.ViewBinder;
-import android.widget.TextView;
-
-public class TeamSelectionActivity extends Activity implements Runnable{
-
-	private static final int ACTIVITY_TEAMCREATION = 0;
-
-	private ImageButton addTeam, back;
-	private ListView availableTeams, selectedTeams;
-	private List<HashMap<String, Object>> availableTeamsList, selectedTeamsList;
-	private TextView txtInfo;
-
-	public void onCreate(Bundle savedInstanceState){
-		super.onCreate(savedInstanceState);
-
-		setContentView(R.layout.team_selector);
-
-		addTeam = (ImageButton) findViewById(R.id.btnAdd);
-		back = (ImageButton) findViewById(R.id.btnBack);
-		txtInfo = (TextView) findViewById(R.id.txtInfo);
-		selectedTeams = (ListView) findViewById(R.id.selectedTeams);
-		availableTeams = (ListView) findViewById(R.id.availableTeams);
-		addTeam.setOnClickListener(addTeamClicker);
-		back.setOnClickListener(backClicker);
-
-		availableTeamsList = new ArrayList<HashMap<String, Object>>();
-		SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry_simple, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty});
-		availableTeams.setAdapter(adapter);
-		availableTeams.setOnItemClickListener(availableClicker);
-		registerForContextMenu(availableTeams);
-
-		selectedTeamsList = new ArrayList<HashMap<String, Object>>();
-		adapter = new SimpleAdapter(this, selectedTeamsList, R.layout.team_selection_entry, new String[]{"txt", "img", "color", "count"}, new int[]{R.id.txtName, R.id.imgDifficulty, R.id.teamColor, R.id.teamCount});
-		adapter.setViewBinder(viewBinder);
-		selectedTeams.setAdapter(adapter);
-		selectedTeams.setOnItemClickListener(selectedClicker);
-
-		txtInfo.setText(String.format(getResources().getString(R.string.teams_info_template), selectedTeams.getChildCount()));
-
-		new Thread(this).start();//load the teams from xml async
-	}
-
-	public void run(){
-		List<HashMap<String, Object>> teamsList = FrontendDataUtils.getTeams(this);//teams from xml
-		ArrayList<Team> teamsStartGame = getIntent().getParcelableArrayListExtra("teams");//possible selected teams
-
-		for(HashMap<String, Object> hashmap : teamsList){
-			boolean added = false;
-			for(Team t : teamsStartGame){
-				if(((Team)hashmap.get("team")).equals(t)){//add to available or add to selected
-					selectedTeamsList.add(FrontendDataUtils.teamToMap(t));//create a new hashmap to ensure all variables are entered into the map
-					added = true;
-					break;
-				}
-			}
-			if(!added) availableTeamsList.add(hashmap);
-		}
-
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged();
-				((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();		
-			}
-		});
-	}
-
-	private ViewBinder viewBinder = new ViewBinder(){
-		public boolean setViewValue(View view, Object data,	String textRepresentation) {
-			switch(view.getId()){
-			case R.id.teamColor:
-				setTeamColor(view, (Integer)data);
-				return true;
-			case R.id.teamCount:
-				setTeamHogCount((ImageView)view, (Integer)data);
-				return true;
-			default:
-				return false;
-			}
-		}
-	};
-
-	public void onActivityResult(int requestCode, int resultCode, Intent data){
-		if(requestCode == ACTIVITY_TEAMCREATION){
-			if(resultCode == Activity.RESULT_OK){
-				updateListViews();
-			}
-		}else{
-			super.onActivityResult(requestCode, resultCode, data);
-		}
-	}
-
-	/*
-	 * Updates the list view when TeamCreationActivity is shutdown and the user returns to this point
-	 */
-	private void updateListViews(){
-		unregisterForContextMenu(availableTeams);
-		availableTeamsList = FrontendDataUtils.getTeams(this);
-		ArrayList<HashMap<String, Object>> toBeRemoved = new ArrayList<HashMap<String, Object>>();
-		for(HashMap<String, Object> hashmap : selectedTeamsList){
-			String name = (String)hashmap.get("txt");
-
-			for(HashMap<String, Object> hash : availableTeamsList){
-				if(name.equals((String)hash.get("txt"))){
-					toBeRemoved.add(hash);
-				}
-			}
-		}
-		for(HashMap<String, Object> hash: toBeRemoved) availableTeamsList.remove(hash);
-
-		SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty});
-		availableTeams.setAdapter(adapter);
-		registerForContextMenu(availableTeams);
-		availableTeams.setOnItemClickListener(availableClicker);
-
-
-	}
-
-	private void setTeamColor(int position, int color){
-		View iv = ((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount);
-		setTeamColor(iv, color);
-	}
-	private void setTeamColor(View iv, int color){
-		iv.setBackgroundColor(0xFF000000 + color);
-	}
-
-	private void setTeamHogCount(int position, int count){
-		ImageView iv = (ImageView)((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount);
-		setTeamHogCount(iv, count);
-	}
-
-	private void setTeamHogCount(ImageView iv, int count){
-
-		switch(count){
-		case 0:
-			iv.setImageResource(R.drawable.teamcount0);
-			break;
-		case 1:
-			iv.setImageResource(R.drawable.teamcount1);
-			break;
-		case 2:
-			iv.setImageResource(R.drawable.teamcount2);
-			break;
-		case 3:
-			iv.setImageResource(R.drawable.teamcount3);
-			break;
-		case 4:
-			iv.setImageResource(R.drawable.teamcount4);
-			break;
-		case 5:
-			iv.setImageResource(R.drawable.teamcount5);
-			break;
-		case 6:
-			iv.setImageResource(R.drawable.teamcount6);
-			break;
-		case 7:
-			iv.setImageResource(R.drawable.teamcount7);
-			break;
-		case 8:
-			iv.setImageResource(R.drawable.teamcount8);
-			break;
-		case 9:
-			iv.setImageResource(R.drawable.teamcount9);
-			break;
-		}
-	}
-
-	public void onBackPressed(){
-		returnTeams();
-		super.onBackPressed();
-	}
-
-	private OnClickListener addTeamClicker = new OnClickListener(){
-		public void onClick(View v) {
-			startActivityForResult(new Intent(TeamSelectionActivity.this, TeamCreatorActivity.class), ACTIVITY_TEAMCREATION);
-		}
-	};
-
-	private OnClickListener backClicker = new OnClickListener(){
-		public void onClick(View v){
-			returnTeams();
-			finish();
-		}
-	};
-
-	private OnItemClickListener availableClicker = new OnItemClickListener(){
-		public void onItemClick(AdapterView<?> arg0, View arg1, int position,long arg3) {
-			selectAvailableTeamsItem(position);
-		}
-	};
-	private OnItemClickListener selectedClicker = new OnItemClickListener(){
-		public void onItemClick(AdapterView<?> arg0, View arg1, int position,long arg3) {
-			availableTeamsList.add((HashMap<String, Object>) selectedTeamsList.get(position));
-			selectedTeamsList.remove(position);
-			((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();
-			((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged();
-
-			txtInfo.setText(String.format(getResources().getString(R.string.teams_info_template), selectedTeamsList.size()));
-		}
-
-	};
-
-	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuinfo){
-		menu.add(ContextMenu.NONE, 0, ContextMenu.NONE, R.string.select);
-		menu.add(ContextMenu.NONE, 2, ContextMenu.NONE, R.string.edit);
-		menu.add(ContextMenu.NONE, 1, ContextMenu.NONE, R.string.delete);
-
-	}
-	public boolean onContextItemSelected(MenuItem item){
-		AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
-		int position = menuInfo.position;
-		switch(item.getItemId()){
-		case 0://select
-			selectAvailableTeamsItem(position);
-			return true;
-		case 1://delete
-			Team team = (Team)availableTeamsList.get(position).get("team");
-			File f = new File(String.format("%s/%s/%s", TeamSelectionActivity.this.getFilesDir(), Team.DIRECTORY_TEAMS, team.file));
-			f.delete();
-			availableTeamsList.remove(position);
-			((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();
-			return true;
-		case 2://edit
-			Intent i = new Intent(TeamSelectionActivity.this, TeamCreatorActivity.class);
-			Team t = (Team)availableTeamsList.get(position).get("team");
-			i.putExtra("team", t);
-			startActivityForResult(i, ACTIVITY_TEAMCREATION);
-			return true;
-		}
-		return false;
-	}
-
-	private void selectAvailableTeamsItem(int position){
-		HashMap<String, Object> hash = (HashMap<String, Object>) availableTeamsList.get(position);
-		Team t = (Team)hash.get("team");
-		int[] illegalcolors = new int[selectedTeamsList.size()];
-		for(int i = 0; i < selectedTeamsList.size(); i++){
-			illegalcolors[i] = ((Team)selectedTeamsList.get(i).get("team")).color;
-		}
-		t.setRandomColor(illegalcolors);
-		hash.put("color", t.color);
-		hash.put("count", t.hogCount);
-
-		selectedTeamsList.add(hash);
-		availableTeamsList.remove(position);
-		((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();
-		((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged();
-
-		txtInfo.setText(String.format(getResources().getString(R.string.teams_info_template), selectedTeamsList.size()));
-	}
-
-	private void returnTeams(){
-		int teamsCount = selectedTeamsList.size();
-		Intent i = new Intent();
-		Parcelable[] teams = new Parcelable[teamsCount];
-		for(int x = 0 ; x < teamsCount; x++){
-			teams[x] = (Team)selectedTeamsList.get(x).get("team");
-		}
-		i.putExtra("teams", teams);
-		setResult(Activity.RESULT_OK, i);
-
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,154 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+public class TeamlistAdapter extends BaseAdapter {
+	private boolean colorHogcountEnabled = false;
+	private Listener listener;
+	private List<TeamInGame> teams = new ArrayList<TeamInGame>();
+	
+	public void setColorHogcountEnabled(boolean colorHogcountEnabled) {
+		this.colorHogcountEnabled = colorHogcountEnabled;
+		notifyDataSetChanged();
+	}
+	
+	public void setListener(Listener listener) {
+		this.listener = listener;
+	}
+	
+	public int getCount() {
+		return teams.size();
+	}
+	
+	public TeamInGame getItem(int position) {
+		return teams.get(position);
+	}
+	
+	public long getItemId(int position) {
+		return position;
+	}
+	
+	@Override
+	public boolean hasStableIds() {
+		return false;
+	}
+	
+	public void updateTeamlist(Collection<TeamInGame> newTeams) {
+		teams.clear();
+		teams.addAll(newTeams);
+		Collections.sort(teams, TeamInGame.NAME_ORDER);
+		notifyDataSetChanged();
+	}
+	
+	public View getView(int position, View convertView, ViewGroup parent) {
+		View v = convertView;
+		if (v == null) {
+			LayoutInflater vi = LayoutInflater.from(parent.getContext());
+			v = vi.inflate(R.layout.listview_team, null);
+		}
+
+		TeamInGame team = getItem(position);
+		TextView teamNameView = (TextView) v.findViewById(android.R.id.text1);
+		ImageButton colorButton = (ImageButton) v.findViewById(R.id.colorButton);
+		ImageButton hogCountButton = (ImageButton) v.findViewById(R.id.hogCountButton);
+		
+		teamNameView.setText(team.team.name);
+		int teamImage;
+		if(team.ingameAttribs.remoteDriven) {
+			teamImage = R.drawable.team_net_by_level;
+		} else {
+			teamImage = R.drawable.team_local_by_level;
+		}
+		
+		Drawable d = parent.getContext().getResources().getDrawable(teamImage).mutate();
+		d.setLevel(team.team.hogs.get(0).level);
+		teamNameView.setCompoundDrawablesWithIntrinsicBounds(d, null, null, null);
+		hogCountButton.getDrawable().setLevel(team.ingameAttribs.hogCount);
+		colorButton.setImageDrawable(new ColorDrawable(TeamIngameAttributes.TEAM_COLORS[team.ingameAttribs.colorIndex]));
+		
+		colorButton.setEnabled(colorHogcountEnabled);
+		hogCountButton.setEnabled(colorHogcountEnabled);
+		
+		colorButton.setOnClickListener(new ButtonClickListener(team, Type.COLOR_BUTTON));
+		hogCountButton.setOnClickListener(new ButtonClickListener(team, Type.HOGCOUNT_BUTTON));
+		
+		if(team.ingameAttribs.remoteDriven) {
+			teamNameView.setClickable(false);
+		} else {
+			teamNameView.setOnClickListener(new ButtonClickListener(team, Type.TEAM_VIEW));
+		}
+		
+		return v;
+	}
+	
+	private static enum Type {COLOR_BUTTON, HOGCOUNT_BUTTON, TEAM_VIEW}
+	private final class ButtonClickListener implements OnClickListener {
+		private final TeamInGame team;
+		private final Type type;
+		
+		public ButtonClickListener(TeamInGame team, Type type) {
+			this.team = team;
+			this.type = type;
+		}
+		
+		public void onClick(View v) {
+			if(listener != null) {
+				switch(type) {
+				case COLOR_BUTTON:
+					listener.onColorClicked(team);
+					break;
+				case HOGCOUNT_BUTTON:
+					listener.onHogcountClicked(team);
+					break;
+				case TEAM_VIEW:
+					listener.onTeamClicked(team);
+					break;
+				default:
+					throw new IllegalStateException();	
+				}
+			}
+		}
+	}
+	
+	public interface Listener {
+		void onTeamClicked(TeamInGame team);
+		void onColorClicked(TeamInGame team);
+		void onHogcountClicked(TeamInGame team);
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamlistFragment.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,119 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+public class TeamlistFragment extends ListFragment implements TeamlistAdapter.Listener {
+	private TeamlistAdapter adapter;
+	private Button addTeamButton;
+	private RoomStateManager stateManager;
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		try {
+			stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager();
+		} catch(ClassCastException e) {
+			throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e);
+		}
+		adapter = new TeamlistAdapter();
+		adapter.updateTeamlist(stateManager.getTeams().values());
+		adapter.setColorHogcountEnabled(stateManager.getChiefStatus());
+		adapter.setListener(this);
+		setListAdapter(adapter);
+		stateManager.addListener(roomStateChangeListener);
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+		View v = inflater.inflate(R.layout.fragment_teamlist, container, false);
+		addTeamButton = (Button)v.findViewById(R.id.addTeamButton);
+		addTeamButton.setOnClickListener(new OnClickListener() {
+			public void onClick(View v) {
+				new TeamAddDialog(getCurrentTeamNames()).show(getFragmentManager(), "team_add_dialog");
+			}
+		});
+		
+		addTeamButton.setEnabled(stateManager.getTeams().size() < Team.maxNumberOfTeams);
+		
+		return v;
+	}
+	
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		adapter.setListener(null);
+		stateManager.removeListener(roomStateChangeListener);
+	}
+
+	private Collection<String> getCurrentTeamNames() {
+		List<String> names = new ArrayList<String>();
+		for(TeamInGame team : stateManager.getTeams().values()) {
+			names.add(team.team.name);
+		}
+		return names;
+	}
+	
+	public void onColorClicked(TeamInGame team) {
+		stateManager.changeTeamColorIndex(team.team.name, (team.ingameAttribs.colorIndex+1)%TeamIngameAttributes.TEAM_COLORS.length);
+	}
+	
+	public void onHogcountClicked(TeamInGame team) {
+		int newHogCount = team.ingameAttribs.hogCount+1;
+		if(newHogCount>Team.HEDGEHOGS_PER_TEAM) {
+			newHogCount = 1;
+		}
+		stateManager.changeTeamHogCount(team.team.name, newHogCount);
+	}
+	
+	public void onTeamClicked(TeamInGame team) {
+		stateManager.requestRemoveTeam(team.team.name);
+	}
+	
+	private final RoomStateManager.Listener roomStateChangeListener = new RoomStateManager.ListenerAdapter() {
+		@Override
+		public void onChiefStatusChanged(boolean isChief) {
+			adapter.setColorHogcountEnabled(isChief);
+		};
+		
+		@Override
+		public void onTeamsChanged(Map<String, TeamInGame> teams) {
+			adapter.updateTeamlist(teams.values());
+			addTeamButton.setEnabled(teams.size() < Team.maxNumberOfTeams);
+		};
+	};
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TextImageAdapter.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TextImageAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,10 +14,9 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-
 package org.hedgewars.hedgeroid;
 import java.util.ArrayList;
 import java.util.HashMap;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/UserInput/TouchInterface.java	Mon Aug 27 11:05:09 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/UserInput/TouchInterface.java	Mon Aug 27 17:40:16 2012 +0200
@@ -2,9 +2,10 @@
  * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
  * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,9 +14,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-
 package org.hedgewars.hedgeroid.UserInput;
 
 import org.hedgewars.hedgeroid.SDLActivity;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java	Mon Aug 27 11:05:09 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-package org.hedgewars.hedgeroid;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Environment;
-import android.util.Log;
-
-public class Utils {
-
-	private static final String ROOT_DIR = "Data/";
-
-	/**
-	 * get the path to which we should download all the data files
-	 * @param c context 
-	 * @return absolute path
-	 */
-	public static String getCachePath(Context c){
-		if(Build.VERSION.SDK_INT < 8){//8 == Build.VERSION_CODES.FROYO
-			return PreFroyoSDCardDir.getDownloadPath(c) + '/';
-		}else{
-			return FroyoSDCardDir.getDownloadPath(c) + '/';
-		}
-	}
-
-	public static String getDataPath(Context c){
-		return getCachePath(c) + ROOT_DIR;
-	}
-
-	static class FroyoSDCardDir{
-		public static String getDownloadPath(Context c){
-			File f =  c.getExternalCacheDir();
-			if(f != null){
-				return f.getAbsolutePath();
-			}else{
-				return null;
-			}	
-		}
-	}
-
-	static class PreFroyoSDCardDir{
-		public static String getDownloadPath(Context c){
-			if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
-				if(Environment.getExternalStorageDirectory() != null)
-					return Environment.getExternalStorageDirectory().getAbsolutePath() + "/Hedgewars/";				
-			}
-			return null;
-		}
-	}
-
-	/**
-	 * Get files from dirName, dir name is relative to {@link getDownloadPath}
-	 * @param dirName
-	 * @param c context
-	 * @return string of files
-	 */
-	public static String[] getFileNamesFromRelativeDir(Context c, String dirName){
-		String prefix = getDataPath(c);
-		File f = new File(prefix + dirName);
-
-		if(f.exists() && f.isDirectory()) return f.list();
-		else{
-
-			Log.e("Utils::", "Couldn't find dir: " + dirName);
-			return new String[0];
-		}
-	}
-
-	/**
-	 * Return a File array with all the files from dirName
-	 * @param c
-	 * @param dirName
-	 * @return
-	 */
-	public static File[] getFilesFromRelativeDir(Context c, String dirName){
-		String prefix = getDataPath(c);
-		File f = new File(prefix + dirName);
-
-		if(f.exists() && f.isDirectory()) return f.listFiles();
-		else {
-			Log.e("Utils::", "Dir not found: " + dirName);
-			return new File[0];
-		}
-	}
-
-	/**
-	 * Checks if this directory has a file with suffix suffix
-	 * @param f - directory
-	 * @return
-	 */
-	public static boolean hasFileWithSuffix(File f, String suffix){
-		if(f.isDirectory()){
-			for(String s : f.list()){
-				if(s.endsWith(suffix)) return true;
-			}
-			return false;
-		}else{
-			return false;
-		}
-	}
-
-	/**
-	 * Gives back all dirs which contain a file with suffix fileSuffix
-	 * @param c
-	 * @param path
-	 * @param fileSuffix
-	 * @return
-	 */
-	public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix){
-		File[] files = getFilesFromRelativeDir(c,path);
-		ArrayList<String> ret = new ArrayList<String>();
-
-		for(File f : files){
-			if(hasFileWithSuffix(f, fileSuffix)) ret.add(f.getName());
-		}
-		return ret;
-	}
-
-	/**
-	 * Get all files from directory dir which have the given suffix
-	 * @param c
-	 * @param dir
-	 * @param suffix
-	 * @param removeSuffix
-	 * @return
-	 */
-	public static ArrayList<String> getFilesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix){
-		String[] files = Utils.getFileNamesFromRelativeDir(c, dir);
-		ArrayList<String> ret = new ArrayList<String>();
-		for(String s : files){
-			if(s.endsWith(suffix)){
-				if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length()));
-				else ret.add(s);
-			}
-		}
-		return ret;
-	}
-
-	/**
-	 * Moves resources pointed to by sourceResId (from @res/raw/) to the app's private data directory
-	 * @param c
-	 * @param sourceResId
-	 * @param directory
-	 */
-	public static void resRawToFilesDir(Context c, int sourceResId, String directory){
-		byte[] buffer = new byte[1024];
-		InputStream bis = null;
-		BufferedOutputStream bos = null;
-		File schemesDirFile = new File(c.getFilesDir().getAbsolutePath() + '/' + directory);
-		schemesDirFile.mkdirs();
-		String schemesDirPath = schemesDirFile.getAbsolutePath() + '/';
-
-		//Get an array with the resource files ID
-		TypedArray ta = c.getResources().obtainTypedArray(sourceResId);
-		int[] resIds = new int[ta.length()];
-		for(int i = 0; i < ta.length(); i++){
-			resIds[i] = ta.getResourceId(i, 0);
-		}
-
-		for(int id : resIds){
-			String fileName = c.getResources().getResourceEntryName(id);
-			File f = new File(schemesDirPath + fileName);
-			try {
-				if(!f.createNewFile()){
-					f.delete();
-					f.createNewFile();
-				}
-
-				bis = c.getResources().openRawResource(id);
-				bos = new BufferedOutputStream(new FileOutputStream(f), 1024);
-				int read = 0;
-				while((read = bis.read(buffer)) != -1){
-					bos.write(buffer, 0, read);
-				}
-
-			} catch (IOException e) {
-				e.printStackTrace();
-			}finally{
-				if(bis != null)
-					try { 
-						bis.close();
-					} catch (IOException e) {
-						e.printStackTrace();
-					}
-					if(bos != null)
-						try {
-							bos.close();
-						} catch (IOException e) {
-							e.printStackTrace();
-						}
-			}
-		}
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetCreatorActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,8 @@
+package org.hedgewars.hedgeroid;
+
+import android.support.v4.app.FragmentActivity;
+
+// TODO
+public class WeaponsetCreatorActivity extends FragmentActivity {
+	public static final String PARAMETER_EXISTING_WEAPONSETNAME="existingWeaponsetName";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/WeaponsetListActivity.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,126 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+public class WeaponsetListActivity extends ListActivity implements OnItemClickListener {
+	private List<Weaponset> userWeaponsets;
+	private Button addButton;
+	
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_weaponsetlist);
+		addButton = (Button)findViewById(R.id.addButton);
+		addButton.setOnClickListener(new OnClickListener() {
+			public void onClick(View v) {
+				editWeaponset(null);
+			}
+		});
+	}
+	
+	@Override
+	public void onResume() {
+		super.onResume();
+		updateList();
+		getListView().setOnItemClickListener(this);
+		registerForContextMenu(getListView());
+	}
+	
+	private List<Map<String, ?>> weaponsetsToMap(List<Weaponset> weaponsets) {
+		List<Map<String, ?>> result = new ArrayList<Map<String, ?>>();
+		for(Weaponset weaponset : weaponsets) {
+			result.add(Collections.singletonMap("txt", weaponset.name));
+		}
+		return result;
+	}
+	
+	public void onItemClick(AdapterView<?> adapterView, View v, int position, long arg3) {
+		editWeaponset(userWeaponsets.get(position).name);
+	}
+	
+	@Override
+	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuinfo){
+		menu.add(ContextMenu.NONE, 0, ContextMenu.NONE, R.string.edit);
+		menu.add(ContextMenu.NONE, 1, ContextMenu.NONE, R.string.delete);
+	}
+	
+	@Override
+	public boolean onContextItemSelected(MenuItem item){
+		AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
+		int position = menuInfo.position;
+		Weaponset weaponset = userWeaponsets.get(position);
+		switch(item.getItemId()){
+		case 0:
+			editWeaponset(weaponset.name);
+			return true;
+		case 1:
+			try {
+				Weaponsets.deleteUserWeaponset(this, weaponset.name);
+			} catch (IOException e) {
+				Toast.makeText(this.getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_SHORT).show();
+			}
+			updateList();
+			return true;
+		}
+		return false;
+	}
+	
+	private void updateList() {
+		try {
+			userWeaponsets = Weaponsets.loadUserWeaponsets(this);
+		} catch (IOException e) {
+			Toast.makeText(this, R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
+			finish();
+		}
+		Collections.sort(userWeaponsets, Weaponset.NAME_ORDER);
+		ListAdapter adapter = new SimpleAdapter(this, weaponsetsToMap(userWeaponsets), android.R.layout.simple_list_item_1, new String[]{"txt"}, new int[]{android.R.id.text1});
+		setListAdapter(adapter);
+	}
+	
+	private void editWeaponset(String weaponsetName) {
+		Intent i = new Intent(this, WeaponsetCreatorActivity.class);
+		i.putExtra(WeaponsetCreatorActivity.PARAMETER_EXISTING_WEAPONSETNAME, weaponsetName);
+		startActivity(i);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/AndroidTypeMapper.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,63 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.frontlib;
+
+import com.sun.jna.DefaultTypeMapper;
+import com.sun.jna.FromNativeContext;
+import com.sun.jna.ToNativeContext;
+import com.sun.jna.TypeConverter;
+import com.sun.jna.TypeMapper;
+
+class AndroidTypeMapper extends DefaultTypeMapper {
+	static final int NATIVE_INT_SIZE = 4;
+	static final int NATIVE_SIZE_T_SIZE = 4;
+	static final int NATIVE_BOOL_SIZE = 1;
+    public static final TypeMapper INSTANCE = new AndroidTypeMapper();
+    
+    protected AndroidTypeMapper() {
+        addTypeConverter(Boolean.class, new BooleanConverter());
+        addTypeConverter(NativeSizeT.class, new SizeTConverter());
+    }
+
+    private static final class BooleanConverter implements TypeConverter {
+    	public Class<Byte> nativeType() {
+    		return Byte.class;
+    	}
+    	public Object fromNative(Object value, FromNativeContext context) {
+    		return ((Byte)value).intValue() != 0 ? Boolean.TRUE : Boolean.FALSE;
+    	}
+    	public Object toNative(Object value, ToNativeContext context) {
+    		return Byte.valueOf((byte)(Boolean.TRUE.equals(value) ? 1 : 0));
+    	}
+    }
+    
+    private static final class SizeTConverter implements TypeConverter {
+    	public Class<Integer> nativeType() {
+    		return Integer.class;
+    	}
+    	public Object fromNative(Object value, FromNativeContext context) {
+    		return NativeSizeT.valueOf((Integer)value);
+    	}
+    	public Object toNative(Object value, ToNativeContext context) {
+    		return Integer.valueOf(value==null ? 0 : ((NativeSizeT)value).intValue());
+    	}
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Flib.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,61 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.frontlib;
+
+import java.util.Collections;
+
+import android.util.Log;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+public class Flib {
+	static {
+		System.loadLibrary("SDL_net");
+		System.setProperty("jna.encoding", "UTF8"); // Ugly global setting, but it seems JNA doesn't allow setting this per-library... 
+	}
+	public static final Frontlib INSTANCE = (Frontlib)Native.loadLibrary("frontlib", Frontlib.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, AndroidTypeMapper.INSTANCE));
+	
+	static {
+		// We'll just do it here and never quit it again...
+		if(Flib.INSTANCE.flib_init() != 0) {
+			throw new RuntimeException("Unable to initialize frontlib");
+		}
+	}
+	
+	// Hook frontlib logging into Android logging
+	private static final Frontlib.LogCallback logCb = new Frontlib.LogCallback() {
+		public void callback(int level, String message) {
+			if(level >= Frontlib.FLIB_LOGLEVEL_ERROR) {
+				Log.e("Frontlib", message);
+			} else if(level == Frontlib.FLIB_LOGLEVEL_WARNING){
+				Log.w("Frontlib", message);
+			} else if(level == Frontlib.FLIB_LOGLEVEL_INFO){
+				Log.i("Frontlib", message);
+			} else if(level <= Frontlib.FLIB_LOGLEVEL_DEBUG){
+				Log.d("Frontlib", message);
+			}
+		}
+	};
+	static {
+		INSTANCE.flib_log_setLevel(Frontlib.FLIB_LOGLEVEL_INFO);
+		INSTANCE.flib_log_setCallback(logCb);
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,1211 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+
+package org.hedgewars.hedgeroid.frontlib;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.hedgewars.hedgeroid.Datastructures.Hog;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.MetaScheme;
+import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Mod;
+import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Setting;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.Room;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Library;
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+import com.sun.jna.PointerType;
+import com.sun.jna.Structure;
+
+/**
+ * Here is an introduction to the most important aspects of the JNA code.
+ * 
+ * This interface permits access to the Hedgewars frontend library (frontlib)
+ * from Java. Each function directly contained in the Frontlib interface
+ * represents a mapped C function. The Structure classes (ending in -Struct) are
+ * mappings of C structs, and the PointerType classes (ending in -Ptr) represent
+ * pointers to structs.
+ * 
+ * Quick notes for USING these classes from outside this package:
+ * 
+ * Usage should be fairly straightforward, but there are a few surprising
+ * gotchas. First, when you implement callbacks, YOU are responsible for
+ * ensuring that the callback objects are not garbage-collected while they might
+ * still be called! So make sure you keep them in member variables or similar,
+ * because Java will not know if there are still native references to them.
+ * 
+ * When using Frontlib from outside its package, you only interact with structs
+ * via the PointerType classes. They allow you to get at the data of the struct
+ * with a function called deref(), which creates a plain normal Java object
+ * representing the data (e.g. SchemePtr.deref() will give you a Scheme object).
+ * 
+ * Remember that you usually have to destroy structs that you receive from the
+ * library, because they are owned by the native code, not Java. The recommended
+ * pattern for most cases is to call deref() on the pointer to get a Java object
+ * (that you can keep as long as you like), and then immediately destroy the
+ * struct if it needs destroying. To find out whether and how the struct needs
+ * to be destroyed, see the library's documentation of the function that you got
+ * the struct from.
+ * 
+ * To pass new structs to the library, you can use the static createJavaOwned()
+ * function in each PointerType, which creates a new struct from the Java object
+ * you provide, and returns a pointer to that struct that you can pass to
+ * library functions. This new structure's memory is owned and managed by Java
+ * code, so do not destroy it with frontlib functions!
+ * 
+ * There is a slight mismatch between the data model for the game setup. The
+ * frontlib supports setting initial health and weaponset per-hog, because the
+ * engine allows for that, but currently neither the networking protocol nor the
+ * PC frontend support this feature, so the Android version does not take
+ * advantage of it either and treats both as per-game settings. The initial
+ * health is contained in the game scheme, the weaponset is explicitly part of
+ * the GameConfig. When converting GameConfig to a native flib_gamesetup, both
+ * are automatically copied to all hogs in the game, and for the reverse
+ * conversion the weaponset of the first hog of the first team is used as the
+ * GameConfig weaponset. This means that GameConfig.weaponset will be null if
+ * there are no teams in the game.
+ * 
+ * When starting a network game, you only need to query the GameSetupPtr from
+ * the netconn and use it to create the gameconn - this is preferable to using
+ * your own recreation of the game setup, because that way the same piece of
+ * code is used to determine the game setup on all platforms.
+ * 
+ * The "context" parameter of the callbacks is never needed here because JNA
+ * generates function code for each callback object. Don't worry about it, just
+ * pass null for context and ignore the context parameter in the callbacks.
+ * 
+ * Finally, the library functions are documented in the actual library, not
+ * here, so check the docs there to find out what exactly each function does!
+ * 
+ * Notes about the structure of this class (for the next one who has to touch
+ * this...):
+ * 
+ * Java/C interop is quite fiddly and error-prone, so as long as things work,
+ * try to stick to the established patterns.
+ * 
+ * Structure types should always be hidden from the outside world, because they
+ * can be misused too easily. For example, if you get a Structure from the
+ * library, change a String value in there and pass it back, JNA will re-write
+ * that string using Java-owned memory without freeing the old native-owned
+ * string, which causes a memory leak and possibly a double-free or other Bad
+ * Things (tm). To avoid problems like this, Structure types are only used
+ * internally, to map existing structures to Java types (without modifying them)
+ * or to create brand-new, Java-owned structures. Both operations are exposed to
+ * the outside through the PointerType classes corresponding to the structures
+ * in question.
+ * 
+ * Since all of the struct mapping happens in Java, it is never checked against
+ * the actual struct declarations in the library. That means strange things can
+ * start happening at runtime if the frontlib structs are modified without
+ * changing the mappings here to match. This also applies to the function
+ * signatures: JNA checks whether the functions actually exist when loading the
+ * library, but it has no way of knowing whether the signatures are correct. If
+ * the signatures here deviate from those in the frontlib, you might get stack
+ * corruption.
+ * 
+ * In order to check at least the function signatures, take a look at the file
+ * extra/jnacontrol.c in the frontlib sources. You can validate whether the
+ * function signatures are still correct by copy-pasting them into jnaControl.c
+ * and compiling it against the frontlib headers. The typedefs and #defines in
+ * that file will make the compiler see the Java method signatures as C function
+ * declarations. Since the same functions are already declared in the frontlib
+ * headers, the compiler will give you errors if the signatures don't match.
+ */
+public interface Frontlib extends Library {
+	public static class NetconnPtr extends PointerType { }
+	public static class MapconnPtr extends PointerType { }
+	public static class GameconnPtr extends PointerType { }
+	
+	public static class MetaschemePtr extends PointerType {
+		public MetaScheme deref() {
+			return deref(getPointer());
+		}
+		
+		public static MetaScheme deref(Pointer p) {
+			MetaschemeStruct struct = new MetaschemeStruct(p);
+			struct.read();
+			return struct.toMetaScheme();
+		}
+	}
+	
+	public static class RoomArrayPtr extends PointerType { 
+		public Room[] getRooms(int count) {
+			Pointer ptr = getPointer();
+			if(ptr == null) {
+				return new Room[0];
+			}
+			Pointer[] untypedPtrs = ptr.getPointerArray(0, count);
+			Room[] result = new Room[count];
+			for(int i=0; i<count; i++) {
+				result[i] = RoomPtr.deref(untypedPtrs[i]);
+			}
+			return result;
+		}
+	}
+	
+	public static class RoomPtr extends PointerType {
+		public Room deref() {
+			return deref(getPointer());
+		}
+		
+		public static Room deref(Pointer p) {
+			RoomStruct struct = new RoomStruct(p);
+			struct.read();
+			return struct.toRoomlistRoom();
+		}
+	}
+	
+	public static class TeamPtr extends PointerType {
+		private TeamStruct javaOwnedInstance; 
+		
+		public TeamInGame deref() {
+			TeamStruct struct = new TeamStruct(getPointer());
+			struct.read();
+			return struct.toTeamInGame();
+		}
+		
+		public static TeamPtr createJavaOwned(Team t) {
+			return createJavaOwned(new TeamInGame(t, null));
+		}
+		
+		public static TeamPtr createJavaOwned(TeamInGame ingameTeam) {
+			TeamPtr result = new TeamPtr();
+			result.javaOwnedInstance = new TeamStruct();
+			result.javaOwnedInstance.fillFrom(ingameTeam.team, ingameTeam.ingameAttribs);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class WeaponsetPtr extends PointerType {
+		private WeaponsetStruct javaOwnedInstance; 
+		
+		public Weaponset deref() {
+			WeaponsetStruct struct = new WeaponsetStruct(getPointer());
+			struct.read();
+			return struct.toWeaponset();
+		}
+		
+		public static WeaponsetPtr createJavaOwned(Weaponset weaponset) {
+			WeaponsetPtr result = new WeaponsetPtr();
+			result.javaOwnedInstance = new WeaponsetStruct();
+			result.javaOwnedInstance.fillFrom(weaponset);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class WeaponsetListPtr extends PointerType {
+		private WeaponsetListStruct javaOwnedInstance;
+		
+		public List<Weaponset> deref() {
+			WeaponsetListStruct struct = new WeaponsetListStruct(getPointer());
+			struct.read();
+			return struct.toWeaponsetList();
+		}
+		
+		public static WeaponsetListPtr createJavaOwned(List<Weaponset> list) {
+			WeaponsetListPtr result = new WeaponsetListPtr();
+			result.javaOwnedInstance = new WeaponsetListStruct();
+			result.javaOwnedInstance.fillFrom(list);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class MapRecipePtr extends PointerType {
+		private MapRecipeStruct javaOwnedInstance;
+		
+		public MapRecipe deref() {
+			MapRecipeStruct struct = new MapRecipeStruct(getPointer());
+			struct.read();
+			return struct.toMapRecipe();
+		}
+		
+		public static MapRecipePtr createJavaOwned(MapRecipe recipe) {
+			MapRecipePtr result = new MapRecipePtr();
+			result.javaOwnedInstance = new MapRecipeStruct();
+			result.javaOwnedInstance.fillFrom(recipe);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class SchemePtr extends PointerType {
+		private SchemeStruct javaOwnedInstance;
+		
+		public Scheme deref() {
+			SchemeStruct struct = new SchemeStruct(getPointer());
+			struct.read();
+			return struct.toScheme();
+		}
+		
+		public static SchemePtr createJavaOwned(Scheme scheme) {
+			SchemePtr result = new SchemePtr();
+			result.javaOwnedInstance = new SchemeStruct();
+			result.javaOwnedInstance.fillFrom(scheme);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class SchemelistPtr extends PointerType {
+		private SchemelistStruct javaOwnedInstance;
+		
+		public List<Scheme> deref() {
+			SchemelistStruct struct = new SchemelistStruct(getPointer());
+			struct.read();
+			return struct.toSchemeList();
+		}
+		
+		public static SchemelistPtr createJavaOwned(List<Scheme> schemes) {
+			SchemelistPtr result = new SchemelistPtr();
+			result.javaOwnedInstance = new SchemelistStruct();
+			result.javaOwnedInstance.fillFrom(schemes);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class GameSetupPtr extends PointerType {
+		private GameSetupStruct javaOwnedInstance;
+		
+		public GameConfig deref() {
+			GameSetupStruct struct = new GameSetupStruct(getPointer());
+			struct.read();
+			return struct.toGameConfig();
+		}
+		
+		public static GameSetupPtr createJavaOwned(GameConfig conf) {
+			GameSetupPtr result = new GameSetupPtr();
+			result.javaOwnedInstance = new GameSetupStruct();
+			result.javaOwnedInstance.fillFrom(conf);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class ByteArrayPtr extends PointerType {
+		public byte[] deref(int size) {
+			return getPointer().getByteArray(0, size);
+		}
+		
+		public static byte[] deref(ByteArrayPtr ptr, int size) {
+			if(ptr==null && size==0) {
+				return null;
+			} else {
+				return ptr.deref(size);
+			}
+		}
+		
+		public static ByteArrayPtr createJavaOwned(byte[] buffer) {
+			if(buffer == null || buffer.length == 0) {
+				return null;
+			}
+			// no need for javaOwnedInstance here because PointerType
+			// remembers the memory as its Pointer
+			Pointer ptr = new Memory(buffer.length);
+			ptr.write(0, buffer, 0, buffer.length);
+			ByteArrayPtr result = new ByteArrayPtr();
+			result.setPointer(ptr);
+			return result;
+		}
+	}
+	
+	static class HogStruct extends Structure {
+		public static class ByVal extends HogStruct implements Structure.ByValue {}
+		public static class ByRef extends HogStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"name", "hat", "rounds", "kills", "deaths", "suicides", "difficulty", "initialHealth", "weaponset"};
+
+		public HogStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public HogStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Hog hog) {
+			difficulty = hog.level;
+			hat = hog.hat;
+			name = hog.name;
+		}
+		
+		public Hog toHog() {
+			return new Hog(name, hat, difficulty);
+		}
+		
+		public String name;
+		public String hat;
+		
+		public int rounds;
+		public int kills;
+		public int deaths;
+		public int suicides;
+	
+		public int difficulty;
+		
+		public int initialHealth;
+		public WeaponsetStruct.ByRef weaponset;
+	}
+	
+	static class TeamStruct extends Structure {
+		public static class ByVal extends TeamStruct implements Structure.ByValue {}
+		public static class ByRef extends TeamStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"hogs", "name", "grave", "fort", "voicepack", "flag", "bindings", "bindingCount", "rounds", "wins", "campaignProgress", "colorIndex", "hogsInGame", "remoteDriven", "ownerName"};
+
+		public TeamStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public TeamStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Team team, TeamIngameAttributes attrs) {
+			if(team != null) {
+				name = team.name;
+				grave = team.grave;
+				flag = team.flag;
+				voicepack = team.voice;
+				fort = team.fort;
+				if(team.hogs.size() != Team.HEDGEHOGS_PER_TEAM) {
+					throw new IllegalArgumentException();
+				}
+				for(int i=0; i<hogs.length; i++) {
+					hogs[i] = new HogStruct();
+					hogs[i].fillFrom(team.hogs.get(i));
+				}
+			}
+			
+			if(attrs != null) {
+				hogsInGame = attrs.hogCount;
+				ownerName = attrs.ownerName;
+				colorIndex = attrs.colorIndex;
+				remoteDriven = attrs.remoteDriven;
+			}
+		}
+		
+		public void fillFrom(TeamInGame team, WeaponsetStruct.ByRef weaponset, int initialHealth) {
+			fillFrom(team.team, team.ingameAttribs);
+			for(int i=0; i<hogs.length; i++) {
+				hogs[i].initialHealth = initialHealth;
+				hogs[i].weaponset = weaponset;
+			}
+		}
+		
+		public Team toTeam() {
+			List<Hog> hogList = new ArrayList<Hog>();
+			for(int i=0; i<hogs.length; i++) {
+				hogList.add(hogs[i].toHog());
+			}
+			return new Team(name, grave, flag, voicepack, fort, hogList);
+		}
+		
+		public TeamIngameAttributes toTeamIngameAttributes() {
+			return new TeamIngameAttributes(ownerName, colorIndex, hogsInGame, remoteDriven);
+		}
+		
+		public TeamInGame toTeamInGame() {
+			return new TeamInGame(toTeam(), toTeamIngameAttributes());
+		}
+		
+		public HogStruct[] hogs = new HogStruct[Team.HEDGEHOGS_PER_TEAM];
+		public String name;
+		public String grave;
+		public String fort;
+		public String voicepack;
+		public String flag;
+		
+		public Pointer bindings;
+		public int bindingCount;
+		
+		public int rounds;
+		public int wins;
+		public int campaignProgress;
+		
+		public int colorIndex;
+		public int hogsInGame;
+		public boolean remoteDriven;
+		public String ownerName;
+	}
+	
+	static class WeaponsetStruct extends Structure {
+		public static class ByVal extends WeaponsetStruct implements Structure.ByValue {}
+		public static class ByRef extends WeaponsetStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"loadout", "crateprob", "crateammo", "delay", "name"};
+		
+		public WeaponsetStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Weaponset weaponset) {
+			fillWeaponInfo(loadout, weaponset.loadout);
+			fillWeaponInfo(crateprob, weaponset.crateProb);
+			fillWeaponInfo(crateammo, weaponset.crateAmmo);
+			fillWeaponInfo(delay, weaponset.delay);
+			name = weaponset.name;
+		}
+		
+		private static void fillWeaponInfo(byte[] array, String str) {
+			for(int i=0; i<array.length-1; i++) {
+				array[i] = (byte) (i<str.length() ? str.charAt(i) : '0');
+			}
+			array[array.length-1] = (byte)0;
+		}
+		
+		public Weaponset toWeaponset() {
+			return new Weaponset(name, weaponInfoToString(loadout), weaponInfoToString(crateprob), weaponInfoToString(crateammo), weaponInfoToString(delay));
+		}
+		
+		private static String weaponInfoToString(byte[] array) {
+			try {
+				return new String(array, 0, array.length-1, "ASCII");
+			} catch (UnsupportedEncodingException e) {
+				throw new AssertionError();
+			}
+		}
+		
+		public byte[] loadout = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] crateprob = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] crateammo = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] delay = new byte[Weaponset.WEAPONS_COUNT+1];
+		public String name;
+	}
+	
+	/**
+	 * Represents a flib_weaponset*, for use as part of a flib_weaponset**
+	 */
+	static class WeaponsetPointerByReference extends Structure implements Structure.ByReference {
+		private static String[] FIELD_ORDER = new String[] {"weaponset"};
+		
+		public WeaponsetPointerByReference() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetPointerByReference(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public WeaponsetStruct.ByRef weaponset;
+	}
+	
+	static class WeaponsetListStruct extends Structure {
+		public static class ByVal extends WeaponsetListStruct implements Structure.ByValue {}
+		public static class ByRef extends WeaponsetListStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"weaponsetCount", "weaponsets"};
+		
+		public WeaponsetListStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetListStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(List<Weaponset> list) {
+			weaponsetCount = list.size();
+			if(weaponsetCount<=0) {
+				weaponsets = null;
+			} else {
+				weaponsets = new WeaponsetPointerByReference();
+				Structure[] structs = weaponsets.toArray(weaponsetCount);
+				
+				for(int i=0; i<weaponsetCount; i++) {
+					WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i];
+					pstruct.weaponset = new WeaponsetStruct.ByRef();
+					pstruct.weaponset.fillFrom(list.get(i));
+				}
+			}
+		}
+		
+		/**
+		 * Only use on native-owned structs!
+		 * Calling this method on a Java-owned struct could cause garbage collection of referenced
+		 * structures.
+		 */
+		public List<Weaponset> toWeaponsetList() {
+			if(weaponsetCount<=0) {
+				return new ArrayList<Weaponset>();
+			} else {
+				List<Weaponset> list = new ArrayList<Weaponset>(weaponsetCount);
+				Structure[] structs = weaponsets.toArray(weaponsetCount);
+				
+				for(int i=0; i<weaponsetCount; i++) {
+					WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i];
+					list.add(pstruct.weaponset.toWeaponset());
+				}
+				return list;
+			}
+		}
+		
+		public int weaponsetCount;
+		public WeaponsetPointerByReference weaponsets;
+	}
+	
+	static class RoomStruct extends Structure {
+		public static class ByVal extends RoomStruct implements Structure.ByValue {}
+		public static class ByRef extends RoomStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"inProgress", "name", "playerCount", "teamCount", "owner", "map", "scheme", "weapons"};
+		
+		public RoomStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public RoomStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+
+		public Room toRoomlistRoom() {
+			return new Room(name, map, scheme, weapons, owner, playerCount, teamCount, inProgress);
+		}
+		
+	    public boolean inProgress;
+	    public String name;
+	    public int playerCount;
+	    public int teamCount;
+	    public String owner;
+	    public String map;
+	    public String scheme;
+	    public String weapons;
+	}
+	
+	static class MapRecipeStruct extends Structure {
+		public static class ByVal extends MapRecipeStruct implements Structure.ByValue {}
+		public static class ByRef extends MapRecipeStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"mapgen", "name", "seed", "theme", "drawData", "drawDataSize", "templateFilter", "mazeSize"};
+		
+		public MapRecipeStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public MapRecipeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(MapRecipe map) {
+			mapgen = map.mapgen;
+			name = map.name;
+			seed = map.seed;
+			theme = map.theme;
+			byte[] buf = map.getDrawData();
+			if(buf==null || buf.length==0) {
+				drawData = null;
+			} else {
+				drawData = ByteArrayPtr.createJavaOwned(buf).getPointer();
+			}
+			drawDataSize = NativeSizeT.valueOf(buf==null ? 0 : buf.length);
+			templateFilter = map.templateFilter;
+			mazeSize = map.mazeSize;
+		}
+		
+		public MapRecipe toMapRecipe() {
+			byte[] buf;
+			int size = drawDataSize.intValue();
+			if(size>0) {
+				buf = drawData.getByteArray(0, size);
+			} else {
+				buf = null;
+			}
+			return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, buf);
+		}
+		
+		public int mapgen;
+		public String name;
+		public String seed;
+		public String theme;
+		public Pointer drawData;			// We can't use ByteArrayPtr in a struct because JNA will overwrite the value with NULL - probably a bug.
+		public NativeSizeT drawDataSize;
+		public int templateFilter;
+		public int mazeSize;
+	}
+	
+	static class MetaschemeSettingStruct extends Structure {
+		public static class ByVal extends MetaschemeSettingStruct implements Structure.ByValue {}
+		public static class ByRef extends MetaschemeSettingStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"name", "engineCommand", "maxMeansInfinity", "times1000", "min", "max", "def"};
+		
+		public MetaschemeSettingStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public MetaschemeSettingStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Setting setting) {
+			name = setting.name;
+			engineCommand = setting.engineCommand;
+			maxMeansInfinity = setting.maxMeansInfinity;
+			times1000 = setting.times1000;
+			min = setting.min;
+			max = setting.max;
+			def = setting.def;
+		}
+		
+		public MetaScheme.Setting toMetaSchemeSetting() {
+			return new MetaScheme.Setting(name, engineCommand, maxMeansInfinity, times1000, min, max, def);
+		}
+		
+		public String name;
+		public String engineCommand;
+		public boolean maxMeansInfinity;
+		public boolean times1000;
+		public int min;
+		public int max;
+		public int def;
+	}
+	
+	static class MetaschemeModStruct extends Structure {
+		public static class ByVal extends MetaschemeModStruct implements Structure.ByValue {}
+		public static class ByRef extends MetaschemeModStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"name", "bitmaskIndex"};
+		
+		public MetaschemeModStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public MetaschemeModStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Mod mod) {
+			name = mod.name;
+			bitmaskIndex = mod.bitmaskIndex;
+		}
+		
+		public MetaScheme.Mod toMetaSchemeMod() {
+			return new MetaScheme.Mod(name, bitmaskIndex);
+		}
+
+		public String name;
+		public int bitmaskIndex;
+
+	}
+	
+	static class MetaschemeStruct extends Structure {
+		public static class ByVal extends MetaschemeStruct implements Structure.ByValue {}
+		public static class ByRef extends MetaschemeStruct implements Structure.ByReference {}
+
+		private static String[] FIELD_ORDER = new String[] {"settingCount", "modCount", "settings", "mods"};
+		
+		public MetaschemeStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public MetaschemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		/**
+		 * Only use on native-owned structs!
+		 * Calling this method on a Java-owned struct could cause garbage collection of referenced
+		 * structures.
+		 */
+		public MetaScheme toMetaScheme() {
+			List<MetaScheme.Setting> settingList = new ArrayList<MetaScheme.Setting>(settingCount);
+			List<MetaScheme.Mod> modList = new ArrayList<MetaScheme.Mod>(modCount);
+			
+			Structure[] settingStructs = settings.toArray(settingCount);
+			Structure[] modStructs = mods.toArray(modCount);
+			
+			for(int i=0; i<settingCount; i++) {
+				MetaschemeSettingStruct mss = (MetaschemeSettingStruct)settingStructs[i];
+				settingList.add(mss.toMetaSchemeSetting());
+			}
+			
+			for(int i=0; i<modCount; i++) {
+				MetaschemeModStruct mms = (MetaschemeModStruct)modStructs[i];
+				modList.add(mms.toMetaSchemeMod());
+			}
+			
+			return new MetaScheme(modList, settingList);
+		}
+		
+		public int settingCount;
+		public int modCount;
+		public MetaschemeSettingStruct.ByRef settings;
+		public MetaschemeModStruct.ByRef mods;
+	}
+	
+	static class SchemeStruct extends Structure {
+		public static class ByVal extends SchemeStruct implements Structure.ByValue {}
+		public static class ByRef extends SchemeStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"name", "settings", "mod"};
+		
+		public SchemeStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public SchemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Scheme scheme) {
+			MetaScheme meta = MetaScheme.INSTANCE;
+			name = scheme.name;
+			settings = new Memory(AndroidTypeMapper.NATIVE_INT_SIZE * meta.settings.size());
+			for(int i=0; i<meta.settings.size(); i++) {
+				Integer value = scheme.settings.get(meta.settings.get(i).name);
+				settings.setInt(AndroidTypeMapper.NATIVE_INT_SIZE*i, value);
+			}
+			mods = new Memory(AndroidTypeMapper.NATIVE_BOOL_SIZE * meta.mods.size());
+			for(int i=0; i<meta.mods.size(); i++) {
+				Boolean value = scheme.mods.get(meta.mods.get(i).name);
+				mods.setByte(AndroidTypeMapper.NATIVE_BOOL_SIZE*i, (byte)(value ? 1 : 0));
+			}
+		}
+
+		public Scheme toScheme() {
+			Map<String, Integer> settingsMap = new HashMap<String, Integer>();
+			MetaScheme meta = MetaScheme.INSTANCE;
+			for(int i=0; i<meta.settings.size(); i++) {
+				settingsMap.put(meta.settings.get(i).name, settings.getInt(AndroidTypeMapper.NATIVE_INT_SIZE*i));
+			}
+			Map<String, Boolean> modsMap = new HashMap<String, Boolean>();
+			for(int i=0; i<meta.mods.size(); i++) {
+				modsMap.put(meta.mods.get(i).name, mods.getByte(i) != 0 ? Boolean.TRUE : Boolean.FALSE);
+			}
+			return new Scheme(name, settingsMap, modsMap);
+		}
+		
+		public String name;
+		public Pointer settings;
+		public Pointer mods;
+	}
+	
+	/**
+	 * Represents a flib_scheme*, for use as part of a flib_scheme**
+	 */
+	static class SchemePointerByReference extends Structure implements Structure.ByReference {
+		private static String[] FIELD_ORDER = new String[] {"scheme"};
+		
+		public SchemePointerByReference() { super(); setFieldOrder(FIELD_ORDER); }
+		public SchemePointerByReference(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public SchemeStruct.ByRef scheme;
+	}
+	
+	static class SchemelistStruct extends Structure {
+		public static class ByVal extends SchemelistStruct implements Structure.ByValue {}
+		public static class ByRef extends SchemelistStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"schemeCount", "schemes"};
+		
+		public SchemelistStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public SchemelistStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(List<Scheme> schemeList) {
+			schemeCount = schemeList.size();
+			if(schemeCount<=0) {
+				schemes = null;
+			} else {
+				schemes = new SchemePointerByReference();
+				Structure[] schemePtrStructs = schemes.toArray(schemeCount);
+				
+				for(int i=0; i<this.schemeCount; i++) {
+					SchemePointerByReference spbr = (SchemePointerByReference)schemePtrStructs[i];
+					spbr.scheme = new SchemeStruct.ByRef();
+					spbr.scheme.fillFrom(schemeList.get(i));
+				}
+			}
+		}
+
+		/**
+		 * Only use on native-owned structs!
+		 * Calling this method on a Java-owned struct could cause garbage collection of referenced
+		 * structures.
+		 */
+		public List<Scheme> toSchemeList() {
+			if(schemeCount<=0) {
+				return new ArrayList<Scheme>();
+			} else {
+				List<Scheme> schemeList = new ArrayList<Scheme>(schemeCount);
+				
+				Structure[] schemePtrStructs = schemes.toArray(schemeCount);
+				
+				for(int i=0; i<schemeCount; i++) {
+					SchemePointerByReference spbr2 = (SchemePointerByReference)schemePtrStructs[i];
+					schemeList.add(spbr2.scheme.toScheme());
+				}
+				return schemeList;
+			}
+		}
+		
+		public int schemeCount;
+		public SchemePointerByReference schemes;
+	}
+	
+	/**
+	 * Represents a flib_team*, for use as part of a flib_team**
+	 */
+	static class TeamPointerByReference extends Structure implements Structure.ByReference {
+		private static String[] FIELD_ORDER = new String[] {"team"};
+		
+		public TeamPointerByReference() { super(); setFieldOrder(FIELD_ORDER); }
+		public TeamPointerByReference(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public TeamStruct.ByRef team;
+	}
+	
+	static class TeamlistStruct extends Structure {
+		public static class ByVal extends TeamlistStruct implements Structure.ByValue {}
+		public static class ByRef extends TeamlistStruct implements Structure.ByReference {}
+
+		private static String[] FIELD_ORDER = new String[] {"teamCount", "teams"};
+		
+		public TeamlistStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public TeamlistStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(List<TeamInGame> teamList, WeaponsetStruct.ByRef weaponset, int initialHealth) {
+			teamCount = teamList.size();
+			if(teamCount <= 0) {
+				teams = null;
+			} else {
+				teams = new TeamPointerByReference();
+				Structure[] teamPtrStructs = teams.toArray(teamCount);
+				
+				for(int i=0; i<this.teamCount; i++) {
+					TeamPointerByReference tpbr = (TeamPointerByReference)teamPtrStructs[i];
+					tpbr.team = new TeamStruct.ByRef();
+					tpbr.team.fillFrom(teamList.get(i), weaponset, initialHealth);
+				}
+			}
+		}
+
+		public List<TeamInGame> toTeamInGameList() {
+			if(teamCount<=0) {
+				return new ArrayList<TeamInGame>();
+			} else {
+				List<TeamInGame> result = new ArrayList<TeamInGame>(teamCount);
+				Structure[] structs = teams.toArray(teamCount);
+				
+				for(int i=0; i<teamCount; i++) {
+					TeamPointerByReference struct = (TeamPointerByReference)structs[i];
+					result.add(struct.team.toTeamInGame());
+				}
+				return result;
+			}
+		}
+		
+		public int teamCount;
+		public TeamPointerByReference teams;
+	}
+	
+	static class GameSetupStruct extends Structure {
+		public static class ByVal extends GameSetupStruct implements Structure.ByValue {}
+		public static class ByRef extends GameSetupStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"script", "gamescheme", "map", "teamlist"};
+		
+		public GameSetupStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public GameSetupStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(GameConfig conf) {
+			script = conf.style;
+			gamescheme = new SchemeStruct.ByRef();
+			gamescheme.fillFrom(conf.scheme);
+			map = new MapRecipeStruct.ByRef();
+			map.fillFrom(conf.map);
+			
+			/*
+			 * At this point we deviate from the usual copying pattern because the frontlib
+			 * expects per-hog weapons and initial health, but the UI models them as per-
+			 * game, so we extract them from the config here and pass them on to be included
+			 * in each hog.
+			 */
+			WeaponsetStruct.ByRef wss = new WeaponsetStruct.ByRef();
+			wss.fillFrom(conf.weaponset);
+			int initialHealth = conf.scheme.getHealth();
+			
+			teamlist = new TeamlistStruct.ByRef();
+			teamlist.fillFrom(conf.teams, wss, initialHealth);
+		}
+		
+		public GameConfig toGameConfig() {
+			Scheme scheme = gamescheme != null ? gamescheme.toScheme() : null;
+			MapRecipe mapRecipe = map != null ? map.toMapRecipe() : null;
+			List<TeamInGame> teams = teamlist != null ? teamlist.toTeamInGameList() : null;
+			
+			WeaponsetStruct weaponsetStruct = teamlist != null && teamlist.teamCount>0 ? teamlist.teams.team.hogs[0].weaponset : null;
+			Weaponset weaponset = weaponsetStruct != null ? weaponsetStruct.toWeaponset() : null;
+			return new GameConfig(script, scheme, mapRecipe, teams, weaponset);
+		}
+
+		public String script;
+		public SchemeStruct.ByRef gamescheme;
+		public MapRecipeStruct.ByRef map;
+		public TeamlistStruct.ByRef teamlist;
+	}
+	
+	/*
+	 * Callback interfaces. The context parameter is never needed here and
+	 * should always be ignored. Be sure to keep a reference to each callback
+	 * for as long as they might be called by native code, to avoid premature
+	 * garbage collection.
+	 */
+	public static interface VoidCallback extends Callback {
+		void callback(Pointer context);
+	}
+	
+	public static interface StrCallback extends Callback {
+		void callback(Pointer context, String arg1);
+	}
+	
+	public static interface IntCallback extends Callback {
+		void callback(Pointer context, int arg1);
+	}
+	
+	public static interface IntStrCallback extends Callback {
+		void callback(Pointer context, int arg1, String arg2);
+	}
+	
+	public static interface StrIntCallback extends Callback {
+		void callback(Pointer context, String arg1, int arg2);
+	}
+	
+	public static interface StrStrCallback extends Callback {
+		void callback(Pointer context, String arg1, String arg2);
+	}
+	
+	public static interface RoomCallback extends Callback {
+		void callback(Pointer context, RoomPtr arg1);
+	}
+	
+	public static interface RoomListCallback extends Callback {
+		void callback(Pointer context, RoomArrayPtr arg1, int count);
+	}
+	
+	public static interface StrRoomCallback extends Callback {
+		void callback(Pointer context, String arg1, RoomPtr arg2);
+	}
+	
+	public static interface BoolCallback extends Callback {
+		void callback(Pointer context, boolean arg1);
+	}
+	
+	public static interface StrBoolCallback extends Callback {
+		void callback(Pointer context, String arg1, boolean arg2);
+	}
+	
+	public static interface TeamCallback extends Callback {
+		void callback(Pointer context, TeamPtr arg1);
+	}
+	
+	public static interface BytesCallback extends Callback {
+		void callback(Pointer context, ByteArrayPtr buffer, NativeSizeT size);
+	}
+	
+	public static interface BytesBoolCallback extends Callback {
+		void callback(Pointer context, ByteArrayPtr buffer, NativeSizeT size, boolean arg3);
+	}
+	
+	public static interface SchemeCallback extends Callback {
+		void callback(Pointer context, SchemePtr arg1);
+	}
+	
+	public static interface MapIntCallback extends Callback {
+		void callback(Pointer context, MapRecipePtr arg1, int arg2);
+	}
+	
+	public static interface WeaponsetCallback extends Callback {
+		void callback(Pointer context, WeaponsetPtr arg1);
+	}
+	
+	public static interface MapimageCallback extends Callback {
+		void callback(Pointer context, ByteArrayPtr buffer, int hedgehogCount);
+	}
+	
+	public static interface LogCallback extends Callback {
+		void callback(int level, String logMessage);
+	}
+	
+	// frontlib.h
+    int flib_init();
+    void flib_quit();
+	
+    // hwconsts.h
+    int flib_get_teamcolor(int colorIndex);
+    int flib_get_teamcolor_count();
+    int flib_get_hedgehogs_per_team();
+    int flib_get_weapons_count();
+	MetaschemePtr flib_get_metascheme();
+	
+    // net/netconn.h
+	static final int NETCONN_STATE_CONNECTING = 0;
+	static final int NETCONN_STATE_LOBBY = 1;
+	static final int NETCONN_STATE_ROOM = 2;
+	static final int NETCONN_STATE_DISCONNECTED = 10;
+	
+	static final int NETCONN_DISCONNECT_NORMAL = 0;
+	static final int NETCONN_DISCONNECT_SERVER_TOO_OLD = 1;
+	static final int NETCONN_DISCONNECT_AUTH_FAILED = 2;
+	static final int NETCONN_DISCONNECT_CONNLOST = 3;
+	static final int NETCONN_DISCONNECT_INTERNAL_ERROR = 100;
+	
+	static final int NETCONN_ROOMLEAVE_ABANDONED = 0;
+	static final int NETCONN_ROOMLEAVE_KICKED = 1;
+	
+	static final int NETCONN_MSG_TYPE_PLAYERINFO = 0;
+	static final int NETCONN_MSG_TYPE_SERVERMESSAGE = 1;
+	static final int NETCONN_MSG_TYPE_WARNING = 2;
+	static final int NETCONN_MSG_TYPE_ERROR = 3;
+	
+	static final int NETCONN_MAPCHANGE_FULL = 0;
+	static final int NETCONN_MAPCHANGE_MAP = 1;
+	static final int NETCONN_MAPCHANGE_MAPGEN = 2;
+	static final int NETCONN_MAPCHANGE_DRAWNMAP = 3;
+	static final int NETCONN_MAPCHANGE_MAZE_SIZE = 4;
+	static final int NETCONN_MAPCHANGE_TEMPLATE = 5;
+	static final int NETCONN_MAPCHANGE_THEME = 6;
+	static final int NETCONN_MAPCHANGE_SEED = 7;
+    
+	NetconnPtr flib_netconn_create(String playerName, String dataDirPath, String host, int port);
+	void flib_netconn_destroy(NetconnPtr conn);
+
+	void flib_netconn_tick(NetconnPtr conn);
+	boolean flib_netconn_is_chief(NetconnPtr conn);
+	String flib_netconn_get_playername(NetconnPtr conn);
+	GameSetupPtr flib_netconn_create_gamesetup(NetconnPtr conn);
+	int flib_netconn_send_quit(NetconnPtr conn, String quitmsg);
+	int flib_netconn_send_chat(NetconnPtr conn, String chat);
+	int flib_netconn_send_teamchat(NetconnPtr conn, String msg);
+	int flib_netconn_send_password(NetconnPtr conn, String passwd);
+	int flib_netconn_send_nick(NetconnPtr conn, String nick);
+	int flib_netconn_send_request_roomlist(NetconnPtr conn);
+	int flib_netconn_send_joinRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_createRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_renameRoom(NetconnPtr conn, String roomName);
+	int flib_netconn_send_leaveRoom(NetconnPtr conn, String msg);
+	int flib_netconn_send_toggleReady(NetconnPtr conn);
+	int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team);
+	int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname);
+	int flib_netconn_send_engineMessage(NetconnPtr conn, ByteArrayPtr message, NativeSizeT size);
+	int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount);
+	int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex);
+	int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset);
+	int flib_netconn_send_map(NetconnPtr conn, MapRecipePtr map);
+	int flib_netconn_send_mapName(NetconnPtr conn, String mapName);
+	int flib_netconn_send_mapGen(NetconnPtr conn, int mapGen);
+	int flib_netconn_send_mapTemplate(NetconnPtr conn, int templateFilter);
+	int flib_netconn_send_mapMazeSize(NetconnPtr conn, int mazeSize);
+	int flib_netconn_send_mapSeed(NetconnPtr conn, String seed);
+	int flib_netconn_send_mapTheme(NetconnPtr conn, String theme);
+	int flib_netconn_send_mapDrawdata(NetconnPtr conn, ByteArrayPtr drawData, NativeSizeT size);
+	int flib_netconn_send_script(NetconnPtr conn, String scriptName);
+	int flib_netconn_send_scheme(NetconnPtr conn, SchemePtr scheme);
+	int flib_netconn_send_roundfinished(NetconnPtr conn, boolean withoutError);
+	int flib_netconn_send_ban(NetconnPtr conn, String playerName);
+	int flib_netconn_send_kick(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerInfo(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerFollow(NetconnPtr conn, String playerName);
+	int flib_netconn_send_startGame(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictJoins(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictTeams(NetconnPtr conn);
+	int flib_netconn_send_clearAccountsCache(NetconnPtr conn);
+	int flib_netconn_send_setServerVar(NetconnPtr conn, String name, String value);
+	int flib_netconn_send_getServerVars(NetconnPtr conn);
+	
+	void flib_netconn_onMessage(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onChat(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onConnected(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onDisconnected(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onRoomlist(NetconnPtr conn, RoomListCallback callback, Pointer context);
+	void flib_netconn_onRoomAdd(NetconnPtr conn, RoomCallback callback, Pointer context);
+	void flib_netconn_onRoomDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomUpdate(NetconnPtr conn, StrRoomCallback callback, Pointer context);
+	void flib_netconn_onLobbyJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onLobbyLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onNickTaken(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onPasswordRequest(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onEnterRoom(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onRoomChiefStatus(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onReadyState(NetconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_netconn_onLeaveRoom(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onTeamAdd(NetconnPtr conn, TeamCallback callback, Pointer context);
+	void flib_netconn_onTeamDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onRunGame(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onTeamAccepted(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onHogCountChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onTeamColorChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onEngineMessage(NetconnPtr conn, BytesCallback callback, Pointer context);
+	void flib_netconn_onSchemeChanged(NetconnPtr conn, SchemeCallback callback, Pointer context);
+	void flib_netconn_onMapChanged(NetconnPtr conn, MapIntCallback callback, Pointer context);
+	void flib_netconn_onScriptChanged(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onWeaponsetChanged(NetconnPtr conn, WeaponsetCallback callback, Pointer context);
+	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
+
+	// ipc/gameconn.h
+	static final int GAME_END_FINISHED = 0;
+	static final int GAME_END_INTERRUPTED = 1;
+	static final int GAME_END_HALTED = 2;
+	static final int GAME_END_ERROR = 3;
+	
+	GameconnPtr flib_gameconn_create(String playerName, GameSetupPtr setup, boolean netgame);
+	GameconnPtr flib_gameconn_create_playdemo(ByteArrayPtr demo, NativeSizeT size);
+	GameconnPtr flib_gameconn_create_loadgame(String playerName, ByteArrayPtr save, NativeSizeT size);
+	GameconnPtr flib_gameconn_create_campaign(String playerName, String seed, String script);
+
+	void flib_gameconn_destroy(GameconnPtr conn);
+	int flib_gameconn_getport(GameconnPtr conn);
+	void flib_gameconn_tick(GameconnPtr conn);
+
+	int flib_gameconn_send_enginemsg(GameconnPtr conn, ByteArrayPtr data, NativeSizeT len);
+	int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg);
+	int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg);
+	int flib_gameconn_send_quit(GameconnPtr conn);
+	int flib_gameconn_send_cmd(GameconnPtr conn, String cmdString);
+	
+	void flib_gameconn_onConnect(GameconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_gameconn_onDisconnect(GameconnPtr conn, IntCallback callback, Pointer context);
+	void flib_gameconn_onErrorMessage(GameconnPtr conn, StrCallback callback, Pointer context);
+	void flib_gameconn_onChat(GameconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_gameconn_onGameRecorded(GameconnPtr conn, BytesBoolCallback callback, Pointer context);
+	void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context);
+	
+	// ipc/mapconn.h
+	public static final int MAPIMAGE_WIDTH = 256;
+	public static final int MAPIMAGE_HEIGHT = 128;
+	public static final int MAPIMAGE_BYTES = (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT);
+	
+	MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc);
+	void flib_mapconn_destroy(MapconnPtr conn);
+	int flib_mapconn_getport(MapconnPtr conn);
+	void flib_mapconn_onSuccess(MapconnPtr conn, MapimageCallback callback, Pointer context);
+	void flib_mapconn_onFailure(MapconnPtr conn, StrCallback callback, Pointer context);
+	void flib_mapconn_tick(MapconnPtr conn);
+	
+	// model/map.h
+	public static final int MAPGEN_REGULAR = 0;
+	public static final int MAPGEN_MAZE = 1;
+	public static final int MAPGEN_DRAWN = 2;
+	public static final int MAPGEN_NAMED = 3;
+
+	public static final int TEMPLATEFILTER_ALL = 0;
+	public static final int TEMPLATEFILTER_SMALL = 1;
+	public static final int TEMPLATEFILTER_MEDIUM = 2;
+	public static final int TEMPLATEFILTER_LARGE = 3;
+	public static final int TEMPLATEFILTER_CAVERN = 4;
+	public static final int TEMPLATEFILTER_WACKY = 5;
+
+	public static final int MAZE_SIZE_SMALL_TUNNELS = 0;
+	public static final int MAZE_SIZE_MEDIUM_TUNNELS = 1;
+	public static final int MAZE_SIZE_LARGE_TUNNELS = 2;
+	public static final int MAZE_SIZE_SMALL_ISLANDS = 3;
+	public static final int MAZE_SIZE_MEDIUM_ISLANDS = 4;
+	public static final int MAZE_SIZE_LARGE_ISLANDS = 5;
+		
+	// model/schemelist.h
+	SchemelistPtr flib_schemelist_from_ini(String filename);
+	int flib_schemelist_to_ini(String filename, SchemelistPtr list);
+	void flib_schemelist_destroy(SchemelistPtr list);
+	
+	// model/team.h
+	TeamPtr flib_team_from_ini(String filename);
+	int flib_team_to_ini(String filename, TeamPtr team);
+	void flib_team_destroy(TeamPtr team);
+	
+	// model/weapon.h
+	WeaponsetListPtr flib_weaponsetlist_from_ini(String filename);
+	int flib_weaponsetlist_to_ini(String filename, WeaponsetListPtr weaponsets);
+	void flib_weaponsetlist_destroy(WeaponsetListPtr list);
+	
+	// model/gamesetup.h
+	void flib_gamesetup_destroy(GameSetupPtr gamesetup);
+	
+	// util/logging.h
+	public static final int FLIB_LOGLEVEL_ALL = -100;
+	public static final int FLIB_LOGLEVEL_DEBUG = -1;
+	public static final int FLIB_LOGLEVEL_INFO = 0;
+	public static final int FLIB_LOGLEVEL_WARNING = 1;
+	public static final int FLIB_LOGLEVEL_ERROR = 2;
+	public static final int FLIB_LOGLEVEL_NONE = 100;
+	
+    void flib_log_setLevel(int level);
+    void flib_log_setCallback(LogCallback callback);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/NativeSizeT.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.frontlib;
+
+/**
+ * This class represents the native C type size_t. On Android, this type could be mapped with int,
+ * but we use a separate type to make it easier to adapt for other platforms if anyone wants to use
+ * the mappings elsewhere. 
+ */
+public final class NativeSizeT extends Number {
+	private static final long serialVersionUID = 1L;
+	private final long value;
+	
+	private NativeSizeT(long value) {
+		this.value = value;
+	}
+	
+	public static NativeSizeT valueOf(long l) {
+		return new NativeSizeT(l);
+	}
+	
+	@Override
+	public int intValue() {
+		return (int)value;
+	}
+	
+	@Override
+	public long longValue() {
+		return value;
+	}
+
+	@Override
+	public double doubleValue() {
+		return value;
+	}
+
+	@Override
+	public float floatValue() {
+		return value;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/GameMessageListener.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,34 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+/**
+ * Interface with several event callbacks that represent network messages which are interesting
+ * for a running game, e.g. because they concern the lifecycle of the game or because they contain
+ * data that needs to be passed on.
+ * 
+ * These functions might be called on any thread.
+ */
+public interface GameMessageListener {
+	void onChatMessage(String nick, String message);
+	void onEngineMessage(byte[] em);
+	void onMessage(int type, String message);
+	void onNetDisconnected();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/MessageLog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,169 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StyleSpan;
+import android.util.Log;
+
+public class MessageLog {
+	private static final int BACKLOG_LINES = 200;
+	
+	private static final int INFO_COLOR = Color.GRAY;
+	private static final int PLAYERINFO_COLOR = Color.GREEN;
+	private static final int CHAT_COLOR = Color.GREEN;
+	private static final int MECHAT_COLOR = Color.CYAN;
+	private static final int WARN_COLOR = Color.RED;
+	private static final int ERROR_COLOR = Color.RED;
+	
+	private final Context context;
+	private List<Listener> observers = new LinkedList<Listener>();
+	private List<CharSequence> log = new LinkedList<CharSequence>();
+	
+	public MessageLog(Context context) {
+		this.context = context;
+	}
+	
+	private Spanned makeLogTime() {
+		String time = DateFormat.getTimeFormat(context).format(new Date());
+		return withColor("[" + time + "] ", INFO_COLOR);
+	}
+	
+	private static Spanned span(CharSequence s, Object o) {
+		Spannable spannable = new SpannableString(s);
+		spannable.setSpan(o, 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		return spannable;
+	}
+	
+	private static Spanned withColor(CharSequence s, int color) {
+		return span(s, new ForegroundColorSpan(color));
+	}
+	
+	private static Spanned bold(CharSequence s) {
+		return span(s, new StyleSpan(Typeface.BOLD));
+	}
+	
+	private void append(CharSequence msg) {
+		SpannableStringBuilder ssb = new SpannableStringBuilder();
+		ssb.append(makeLogTime()).append(msg);
+		appendRaw(ssb);
+	}
+	
+	private void appendRaw(CharSequence msg) {
+		if(log.size() > BACKLOG_LINES) {
+			log.remove(0);
+			for(Listener o : observers) {
+				o.lineRemoved();
+			}
+		}
+		log.add(msg);
+		for(Listener o : observers) {
+			o.lineAdded(msg);
+		}
+	}
+	
+	void appendPlayerJoin(String playername) {
+		append(withColor("***" + context.getResources().getString(R.string.log_player_join, playername), INFO_COLOR));
+	}
+	
+	void appendPlayerLeave(String playername, String partMsg) {
+		String msg = "***";
+		if(partMsg != null) {
+			msg += context.getResources().getString(R.string.log_player_leave_with_msg, playername, partMsg);
+		} else {
+			msg += context.getResources().getString(R.string.log_player_leave, playername);
+		}
+		append(withColor(msg, INFO_COLOR));
+	}
+	
+	void appendChat(String playerName, String msg) {
+		if(msg.startsWith("/me ")) {
+			append(withColor("*"+playerName+" "+msg.substring(4), MECHAT_COLOR));
+		} else {
+			Spanned name = bold(playerName+": ");
+			Spanned fullMessage = withColor(TextUtils.concat(name, msg), CHAT_COLOR);
+			append(fullMessage);			
+		}
+	}
+	
+	void appendMessage(int type, String msg) {
+		switch(type) {
+		case Frontlib.NETCONN_MSG_TYPE_ERROR:
+			append(withColor("***"+msg, ERROR_COLOR));
+			break;
+		case Frontlib.NETCONN_MSG_TYPE_WARNING:
+			append(withColor("***"+msg, WARN_COLOR));
+			break;
+		case Frontlib.NETCONN_MSG_TYPE_PLAYERINFO:
+			append(withColor(msg.replace("\n", " "), PLAYERINFO_COLOR));
+			break;
+		case Frontlib.NETCONN_MSG_TYPE_SERVERMESSAGE:
+			appendRaw(span(TextUtils.concat("\n", Html.fromHtml(msg), "\n"), new RelativeSizeSpan(1.5f)));
+			break;
+		default:
+			Log.e("MessageLog", "Unknown messagetype "+type);
+		}
+	}
+	
+	void clear() {
+		for(Listener o : observers) {
+			o.clear();
+		}
+		log.clear();
+	}
+	
+	public void addListener(Listener o) {
+		observers.add(o);
+	}
+	
+	public void removeListener(Listener o) {
+		observers.remove(o);
+	}
+	
+	public static interface Listener {
+		void lineAdded(CharSequence text);
+		void lineRemoved();
+		void clear();
+	}
+
+	public Collection<CharSequence> getLog() {
+		return Collections.unmodifiableList(log);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/NetRoomState.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,185 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.*;
+import static org.hedgewars.hedgeroid.util.ObjectUtils.equal;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.hedgewars.hedgeroid.BasicRoomState;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType;
+
+/**
+ * This class manages the room state in a network game.
+ */
+class NetRoomState extends BasicRoomState {
+	final Map<String, TeamInGame> requestedTeams = new TreeMap<String, TeamInGame>();
+	private Netplay netplay;
+	
+	public NetRoomState(Netplay netplay) {
+		this.netplay = netplay;
+		initRoomState(false);
+	}
+
+	public void changeWeaponset(Weaponset weaponset) {
+		if(getChiefStatus() && !equal(weaponset, getWeaponset())) {
+			sendToNet(MSG_SEND_WEAPONSET, weaponset);
+			setWeaponset(weaponset);
+		}
+	}
+	
+	public void changeMapRecipe(MapRecipe mapRecipe) {
+		if(getChiefStatus() && !equal(mapRecipe, getMapRecipe())) {
+			sendToNet(MSG_SEND_MAP, mapRecipe);
+			setMapRecipe(mapRecipe);
+		}
+	}
+	
+	public void changeMapNameAndGenerator(String mapName) {
+		if(getChiefStatus() && !equal(mapName, getMapRecipe().name)) {
+			int newGenerator = MapRecipe.generatorForMapname(mapName);
+			if(newGenerator != getMapRecipe().mapgen) {
+				sendToNet(MSG_SEND_MAP_GENERATOR, newGenerator, null);
+			}
+			sendToNet(MSG_SEND_MAP_NAME, mapName);
+			setMapRecipe(getMapRecipe().withName(mapName).withMapgen(newGenerator));
+		}
+	}
+	
+	public void changeMapTemplate(int template) {
+		if(getChiefStatus() && template != getMapRecipe().templateFilter) {
+			sendToNet(MSG_SEND_MAP_TEMPLATE, template, null);
+			setMapRecipe(getMapRecipe().withTemplateFilter(template));
+		}
+	}
+	
+	public void changeMazeSize(int mazeSize) {
+		if(getChiefStatus() && mazeSize != getMapRecipe().mazeSize) {
+			sendToNet(MSG_SEND_MAZE_SIZE, mazeSize, 0);
+			setMapRecipe(getMapRecipe().withMazeSize(mazeSize));
+		}
+	}
+	
+	public void changeMapSeed(String seed) {
+		if(getChiefStatus() && !equal(seed, getMapRecipe().seed)) {
+			sendToNet(MSG_SEND_MAP_SEED, seed);
+			setMapRecipe(getMapRecipe().withSeed(seed));
+		}
+	}
+	
+	public void changeMapTheme(String theme) {
+		if(getChiefStatus() && !equal(theme, getMapRecipe().theme)) {
+			sendToNet(MSG_SEND_MAP_THEME, theme);
+			setMapRecipe(getMapRecipe().withTheme(theme));
+		}
+	}
+	
+	public void changeMapDrawdata(byte[] drawdata) {
+		if(getChiefStatus() && !Arrays.equals(drawdata, getMapRecipe().getDrawData())) {
+			sendToNet(MSG_SEND_MAP_DRAWDATA, drawdata);
+			setMapRecipe(getMapRecipe().withDrawData(drawdata));
+		}
+	}
+	
+	public void changeGameStyle(String gameStyle) {
+		if(getChiefStatus() && !equal(gameStyle, getGameStyle())) {
+			sendToNet(MSG_SEND_GAMESTYLE, gameStyle);
+			setGameStyle(gameStyle);
+		}
+	}
+	
+	public void changeScheme(Scheme scheme) {
+		if(getChiefStatus() && !equal(scheme, getScheme())) {
+			sendToNet(MSG_SEND_SCHEME, scheme);
+			setScheme(scheme);
+		}
+	}
+	
+	void initRoomState(boolean chief) {
+		setTeams(Collections.<String, TeamInGame>emptyMap());
+		requestedTeams.clear();
+		
+		setChief(chief);
+		setGameStyle(GameConfig.DEFAULT_STYLE);
+		setMapRecipe(MapRecipe.makeRandomMap(0, "seed", GameConfig.DEFAULT_THEME));
+		setScheme(netplay.defaultScheme);
+		setWeaponset(netplay.defaultWeaponset);
+		sendFullConfig();
+	}
+	
+	void sendFullConfig() {
+		if(getChiefStatus()) {
+			sendToNet(MSG_SEND_GAMESTYLE, getGameStyle());
+			sendToNet(MSG_SEND_SCHEME, getScheme());
+			sendToNet(MSG_SEND_WEAPONSET, getWeaponset());
+			sendToNet(MSG_SEND_MAP, getMapRecipe());
+		}
+	}
+	
+	private boolean sendToNet(ToNetMsgType what, Object obj) {
+		return netplay.sendToNet(what, 0, obj);
+	}
+	
+	private boolean sendToNet(ToNetMsgType what, int arg1, Object obj) {
+		return netplay.sendToNet(what, arg1, obj);
+	}
+
+	public void requestAddTeam(Team team, int colorIndex) {
+		TeamIngameAttributes tia = new TeamIngameAttributes(netplay.getPlayerName(), colorIndex, TeamIngameAttributes.DEFAULT_HOG_COUNT, false);
+		TeamInGame newTeamInGame = new TeamInGame(team, tia);
+		requestedTeams.put(team.name, newTeamInGame);
+		sendToNet(MSG_SEND_ADD_TEAM, newTeamInGame);
+	}
+
+	public void requestRemoveTeam(String teamname) {
+		sendToNet(MSG_SEND_REMOVE_TEAM, teamname);
+	}
+
+	public void changeTeamColorIndex(String teamname, int colorIndex) {
+		if(getChiefStatus()) {
+			TeamInGame team = getTeams().get(teamname);
+			if(team.ingameAttribs.colorIndex != colorIndex) {
+				sendToNet(MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamname);
+				putTeam(team.withAttribs(team.ingameAttribs.withColorIndex(colorIndex)));
+			}
+		}
+	}
+
+	public void changeTeamHogCount(String teamname, int hogcount) {
+		if(getChiefStatus()) {
+			TeamInGame team = getTeams().get(teamname);
+			if(team.ingameAttribs.hogCount != hogcount) {
+				sendToNet(MSG_SEND_TEAM_HOG_COUNT, hogcount, teamname);
+				putTeam(team.withAttribs(team.ingameAttribs.withHogCount(hogcount)));
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,535 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import static org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.RoomStateManager;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Player;
+import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
+import org.hedgewars.hedgeroid.Datastructures.Room;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.Schemes;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
+import org.hedgewars.hedgeroid.netplay.ThreadedNetConnection.ToNetMsgType;
+import org.hedgewars.hedgeroid.util.ObservableTreeMap;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.util.Pair;
+
+
+/**
+ * This class manages the application's networking state.
+ */
+public class Netplay {
+	public static enum State { NOT_CONNECTED, CONNECTING, LOBBY, ROOM }
+	
+	// Extras in broadcasts
+	public static final String EXTRA_PLAYERNAME = "playerName";
+	public static final String EXTRA_MESSAGE = "message";
+	public static final String EXTRA_HAS_ERROR = "hasError";
+	public static final String EXTRA_REASON = "reason";
+	
+	private static final String ACTIONPREFIX = "org.hedgewars.hedgeroid.netconn.";
+	public static final String ACTION_DISCONNECTED = ACTIONPREFIX+"DISCONNECTED";
+	public static final String ACTION_CONNECTED = ACTIONPREFIX+"CONNECTED";
+	public static final String ACTION_PASSWORD_REQUESTED = ACTIONPREFIX+"PASSWORD_REQUESTED";
+	public static final String ACTION_ENTERED_ROOM_FROM_LOBBY = ACTIONPREFIX+"ENTERED_ROOM";
+	public static final String ACTION_LEFT_ROOM = ACTIONPREFIX+"LEFT_ROOM";
+	public static final String ACTION_STATE_CHANGED = ACTIONPREFIX+"STATE_CHANGED";
+	
+	public static final String DEFAULT_SERVER = "netserver.hedgewars.org";
+	public static final int DEFAULT_PORT = 46631;
+		
+	private final Context appContext;
+	private final LocalBroadcastManager broadcastManager;
+	private final FromNetHandler fromNetHandler = new FromNetHandler();
+	public final Scheme defaultScheme;
+	public final Weaponset defaultWeaponset;
+	
+	private State state = State.NOT_CONNECTED;
+	private String playerName;
+	
+	// null or stale if not in room state
+	private final NetRoomState netRoomState = new NetRoomState(this);
+	
+	// null if there is no running connection (==state is NOT_CONNECTED)
+	private ThreadedNetConnection connection;
+	
+	public final ObservableTreeMap<String, Player> lobbyPlayerlist = new ObservableTreeMap<String, Player>();
+	public final ObservableTreeMap<String, PlayerInRoom> roomPlayerlist = new ObservableTreeMap<String, PlayerInRoom>();
+	public final Roomlist roomList = new Roomlist();
+	public final MessageLog lobbyChatlog;
+	public final MessageLog roomChatlog;
+	
+	private final List<GameMessageListener> gameMessageListeners = new LinkedList<GameMessageListener>();
+	private final List<RunGameListener> runGameListeners = new LinkedList<RunGameListener>();
+	
+	public Netplay(Context appContext, Scheme defaultScheme, Weaponset defaultWeaponset) {
+		this.appContext = appContext;
+		broadcastManager = LocalBroadcastManager.getInstance(appContext);
+		lobbyChatlog = new MessageLog(appContext);
+		roomChatlog = new MessageLog(appContext);
+		this.defaultScheme = defaultScheme;
+		this.defaultWeaponset = defaultWeaponset;
+	}
+	
+	public RoomStateManager getRoomStateManager() {
+		return netRoomState;
+	}
+	
+	private void clearLobbyState() {
+		lobbyPlayerlist.clear();
+		roomList.clear();
+		lobbyChatlog.clear();
+	}
+	
+	private void initRoomState(boolean chief) {
+		roomChatlog.clear();
+		roomPlayerlist.clear();
+		netRoomState.initRoomState(chief);
+	}
+	
+	public void registerGameMessageListener(GameMessageListener listener) {
+		gameMessageListeners.add(listener);
+	}
+	
+	public void unregisterGameMessageListener(GameMessageListener listener) {
+		gameMessageListeners.remove(listener);
+	}
+	
+	public void registerRunGameListener(RunGameListener listener) {
+		runGameListeners.add(listener);
+	}
+	
+	public void unregisterRunGameListener(RunGameListener listener) {
+		runGameListeners.remove(listener);
+	}
+	
+	public void connectToDefaultServer(String playerName) {
+		connect(playerName, DEFAULT_SERVER, DEFAULT_PORT);
+	}
+	
+	/**
+	 * Establish a new connection. Only call if the current state is NOT_CONNECTED.
+	 * 
+	 * The state will switch to CONNECTING immediately. After that, it can asynchronously change to any other state.
+	 * State changes are indicated by broadcasts. In particular, if an error occurs while trying to connect, the state
+	 * will change back to NOT_CONNECTED and an ACTION_DISCONNECTED broadcast is sent.
+	 */
+	public void connect(String name, String host, int port) {
+		playerName = name;
+		if(state != State.NOT_CONNECTED) {
+			throw new IllegalStateException("Attempt to start a new connection while the old one was still running.");
+		}
+		
+		clearLobbyState();
+		changeState(State.CONNECTING);
+		connection = ThreadedNetConnection.startConnection(appContext, fromNetHandler, name, host, port);
+		connection.setFastTickRate(true);
+	}
+	
+	public void sendNick(String nick) {
+		playerName = nick;
+		sendToNet(MSG_SEND_NICK, nick);
+	}
+	public void sendPassword(String password) { sendToNet(MSG_SEND_PASSWORD, password); }
+	public void sendQuit(String message) { sendToNet(MSG_SEND_QUIT, message); }
+	public void sendRoomlistRequest() { sendToNet(MSG_SEND_ROOMLIST_REQUEST); }
+	public void sendPlayerInfoQuery(String name) { sendToNet(MSG_SEND_PLAYER_INFO_REQUEST, name); }
+	public void sendChat(String s) { sendToNet(MSG_SEND_CHAT, s); }
+	public void sendTeamChat(String s) { sendToNet(MSG_SEND_TEAMCHAT, s); }
+	public void sendFollowPlayer(String nick) { sendToNet(MSG_SEND_FOLLOW_PLAYER, nick); }
+	public void sendJoinRoom(String name) { sendToNet(MSG_SEND_JOIN_ROOM, name); }
+	public void sendCreateRoom(String name) { sendToNet(MSG_SEND_CREATE_ROOM, name); }
+	public void sendLeaveRoom(String message) { sendToNet(MSG_SEND_LEAVE_ROOM, message); }
+	public void sendKick(String player) { sendToNet(MSG_SEND_KICK, player); }
+	public void sendEngineMessage(byte[] engineMessage) { sendToNet(MSG_SEND_ENGINE_MESSAGE, engineMessage); }
+	public void sendRoundFinished(boolean withoutError) { sendToNet(MSG_SEND_ROUND_FINISHED, Boolean.valueOf(withoutError)); }
+	public void sendToggleReady() { sendToNet(MSG_SEND_TOGGLE_READY); }
+	public void sendStartGame() { sendToNet(MSG_SEND_START_GAME); }
+	
+	public void disconnect() { sendToNet(MSG_DISCONNECT, "User Quit"); }
+	
+	private static Netplay instance;
+	
+	/**
+	 * Retrieve the single app-wide instance of the netplay interface, creating it if it
+	 * does not exist yet.
+	 * 
+	 * @param applicationContext
+	 * @return
+	 */
+	public static Netplay getAppInstance(Context applicationContext) {
+		if(instance == null) {
+			// We will need some default values for rooms, best load them here
+			Scheme defaultScheme = null;
+			Weaponset defaultWeaponset = null;
+			try {
+				List<Scheme> schemes = Schemes.loadBuiltinSchemes(applicationContext);
+				for(Scheme scheme : schemes) {
+					if(scheme.name.equals(GameConfig.DEFAULT_SCHEME)) {
+						defaultScheme = scheme;
+					}
+				}
+				List<Weaponset> weaponsets = Weaponsets.loadBuiltinWeaponsets(applicationContext);
+				for(Weaponset weaponset : weaponsets) {
+					if(weaponset.name.equals(GameConfig.DEFAULT_WEAPONSET)) {
+						defaultWeaponset = weaponset;
+					}
+				}
+			} catch(IOException e) {
+				throw new RuntimeException(e);
+			}
+			
+			if(defaultScheme==null || defaultWeaponset==null) {
+				throw new RuntimeException("Unable to load default scheme or weaponset");
+			}
+			
+			instance = new Netplay(applicationContext, defaultScheme, defaultWeaponset);
+		}
+		return instance;
+	}
+
+	public State getState() {
+		return state;
+	}
+	
+	private void changeState(State newState) {
+		if(newState != state) {
+			state = newState;
+			broadcastManager.sendBroadcastSync(new Intent(ACTION_STATE_CHANGED));
+		}
+	}
+	
+	public boolean isChief() {
+		if(netRoomState != null) {
+			return netRoomState.getChiefStatus();
+		} else {
+			return false;
+		}
+	}
+	
+	public String getPlayerName() {
+		return playerName;
+	}
+	
+	boolean sendToNet(ToNetMsgType what) {
+		return sendToNet(what, 0, null);
+	}
+	
+	boolean sendToNet(ToNetMsgType what, Object obj) {
+		return sendToNet(what, 0, obj);
+	}
+	
+	boolean sendToNet(ToNetMsgType what, int arg1, Object obj) {
+		if(connection != null) {
+			Handler handler = connection.toNetHandler;
+			return handler.sendMessage(handler.obtainMessage(what.ordinal(), arg1, 0, obj));
+		} else {
+			return false;
+		}
+	}
+	
+	private MessageLog getCurrentLog() {
+		if(state == State.ROOM) {
+			return roomChatlog;
+		} else {
+			return lobbyChatlog;
+		}
+	}
+	
+	public static enum FromNetMsgType {
+		MSG_LOBBY_JOIN,
+		MSG_LOBBY_LEAVE,
+		MSG_ROOM_JOIN,
+		MSG_ROOM_LEAVE,
+		MSG_CHAT,
+		MSG_MESSAGE,
+		MSG_ROOM_ADD,
+		MSG_ROOM_UPDATE,
+		MSG_ROOM_DELETE,
+		MSG_ROOMLIST,
+		MSG_CONNECTED,
+		MSG_DISCONNECTED,
+		MSG_PASSWORD_REQUEST,
+		MSG_ENTER_ROOM_FROM_LOBBY,
+		MSG_LEAVE_ROOM,
+		MSG_READYSTATE,
+		MSG_TEAM_ADDED,
+		MSG_TEAM_DELETED,
+		MSG_TEAM_ACCEPTED,
+		MSG_TEAM_COLOR_CHANGED,
+		MSG_HOG_COUNT_CHANGED,
+		MSG_ENGINE_MESSAGE,
+		MSG_RUN_GAME,
+		MSG_SCHEME_CHANGED,
+		MSG_MAP_CHANGED,
+		MSG_ROOM_CHIEF_STATUS_CHANGED,
+		MSG_SCRIPT_CHANGED,
+		MSG_WEAPONSET_CHANGED;
+		
+		static final List<FromNetMsgType> values = Collections.unmodifiableList(Arrays.asList(FromNetMsgType.values()));
+	}
+	
+	/**
+	 * Processes messages from the networking system. Always runs on the main thread.
+	 */
+	@SuppressLint("HandlerLeak")
+	final class FromNetHandler extends Handler {
+		public FromNetHandler() {
+			super(Looper.getMainLooper());
+		}
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		public void handleMessage(Message msg) {
+			switch(FromNetMsgType.values.get(msg.what)) {
+			case MSG_LOBBY_JOIN: {
+				String name = (String)msg.obj;
+				lobbyPlayerlist.put(name, new Player(name, false, false));
+				lobbyChatlog.appendPlayerJoin(name);
+				break;
+			}
+			case MSG_LOBBY_LEAVE: {
+				Pair<String, String> args = (Pair<String, String>)msg.obj;
+				lobbyPlayerlist.remove(args.first);
+				lobbyChatlog.appendPlayerLeave(args.first, args.second);
+				break;
+			}
+			case MSG_ROOM_JOIN: {
+				String name = (String)msg.obj;
+				Player p = lobbyPlayerlist.get(name);
+				if(p==null) {
+					Log.w("Netplay", "Unknown player joined room: "+name);
+					p = new Player(name, false, false);
+				}
+				roomPlayerlist.put(name, new PlayerInRoom(p, false));
+				roomChatlog.appendPlayerJoin(name);
+				break;
+			}
+			case MSG_ROOM_LEAVE: {
+				Pair<String, String> args = (Pair<String, String>)msg.obj;
+				roomPlayerlist.remove(args.first);
+				roomChatlog.appendPlayerLeave(args.first, args.second);
+				break;
+			}
+			case MSG_CHAT: {
+				Pair<String, String> args = (Pair<String, String>)msg.obj;
+				getCurrentLog().appendChat(args.first, args.second);
+				for(GameMessageListener listener : gameMessageListeners) {
+					listener.onChatMessage(args.first, args.second);
+				}
+				break;
+			}
+			case MSG_MESSAGE: {
+				getCurrentLog().appendMessage(msg.arg1, (String)msg.obj);
+				for(GameMessageListener listener : gameMessageListeners) {
+					listener.onMessage(1, (String)msg.obj);
+				}
+				break;
+			}
+			case MSG_ROOM_ADD: {
+				Room room = (Room)msg.obj;
+				roomList.addRoomWithNewId(room);
+				break;
+			}
+			case MSG_ROOM_UPDATE: {
+				Pair<String, Room> args = (Pair<String, Room>)msg.obj;
+				roomList.updateRoom(args.first, args.second);
+				break;
+			}
+			case MSG_ROOM_DELETE: {
+				roomList.remove((String)msg.obj);
+				break;
+			}
+			case MSG_ROOMLIST: {
+				Room[] rooms = (Room[])msg.obj;
+				roomList.updateList(rooms);
+				break;
+			}
+			case MSG_CONNECTED: {
+				playerName = (String)msg.obj;
+				changeState(State.LOBBY);
+				broadcastManager.sendBroadcast(new Intent(ACTION_CONNECTED));
+				break;
+			}
+			case MSG_DISCONNECTED: {
+				Pair<Boolean, String> args = (Pair<Boolean, String>)msg.obj;
+				for(GameMessageListener listener : gameMessageListeners) {
+					listener.onNetDisconnected();
+				}
+				changeState(State.NOT_CONNECTED);
+				connection = null;
+				Intent intent = new Intent(ACTION_DISCONNECTED);
+				intent.putExtra(EXTRA_HAS_ERROR, args.first);
+				intent.putExtra(EXTRA_MESSAGE, args.second);
+				broadcastManager.sendBroadcastSync(intent);
+				break;
+			}
+			case MSG_PASSWORD_REQUEST: {
+				Intent intent = new Intent(ACTION_PASSWORD_REQUESTED);
+				intent.putExtra(EXTRA_PLAYERNAME, (String)msg.obj);
+				broadcastManager.sendBroadcast(intent);
+				break;
+			}
+			case MSG_ENTER_ROOM_FROM_LOBBY: {
+				initRoomState((Boolean)msg.obj);
+				changeState(State.ROOM);
+				Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
+				broadcastManager.sendBroadcastSync(intent);
+				break;
+			}
+			case MSG_LEAVE_ROOM: {
+				changeState(State.LOBBY);
+				Intent intent = new Intent(ACTION_LEFT_ROOM);
+				intent.putExtra(EXTRA_MESSAGE, (String)msg.obj);
+				intent.putExtra(EXTRA_REASON, msg.arg1);
+				broadcastManager.sendBroadcastSync(intent);
+				break;
+			}
+			case MSG_READYSTATE: {
+				Pair<String, Boolean> args = (Pair<String, Boolean>)msg.obj;
+				String name = args.first;
+				Boolean newReadyState = args.second;
+				PlayerInRoom oldEntry = roomPlayerlist.get(name);
+				if(oldEntry==null) {
+					Log.e("Netplay", "Setting readystate for unknown player "+name);
+				} else {
+					roomPlayerlist.put(name, new PlayerInRoom(oldEntry.player, newReadyState));
+				}
+				break;
+			}
+			case MSG_TEAM_ADDED: {
+				TeamInGame newTeam = (TeamInGame)msg.obj;
+				if(isChief()) {
+					int freeColor = TeamInGame.getUnusedOrRandomColorIndex(netRoomState.getTeams().values());
+					sendToNet(MSG_SEND_TEAM_HOG_COUNT, newTeam.ingameAttribs.hogCount, newTeam.team.name);
+					sendToNet(MSG_SEND_TEAM_COLOR_INDEX, freeColor, newTeam.team.name);
+					newTeam = newTeam.withAttribs(newTeam.ingameAttribs.withColorIndex(freeColor));
+				}
+				netRoomState.putTeam(newTeam);
+				break;
+			}
+			case MSG_TEAM_DELETED: {
+				netRoomState.removeTeam((String)msg.obj);
+				break;
+			}
+			case MSG_TEAM_ACCEPTED: {
+				TeamInGame requestedTeam = netRoomState.requestedTeams.remove(msg.obj);
+				if(requestedTeam!=null) {
+					netRoomState.putTeam(requestedTeam);
+					if(isChief()) {
+						// Not strictly necessary, but QtFrontend does it...
+						sendToNet(MSG_SEND_TEAM_HOG_COUNT, requestedTeam.ingameAttribs.hogCount, requestedTeam.team.name);
+					}
+				} else {
+					Log.e("Netplay", "Got accepted message for team that was never requested.");
+				}
+				break;
+			}
+			case MSG_TEAM_COLOR_CHANGED: {
+				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
+				if(oldEntry != null) {
+					/*
+					 * If we are chief, we ignore colors from the outside. They only come from the server
+					 * when someone adds a team then, and we override that choice anyway.
+					 * Worse, that color message arrives *after* we have overridden the color, so it would
+					 * re-override it right back.
+					 */
+					if(!isChief()) {
+						TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withColorIndex(msg.arg1);
+						netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
+					}
+				} else {
+					Log.e("Netplay", "Color update for unknown team "+msg.obj);
+				}
+				break;
+			}
+			case MSG_HOG_COUNT_CHANGED: {
+				TeamInGame oldEntry = netRoomState.getTeams().get((String)msg.obj);
+				if(oldEntry != null) {
+					TeamIngameAttributes newAttribs = oldEntry.ingameAttribs.withHogCount(msg.arg1);
+					netRoomState.putTeam(oldEntry.withAttribs(newAttribs));
+				} else {
+					Log.e("Netplay", "Hog count update for unknown team "+msg.obj);
+				}
+				break;
+			}
+			case MSG_ENGINE_MESSAGE: {
+				byte[] em = (byte[])msg.obj;
+				for(GameMessageListener listener : gameMessageListeners) {
+					listener.onEngineMessage(em);
+				}
+				break;
+			}
+			case MSG_RUN_GAME: {
+				GameConfig config = (GameConfig)msg.obj;
+				for(RunGameListener listener : runGameListeners) {
+					listener.runGame(config);
+				}
+				break;
+			}
+			case MSG_MAP_CHANGED: {
+				netRoomState.setMapRecipe((MapRecipe)msg.obj);
+				break;
+			}
+			case MSG_ROOM_CHIEF_STATUS_CHANGED: {
+				netRoomState.setChief((Boolean)msg.obj);
+				break;
+			}
+			case MSG_SCHEME_CHANGED: {
+				netRoomState.setScheme((Scheme)msg.obj);
+				break;
+			}
+			case MSG_SCRIPT_CHANGED: {
+				netRoomState.setGameStyle((String)msg.obj);
+				break;
+			}
+			case MSG_WEAPONSET_CHANGED: {
+				netRoomState.setWeaponset((Weaponset)msg.obj);
+				break;
+			}
+			default: {
+				Log.e("FromNetHandler", "Unknown message type: "+msg.what);
+				break;
+			}
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Roomlist.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.hedgewars.hedgeroid.Datastructures.Room;
+import org.hedgewars.hedgeroid.Datastructures.RoomWithId;
+import org.hedgewars.hedgeroid.util.ObservableTreeMap;
+
+public class Roomlist extends ObservableTreeMap<String, RoomWithId> {
+	private long nextId = 1;
+	
+	public void updateList(Room[] newRooms) {
+		Map<String, RoomWithId> newMap = new TreeMap<String, RoomWithId>();
+		for(Room room : newRooms) {
+			RoomWithId oldEntry = get(room.name);
+			if(oldEntry == null) {
+				newMap.put(room.name, new RoomWithId(room, nextId++));
+			} else {
+				newMap.put(room.name, new RoomWithId(room, oldEntry.id));
+			}
+		}
+		replaceContent(newMap);
+	}
+	
+	public void addRoomWithNewId(Room room) {
+		put(room.name, new RoomWithId(room, nextId++));
+	}
+	
+	public void updateRoom(String name, Room room) {
+		RoomWithId oldEntry = get(name);
+		if(oldEntry == null) {
+			addRoomWithNewId(room);
+		} else {
+			remove(name);
+			put(room.name, new RoomWithId(room, oldEntry.id));
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RunGameListener.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,26 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+
+public interface RunGameListener {
+	void runGame(GameConfig config);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,584 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.netplay;
+
+import static org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType.*;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
+import org.hedgewars.hedgeroid.Datastructures.Scheme;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.BoolCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.ByteArrayPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.BytesCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.IntStrCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.MapIntCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.NetconnPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomArrayPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomListCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemeCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemePtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.VoidCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetPtr;
+import org.hedgewars.hedgeroid.frontlib.NativeSizeT;
+import org.hedgewars.hedgeroid.netplay.Netplay.FromNetHandler;
+import org.hedgewars.hedgeroid.netplay.Netplay.FromNetMsgType;
+import org.hedgewars.hedgeroid.util.FileUtils;
+import org.hedgewars.hedgeroid.util.TickHandler;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.Pair;
+
+import com.sun.jna.Pointer;
+
+/**
+ * This class handles the actual communication with the networking library, running on a separate thread.
+ * 
+ * In order to process net messages, this class regularly runs a tick() function on the frontlib. This
+ * usually happens several times per second, but it can be slowed down a lot if no fast reaction to
+ * events is required (e.g. to conserve battery if the application is in the background).
+ */
+class ThreadedNetConnection {
+	private static final long TICK_INTERVAL_FAST = 100;
+	private static final long TICK_INTERVAL_SLOW = 5000;
+	private static final Frontlib FLIB = Flib.INSTANCE;
+	
+	public final ToNetHandler toNetHandler;
+	
+	private final Context appContext;
+	private final FromNetHandler fromNetHandler;
+	private final TickHandler tickHandler;
+
+	/**
+	 * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting,
+	 * in the same message (the looper is shut down on disconnect, so there will be no messages after that).
+	 */
+	private NetconnPtr conn;
+	private String playerName;
+	
+	private ThreadedNetConnection(Context appContext, FromNetHandler fromNetHandler) {
+		this.appContext = appContext;
+		this.fromNetHandler = fromNetHandler;
+		
+		HandlerThread thread = new HandlerThread("NetThread");
+		thread.start();
+		toNetHandler = new ToNetHandler(thread.getLooper());
+		tickHandler = new TickHandler(thread.getLooper(), TICK_INTERVAL_FAST, tickCb);
+	}
+	
+	private void connect(final String name, final String host, final int port) {
+		toNetHandler.post(new Runnable() {
+			public void run() {
+				playerName = name == null ? "Player" : name;
+				File dataPath;
+				try {
+					dataPath = FileUtils.getDataPathFile(appContext);
+				} catch (FileNotFoundException e) {
+					shutdown(true, appContext.getString(R.string.sdcard_not_mounted));
+					return;
+				}
+				conn = FLIB.flib_netconn_create(playerName, dataPath.getAbsolutePath()+"/", host, port);
+				if(conn == null) {
+					shutdown(true, appContext.getString(R.string.error_connection_failed));
+					return;
+				}
+
+				// FLIB.flib_netconn_onAdminAccess(conn, adminAccessCb, null)
+				FLIB.flib_netconn_onSchemeChanged(conn, cfgSchemeCb, null);
+				FLIB.flib_netconn_onChat(conn, chatCb, null);
+				FLIB.flib_netconn_onConnected(conn, connectedCb, null);
+				FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
+				FLIB.flib_netconn_onEngineMessage(conn, engineMessageCb, null);
+				FLIB.flib_netconn_onEnterRoom(conn, enterRoomCb, null);
+				FLIB.flib_netconn_onHogCountChanged(conn, hogCountChangedCb, null);
+				FLIB.flib_netconn_onLeaveRoom(conn, leaveRoomCb, null);
+				FLIB.flib_netconn_onLobbyJoin(conn, lobbyJoinCb, null);
+				FLIB.flib_netconn_onLobbyLeave(conn, lobbyLeaveCb, null);
+				FLIB.flib_netconn_onMapChanged(conn, mapChangedCb, null);
+				FLIB.flib_netconn_onMessage(conn, messageCb, null);
+				FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null);
+				FLIB.flib_netconn_onReadyState(conn, readyStateCb, null);
+				FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null);
+				FLIB.flib_netconn_onRoomChiefStatus(conn, roomChiefStatusCb, null);
+				FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null);
+				FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null);
+				FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null);
+				FLIB.flib_netconn_onRoomlist(conn, roomlistCb, null);
+				FLIB.flib_netconn_onRoomUpdate(conn, roomUpdateCb, null);
+				FLIB.flib_netconn_onRunGame(conn, runGameCb, null);
+				FLIB.flib_netconn_onScriptChanged(conn, scriptChangedCb, null);
+				// FLIB.flib_netconn_onServerVar(conn, serverVarCb, null);
+				FLIB.flib_netconn_onTeamAccepted(conn, teamAcceptedCb, null);
+				FLIB.flib_netconn_onTeamAdd(conn, teamAddedCb, null);
+				FLIB.flib_netconn_onTeamColorChanged(conn, teamColorChangedCb, null);
+				FLIB.flib_netconn_onTeamDelete(conn, teamDeletedCb, null);
+				FLIB.flib_netconn_onWeaponsetChanged(conn, weaponsetChangedCb, null);
+				
+				tickHandler.start();
+			}
+		});
+	}
+	
+	public static ThreadedNetConnection startConnection(Context appContext, FromNetHandler fromNetHandler, String playerName, String host, int port) {
+		ThreadedNetConnection result = new ThreadedNetConnection(appContext, fromNetHandler);
+		result.connect(playerName, host, port);
+		return result;
+	}
+	
+	public void setFastTickRate(boolean fastTickRate) {
+		tickHandler.setInterval(fastTickRate ? TICK_INTERVAL_FAST : TICK_INTERVAL_SLOW);
+	}
+	
+	private final Runnable tickCb = new Runnable() {
+		public void run() {
+			FLIB.flib_netconn_tick(conn);
+		}
+	};
+	
+	private final SchemeCallback cfgSchemeCb = new SchemeCallback() {
+		public void callback(Pointer context, SchemePtr schemePtr) {
+			sendFromNet(MSG_SCHEME_CHANGED, schemePtr.deref());
+		}
+	};
+	
+	private final MapIntCallback mapChangedCb = new MapIntCallback() {
+		public void callback(Pointer context, MapRecipePtr mapPtr, int updateType) {
+			sendFromNet(MSG_MAP_CHANGED, updateType, mapPtr.deref());
+		}
+	};
+	
+	private final BoolCallback roomChiefStatusCb = new BoolCallback() {
+		public void callback(Pointer context, boolean chief) {
+			sendFromNet(MSG_ROOM_CHIEF_STATUS_CHANGED, chief);
+		}
+	};
+	
+	private final StrCallback scriptChangedCb = new StrCallback() {
+		public void callback(Pointer context, String script) {
+			sendFromNet(MSG_SCRIPT_CHANGED, script);
+		}
+	};
+	
+	private final WeaponsetCallback weaponsetChangedCb = new WeaponsetCallback() {
+		public void callback(Pointer context, WeaponsetPtr weaponsetPtr) {
+			sendFromNet(MSG_WEAPONSET_CHANGED, weaponsetPtr.deref());				
+		}
+	};
+	
+	private final StrCallback lobbyJoinCb = new StrCallback() {
+		public void callback(Pointer context, String name) {
+			sendFromNet(MSG_LOBBY_JOIN, name);
+		}
+	};
+	
+	private final StrStrCallback lobbyLeaveCb = new StrStrCallback() {
+		public void callback(Pointer context, String name, String msg) {
+			sendFromNet(MSG_LOBBY_LEAVE, Pair.create(name, msg));
+		}
+	};
+	
+	private final StrCallback roomJoinCb = new StrCallback() {
+		public void callback(Pointer context, String name) {
+			sendFromNet(MSG_ROOM_JOIN, name);
+		}
+	};
+	private final StrStrCallback roomLeaveCb = new StrStrCallback() {
+		public void callback(Pointer context, String name, String message) {
+			sendFromNet(MSG_ROOM_LEAVE, Pair.create(name, message));
+		}
+	};
+	private final StrStrCallback chatCb = new StrStrCallback() {
+		public void callback(Pointer context, String name, String msg) {
+			sendFromNet(MSG_CHAT, Pair.create(name, msg));
+		}
+	};
+	
+	private final IntStrCallback messageCb = new IntStrCallback() {
+		public void callback(Pointer context, int type, String msg) {
+			sendFromNet(MSG_MESSAGE, type, msg);
+		}
+	};
+	
+	private final RoomCallback roomAddCb = new RoomCallback() {
+		public void callback(Pointer context, RoomPtr roomPtr) {
+			sendFromNet(MSG_ROOM_ADD, roomPtr.deref());
+		}
+	};
+	
+	private final StrRoomCallback roomUpdateCb = new StrRoomCallback() {
+		public void callback(Pointer context, String name, RoomPtr roomPtr) {
+			sendFromNet(MSG_ROOM_UPDATE, Pair.create(name, roomPtr.deref()));
+		}
+	};
+	
+	private final StrCallback roomDeleteCb = new StrCallback() {
+		public void callback(Pointer context, final String name) {
+			sendFromNet(MSG_ROOM_DELETE, name);
+		}
+	};
+	
+	private final RoomListCallback roomlistCb = new RoomListCallback() {
+		public void callback(Pointer context, RoomArrayPtr arg1, int count) {
+			sendFromNet(MSG_ROOMLIST, arg1.getRooms(count));
+		}
+	};
+	
+	private final VoidCallback connectedCb = new VoidCallback() {
+		public void callback(Pointer context) {
+			FLIB.flib_netconn_send_request_roomlist(conn);
+			playerName = FLIB.flib_netconn_get_playername(conn);
+			sendFromNet(MSG_CONNECTED, playerName);
+		}
+	};
+	
+	private final StrCallback passwordRequestCb = new StrCallback() {
+		public void callback(Pointer context, String nickname) {
+			sendFromNet(MSG_PASSWORD_REQUEST, playerName);
+		}
+	};
+	
+	private final BoolCallback enterRoomCb = new BoolCallback() {
+		public void callback(Pointer context, boolean isChief) {
+			sendFromNet(MSG_ENTER_ROOM_FROM_LOBBY, isChief);
+		}
+	};
+	
+	private final IntStrCallback leaveRoomCb = new IntStrCallback() {
+		public void callback(Pointer context, int reason, String message) {
+			sendFromNet(MSG_LEAVE_ROOM, reason, message);
+		}
+	};
+	
+	private final StrBoolCallback readyStateCb = new StrBoolCallback() {
+		public void callback(Pointer context, String player, boolean ready) {
+			sendFromNet(MSG_READYSTATE, Pair.create(player, ready));
+		}
+	};
+	
+	private final TeamCallback teamAddedCb = new TeamCallback() {
+		public void callback(Pointer context, TeamPtr team) {
+			sendFromNet(MSG_TEAM_ADDED, team.deref());
+		}
+	};
+	
+	private final StrCallback teamDeletedCb = new StrCallback() {
+		public void callback(Pointer context, String teamName) {
+			sendFromNet(MSG_TEAM_DELETED, teamName);
+		}
+	};
+	
+	private final StrCallback teamAcceptedCb = new StrCallback() {
+		public void callback(Pointer context, String teamName) {
+			sendFromNet(MSG_TEAM_ACCEPTED, teamName);
+		}
+	};
+	
+	private final StrIntCallback teamColorChangedCb = new StrIntCallback() {
+		public void callback(Pointer context, String teamName, int colorIndex) {
+			sendFromNet(MSG_TEAM_COLOR_CHANGED, colorIndex, teamName);
+		}
+	};
+	
+	private final StrIntCallback hogCountChangedCb = new StrIntCallback() {
+		public void callback(Pointer context, String teamName, int hogCount) {
+			sendFromNet(MSG_HOG_COUNT_CHANGED, hogCount, teamName);
+		}
+	};
+	
+	private final BytesCallback engineMessageCb = new BytesCallback() {
+		public void callback(Pointer context, ByteArrayPtr buffer, NativeSizeT size) {
+			sendFromNet(MSG_ENGINE_MESSAGE, buffer.deref(size.intValue()));
+		}
+	};
+	
+	private final VoidCallback runGameCb = new VoidCallback() {
+		public void callback(Pointer context) {
+			GameSetupPtr configPtr = FLIB.flib_netconn_create_gamesetup(conn);
+			sendFromNet(MSG_RUN_GAME, configPtr.deref());
+			FLIB.flib_gamesetup_destroy(configPtr);
+		}
+	};
+	
+	private void shutdown(boolean error, String message) {
+		if(conn != null) {
+			FLIB.flib_netconn_destroy(conn);
+			conn = null;
+		}
+		tickHandler.stop();
+		toNetHandler.getLooper().quit();
+		sendFromNet(MSG_DISCONNECTED, Pair.create(error, message));
+	}
+	
+	private final IntStrCallback disconnectCb = new IntStrCallback() {
+		public void callback(Pointer context, int reason, String message) {
+			Boolean error = reason != Frontlib.NETCONN_DISCONNECT_NORMAL;
+			String messageForUser = createDisconnectUserMessage(appContext.getResources(), reason, message);
+			shutdown(error, messageForUser);
+		}
+	};
+	
+	private static String createDisconnectUserMessage(Resources res, int reason, String message) {
+		switch(reason) {
+		case Frontlib.NETCONN_DISCONNECT_AUTH_FAILED:
+			return res.getString(R.string.error_auth_failed);
+		case Frontlib.NETCONN_DISCONNECT_CONNLOST:
+			return res.getString(R.string.error_connection_lost);
+		case Frontlib.NETCONN_DISCONNECT_INTERNAL_ERROR:
+			return res.getString(R.string.error_unexpected, message);
+		case Frontlib.NETCONN_DISCONNECT_SERVER_TOO_OLD:
+			return res.getString(R.string.error_server_too_old);
+		default:
+			return message;
+		}
+	}
+	
+	private boolean sendFromNet(FromNetMsgType what, Object obj) {
+		return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), obj));
+	}
+	
+	private boolean sendFromNet(FromNetMsgType what, int arg1, Object obj) {
+		return fromNetHandler.sendMessage(fromNetHandler.obtainMessage(what.ordinal(), arg1, 0, obj));
+	}
+	
+	static enum ToNetMsgType {
+		MSG_SEND_NICK,
+		MSG_SEND_PASSWORD,
+		MSG_SEND_QUIT,
+		MSG_SEND_ROOMLIST_REQUEST,
+		MSG_SEND_PLAYER_INFO_REQUEST,
+		MSG_SEND_CHAT,
+		MSG_SEND_TEAMCHAT,
+		MSG_SEND_FOLLOW_PLAYER,
+		MSG_SEND_JOIN_ROOM,
+		MSG_SEND_CREATE_ROOM,
+		MSG_SEND_LEAVE_ROOM,
+		MSG_SEND_KICK,
+		MSG_SEND_ADD_TEAM,
+		MSG_SEND_REMOVE_TEAM,
+		MSG_DISCONNECT,
+		MSG_SEND_TEAM_COLOR_INDEX,
+		MSG_SEND_TEAM_HOG_COUNT,
+		MSG_SEND_ENGINE_MESSAGE,
+		MSG_SEND_ROUND_FINISHED,
+		MSG_SEND_TOGGLE_READY,
+		MSG_SEND_START_GAME,
+		MSG_SEND_WEAPONSET,
+		MSG_SEND_MAP,
+		MSG_SEND_MAP_NAME,
+		MSG_SEND_MAP_GENERATOR,
+		MSG_SEND_MAP_TEMPLATE,
+		MSG_SEND_MAZE_SIZE,
+		MSG_SEND_MAP_SEED,
+		MSG_SEND_MAP_THEME,
+		MSG_SEND_MAP_DRAWDATA,
+		MSG_SEND_GAMESTYLE,
+		MSG_SEND_SCHEME;
+		
+		static final List<ThreadedNetConnection.ToNetMsgType> values = Collections.unmodifiableList(Arrays.asList(ToNetMsgType.values()));
+	}
+	
+	/**
+	 * Processes messages to the networking system. Runs on a non-main thread.
+	 */
+	@SuppressLint("HandlerLeak")
+	public final class ToNetHandler extends Handler {
+		
+		public ToNetHandler(Looper looper) {
+			super(looper);
+		}
+		
+		@Override
+		public void handleMessage(Message msg) {
+			switch(ToNetMsgType.values.get(msg.what)) {
+			case MSG_SEND_NICK: {
+				FLIB.flib_netconn_send_nick(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_PASSWORD: {
+				FLIB.flib_netconn_send_password(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_QUIT: {
+				FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_ROOMLIST_REQUEST: {
+				FLIB.flib_netconn_send_request_roomlist(conn);
+				break;
+			}
+			case MSG_SEND_PLAYER_INFO_REQUEST: {
+				FLIB.flib_netconn_send_playerInfo(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_CHAT: {
+				if(FLIB.flib_netconn_send_chat(conn, (String)msg.obj) == 0) {
+					sendFromNet(MSG_CHAT, Pair.create(playerName, (String)msg.obj));
+				}
+				break;
+			}
+			case MSG_SEND_TEAMCHAT: {
+				FLIB.flib_netconn_send_teamchat(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_FOLLOW_PLAYER: {
+				FLIB.flib_netconn_send_playerFollow(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_JOIN_ROOM: {
+				FLIB.flib_netconn_send_joinRoom(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_CREATE_ROOM: {
+				FLIB.flib_netconn_send_createRoom(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_LEAVE_ROOM: {
+				if(FLIB.flib_netconn_send_leaveRoom(conn, (String)msg.obj) == 0) {
+					sendFromNet(MSG_LEAVE_ROOM, -1, "");
+				}
+				break;
+			}
+			case MSG_SEND_KICK: {
+				FLIB.flib_netconn_send_kick(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_ADD_TEAM: {
+				FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((TeamInGame)msg.obj));
+				break;
+			}
+			case MSG_SEND_REMOVE_TEAM: {
+				if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) {
+					sendFromNet(MSG_TEAM_DELETED, msg.obj);
+				}
+				break;
+			}
+			case MSG_DISCONNECT: {
+				FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
+				shutdown(false, "User quit");
+				break;
+			}
+			case MSG_SEND_TEAM_COLOR_INDEX: {
+				FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1);
+				break;
+			}
+			case MSG_SEND_TEAM_HOG_COUNT: {
+				FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1);
+				break;
+			}
+			case MSG_SEND_ENGINE_MESSAGE: {
+				byte[] message = (byte[])msg.obj;
+				ByteArrayPtr ptr = ByteArrayPtr.createJavaOwned(message);
+				FLIB.flib_netconn_send_engineMessage(conn, ptr, NativeSizeT.valueOf(message.length));
+				break;
+			}
+			case MSG_SEND_ROUND_FINISHED: {
+				FLIB.flib_netconn_send_roundfinished(conn, (Boolean)msg.obj);
+				break;
+			}
+			case MSG_SEND_TOGGLE_READY: {
+				FLIB.flib_netconn_send_toggleReady(conn);
+				break;
+			}
+			case MSG_SEND_START_GAME: {
+				FLIB.flib_netconn_send_startGame(conn);
+				break;
+			}
+			case MSG_SEND_WEAPONSET: {
+				FLIB.flib_netconn_send_weaponset(conn, WeaponsetPtr.createJavaOwned((Weaponset)msg.obj));
+				break;
+			}
+			case MSG_SEND_MAP: {
+				FLIB.flib_netconn_send_map(conn, MapRecipePtr.createJavaOwned((MapRecipe)msg.obj));
+				break;
+			}
+			case MSG_SEND_MAP_NAME: {
+				FLIB.flib_netconn_send_mapName(conn, (String)msg.obj);
+				break;
+			}
+			case MSG_SEND_MAP_GENERATOR: {
+				FLIB.flib_netconn_send_mapGen(conn, msg.arg1);
+				break;
+			}
+			case MSG_SEND_MAP_TEMPLATE: {
+				FLIB.flib_netconn_send_mapTemplate(conn, msg.arg1);
+				break;
+			}
+			case MSG_SEND_MAZE_SIZE: {
+				FLIB.flib_netconn_send_mapMazeSize(conn, msg.arg1);
+				break;
+			}
+			case MSG_SEND_MAP_SEED: {
+				FLIB.flib_netconn_send_mapSeed(conn, (String) msg.obj);
+				break;
+			}
+			case MSG_SEND_MAP_THEME: {
+				FLIB.flib_netconn_send_mapTheme(conn, (String) msg.obj);
+				break;
+			}
+			case MSG_SEND_MAP_DRAWDATA: {
+				byte[] message = (byte[])msg.obj;
+				ByteArrayPtr ptr = ByteArrayPtr.createJavaOwned(message);
+				FLIB.flib_netconn_send_mapDrawdata(conn, ptr, NativeSizeT.valueOf(message.length));
+				break;
+			}
+			case MSG_SEND_GAMESTYLE: {
+				FLIB.flib_netconn_send_script(conn, (String) msg.obj);
+				break;
+			}
+			case MSG_SEND_SCHEME: {
+				FLIB.flib_netconn_send_scheme(conn, SchemePtr.createJavaOwned((Scheme) msg.obj));
+				break;
+			}
+			default: {
+				Log.e("ToNetHandler", "Unknown message type: "+msg.what);
+				break;
+			}
+			}
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/CalmDownHandler.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,62 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * This class allows you to define a runnable that is called when there has been
+ * no activity for a set amount of time, where activity is determined by calls
+ * to the activity() method of the handler. It is used to update the map preview
+ * when there have been no updates to the relevant map information for a time,
+ * to prevent triggering several updates at once when different parts of the
+ * information change.
+ */
+public final class CalmDownHandler extends Handler {
+	int runningMessagesCounter = 0;
+	final Runnable inactivityRunnable;
+	final long inactivityMs;
+	boolean stopped;
+
+	public CalmDownHandler(Looper looper, Runnable runnable, long inactivityMs) {
+		super(looper);
+		this.inactivityRunnable = runnable;
+		this.inactivityMs = inactivityMs;
+	}
+	
+	public void activity() {
+		runningMessagesCounter++;
+		sendMessageDelayed(obtainMessage(), inactivityMs);
+	}
+	
+	@Override
+	public void handleMessage(Message msg) {
+		runningMessagesCounter--;
+		if(runningMessagesCounter==0 && !stopped) {
+			inactivityRunnable.run();
+		}
+	}
+	
+	public void stop() {
+		stopped = true;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/FileUtils.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,259 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+
+public class FileUtils {
+	private static final String ROOT_DIR = "Data";
+	private static final String TAG = FileUtils.class.getSimpleName();
+
+	/**
+	 * @return true if the data path is currently available. However, it can vanish at any time so
+	 * normally you should just try to use it and rely on the exceptions.
+	 */
+	public static boolean isDataPathAvailable() {
+		return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
+	}
+	
+	/**
+	 * get the path to which we should download all the data files
+	 * @param c context 
+	 * @return The directory
+	 * @throws FileNotFoundException if external storage is not available at the moment
+	 */
+	public static File getCachePath(Context c) throws FileNotFoundException {
+		File cachePath = null;
+		if(Build.VERSION.SDK_INT < 8){//8 == Build.VERSION_CODES.FROYO
+			cachePath = PreFroyoSDCardDir.getDownloadPath(c);
+		} else {
+			cachePath = FroyoSDCardDir.getDownloadPath(c);
+		}
+		if(cachePath==null) {
+			throw new FileNotFoundException("External storage is currently unavailable");
+		} else {
+			return cachePath;
+		}
+	}
+
+	public static File getDataPathFile(Context c, String...subpath) throws FileNotFoundException {
+		File file = new File(getCachePath(c), ROOT_DIR);
+		for(String pathcomponent : subpath) {
+			file = new File(file, pathcomponent);
+		}
+		return file;
+	}
+	
+	@TargetApi(8)
+	private static class FroyoSDCardDir{
+		public static File getDownloadPath(Context c){
+			return c.getExternalCacheDir();
+		}
+	}
+
+	private static class PreFroyoSDCardDir{
+		public static File getDownloadPath(Context c){
+			if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
+				File extStorageDir = Environment.getExternalStorageDirectory();
+				if(extStorageDir != null) {
+					return new File(extStorageDir, "Hedgewars");
+				}
+			}
+			return null;
+		}
+	}
+
+	/**
+	 * Return a File array with all the files from dirName
+	 * @param c
+	 * @param dirName
+	 * @return
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dirName" does not exist
+	 */
+	public static File[] getFilesFromRelativeDir(Context c, String dirName) throws FileNotFoundException {
+		File f = getDataPathFile(c, dirName);
+
+		if(f.isDirectory()) {
+			return f.listFiles();
+		} else {
+			throw new FileNotFoundException("Directory "+dirName+" does not exist.");
+		}
+	}
+
+	/**
+	 * Checks if this directory has a file with suffix suffix
+	 * @param f - directory
+	 * @return
+	 */
+	public static boolean hasFileWithSuffix(File f, String suffix){
+		if(f.isDirectory()){
+			for(String s : f.list()){
+				if(s.endsWith(suffix)) return true;
+			}
+			return false;
+		}else{
+			return false;
+		}
+	}
+
+	/**
+	 * Gives back all dirs which contain a file with suffix fileSuffix
+	 * @param c
+	 * @param path
+	 * @param fileSuffix
+	 * @return
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "path" does not exist
+	 */
+	public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix) throws FileNotFoundException{
+		File[] files = getFilesFromRelativeDir(c,path);
+		ArrayList<String> ret = new ArrayList<String>();
+
+		for(File f : files){
+			if(hasFileWithSuffix(f, fileSuffix)) ret.add(f.getName());
+		}
+		return ret;
+	}
+
+	/**
+	 * Get all files from directory dir which have the given suffix
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dir" does not exist
+	 */
+	public static List<String> getFileNamesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix) throws FileNotFoundException{
+		File[] files = FileUtils.getFilesFromRelativeDir(c, dir);
+		List<String> ret = new ArrayList<String>();
+		for(File file : files){
+			String s = file.getName();
+			if(s.endsWith(suffix)){
+				if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length()));
+				else ret.add(s);
+			}
+		}
+		return ret;
+	}
+
+	/**
+	 * Close a resource (possibly null), ignoring any IOException.
+	 */
+	public static void closeQuietly(Closeable c) {
+		if(c!=null) {
+			try {
+				c.close();
+			} catch(IOException e) {
+				Log.w(TAG, e);
+			}
+		}
+	}
+	
+	/**
+	 * Write all data from the input stream to the file, creating or overwriting it.
+	 * The input stream will be closed.
+	 * 
+	 * @throws IOException
+	 */
+	public static void writeStreamToFile(InputStream is, File file) throws IOException {
+		OutputStream os = null;
+		byte[] buffer = new byte[8192];
+		try {
+			os = new FileOutputStream(file);
+			int size;
+			while((size=is.read(buffer)) != -1) {
+				os.write(buffer, 0, size);
+			}
+			os.close(); // Important to close this non-quietly, in case of exceptions when flushing
+		} finally {
+			FileUtils.closeQuietly(is);
+			FileUtils.closeQuietly(os);
+		}
+	}
+	
+	/**
+	 * Moves resources pointed to by sourceResId (from @res/raw/) to the app's private data directory
+	 * @param c
+	 * @param sourceResId
+	 * @param directory
+	 */
+	public static void resRawToFilesDir(Context c, int sourceResId, int targetFilenames, String directory) throws IOException {
+		File targetDir = new File(c.getFilesDir(), directory);
+		targetDir.mkdirs();
+
+		//Get an array with the resource files ID
+		Resources resources = c.getResources();
+		TypedArray ta = resources.obtainTypedArray(sourceResId);
+		TypedArray filenames = resources.obtainTypedArray(targetFilenames);
+		for(int i = 0; i < ta.length(); i++){
+			int resId =  ta.getResourceId(i, 0);
+			String fileName = filenames.getString(i);
+			File f = new File(targetDir, fileName);
+			writeStreamToFile(resources.openRawResource(resId), f);
+		}
+	}
+
+	public static String readToString(InputStream is) throws IOException {
+		try {
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			byte[] buffer = new byte[8192];
+			int size;
+			while((size=is.read(buffer)) != -1) {
+				os.write(buffer, 0, size);
+			}
+			return new String(os.toByteArray());
+		} finally {
+			closeQuietly(is);
+		}
+	}
+	
+	private static final char[] badFilenameChars = new char[] { '/', '\\', ':', '*', '?', '\"', '<', '>', '|', '.', '\0' };
+	
+	/**
+	 * Modify the given String so that it can be used as part of a filename
+	 * without causing problems from illegal/special characters.
+	 * 
+	 * The result should be similar to the input, but isn't necessarily
+	 * reversible.
+	 */
+	public static String replaceBadChars(String name) {
+		if (name == null || name.trim().length()==0) {
+			return "_";
+		}
+		name = name.trim();
+		for (char badChar : badFilenameChars) {
+			name = name.replace(badChar, '_');
+		}
+		return name;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObjectUtils.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,32 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+public final class ObjectUtils {
+	public static boolean equal(Object o1, Object o2) {
+		if(o1==o2) {
+			return true;
+		} else if(o1==null || o2 == null) {
+			return false;
+		} else {
+			return o1.equals(o2);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMap.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,62 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import android.database.DataSetObservable;
+
+public class ObservableTreeMap<K,V> extends DataSetObservable {
+	private final Map<K, V> map = new TreeMap<K, V>();
+	
+	public void replaceContent(Map<? extends K, ? extends V> newMap) {
+		map.clear();
+		map.putAll(newMap);
+		notifyChanged();
+	}
+	
+	public void put(K key, V value) {
+		map.put(key, value);
+		notifyChanged();
+	}
+	
+	public V get(K key) {
+		return map.get(key);
+	}
+	
+	public void remove(K key) {
+		if(map.remove(key) != null) {
+			notifyChanged();
+		}
+	}
+	
+	public void clear() {
+		if(!map.isEmpty()) {
+			map.clear();
+			notifyChanged();
+		}
+	}
+	
+	public Map<K, V> getMap() {
+		return Collections.unmodifiableMap(map);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/ObservableTreeMapAdapter.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,94 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.database.DataSetObserver;
+import android.widget.BaseAdapter;
+
+public abstract class ObservableTreeMapAdapter<K,V> extends BaseAdapter {
+	private boolean sourceChanged = true;
+	private List<V> entries = new ArrayList<V>();
+	private ObservableTreeMap<K, V> source;
+	
+	private DataSetObserver observer = new DataSetObserver() {
+		@Override
+		public void onChanged() {
+			sourceChanged = true;
+			notifyDataSetChanged();
+		}
+		
+		@Override
+		public void onInvalidated() {
+			invalidate();
+		}
+	};
+	
+	abstract protected Comparator<V> getEntryOrder();
+	
+	protected List<V> getEntries() {
+		if(sourceChanged) {
+			entries.clear();
+			entries.addAll(source.getMap().values());
+			Collections.sort(entries, getEntryOrder());
+			sourceChanged = false;
+		}
+		return entries;
+	}
+	
+	public int getCount() {
+		return getEntries().size();
+	}
+
+	public V getItem(int position) {
+		return getEntries().get(position);
+	}
+	
+	public long getItemId(int position) {
+		return position;
+	}
+	
+	@Override
+	public boolean hasStableIds() {
+		return false;
+	}
+	
+	public void setSource(ObservableTreeMap<K,V> source) {
+		if(this.source != null) {
+			this.source.unregisterObserver(observer);
+		}
+		this.source = source;
+		this.source.registerObserver(observer);
+		sourceChanged = true;
+		notifyDataSetChanged();
+	}
+	
+	public void invalidate() {
+		if(source != null) {
+			source.unregisterObserver(observer);
+		}
+		source = null;
+		notifyDataSetInvalidated();
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TextInputDialog.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,148 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * A generic text input dialog with configurable text. The Activity must implement the callback
+ * interface TextInputDialogListener, which will be called by the dialog if it is submitted or cancelled.
+ */
+public class TextInputDialog extends DialogFragment {
+	private static final String BUNDLE_DIALOG_ID = "dialogId";
+	private static final String BUNDLE_TITLE_TEXT = "title";
+	private static final String BUNDLE_MESSAGE_TEXT = "message";
+	private static final String BUNDLE_HINT_TEXT = "hint";
+	
+	private int dialogId, titleText, messageText, hintText;
+	private TextInputDialogListener listener;
+	
+	public interface TextInputDialogListener {
+		void onTextInputDialogSubmitted(int dialogId, String text);
+		void onTextInputDialogCancelled(int dialogId);
+	}
+	
+	/**
+	 * The dialogId is only used for passing back to the callback on the activity, the
+	 * other parameters are text resource IDs. Pass 0 for any of them to not use this
+	 * text.
+	 */
+	public TextInputDialog(int dialogId, int titleText, int messageText, int hintText) {
+		this.dialogId = dialogId;
+		this.titleText = titleText;
+		this.messageText = messageText;
+		this.hintText = hintText;
+	}
+	
+	public TextInputDialog() {
+		// Only for reflection-based instantiation by the framework
+	}
+	
+	@Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
+		try {
+			listener = (TextInputDialogListener) activity;
+		} catch(ClassCastException e) {
+			throw new ClassCastException("Activity " + activity + " must implement TextInputDialogListener to use TextInputDialog.");
+		}
+	}
+	
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		listener = null;
+	}
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		if(savedInstanceState != null) {
+			dialogId = savedInstanceState.getInt(BUNDLE_DIALOG_ID, dialogId);
+			titleText = savedInstanceState.getInt(BUNDLE_TITLE_TEXT, titleText);
+			messageText = savedInstanceState.getInt(BUNDLE_MESSAGE_TEXT, messageText);
+			hintText = savedInstanceState.getInt(BUNDLE_HINT_TEXT, hintText);
+		}
+		
+		final EditText editText = new EditText(getActivity());
+		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		
+		if(titleText != 0) {
+			builder.setTitle(titleText);
+		}
+		if(messageText != 0) {
+			builder.setTitle(messageText);
+		}
+		if(hintText != 0) {
+			editText.setHint(hintText);
+		}
+		
+		editText.setId(android.R.id.text1);
+		editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
+		editText.setSingleLine();
+
+		builder.setView(editText);
+		builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				dialog.cancel();
+			}
+		});
+		
+		editText.setOnEditorActionListener(new OnEditorActionListener() {
+			public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+				listener.onTextInputDialogSubmitted(dialogId, v.getText().toString());
+				dismiss();
+				return true;
+			}
+		});
+		
+		builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				listener.onTextInputDialogSubmitted(dialogId, editText.getText().toString());
+			}
+		});
+
+		return builder.create();
+	}
+	
+	@Override
+	public void onSaveInstanceState(Bundle icicle) {
+		super.onSaveInstanceState(icicle);
+		icicle.putInt(BUNDLE_DIALOG_ID, dialogId);
+		icicle.putInt(BUNDLE_TITLE_TEXT, titleText);
+		icicle.putInt(BUNDLE_MESSAGE_TEXT, messageText);
+		icicle.putInt(BUNDLE_HINT_TEXT, hintText);
+	}
+	
+	@Override
+	public void onCancel(DialogInterface dialog) {
+		super.onCancel(dialog);
+		listener.onTextInputDialogCancelled(dialogId);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/TickHandler.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,73 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * This class handles regularly calling a specified runnable
+ * on the looper provided in the constructor. The first call
+ * occurs without delay (though still via the looper), all
+ * following calls are delayed by (approximately) the interval.
+ * The interval can be changed at any time, which will cause
+ * an immediate execution of the runnable again.
+ */
+public class TickHandler extends Handler {
+	private final Runnable callback;
+	private int messageId;
+	private long interval;
+	private boolean running;
+	
+	public TickHandler(Looper looper, long interval, Runnable callback) {
+		super(looper);
+		this.callback = callback;
+		this.interval = interval;
+	}
+	
+	public synchronized void stop() {
+		messageId++;
+		running = false;
+	}
+	
+	public synchronized void start() {
+		messageId++;
+		sendMessage(obtainMessage(messageId));
+		running = true;
+	}
+	
+	public synchronized void setInterval(long interval) {
+		this.interval = interval;
+		if(running) {
+			start();
+		}
+	}
+	
+	@Override
+	public synchronized void handleMessage(Message msg) {
+		if(msg.what == messageId) {
+			callback.run();
+		}
+		if(msg.what == messageId) {
+			sendMessageDelayed(obtainMessage(messageId), interval);
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/util/UiUtils.java	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,53 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.hedgewars.hedgeroid.util;
+
+import org.hedgewars.hedgeroid.R;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+public final class UiUtils {
+	private UiUtils() {
+		throw new AssertionError("This class is not meant to be instantiated");
+	}
+
+	public static View createVerticalTabIndicator(TabHost tabHost, int label, int icon) {
+		LayoutInflater inflater = (LayoutInflater) tabHost.getContext()
+				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+		View view = inflater.inflate(R.layout.tab_indicator_vertical,
+				tabHost.getTabWidget(), false);
+
+		final TextView tv = (TextView) view.findViewById(R.id.title);
+		tv.setText(label);
+
+		if (icon != 0) {
+			ImageView iconView = (ImageView) view.findViewById(R.id.icon);
+			iconView.setImageResource(icon);
+		}
+
+		return view;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/cmdlineClient/cmdlineClient.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,475 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <frontlib.h>
+#include <util/logging.h>
+#include <util/util.h>
+#include <base64/base64.h>
+#include <model/schemelist.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <conio.h>
+#include <windows.h>
+
+#define ENGINE_DIR ".\\"
+#define CONFIG_DIR "..\\share\\hedgewars"
+#define DATA_DIR CONFIG_DIR"\\Data"
+
+static flib_netconn *netconn;
+static flib_gameconn *gameconn;
+static flib_mapconn *mapconn;
+static char nickname[128];
+static flib_metascheme *metacfg;
+static bool netConnected = false;
+
+// Callback function that will be called when the map is rendered
+static void handleMapGenerated(void *context, const uint8_t *bitmap, int numHedgehogs) {
+	printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+	// Draw the map as ASCII art
+	for(int y=0; y<MAPIMAGE_HEIGHT; y+=8) {
+		for(int x=0; x<MAPIMAGE_WIDTH; x+=6) {
+			int pixelnum = x + y*MAPIMAGE_WIDTH;
+			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+			printf(pixel ? "#" : " ");
+		}
+		printf("\n");
+	}
+
+	flib_mapconn_destroy(mapconn);
+	mapconn = NULL;
+}
+
+static void onGameDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn_destroy(gameconn);
+	gameconn = NULL;
+	if(netconn) {
+		flib_netconn_send_roundfinished(netconn, reason==GAME_END_FINISHED);
+	}
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+	flib_log_e("Map rendering failed: %s", errormessage);
+	flib_mapconn_destroy(mapconn);
+	mapconn = NULL;
+}
+
+static void startEngineMap(int port) {
+	char cmdbuffer[255];
+	char argbuffer[255];
+	snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+	snprintf(argbuffer, 255, "%s %i landpreview", CONFIG_DIR, port);
+	ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+static void startEngineGame(int port) {
+	char cmdbuffer[255];
+	char argbuffer[255];
+	char base64PlayerName[255];
+	base64_encode(nickname, strlen(nickname), base64PlayerName, sizeof(base64PlayerName));
+	snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+	snprintf(argbuffer, 255, "%s 1024 768 32 %i 0 0 0 10 10 %s 0 0 %s 0 0 en.txt", CONFIG_DIR, port, DATA_DIR, base64PlayerName);
+	ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+void handleNetDisconnect(void *context, int reason, const char *message) {
+	printf("Disconnected: %s", message);
+	flib_netconn_destroy(netconn);
+	netconn = NULL;
+}
+
+/*void printRoomList() {
+	const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+	if(roomlist) {
+		if(roomlist->roomCount>0) {
+			for(int i=0; i<roomlist->roomCount; i++) {
+				if(i>0) {
+					printf(", ");
+				}
+				flib_room *room = roomlist->rooms[i];
+				printf("%s", room->name);
+			}
+		} else {
+			puts("Unfortunately, there are no rooms at the moment.");
+		}
+	} else {
+		puts("Sorry, due to an error the room list is not available.");
+	}
+	puts("\n");
+}*/
+
+void printTeamList() {
+	flib_gamesetup *setup = flib_netconn_create_gamesetup(netconn);
+	if(setup) {
+		puts("The following teams are in this room:");
+		for(int i=0; i<setup->teamlist->teamCount; i++) {
+			if(i>0) {
+				printf(", ");
+			}
+			printf("%s", setup->teamlist->teams[i]->name);
+		}
+		puts("\n");
+	} else {
+		puts("Sorry, due to an error the team list is not available.");
+	}
+	flib_gamesetup_destroy(setup);
+}
+
+void handleNetConnected(void *context) {
+	printf("You enter the lobby of a strange house inhabited by hedgehogs. Looking around, you see hallways branching off to these rooms:\n");
+	//printRoomList();
+	printf("\n\nNow, you can chat by just entering text, or join a room with /join <roomname>.");
+	printf(" You can also /quit or let me /describe <roomname>. Once in a room, you can /add <teamname> and set yourself /ready. You can also /list the available rooms (in the lobby) or the teams (in a room).\n");
+	netConnected = true;
+}
+
+void handleChat(void *context, const char *nick, const char *msg) {
+	if(gameconn) {
+		flib_gameconn_send_chatmsg(gameconn, nick, msg);
+	}
+	printf("%s: %s\n", nick, msg);
+}
+
+void handleEnterRoom(void *context, bool isChief) {
+	puts("You have entered the room.");
+}
+
+void handleRoomJoin(void *context, const char *nick) {
+	if(strcmp(nick, nickname)) {
+		printf("%s is here.\n", nick);
+	}
+}
+
+void handleRoomLeave(void *context, const char *nick, const char *partmsg) {
+	if(strcmp(nick, nickname)) {
+		printf("%s leaves.\n", nick);
+	}
+}
+
+void handleReady(void *context, const char *nick, bool ready) {
+	if(strcmp(nick, nickname)) {
+		if(ready) {
+			printf("%s is ready to go.\n", nick);
+		} else {
+			printf("%s is not ready.\n", nick);
+		}
+	} else {
+		if(ready) {
+			printf("You are ready to go.\n");
+		} else {
+			printf("You are not ready.\n");
+		}
+	}
+}
+
+void handleEmFromNet(void *context, const uint8_t *em, size_t size) {
+	if(gameconn) {
+		flib_gameconn_send_enginemsg(gameconn, em, size);
+	}
+}
+
+void handleEmFromEngine(void *context, const uint8_t *em, size_t size) {
+	if(netconn) {
+		flib_netconn_send_engineMessage(netconn, em, size);
+	}
+}
+
+void handleChatFromGame(void *context, const char *message, bool teamchat) {
+	if(netconn) {
+		if(teamchat) {
+			flib_netconn_send_teamchat(netconn, message);
+		} else {
+			flib_netconn_send_chat(netconn, message);
+		}
+	}
+}
+
+void handleRunGame(void *context) {
+	flib_gamesetup *gamesetup = flib_netconn_create_gamesetup(netconn);
+	if(gameconn) {
+		flib_log_e("Request to start game, but a game is already running.");
+	} else if(gamesetup) {
+		gameconn = flib_gameconn_create(nickname, gamesetup, true);
+		flib_gameconn_onEngineMessage(gameconn, handleEmFromEngine, NULL);
+		flib_gameconn_onDisconnect(gameconn, onGameDisconnect, NULL);
+		flib_gameconn_onChat(gameconn, handleChatFromGame, NULL);
+		startEngineGame(flib_gameconn_getport(gameconn));
+	}
+	flib_gamesetup_destroy(gamesetup);
+}
+
+void handleNickTaken(void *context, const char *nick) {
+	printf("The nickname %s is already in use, please choose a different one:\n", nick);
+	flib_gets(nickname, sizeof(nickname));
+	flib_netconn_send_nick(netconn, nickname);
+}
+
+void handlePwRequest(void *context, const char *nick) {
+	printf("A password is required to log in as %s, please enter (warning: shown in cleartext):\n", nick);
+	char password[256];
+	flib_gets(password, sizeof(password));
+	flib_netconn_send_password(netconn, password);
+}
+
+void handleMessage(void *context, int type, const char *msg) {
+	if(gameconn) {
+		flib_gameconn_send_textmsg(gameconn, 1, msg);
+	}
+	printf("*** %s\n", msg);
+}
+
+void handleTeamAccepted(void *context, const char *teamname) {
+	printf("The team %s has been accepted.\n", teamname);
+}
+
+void handleMapChanged(void *context, const flib_map *map, int changetype) {
+	if(map->mapgen != MAPGEN_NAMED && changetype != NETCONN_MAPCHANGE_THEME) {
+		if(mapconn) {
+			flib_mapconn_destroy(mapconn);
+			mapconn = NULL;
+		}
+		mapconn = flib_mapconn_create(map);
+		if(mapconn) {
+			flib_mapconn_onSuccess(mapconn, handleMapGenerated, NULL);
+			flib_mapconn_onFailure(mapconn, handleMapFailure, NULL);
+			startEngineMap(flib_mapconn_getport(mapconn));
+		}
+	} else if(map->mapgen == MAPGEN_NAMED) {
+		printf("The map %s has been selected.\n", map->name);
+	}
+}
+
+void handleLeaveRoom(void *context, int reason, const char *msg) {
+	if(reason == NETCONN_ROOMLEAVE_ABANDONED) {
+		printf("The chief has abandoned the room.");
+	} else if(reason == NETCONN_ROOMLEAVE_KICKED) {
+		printf("You have been kicked from the room.");
+	}
+	if(msg) {
+		printf(" (%s)", msg);
+	}
+	puts(" You are back in the lobby.");
+}
+
+void handleSchemeChanged(void *context, const flib_scheme *scheme) {
+	printf("Game scheme: %s.\n", scheme->name);
+}
+
+void handleWeaponsetChanged(void *context, const flib_weaponset *weaponset) {
+	printf("Weaponset: %s.\n", weaponset->name);
+}
+
+void handleHogcountChanged(void *context, const char *team, int count) {
+	printf("Team %s will send %i hogs into the fight.\n", team, count);
+}
+
+void handleRoomAdd(void *context, const flib_room *room) {
+	printf("%s created a new room called %s.\n", room->owner, room->name);
+}
+
+void handleRoomDelete(void *context, const char *roomName) {
+	printf("The room %s has collapsed.\n", roomName);
+}
+
+void handleScriptChanged(void *context, const char *script) {
+	printf("Game Type: %s\n", script);
+}
+
+void handleTeamAdd(void *context, const flib_team *team) {
+	printf("%s puts the team %s to the planning board.\n", team->ownerName, team->name);
+}
+
+void handleTeamDelete(void *context, const char *teamName) {
+	printf("The team %s decided not to fight this battle after all.\n", teamName);
+}
+
+void handleTeamColorChanged(void *context, const char *name, int colorIndex) {
+	static const char* colorNames[] = {"red", "blue", "teal", "purple", "pink", "green", "orange", "brown", "yellow"};
+	const char *colorName = "strange";
+	if(colorIndex>=0 && colorIndex < 9) {
+		colorName = colorNames[colorIndex];
+	}
+	printf("The team %s will wear %s uniforms today.\n", name, colorName);
+}
+
+void tick() {
+	if(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+	if(netconn) {
+		flib_netconn_tick(netconn);
+	}
+	if(mapconn) {
+		flib_mapconn_tick(mapconn);
+	}
+}
+
+static HANDLE hStdin;
+
+static int init() {
+	hStdin = GetStdHandle(STD_INPUT_HANDLE);
+	if(hStdin == INVALID_HANDLE_VALUE) {
+		flib_log_e("Unable to get stdin handle");
+		return 1;
+	}
+	if(!flib_init(0)) {
+		flib_log_setLevel(FLIB_LOGLEVEL_WARNING);
+		freopen( "CON", "w", stdout );
+		freopen( "CON", "w", stderr );
+		metacfg = flib_metascheme_from_ini("metasettings.ini");
+		if(!metacfg) {
+			flib_quit();
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int main(int argc, char *argv[]) {
+	if(init()) {
+		return -1;
+	}
+
+	puts("Please enter a nickname:");
+	flib_gets(nickname, sizeof(nickname));
+
+	netconn = flib_netconn_create(nickname, metacfg, DATA_DIR"\\", "140.247.62.101", 46631);
+	if(!netconn) {
+		flib_quit();
+		return -1;
+	}
+
+	flib_netconn_onConnected(netconn, handleNetConnected, NULL);
+	flib_netconn_onDisconnected(netconn, handleNetDisconnect, NULL);
+	flib_netconn_onChat(netconn, handleChat, NULL);
+	flib_netconn_onEnterRoom(netconn, handleEnterRoom, NULL);
+	flib_netconn_onRunGame(netconn, handleRunGame, NULL);
+	flib_netconn_onEngineMessage(netconn, handleEmFromNet, NULL);
+	flib_netconn_onRoomJoin(netconn, handleRoomJoin, NULL);
+	flib_netconn_onRoomLeave(netconn, handleRoomLeave, NULL);
+	flib_netconn_onReadyState(netconn, handleReady, NULL);
+	flib_netconn_onNickTaken(netconn, handleNickTaken, NULL);
+	flib_netconn_onPasswordRequest(netconn, handlePwRequest, NULL);
+	flib_netconn_onMessage(netconn, handleMessage, NULL);
+	flib_netconn_onTeamAccepted(netconn, handleTeamAccepted, NULL);
+	flib_netconn_onMapChanged(netconn, handleMapChanged, NULL);
+	flib_netconn_onLeaveRoom(netconn, handleLeaveRoom, NULL);
+	flib_netconn_onCfgScheme(netconn, handleSchemeChanged, NULL);
+	flib_netconn_onWeaponsetChanged(netconn, handleWeaponsetChanged, NULL);
+	flib_netconn_onHogCountChanged(netconn, handleHogcountChanged, NULL);
+	flib_netconn_onRoomAdd(netconn, handleRoomAdd, NULL);
+	flib_netconn_onRoomDelete(netconn, handleRoomDelete, NULL);
+	flib_netconn_onScriptChanged(netconn, handleScriptChanged, NULL);
+	flib_netconn_onTeamAdd(netconn, handleTeamAdd, NULL);
+	flib_netconn_onTeamDelete(netconn, handleTeamDelete, NULL);
+	flib_netconn_onTeamColorChanged(netconn, handleTeamColorChanged, NULL);
+
+	INPUT_RECORD inputRecord;
+	DWORD eventCount = 0;
+
+	while(netconn || gameconn) {
+		tick();
+		if(netconn && netConnected) {
+			while(PeekConsoleInput(hStdin, &inputRecord, 1, &eventCount) && eventCount>0) {
+				if(inputRecord.EventType != KEY_EVENT) {
+					ReadConsoleInput(hStdin, &inputRecord, 1, &eventCount);
+				} else {
+					printf("%s: ", nickname);
+					char input[256];
+					if(!flib_gets(input, sizeof(input))) {
+						if(!memcmp("/quit", input, strlen("/quit"))) {
+							flib_netconn_send_quit(netconn, "Player quit.");
+						} else if(!memcmp("/describe ", input, strlen("/describe "))) {
+							const char *roomname = input+strlen("/describe ");
+							/*const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+							flib_room *room = flib_roomlist_find(roomlist, roomname);
+							if(!room) {
+								puts("Unknown room.");
+							} else {
+								char *text = flib_asprintf(
+										"%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
+										room->name,
+										room->owner,
+										room->playerCount,
+										room->teamCount,
+										room->inProgress ? "fighting" : "preparing to fight",
+										room->map[0]=='+' ? "" : "the map ",
+										!strcmp("+rnd+", room->map) ? "a random map" :
+												!strcmp("+maze+", room->map) ? "a random maze" :
+												!strcmp("+drawn+", room->map) ? "a hand-drawn map" :
+												room->map,
+										room->scheme,
+										room->weapons);
+								if(text) {
+									puts(text);
+								}
+								free(text);
+							}*/
+						} else if(!memcmp("/join ", input, strlen("/join "))) {
+							const char *roomname = input+strlen("/join ");
+							flib_netconn_send_joinRoom(netconn, roomname);
+						} else if(!memcmp("/ready", input, strlen("/ready"))) {
+							flib_netconn_send_toggleReady(netconn);
+						} else if(!memcmp("/loglevel ", input, strlen("/loglevel "))) {
+							int loglevel = atoi(input+strlen("/loglevel "));
+							flib_log_setLevel(loglevel);
+						} else if(!memcmp("/list", input, strlen("/list"))) {
+							if(flib_netconn_is_in_room_context(netconn)) {
+								printTeamList();
+							} else {
+								puts("From this big and expansive lobby, hallways branch off to these rooms:");
+								//printRoomList();
+							}
+						} else if(!memcmp("/addteam ", input, strlen("/addteam "))) {
+							const char *teamname = input+strlen("/addteam ");
+							if(!flib_contains_dir_separator(teamname)) {
+								char *teamfilename = flib_asprintf("%s.hwt", teamname);
+								if(teamfilename) {
+									flib_team *team = flib_team_from_ini(teamfilename);
+									if(team) {
+										flib_netconn_send_addTeam(netconn, team);
+									} else {
+										printf("Teamfile %s not found.\n", teamfilename);
+									}
+									flib_team_destroy(team);
+								}
+								free(teamfilename);
+							}
+						} else if(strlen(input)>0) {
+							flib_netconn_send_chat(netconn, input);
+						}
+					}
+				}
+			}
+		}
+		fflush(stdout);
+		Sleep(10);
+	}
+
+
+	flib_metascheme_release(metacfg);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/Android.mk	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,22 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := frontlib
+
+LOCAL_CFLAGS := -I$(LOCAL_PATH)/../Android-build/SDL-android-project/jni/SDL_net -std=c99 -DWITHOUT_SDL
+
+LOCAL_SRC_FILES := base64/base64.c iniparser/iniparser.c \
+	iniparser/dictionary.c ipc/gameconn.c ipc/ipcbase.c \
+	ipc/ipcprotocol.c ipc/mapconn.c md5/md5.c model/scheme.c \
+	model/gamesetup.c model/map.c model/mapcfg.c model/room.c \
+	model/schemelist.c model/team.c model/teamlist.c model/weapon.c \
+	net/netbase.c net/netconn_callbacks.c net/netconn_send.c \
+	net/netconn.c net/netprotocol.c util/buffer.c util/inihelper.c \
+	util/logging.c util/util.c frontlib.c hwconsts.c socket.c \
+	extra/jnacontrol.c
+
+LOCAL_SHARED_LIBRARIES += SDL_net
+LOCAL_LDLIBS += -lz
+
+include $(BUILD_SHARED_LIBRARY)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/base64/base64.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,572 @@
+/* base64.c -- Encode binary data using printable characters.
+   Copyright (C) 1999-2001, 2004-2006, 2009-2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Simon Josefsson.  Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28).  Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking.  Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ *   FAIL: input was not valid base64
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ *   FAIL: input too long
+ * if (out == NULL)
+ *   FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+#include <string.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+  return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+   If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+   possible.  If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+   terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+               char *restrict out, size_t outlen)
+{
+  static const char b64str[64] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  while (inlen && outlen)
+    {
+      *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+      if (!--outlen)
+        break;
+      *out++ = b64str[((to_uchar (in[0]) << 4)
+                       + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+                      & 0x3f];
+      if (!--outlen)
+        break;
+      *out++ =
+        (inlen
+         ? b64str[((to_uchar (in[1]) << 2)
+                   + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+                  & 0x3f]
+         : '=');
+      if (!--outlen)
+        break;
+      *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+      if (!--outlen)
+        break;
+      if (inlen)
+        inlen--;
+      if (inlen)
+        in += 3;
+    }
+
+  if (outlen)
+    *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+   from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+   the length of the encoded data, excluding the terminating zero.  On
+   return, the OUT variable will hold a pointer to newly allocated
+   memory that must be deallocated by the caller.  If output string
+   length would overflow, 0 is returned and OUT is set to NULL.  If
+   memory allocation failed, OUT is set to NULL, and the return value
+   indicates length of the requested memory block, i.e.,
+   BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+  size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+  /* Check for overflow in outlen computation.
+   *
+   * If there is no overflow, outlen >= inlen.
+   *
+   * If the operation (inlen + 2) overflows then it yields at most +1, so
+   * outlen is 0.
+   *
+   * If the multiplication overflows, we lose at least half of the
+   * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+   * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+   * (inlen > 4).
+   */
+  if (inlen > outlen)
+    {
+      *out = NULL;
+      return 0;
+    }
+
+  *out = malloc (outlen);
+  if (!*out)
+    return outlen;
+
+  base64_encode (in, inlen, *out, outlen);
+
+  return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+   (think EBCDIC).  However, it does assume that the characters in the
+   Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255.  POSIX
+   1003.1-2001 require that char and unsigned char are 8-bit
+   quantities, though, taking care of that problem.  But this may be a
+   potential problem on non-POSIX C99 platforms.
+
+   IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+   as the formal parameter rather than "x".  */
+#define B64(_)                                  \
+  ((_) == 'A' ? 0                               \
+   : (_) == 'B' ? 1                             \
+   : (_) == 'C' ? 2                             \
+   : (_) == 'D' ? 3                             \
+   : (_) == 'E' ? 4                             \
+   : (_) == 'F' ? 5                             \
+   : (_) == 'G' ? 6                             \
+   : (_) == 'H' ? 7                             \
+   : (_) == 'I' ? 8                             \
+   : (_) == 'J' ? 9                             \
+   : (_) == 'K' ? 10                            \
+   : (_) == 'L' ? 11                            \
+   : (_) == 'M' ? 12                            \
+   : (_) == 'N' ? 13                            \
+   : (_) == 'O' ? 14                            \
+   : (_) == 'P' ? 15                            \
+   : (_) == 'Q' ? 16                            \
+   : (_) == 'R' ? 17                            \
+   : (_) == 'S' ? 18                            \
+   : (_) == 'T' ? 19                            \
+   : (_) == 'U' ? 20                            \
+   : (_) == 'V' ? 21                            \
+   : (_) == 'W' ? 22                            \
+   : (_) == 'X' ? 23                            \
+   : (_) == 'Y' ? 24                            \
+   : (_) == 'Z' ? 25                            \
+   : (_) == 'a' ? 26                            \
+   : (_) == 'b' ? 27                            \
+   : (_) == 'c' ? 28                            \
+   : (_) == 'd' ? 29                            \
+   : (_) == 'e' ? 30                            \
+   : (_) == 'f' ? 31                            \
+   : (_) == 'g' ? 32                            \
+   : (_) == 'h' ? 33                            \
+   : (_) == 'i' ? 34                            \
+   : (_) == 'j' ? 35                            \
+   : (_) == 'k' ? 36                            \
+   : (_) == 'l' ? 37                            \
+   : (_) == 'm' ? 38                            \
+   : (_) == 'n' ? 39                            \
+   : (_) == 'o' ? 40                            \
+   : (_) == 'p' ? 41                            \
+   : (_) == 'q' ? 42                            \
+   : (_) == 'r' ? 43                            \
+   : (_) == 's' ? 44                            \
+   : (_) == 't' ? 45                            \
+   : (_) == 'u' ? 46                            \
+   : (_) == 'v' ? 47                            \
+   : (_) == 'w' ? 48                            \
+   : (_) == 'x' ? 49                            \
+   : (_) == 'y' ? 50                            \
+   : (_) == 'z' ? 51                            \
+   : (_) == '0' ? 52                            \
+   : (_) == '1' ? 53                            \
+   : (_) == '2' ? 54                            \
+   : (_) == '3' ? 55                            \
+   : (_) == '4' ? 56                            \
+   : (_) == '5' ? 57                            \
+   : (_) == '6' ? 58                            \
+   : (_) == '7' ? 59                            \
+   : (_) == '8' ? 60                            \
+   : (_) == '9' ? 61                            \
+   : (_) == '+' ? 62                            \
+   : (_) == '/' ? 63                            \
+   : -1)
+
+static const signed char b64[0x100] = {
+  B64 (0), B64 (1), B64 (2), B64 (3),
+  B64 (4), B64 (5), B64 (6), B64 (7),
+  B64 (8), B64 (9), B64 (10), B64 (11),
+  B64 (12), B64 (13), B64 (14), B64 (15),
+  B64 (16), B64 (17), B64 (18), B64 (19),
+  B64 (20), B64 (21), B64 (22), B64 (23),
+  B64 (24), B64 (25), B64 (26), B64 (27),
+  B64 (28), B64 (29), B64 (30), B64 (31),
+  B64 (32), B64 (33), B64 (34), B64 (35),
+  B64 (36), B64 (37), B64 (38), B64 (39),
+  B64 (40), B64 (41), B64 (42), B64 (43),
+  B64 (44), B64 (45), B64 (46), B64 (47),
+  B64 (48), B64 (49), B64 (50), B64 (51),
+  B64 (52), B64 (53), B64 (54), B64 (55),
+  B64 (56), B64 (57), B64 (58), B64 (59),
+  B64 (60), B64 (61), B64 (62), B64 (63),
+  B64 (64), B64 (65), B64 (66), B64 (67),
+  B64 (68), B64 (69), B64 (70), B64 (71),
+  B64 (72), B64 (73), B64 (74), B64 (75),
+  B64 (76), B64 (77), B64 (78), B64 (79),
+  B64 (80), B64 (81), B64 (82), B64 (83),
+  B64 (84), B64 (85), B64 (86), B64 (87),
+  B64 (88), B64 (89), B64 (90), B64 (91),
+  B64 (92), B64 (93), B64 (94), B64 (95),
+  B64 (96), B64 (97), B64 (98), B64 (99),
+  B64 (100), B64 (101), B64 (102), B64 (103),
+  B64 (104), B64 (105), B64 (106), B64 (107),
+  B64 (108), B64 (109), B64 (110), B64 (111),
+  B64 (112), B64 (113), B64 (114), B64 (115),
+  B64 (116), B64 (117), B64 (118), B64 (119),
+  B64 (120), B64 (121), B64 (122), B64 (123),
+  B64 (124), B64 (125), B64 (126), B64 (127),
+  B64 (128), B64 (129), B64 (130), B64 (131),
+  B64 (132), B64 (133), B64 (134), B64 (135),
+  B64 (136), B64 (137), B64 (138), B64 (139),
+  B64 (140), B64 (141), B64 (142), B64 (143),
+  B64 (144), B64 (145), B64 (146), B64 (147),
+  B64 (148), B64 (149), B64 (150), B64 (151),
+  B64 (152), B64 (153), B64 (154), B64 (155),
+  B64 (156), B64 (157), B64 (158), B64 (159),
+  B64 (160), B64 (161), B64 (162), B64 (163),
+  B64 (164), B64 (165), B64 (166), B64 (167),
+  B64 (168), B64 (169), B64 (170), B64 (171),
+  B64 (172), B64 (173), B64 (174), B64 (175),
+  B64 (176), B64 (177), B64 (178), B64 (179),
+  B64 (180), B64 (181), B64 (182), B64 (183),
+  B64 (184), B64 (185), B64 (186), B64 (187),
+  B64 (188), B64 (189), B64 (190), B64 (191),
+  B64 (192), B64 (193), B64 (194), B64 (195),
+  B64 (196), B64 (197), B64 (198), B64 (199),
+  B64 (200), B64 (201), B64 (202), B64 (203),
+  B64 (204), B64 (205), B64 (206), B64 (207),
+  B64 (208), B64 (209), B64 (210), B64 (211),
+  B64 (212), B64 (213), B64 (214), B64 (215),
+  B64 (216), B64 (217), B64 (218), B64 (219),
+  B64 (220), B64 (221), B64 (222), B64 (223),
+  B64 (224), B64 (225), B64 (226), B64 (227),
+  B64 (228), B64 (229), B64 (230), B64 (231),
+  B64 (232), B64 (233), B64 (234), B64 (235),
+  B64 (236), B64 (237), B64 (238), B64 (239),
+  B64 (240), B64 (241), B64 (242), B64 (243),
+  B64 (244), B64 (245), B64 (246), B64 (247),
+  B64 (248), B64 (249), B64 (250), B64 (251),
+  B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+   false otherwise.  Note that '=' is padding and not considered to be
+   part of the alphabet.  */
+bool
+isbase64 (char ch)
+{
+  return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Initialize decode-context buffer, CTX.  */
+void
+base64_decode_ctx_init (struct base64_decode_context *ctx)
+{
+  ctx->i = 0;
+}
+
+/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
+   none of those four is a newline, then return *IN.  Otherwise, copy up to
+   4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
+   index CTX->i and setting CTX->i to reflect the number of bytes copied,
+   and return CTX->buf.  In either case, advance *IN to point to the byte
+   after the last one processed, and set *N_NON_NEWLINE to the number of
+   verified non-newline bytes accessible through the returned pointer.  */
+static inline char *
+get_4 (struct base64_decode_context *ctx,
+       char const *restrict *in, char const *restrict in_end,
+       size_t *n_non_newline)
+{
+  if (ctx->i == 4)
+    ctx->i = 0;
+
+  if (ctx->i == 0)
+    {
+      char const *t = *in;
+      if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
+        {
+          /* This is the common case: no newline.  */
+          *in += 4;
+          *n_non_newline = 4;
+          return (char *) t;
+        }
+    }
+
+  {
+    /* Copy non-newline bytes into BUF.  */
+    char const *p = *in;
+    while (p < in_end)
+      {
+        char c = *p++;
+        if (c != '\n')
+          {
+            ctx->buf[ctx->i++] = c;
+            if (ctx->i == 4)
+              break;
+          }
+      }
+
+    *in = p;
+    *n_non_newline = ctx->i;
+    return ctx->buf;
+  }
+}
+
+#define return_false                            \
+  do                                            \
+    {                                           \
+      *outp = out;                              \
+      return false;                             \
+    }                                           \
+  while (false)
+
+/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
+   into the output buffer, *OUT, of size *OUTLEN bytes.  Return true if
+   decoding is successful, false otherwise.  If *OUTLEN is too small,
+   as many bytes as possible are written to *OUT.  On return, advance
+   *OUT to point to the byte after the last one written, and decrement
+   *OUTLEN to reflect the number of bytes remaining in *OUT.  */
+static inline bool
+decode_4 (char const *restrict in, size_t inlen,
+          char *restrict *outp, size_t *outleft)
+{
+  char *out = *outp;
+  if (inlen < 2)
+    return false;
+
+  if (!isbase64 (in[0]) || !isbase64 (in[1]))
+    return false;
+
+  if (*outleft)
+    {
+      *out++ = ((b64[to_uchar (in[0])] << 2)
+                | (b64[to_uchar (in[1])] >> 4));
+      --*outleft;
+    }
+
+  if (inlen == 2)
+    return_false;
+
+  if (in[2] == '=')
+    {
+      if (inlen != 4)
+        return_false;
+
+      if (in[3] != '=')
+        return_false;
+    }
+  else
+    {
+      if (!isbase64 (in[2]))
+        return_false;
+
+      if (*outleft)
+        {
+          *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+                    | (b64[to_uchar (in[2])] >> 2));
+          --*outleft;
+        }
+
+      if (inlen == 3)
+        return_false;
+
+      if (in[3] == '=')
+        {
+          if (inlen != 4)
+            return_false;
+        }
+      else
+        {
+          if (!isbase64 (in[3]))
+            return_false;
+
+          if (*outleft)
+            {
+              *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+                        | b64[to_uchar (in[3])]);
+              --*outleft;
+            }
+        }
+    }
+
+  *outp = out;
+  return true;
+}
+
+/* Decode base64-encoded input array IN of length INLEN to output array
+   OUT that can hold *OUTLEN bytes.  The input data may be interspersed
+   with newlines.  Return true if decoding was successful, i.e. if the
+   input was valid base64 data, false otherwise.  If *OUTLEN is too
+   small, as many bytes as possible will be written to OUT.  On return,
+   *OUTLEN holds the length of decoded bytes in OUT.  Note that as soon
+   as any non-alphabet, non-newline character is encountered, decoding
+   is stopped and false is returned.  If INLEN is zero, then process
+   only whatever data is stored in CTX.
+
+   Initially, CTX must have been initialized via base64_decode_ctx_init.
+   Subsequent calls to this function must reuse whatever state is recorded
+   in that buffer.  It is necessary for when a quadruple of base64 input
+   bytes spans two input buffers.
+
+   If CTX is NULL then newlines are treated as garbage and the input
+   buffer is processed as a unit.  */
+
+bool
+base64_decode_ctx (struct base64_decode_context *ctx,
+                   const char *restrict in, size_t inlen,
+                   char *restrict out, size_t *outlen)
+{
+  size_t outleft = *outlen;
+  bool ignore_newlines = ctx != NULL;
+  bool flush_ctx = false;
+  unsigned int ctx_i = 0;
+
+  if (ignore_newlines)
+    {
+      ctx_i = ctx->i;
+      flush_ctx = inlen == 0;
+    }
+
+
+  while (true)
+    {
+      size_t outleft_save = outleft;
+      if (ctx_i == 0 && !flush_ctx)
+        {
+          while (true)
+            {
+              /* Save a copy of outleft, in case we need to re-parse this
+                 block of four bytes.  */
+              outleft_save = outleft;
+              if (!decode_4 (in, inlen, &out, &outleft))
+                break;
+
+              in += 4;
+              inlen -= 4;
+            }
+        }
+
+      if (inlen == 0 && !flush_ctx)
+        break;
+
+      /* Handle the common case of 72-byte wrapped lines.
+         This also handles any other multiple-of-4-byte wrapping.  */
+      if (inlen && *in == '\n' && ignore_newlines)
+        {
+          ++in;
+          --inlen;
+          continue;
+        }
+
+      /* Restore OUT and OUTLEFT.  */
+      out -= outleft_save - outleft;
+      outleft = outleft_save;
+
+      {
+        char const *in_end = in + inlen;
+        char const *non_nl;
+
+        if (ignore_newlines)
+          non_nl = get_4 (ctx, &in, in_end, &inlen);
+        else
+          non_nl = in;  /* Might have nl in this case. */
+
+        /* If the input is empty or consists solely of newlines (0 non-newlines),
+           then we're done.  Likewise if there are fewer than 4 bytes when not
+           flushing context and not treating newlines as garbage.  */
+        if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines))
+          {
+            inlen = 0;
+            break;
+          }
+        if (!decode_4 (non_nl, inlen, &out, &outleft))
+          break;
+
+        inlen = in_end - in;
+      }
+    }
+
+  *outlen -= outleft;
+
+  return inlen == 0;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+   data stored in IN of size INLEN to the *OUT buffer.  On return, the
+   size of the decoded data is stored in *OUTLEN.  OUTLEN may be NULL,
+   if the caller is not interested in the decoded length.  *OUT may be
+   NULL to indicate an out of memory error, in which case *OUTLEN
+   contains the size of the memory block needed.  The function returns
+   true on successful decoding and memory allocation errors.  (Use the
+   *OUT and *OUTLEN parameters to differentiate between successful
+   decoding and memory error.)  The function returns false if the
+   input was invalid, in which case *OUT is NULL and *OUTLEN is
+   undefined. */
+bool
+base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+                         const char *in, size_t inlen, char **out,
+                         size_t *outlen)
+{
+  /* This may allocate a few bytes too many, depending on input,
+     but it's not worth the extra CPU time to compute the exact size.
+     The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
+     input ends with "=" and minus another 1 if the input ends with "==".
+     Dividing before multiplying avoids the possibility of overflow.  */
+  size_t needlen = 3 * (inlen / 4) + 3;
+
+  *out = malloc (needlen);
+  if (!*out)
+    return true;
+
+  if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
+    {
+      free (*out);
+      *out = NULL;
+      return false;
+    }
+
+  if (outlen)
+    *outlen = needlen;
+
+  return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/base64/base64.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,60 @@
+/* base64.h -- Encode binary data using printable characters.
+   Copyright (C) 2004-2006, 2009-2012 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+   integer >= n/k, i.e., the ceiling of n/k.  */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+struct base64_decode_context
+{
+  unsigned int i;
+  char buf[4];
+};
+
+extern bool isbase64 (char ch);
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+                           char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
+
+extern bool base64_decode_ctx (struct base64_decode_context *ctx,
+                               const char *restrict in, size_t inlen,
+                               char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+                                     const char *in, size_t inlen,
+                                     char **out, size_t *outlen);
+
+#define base64_decode(in, inlen, out, outlen) \
+        base64_decode_ctx (NULL, in, inlen, out, outlen)
+
+#define base64_decode_alloc(in, inlen, out, outlen) \
+        base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
+
+#endif /* BASE64_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/extra/jnacontrol.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,241 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * This file is not directly part of the frontlib and is not required to build it.
+ * However, it is recommended to include it in compilation when building for Android. The purpose of this file
+ * is to ensure consistency between the function signatures of the JNA Java bindings of the Android port and the
+ * frontlib functions.
+ *
+ * This file, in essence, consists only of function declarations. They are duplicates of function declarations
+ * from the frontlib headers that are referenced from JNA bindings. If the signature of one of these functions
+ * changes in the frontlib, it will no longer match the signature in this file, and the compiler will show an error.
+ * If that happens, you need to update the JNA bindings in Hedgeroid to match the new function signature, and then
+ * update this file.
+ *
+ * The reason for all this is that JNA does not actually know the function signatures of the functions it binds,
+ * it derives them from Java method declarations. If those do not match the actual function signatures, you will
+ * only notice when you suddenly get strange (and possibly hard to track down) problems at runtime. This file is
+ * an attempt to detect these problems at compile time instead. Notice that it will NOT detect changes to structs
+ * or constants though, which also require updates to the JNA bindings.
+ */
+
+/*
+ * Before we include the frontlib headers, we define away the const keyword. This is necessary because there is no
+ * distinction between const and non-const types on the JNA side, and we don't want the compiler to complain because
+ * of bad constness.
+ *
+ * This is so evil, but it works...
+ */
+#define const
+
+#include "../frontlib.h"
+
+/*
+ * Now we map the Java types to the corresponding C types...
+ */
+typedef flib_netconn *NetconnPtr;
+typedef flib_gameconn *GameconnPtr;
+typedef flib_mapconn *MapconnPtr;
+typedef flib_metascheme *MetaschemePtr;
+typedef flib_room **RoomArrayPtr;
+typedef flib_weaponset *WeaponsetPtr;
+typedef flib_weaponsetlist *WeaponsetListPtr;
+typedef flib_map *MapRecipePtr;
+typedef flib_scheme *SchemePtr;
+typedef flib_schemelist *SchemelistPtr;
+
+typedef flib_room *RoomPtr;
+typedef flib_team *TeamPtr;
+typedef flib_gamesetup *GameSetupPtr;
+typedef bool boolean;
+typedef size_t NativeSizeT;
+typedef void *Pointer;
+typedef uint8_t *ByteArrayPtr;
+typedef char *String;
+
+/*
+ * Mapping callback types
+ */
+typedef void (*VoidCallback)(Pointer context);
+typedef void (*StrCallback)(Pointer context, String arg1);
+typedef void (*IntCallback)(Pointer context, int arg1);
+typedef void (*IntStrCallback)(Pointer context, int arg1, String arg2);
+typedef void (*StrIntCallback)(Pointer context, String arg1, int arg2);
+typedef void (*StrStrCallback)(Pointer context, String arg1, String arg2);
+typedef void (*RoomCallback)(Pointer context, RoomPtr arg1);
+typedef void (*RoomListCallback)(Pointer context, RoomArrayPtr arg1, int arg2);
+typedef void (*StrRoomCallback)(Pointer context, String arg1, RoomPtr arg2);
+typedef void (*BoolCallback)(Pointer context, boolean arg1);
+typedef void (*StrBoolCallback)(Pointer context, String arg1, boolean arg2);
+typedef void (*TeamCallback)(Pointer context, TeamPtr arg1);
+typedef void (*BytesCallback)(Pointer context, const uint8_t *buffer, NativeSizeT size);
+typedef void (*BytesBoolCallback)(Pointer context, const uint8_t *buffer, NativeSizeT size, boolean arg3);
+typedef void (*SchemeCallback)(Pointer context, SchemePtr arg1);
+typedef void (*MapIntCallback)(Pointer context, MapRecipePtr arg1, int arg2);
+typedef void (*WeaponsetCallback)(Pointer context, WeaponsetPtr arg1);
+typedef void (*MapimageCallback)(Pointer context, const uint8_t *mapimage, int hogs);
+typedef void (*LogCallback)(int arg1, String arg2);
+
+/*
+ * Below here are the copypasted method declarations from the JNA bindings
+ */
+
+	// frontlib.h
+    int flib_init();
+    void flib_quit();
+
+    // hwconsts.h
+    int flib_get_teamcolor_count();
+    int flib_get_hedgehogs_per_team();
+    int flib_get_weapons_count();
+	MetaschemePtr flib_get_metascheme();
+
+    // net/netconn.h
+	NetconnPtr flib_netconn_create(String playerName, String dataDirPath, String host, int port);
+	void flib_netconn_destroy(NetconnPtr conn);
+
+	void flib_netconn_tick(NetconnPtr conn);
+	boolean flib_netconn_is_chief(NetconnPtr conn);
+	String flib_netconn_get_playername(NetconnPtr conn);
+	GameSetupPtr flib_netconn_create_gamesetup(NetconnPtr conn);
+	int flib_netconn_send_quit(NetconnPtr conn, String quitmsg);
+	int flib_netconn_send_chat(NetconnPtr conn, String chat);
+	int flib_netconn_send_teamchat(NetconnPtr conn, String msg);
+	int flib_netconn_send_password(NetconnPtr conn, String passwd);
+	int flib_netconn_send_nick(NetconnPtr conn, String nick);
+	int flib_netconn_send_request_roomlist(NetconnPtr conn);
+	int flib_netconn_send_joinRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_createRoom(NetconnPtr conn, String room);
+	int flib_netconn_send_renameRoom(NetconnPtr conn, String roomName);
+	int flib_netconn_send_leaveRoom(NetconnPtr conn, String msg);
+	int flib_netconn_send_toggleReady(NetconnPtr conn);
+	int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team);
+	int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname);
+	int flib_netconn_send_engineMessage(NetconnPtr conn, ByteArrayPtr message, NativeSizeT size);
+	int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount);
+	int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex);
+	int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset);
+	int flib_netconn_send_map(NetconnPtr conn, MapRecipePtr map);
+	int flib_netconn_send_mapName(NetconnPtr conn, String mapName);
+	int flib_netconn_send_mapGen(NetconnPtr conn, int mapGen);
+	int flib_netconn_send_mapTemplate(NetconnPtr conn, int templateFilter);
+	int flib_netconn_send_mapMazeSize(NetconnPtr conn, int mazeSize);
+	int flib_netconn_send_mapSeed(NetconnPtr conn, String seed);
+	int flib_netconn_send_mapTheme(NetconnPtr conn, String theme);
+	int flib_netconn_send_mapDrawdata(NetconnPtr conn, ByteArrayPtr drawData, NativeSizeT size);
+	int flib_netconn_send_script(NetconnPtr conn, String scriptName);
+	int flib_netconn_send_scheme(NetconnPtr conn, SchemePtr scheme);
+	int flib_netconn_send_roundfinished(NetconnPtr conn, boolean withoutError);
+	int flib_netconn_send_ban(NetconnPtr conn, String playerName);
+	int flib_netconn_send_kick(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerInfo(NetconnPtr conn, String playerName);
+	int flib_netconn_send_playerFollow(NetconnPtr conn, String playerName);
+	int flib_netconn_send_startGame(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictJoins(NetconnPtr conn);
+	int flib_netconn_send_toggleRestrictTeams(NetconnPtr conn);
+	int flib_netconn_send_clearAccountsCache(NetconnPtr conn);
+	int flib_netconn_send_setServerVar(NetconnPtr conn, String name, String value);
+	int flib_netconn_send_getServerVars(NetconnPtr conn);
+
+	void flib_netconn_onMessage(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onChat(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onConnected(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onDisconnected(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onRoomlist(NetconnPtr conn, RoomListCallback callback, Pointer context);
+	void flib_netconn_onRoomAdd(NetconnPtr conn, RoomCallback callback, Pointer context);
+	void flib_netconn_onRoomDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomUpdate(NetconnPtr conn, StrRoomCallback callback, Pointer context);
+	void flib_netconn_onLobbyJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onLobbyLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onNickTaken(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onPasswordRequest(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onEnterRoom(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onRoomChiefStatus(NetconnPtr conn, BoolCallback callback, Pointer context);
+	void flib_netconn_onReadyState(NetconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_netconn_onLeaveRoom(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onTeamAdd(NetconnPtr conn, TeamCallback callback, Pointer context);
+	void flib_netconn_onTeamDelete(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomJoin(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onRoomLeave(NetconnPtr conn, StrStrCallback callback, Pointer context);
+	void flib_netconn_onRunGame(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onTeamAccepted(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onHogCountChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onTeamColorChanged(NetconnPtr conn, StrIntCallback callback, Pointer context);
+	void flib_netconn_onEngineMessage(NetconnPtr conn, BytesCallback callback, Pointer context);
+	void flib_netconn_onSchemeChanged(NetconnPtr conn, SchemeCallback callback, Pointer context);
+	void flib_netconn_onMapChanged(NetconnPtr conn, MapIntCallback callback, Pointer context);
+	void flib_netconn_onScriptChanged(NetconnPtr conn, StrCallback callback, Pointer context);
+	void flib_netconn_onWeaponsetChanged(NetconnPtr conn, WeaponsetCallback callback, Pointer context);
+	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
+
+	// ipc/gameconn.h
+
+	GameconnPtr flib_gameconn_create(String playerName, GameSetupPtr setup, boolean netgame);
+	GameconnPtr flib_gameconn_create_playdemo(ByteArrayPtr demo, NativeSizeT size);
+	GameconnPtr flib_gameconn_create_loadgame(String playerName, ByteArrayPtr save, NativeSizeT size);
+	GameconnPtr flib_gameconn_create_campaign(String playerName, String seed, String script);
+
+	void flib_gameconn_destroy(GameconnPtr conn);
+	int flib_gameconn_getport(GameconnPtr conn);
+	void flib_gameconn_tick(GameconnPtr conn);
+
+	int flib_gameconn_send_enginemsg(GameconnPtr conn, ByteArrayPtr data, NativeSizeT len);
+	int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg);
+	int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg);
+	int flib_gameconn_send_quit(GameconnPtr conn);
+	int flib_gameconn_send_cmd(GameconnPtr conn, String cmdString);
+
+	void flib_gameconn_onConnect(GameconnPtr conn, VoidCallback callback, Pointer context);
+	void flib_gameconn_onDisconnect(GameconnPtr conn, IntCallback callback, Pointer context);
+	void flib_gameconn_onErrorMessage(GameconnPtr conn, StrCallback callback, Pointer context);
+	void flib_gameconn_onChat(GameconnPtr conn, StrBoolCallback callback, Pointer context);
+	void flib_gameconn_onGameRecorded(GameconnPtr conn, BytesBoolCallback callback, Pointer context);
+	void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context);
+
+	// ipc/mapconn.h
+	MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc);
+	void flib_mapconn_destroy(MapconnPtr conn);
+	int flib_mapconn_getport(MapconnPtr conn);
+	void flib_mapconn_onSuccess(MapconnPtr conn, MapimageCallback callback, Pointer context);
+	void flib_mapconn_onFailure(MapconnPtr conn, StrCallback callback, Pointer context);
+	void flib_mapconn_tick(MapconnPtr conn);
+
+	// model/schemelist.h
+	SchemelistPtr flib_schemelist_from_ini(String filename);
+	int flib_schemelist_to_ini(String filename, SchemelistPtr list);
+	void flib_schemelist_destroy(SchemelistPtr list);
+
+	// model/team.h
+	TeamPtr flib_team_from_ini(String filename);
+	int flib_team_to_ini(String filename, TeamPtr team);
+	void flib_team_destroy(TeamPtr team);
+
+	// model/weapon.h
+	WeaponsetListPtr flib_weaponsetlist_from_ini(String filename);
+	int flib_weaponsetlist_to_ini(String filename, WeaponsetListPtr weaponsets);
+	void flib_weaponsetlist_destroy(WeaponsetListPtr list);
+
+	// model/gamesetup.h
+	void flib_gamesetup_destroy(GameSetupPtr gamesetup);
+
+	// util/logging.h
+    void flib_log_setLevel(int level);
+    void flib_log_setCallback(LogCallback callback);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,37 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "frontlib.h"
+#include "util/logging.h"
+#include <SDL_net.h>
+
+int flib_init() {
+	flib_log_d("Initializing frontlib");
+	if(SDLNet_Init()==-1) {
+		flib_log_e("Error in SDLNet_Init: %s", SDLNet_GetError());
+		return -1;
+	}
+
+	return 0;
+}
+
+void flib_quit() {
+	flib_log_d("Shutting down frontlib");
+	SDLNet_Quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Public header file for the hedgewars frontent networking library.
+ *
+ * This is the only header you should need to include from frontend code.
+ */
+
+#ifndef FRONTLIB_H_
+#define FRONTLIB_H_
+
+#include "ipc/gameconn.h"
+#include "ipc/mapconn.h"
+#include "net/netconn.h"
+#include "util/logging.h"
+#include "model/schemelist.h"
+
+/**
+ * Call this function before anything else in this library.
+ * Returns 0 on success, -1 on error.
+ */
+int flib_init();
+
+/**
+ * Free resources associated with the library. Call this function once
+ * the library is no longer needed. You can re-initialize the library by calling
+ * flib_init again.
+ */
+void flib_quit();
+
+#endif /* FRONTLIB_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/hwconsts.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,100 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "hwconsts.h"
+
+const uint32_t flib_teamcolors[] = HW_TEAMCOLOR_ARRAY;
+const size_t flib_teamcolor_count = sizeof(flib_teamcolors)/sizeof(uint32_t)-1;
+
+static const flib_metascheme_setting metaSchemeSettings[] = {
+	{ .name = "damagefactor",      .times1000 = false, .engineCommand = "e$damagepct",   .maxMeansInfinity = false, .min = 10, .max = 300,  .def = 100 },
+	{ .name = "turntime",          .times1000 = true,  .engineCommand = "e$turntime",    .maxMeansInfinity = true,  .min = 1,  .max = 9999, .def = 45  },
+	{ .name = "health",            .times1000 = false, .engineCommand = NULL,            .maxMeansInfinity = false, .min = 50, .max = 200,  .def = 100 },
+	{ .name = "suddendeath",       .times1000 = false, .engineCommand = "e$sd_turns",    .maxMeansInfinity = true,  .min = 0,  .max = 50,   .def = 15  },
+	{ .name = "caseprobability",   .times1000 = false, .engineCommand = "e$casefreq",    .maxMeansInfinity = false, .min = 0,  .max = 9,    .def = 5   },
+	{ .name = "minestime",         .times1000 = true,  .engineCommand = "e$minestime",   .maxMeansInfinity = false, .min = -1, .max = 5,    .def = 3   },
+	{ .name = "minesnum",          .times1000 = false, .engineCommand = "e$minesnum",    .maxMeansInfinity = false, .min = 0,  .max = 80,   .def = 4   },
+	{ .name = "minedudpct",        .times1000 = false, .engineCommand = "e$minedudpct",  .maxMeansInfinity = false, .min = 0,  .max = 100,  .def = 0   },
+	{ .name = "explosives",        .times1000 = false, .engineCommand = "e$explosives",  .maxMeansInfinity = false, .min = 0,  .max = 40,   .def = 2   },
+	{ .name = "healthprobability", .times1000 = false, .engineCommand = "e$healthprob",  .maxMeansInfinity = false, .min = 0,  .max = 100,  .def = 35  },
+	{ .name = "healthcaseamount",  .times1000 = false, .engineCommand = "e$hcaseamount", .maxMeansInfinity = false, .min = 0,  .max = 200,  .def = 25  },
+	{ .name = "waterrise",         .times1000 = false, .engineCommand = "e$waterrise",   .maxMeansInfinity = false, .min = 0,  .max = 100,  .def = 47  },
+	{ .name = "healthdecrease",    .times1000 = false, .engineCommand = "e$healthdec",   .maxMeansInfinity = false, .min = 0,  .max = 100,  .def = 5   },
+	{ .name = "ropepct",           .times1000 = false, .engineCommand = "e$ropepct",     .maxMeansInfinity = false, .min = 25, .max = 999,  .def = 100 },
+	{ .name = "getawaytime",       .times1000 = false, .engineCommand = "e$getawaytime", .maxMeansInfinity = false, .min = 0,  .max = 999,  .def = 100 }
+};
+
+static const flib_metascheme_mod metaSchemeMods[] = {
+	{ .name = "fortsmode",          .bitmaskIndex = 12 },
+	{ .name = "divteams",           .bitmaskIndex = 4  },
+	{ .name = "solidland",          .bitmaskIndex = 2  },
+	{ .name = "border",             .bitmaskIndex = 3  },
+	{ .name = "lowgrav",            .bitmaskIndex = 5  },
+	{ .name = "laser",              .bitmaskIndex = 6  },
+	{ .name = "invulnerability",    .bitmaskIndex = 7  },
+	{ .name = "resethealth",        .bitmaskIndex = 8  },
+	{ .name = "vampiric",           .bitmaskIndex = 9  },
+	{ .name = "karma",              .bitmaskIndex = 10 },
+	{ .name = "artillery",          .bitmaskIndex = 11 },
+	{ .name = "randomorder",        .bitmaskIndex = 13 },
+	{ .name = "king",               .bitmaskIndex = 14 },
+	{ .name = "placehog",           .bitmaskIndex = 15 },
+	{ .name = "sharedammo",         .bitmaskIndex = 16 },
+	{ .name = "disablegirders",     .bitmaskIndex = 17 },
+	{ .name = "disablelandobjects", .bitmaskIndex = 18 },
+	{ .name = "aisurvival",         .bitmaskIndex = 19 },
+	{ .name = "infattack",          .bitmaskIndex = 20 },
+	{ .name = "resetweps",          .bitmaskIndex = 21 },
+	{ .name = "perhogammo",         .bitmaskIndex = 22 },
+	{ .name = "disablewind",        .bitmaskIndex = 23 },
+	{ .name = "morewind",           .bitmaskIndex = 24 },
+	{ .name = "tagteam",            .bitmaskIndex = 25 },
+	{ .name = "bottomborder",       .bitmaskIndex = 26 }
+};
+
+const flib_metascheme flib_meta = {
+	.settingCount = sizeof(metaSchemeSettings)/sizeof(flib_metascheme_setting),
+	.modCount = sizeof(metaSchemeMods)/sizeof(flib_metascheme_mod),
+	.settings = metaSchemeSettings,
+	.mods = metaSchemeMods
+};
+
+uint32_t flib_get_teamcolor(int colorIndex) {
+	if(colorIndex>=0 && colorIndex < flib_teamcolor_count) {
+		return flib_teamcolors[colorIndex];
+	} else {
+		return 0;
+	}
+}
+
+int flib_get_teamcolor_count() {
+	return flib_teamcolor_count;
+}
+
+int flib_get_hedgehogs_per_team() {
+	return HEDGEHOGS_PER_TEAM;
+}
+
+int flib_get_weapons_count() {
+	return WEAPONS_COUNT;
+}
+
+const flib_metascheme *flib_get_metascheme() {
+	return &flib_meta;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/hwconsts.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,117 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/**
+ * This file contains important constants which might need to be changed to adapt to
+ * changes in the engine or protocols.
+ *
+ * It also contains getter functions for some constants (in particular for constants
+ * that are important for the layout of data structures), so that client code can
+ * query the constants that the library was built with.
+ */
+
+#ifndef HWCONSTS_H_
+#define HWCONSTS_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#define HEDGEHOGS_PER_TEAM 8
+#define DEFAULT_HEDGEHOG_COUNT 4
+#define DEFAULT_COLOR_INDEX 0
+
+#define NETGAME_DEFAULT_PORT 46631
+#define PROTOCOL_VERSION 42
+#define MIN_SERVER_VERSION 1
+
+// Used for sending scripts to the engine
+#define MULTIPLAYER_SCRIPT_PATH "Scripts/Multiplayer/"
+
+#define WEAPONS_COUNT 55
+
+/* A merge of mikade/bugq colours w/ a bit of channel feedback */
+#define HW_TEAMCOLOR_ARRAY  { UINT32_C(0xffff0204), /* red    */ \
+                              UINT32_C(0xff4980c1), /* blue   */ \
+                              UINT32_C(0xff1de6ba), /* teal   */ \
+                              UINT32_C(0xffb541ef), /* purple */ \
+                              UINT32_C(0xffe55bb0), /* pink   */ \
+                              UINT32_C(0xff20bf00), /* green  */ \
+                              UINT32_C(0xfffe8b0e), /* orange */ \
+                              UINT32_C(0xff5f3605), /* brown  */ \
+                              UINT32_C(0xffffff01), /* yellow */ \
+                              /* add new colors here */ \
+                              0 } /* Keep this 0 at the end */
+
+extern const size_t flib_teamcolor_count;
+extern const uint32_t flib_teamcolors[];
+
+/**
+ * Returns the team color (ARGB) corresponding to the color index (0 if index out of bounds)
+ */
+uint32_t flib_get_teamcolor(int colorIndex);
+
+/**
+ * Returns the number of team colors (i.e. the length of the flib_teamcolors array)
+ */
+int flib_get_teamcolor_count();
+
+/**
+ * Returns the HEDGEHOGS_PER_TEAM constant
+ */
+int flib_get_hedgehogs_per_team();
+
+/**
+ * Returns the WEAPONS_COUNT constant
+ */
+int flib_get_weapons_count();
+
+/*
+ * These structs define the meaning of values in the flib_scheme struct, i.e. their correspondence to
+ * ini settings, engine commands and positions in the network protocol (the last is encoded in the
+ * order of settings/mods).
+ */
+typedef struct {
+    const char *name;				// A name identifying this setting (used as key in the schemes file)
+    const char *engineCommand;		// The command needed to send the setting to the engine. May be null if the setting is not sent to the engine (for the "health" setting)
+    const bool maxMeansInfinity;	// If true, send a very high number to the engine if the setting is equal to its maximum
+    const bool times1000;			// If true (for time-based settings), multiply the setting by 1000 before sending it to the engine.
+    const int min;					// The smallest allowed value
+    const int max;					// The highest allowed value
+    const int def;					// The default value
+} flib_metascheme_setting;
+
+typedef struct {
+    const char *name;				// A name identifying this mod (used as key in the schemes file)
+    const int bitmaskIndex;			// Mods are sent to the engine in a single integer, this field describes which bit of that integer is used
+    								// for this particular mod.
+} flib_metascheme_mod;
+
+typedef struct {
+	const int settingCount;
+	const int modCount;
+	const flib_metascheme_setting *settings;
+	const flib_metascheme_mod *mods;
+} flib_metascheme;
+
+extern const flib_metascheme flib_meta;
+
+const flib_metascheme *flib_get_metascheme();
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/LICENSE	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,23 @@
+Copyright (c) 2000-2012 by Nicolas Devillard.
+MIT License
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/VERSION	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,2 @@
+This is version 3.1 of the iniparser library developed by N. Devillard.
+See http://ndevilla.free.fr/iniparser/ for details and new versions.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/dictionary.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.c
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ    1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ   128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY    ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                            Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, int size)
+{
+    void * newptr ;
+ 
+    newptr = calloc(2*size, 1);
+    if (newptr==NULL) {
+        return NULL ;
+    }
+    memcpy(newptr, ptr, size);
+    free(ptr);
+    return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Duplicate a string
+  @param    s String to duplicate
+  @return   Pointer to a newly allocated string, to be freed with free()
+
+  This is a replacement for strdup(). This implementation is provided
+  for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+    char * t ;
+    if (!s)
+        return NULL ;
+    t = (char*)malloc(strlen(s)+1) ;
+    if (t) {
+        strcpy(t,s);
+    }
+    return t ;
+}
+
+/*---------------------------------------------------------------------------
+                            Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+    int         len ;
+    unsigned    hash ;
+    int         i ;
+
+    len = strlen(key);
+    for (hash=0, i=0 ; i<len ; i++) {
+        hash += (unsigned)key[i] ;
+        hash += (hash<<10);
+        hash ^= (hash>>6) ;
+    }
+    hash += (hash <<3);
+    hash ^= (hash >>11);
+    hash += (hash <<15);
+    return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size)
+{
+    dictionary  *   d ;
+
+    /* If no size was specified, allocate space for DICTMINSZ */
+    if (size<DICTMINSZ) size=DICTMINSZ ;
+
+    if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+        return NULL;
+    }
+    d->size = size ;
+    d->val  = (char **)calloc(size, sizeof(char*));
+    d->key  = (char **)calloc(size, sizeof(char*));
+    d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+    return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+    int     i ;
+
+    if (d==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]!=NULL)
+            free(d->key[i]);
+        if (d->val[i]!=NULL)
+            free(d->val[i]);
+    }
+    free(d->val);
+    free(d->key);
+    free(d->hash);
+    free(d);
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+    unsigned    hash ;
+    int         i ;
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                return d->val[i] ;
+            }
+        }
+    }
+    return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+    int         i ;
+    unsigned    hash ;
+
+    if (d==NULL || key==NULL) return -1 ;
+    
+    /* Compute hash for this key */
+    hash = dictionary_hash(key) ;
+    /* Find if value is already in dictionary */
+    if (d->n>0) {
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            if (hash==d->hash[i]) { /* Same hash value */
+                if (!strcmp(key, d->key[i])) {   /* Same key */
+                    /* Found a value: modify and return */
+                    if (d->val[i]!=NULL)
+                        free(d->val[i]);
+                    d->val[i] = val ? xstrdup(val) : NULL ;
+                    /* Value has been modified: return */
+                    return 0 ;
+                }
+            }
+        }
+    }
+    /* Add a new value */
+    /* See if dictionary needs to grow */
+    if (d->n==d->size) {
+
+        /* Reached maximum size: reallocate dictionary */
+        d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ;
+        d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ;
+        d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+            /* Cannot grow dictionary */
+            return -1 ;
+        }
+        /* Double size */
+        d->size *= 2 ;
+    }
+
+    /* Insert key in the first empty slot. Start at d->n and wrap at
+       d->size. Because d->n < d->size this will necessarily
+       terminate. */
+    for (i=d->n ; d->key[i] ; ) {
+        if(++i == d->size) i = 0;
+    }
+    /* Copy key */
+    d->key[i]  = xstrdup(key);
+    d->val[i]  = val ? xstrdup(val) : NULL ;
+    d->hash[i] = hash;
+    d->n ++ ;
+    return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+    unsigned    hash ;
+    int         i ;
+
+    if (key == NULL) {
+        return;
+    }
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                /* Found key */
+                break ;
+            }
+        }
+    }
+    if (i>=d->size)
+        /* Key not found */
+        return ;
+
+    free(d->key[i]);
+    d->key[i] = NULL ;
+    if (d->val[i]!=NULL) {
+        free(d->val[i]);
+        d->val[i] = NULL ;
+    }
+    d->hash[i] = 0 ;
+    d->n -- ;
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+    int     i ;
+
+    if (d==NULL || out==NULL) return ;
+    if (d->n<1) {
+        fprintf(out, "empty dictionary\n");
+        return ;
+    }
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]) {
+            fprintf(out, "%20s\t[%s]\n",
+                    d->key[i],
+                    d->val[i] ? d->val[i] : "UNDEF");
+        }
+    }
+    return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+    dictionary  *   d ;
+    char    *   val ;
+    int         i ;
+    char        cval[90] ;
+
+    /* Allocate dictionary */
+    printf("allocating...\n");
+    d = dictionary_new(0);
+    
+    /* Set values in dictionary */
+    printf("setting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_set(d, cval, "salut");
+    }
+    printf("getting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        val = dictionary_get(d, cval, DICT_INVALID_KEY);
+        if (val==DICT_INVALID_KEY) {
+            printf("cannot get value for key [%s]\n", cval);
+        }
+    }
+    printf("unsetting %d values...\n", NVALS);
+    for (i=0 ; i<NVALS ; i++) {
+        sprintf(cval, "%04d", i);
+        dictionary_unset(d, cval);
+    }
+    if (d->n != 0) {
+        printf("error deleting values\n");
+    }
+    printf("deallocating...\n");
+    dictionary_del(d);
+    return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/dictionary.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,165 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.h
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*---------------------------------------------------------------------------
+                                New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dictionary object
+
+  This object contains a list of string/string associations. Each
+  association is identified by a unique string key. Looking up values
+  in the dictionary is speeded up by the use of a (hopefully collision-free)
+  hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+    int             n ;     /** Number of entries in dictionary */
+    int             size ;  /** Storage size */
+    char        **  val ;   /** List of string values */
+    char        **  key ;   /** List of string keys */
+    unsigned     *  hash ;  /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+                            Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary objet.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/iniparser.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,748 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.c
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ         (1024)
+#define INI_INVALID_KEY     ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                        Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+    LINE_UNPROCESSED,
+    LINE_ERROR,
+    LINE_EMPTY,
+    LINE_COMMENT,
+    LINE_SECTION,
+    LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Convert a string to lowercase.
+  @param    s   String to convert.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string
+  containing a lowercased version of the input string. Do not free
+  or modify the returned string! Since the returned string is statically
+  allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    int i ;
+
+    if (s==NULL) return NULL ;
+    memset(l, 0, ASCIILINESZ+1);
+    i=0 ;
+    while (s[i] && i<ASCIILINESZ) {
+        l[i] = (char)tolower((int)s[i]);
+        i++ ;
+    }
+    l[ASCIILINESZ]=(char)0;
+    return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Remove blanks at the beginning and the end of a string.
+  @param    s   String to parse.
+  @return   ptr to statically allocated string.
+
+  This function returns a pointer to a statically allocated string,
+  which is identical to the input string, except that all blank
+  characters at the end and the beg. of the string have been removed.
+  Do not free or modify the returned string! Since the returned string
+  is statically allocated, it will be modified at each function call
+  (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(const char * s)
+{
+    static char l[ASCIILINESZ+1];
+    char * last ;
+    
+    if (s==NULL) return NULL ;
+    
+    while (isspace((int)*s) && *s) s++;
+    memset(l, 0, ASCIILINESZ+1);
+    strcpy(l, s);
+    last = l + strlen(l);
+    while (last > l) {
+        if (!isspace((int)*(last-1)))
+            break ;
+        last -- ;
+    }
+    *last = (char)0;
+    return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+    int i ;
+    int nsec ;
+
+    if (d==NULL) return -1 ;
+    nsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            nsec ++ ;
+        }
+    }
+    return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+    int i ;
+    int foundsec ;
+
+    if (d==NULL || n<0) return NULL ;
+    foundsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            foundsec++ ;
+            if (foundsec>n)
+                break ;
+        }
+    }
+    if (foundsec<=n) {
+        return NULL ;
+    }
+    return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+    int     i ;
+
+    if (d==NULL || f==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (d->val[i]!=NULL) {
+            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+        } else {
+            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+        }
+    }
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+    int     i ;
+    int     nsec ;
+    char *  secname ;
+
+    if (d==NULL || f==NULL) return ;
+
+    nsec = iniparser_getnsec(d);
+    if (nsec<1) {
+        /* No section in file: dump all keys as they are */
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+        }
+        return ;
+    }
+    for (i=0 ; i<nsec ; i++) {
+        secname = iniparser_getsecname(d, i) ;
+        iniparser_dumpsection_ini(d, secname, f) ;
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+{
+    int     j ;
+    char    keym[ASCIILINESZ+1];
+    int     seclen ;
+
+    if (d==NULL || f==NULL) return ;
+    if (! iniparser_find_entry(d, s)) return ;
+
+    seclen  = (int)strlen(s);
+    fprintf(f, "\n[%s]\n", s);
+    sprintf(keym, "%s:", s);
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            fprintf(f,
+                    "%-30s = %s\n",
+                    d->key[j]+seclen+1,
+                    d->val[j] ? d->val[j] : "");
+        }
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+    int     seclen, nkeys ;
+    char    keym[ASCIILINESZ+1];
+    int j ;
+
+    nkeys = 0;
+
+    if (d==NULL) return nkeys;
+    if (! iniparser_find_entry(d, s)) return nkeys;
+
+    seclen  = (int)strlen(s);
+    sprintf(keym, "%s:", s);
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) 
+            nkeys++;
+    }
+
+    return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+  
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+    char **keys;
+
+    int i, j ;
+    char    keym[ASCIILINESZ+1];
+    int     seclen, nkeys ;
+
+    keys = NULL;
+
+    if (d==NULL) return keys;
+    if (! iniparser_find_entry(d, s)) return keys;
+
+    nkeys = iniparser_getsecnkeys(d, s);
+
+    keys = (char**) malloc(nkeys*sizeof(char*));
+
+    seclen  = (int)strlen(s);
+    sprintf(keym, "%s:", s);
+    
+    i = 0;
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            keys[i] = d->key[j];
+            i++;
+        }
+    }
+
+    return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+    char * lc_key ;
+    char * sval ;
+
+    if (d==NULL || key==NULL)
+        return def ;
+
+    lc_key = strlwc(key);
+    sval = dictionary_get(d, lc_key, def);
+    return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  "42"      ->  42
+  "042"     ->  34 (octal -> decimal)
+  "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+    char    *   str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+    char    *   c ;
+    int         ret ;
+
+    c = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (c==INI_INVALID_KEY) return notfound ;
+    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+        ret = 1 ;
+    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+        ret = 0 ;
+    } else {
+        ret = notfound ;
+    }
+    return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+    dictionary  *   ini,
+    const char  *   entry
+)
+{
+    int found=0 ;
+    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+        found = 1 ;
+    }
+    return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+    return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+    dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Load a single line from an INI file
+  @param    input_line  Input line, may be concatenated multi-line input
+  @param    section     Output space to store section
+  @param    key         Output space to store key
+  @param    value       Output space to store value
+  @return   line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+    const char * input_line,
+    char * section,
+    char * key,
+    char * value)
+{   
+    line_status sta ;
+    char        line[ASCIILINESZ+1];
+    int         len ;
+
+    strcpy(line, strstrip(input_line));
+    len = (int)strlen(line);
+
+    sta = LINE_UNPROCESSED ;
+    if (len<1) {
+        /* Empty line */
+        sta = LINE_EMPTY ;
+    } else if (line[0]=='#' || line[0]==';') {
+        /* Comment line */
+        sta = LINE_COMMENT ; 
+    } else if (line[0]=='[' && line[len-1]==']') {
+        /* Section name */
+        sscanf(line, "[%[^]]", section);
+        strcpy(section, strstrip(section));
+        strcpy(section, strlwc(section));
+        sta = LINE_SECTION ;
+    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
+           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
+        /* Usual key=value, with or without comments */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        strcpy(value, strstrip(value));
+        /*
+         * sscanf cannot handle '' or "" as empty values
+         * this is done here
+         */
+        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+            value[0]=0 ;
+        }
+        sta = LINE_VALUE ;
+    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
+        /*
+         * Special cases:
+         * key=
+         * key=;
+         * key=#
+         */
+        strcpy(key, strstrip(key));
+        strcpy(key, strlwc(key));
+        value[0]=0 ;
+        sta = LINE_VALUE ;
+    } else {
+        /* Generate syntax error */
+        sta = LINE_ERROR ;
+    }
+    return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+    FILE * in ;
+
+    char line    [ASCIILINESZ+1] ;
+    char section [ASCIILINESZ+1] ;
+    char key     [ASCIILINESZ+1] ;
+    char tmp     [ASCIILINESZ+1] ;
+    char val     [ASCIILINESZ+1] ;
+
+    int  last=0 ;
+    int  len ;
+    int  lineno=0 ;
+    int  errs=0;
+
+    dictionary * dict ;
+
+    if ((in=fopen(ininame, "r"))==NULL) {
+        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+        return NULL ;
+    }
+
+    dict = dictionary_new(0) ;
+    if (!dict) {
+        fclose(in);
+        return NULL ;
+    }
+
+    memset(line,    0, ASCIILINESZ);
+    memset(section, 0, ASCIILINESZ);
+    memset(key,     0, ASCIILINESZ);
+    memset(val,     0, ASCIILINESZ);
+    last=0 ;
+
+    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+        lineno++ ;
+        len = (int)strlen(line)-1;
+        if (len==0)
+            continue;
+        /* Safety check against buffer overflows */
+        if (line[len]!='\n') {
+            fprintf(stderr,
+                    "iniparser: input line too long in %s (%d)\n",
+                    ininame,
+                    lineno);
+            dictionary_del(dict);
+            fclose(in);
+            return NULL ;
+        }
+        /* Get rid of \n and spaces at end of line */
+        while ((len>=0) &&
+                ((line[len]=='\n') || (isspace(line[len])))) {
+            line[len]=0 ;
+            len-- ;
+        }
+        /* Detect multi-line */
+        if (line[len]=='\\') {
+            /* Multi-line value */
+            last=len ;
+            continue ;
+        } else {
+            last=0 ;
+        }
+        switch (iniparser_line(line, section, key, val)) {
+            case LINE_EMPTY:
+            case LINE_COMMENT:
+            break ;
+
+            case LINE_SECTION:
+            errs = dictionary_set(dict, section, NULL);
+            break ;
+
+            case LINE_VALUE:
+            sprintf(tmp, "%s:%s", section, key);
+            errs = dictionary_set(dict, tmp, val) ;
+            break ;
+
+            case LINE_ERROR:
+            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+                    ininame,
+                    lineno);
+            fprintf(stderr, "-> %s\n", line);
+            errs++ ;
+            break;
+
+            default:
+            break ;
+        }
+        memset(line, 0, ASCIILINESZ);
+        last=0;
+        if (errs<0) {
+            fprintf(stderr, "iniparser: memory allocation failure\n");
+            break ;
+        }
+    }
+    if (errs) {
+        dictionary_del(dict);
+        dict = NULL ;
+    }
+    fclose(in);
+    return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+    dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/iniparser.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,307 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.h
+   @author  N. Devillard
+   @brief   Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   pointer to statically allocated character strings
+
+  This function queries a dictionary and finds all keys in a given section.
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  - "42"      ->  42
+  - "042"     ->  34 (octal -> decimal)
+  - "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, -1 is returned.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,452 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "gameconn.h"
+#include "ipcbase.h"
+#include "ipcprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../hwconsts.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum {
+	AWAIT_CONNECTION,
+	CONNECTED,
+	FINISHED
+} gameconn_state;
+
+struct _flib_gameconn {
+	flib_ipcbase *ipcBase;
+	flib_vector *configBuffer;
+	flib_vector *demoBuffer;
+	char *playerName;
+
+	gameconn_state state;
+	bool netgame;
+	int disconnectReason;
+
+	void (*onConnectCb)(void* context);
+	void *onConnectCtx;
+
+	void (*onDisconnectCb)(void* context, int reason);
+	void *onDisconnectCtx;
+
+	void (*onErrorMessageCb)(void* context, const char *msg);
+	void *onErrorMessageCtx;
+
+	void (*onChatCb)(void* context, const char *msg, bool teamchat);
+	void *onChatCtx;
+
+	void (*onGameRecordedCb)(void *context, const uint8_t *record, size_t size, bool isSavegame);
+	void *onGameRecordedCtx;
+
+	void (*onEngineMessageCb)(void *context, const uint8_t *em, size_t size);
+	void *onEngineMessageCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onErrorMessage(void* context, const char *msg) {
+	flib_log_w("Error from engine (no callback set): %s", msg);
+}
+
+static void clearCallbacks(flib_gameconn *conn) {
+	flib_gameconn_onConnect(conn, NULL, NULL);
+	flib_gameconn_onDisconnect(conn, NULL, NULL);
+	flib_gameconn_onErrorMessage(conn, NULL, NULL);
+	flib_gameconn_onChat(conn, NULL, NULL);
+	flib_gameconn_onGameRecorded(conn, NULL, NULL);
+	flib_gameconn_onEngineMessage(conn, NULL, NULL);
+}
+
+static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
+	if(tempConn) {
+		tempConn->ipcBase = flib_ipcbase_create();
+		tempConn->configBuffer = flib_vector_create();
+		tempConn->playerName = flib_strdupnull(playerName);
+		if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) {
+			if(record) {
+				tempConn->demoBuffer = flib_vector_create();
+			}
+			tempConn->state = AWAIT_CONNECTION;
+			tempConn->netgame = netGame;
+			tempConn->disconnectReason = GAME_END_ERROR;
+			clearCallbacks(tempConn);
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) {
+	if(log_badargs_if2(playerName==NULL, setup==NULL)) {
+		return NULL;
+	}
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
+	if(tempConn) {
+		if(flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) {
+			flib_log_e("Error generating full game configuration for the engine.");
+		} else {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demoFileContent, size_t size) {
+	if(log_badargs_if(demoFileContent==NULL && size>0)) {
+		return NULL;
+	}
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
+	if(tempConn) {
+		if(!flib_vector_append(tempConn->configBuffer, demoFileContent, size)) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *saveFileContent, size_t size) {
+	if(log_badargs_if(saveFileContent==NULL && size>0)) {
+		return NULL;
+	}
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(!flib_vector_append(tempConn->configBuffer, saveFileContent, size)) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) {
+	if(log_badargs_if3(playerName==NULL, seed==NULL, script==NULL)) {
+		return NULL;
+	}
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(!flib_ipc_append_message(tempConn->configBuffer, "TL")
+				&& !flib_ipc_append_seed(tempConn->configBuffer, seed)
+				&& !flib_ipc_append_script(tempConn->configBuffer, script)
+				&& !flib_ipc_append_message(tempConn->configBuffer, "!")) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+void flib_gameconn_destroy(flib_gameconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_ipcbase_destroy(conn->ipcBase);
+			flib_vector_destroy(conn->configBuffer);
+			flib_vector_destroy(conn->demoBuffer);
+			free(conn->playerName);
+			free(conn);
+		}
+	}
+}
+
+int flib_gameconn_getport(flib_gameconn *conn) {
+	if(log_badargs_if(conn==NULL)) {
+		return 0;
+	}
+	return flib_ipcbase_port(conn->ipcBase);
+}
+
+static void demo_append(flib_gameconn *conn, const void *data, size_t len) {
+	if(conn->demoBuffer) {
+		if(flib_vector_append(conn->demoBuffer, data, len)) {
+			flib_log_e("Error recording demo: Out of memory.");
+			flib_vector_destroy(conn->demoBuffer);
+			conn->demoBuffer = NULL;
+		}
+	}
+}
+
+static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) {
+	size_t msglen = strlen(message);
+
+	// If the message starts with /me, it will be displayed differently.
+	bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4);
+	const char *template = meMessage ? "s\x02* %s %s  " : "s\x01%s: %s  ";
+	int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message);
+	if(log_e_if(size<=0, "printf error")) {
+		return -1;
+	} else {
+		buffer[0] = size>255 ? 255 : size;
+		return 0;
+	}
+}
+
+static void demo_append_chatmessage(flib_gameconn *conn, const char *message) {
+	// Chat messages are reformatted to make them look as if they were received, not sent.
+	uint8_t converted[257];
+	if(!format_chatmessage(converted, conn->playerName, message)) {
+		demo_append(conn, converted, converted[0]+1);
+	}
+}
+
+static void demo_replace_gamemode(flib_buffer buf, char gamemode) {
+	size_t msgStart = 0;
+	uint8_t *data = (uint8_t*)buf.data;
+	while(msgStart+2 < buf.size) {
+		if(!memcmp(data+msgStart, "\x02T", 2)) {
+			data[msgStart+2] = gamemode;
+		}
+		msgStart += (uint8_t)data[msgStart]+1;
+	}
+}
+
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len) {
+	if(log_badargs_if2(conn==NULL, data==NULL && len>0)) {
+		return -1;
+	}
+	int result = flib_ipcbase_send_raw(conn->ipcBase, data, len);
+	if(!result) {
+		demo_append(conn, data, len);
+	}
+	return result;
+}
+
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) {
+	if(log_badargs_if2(conn==NULL, msg==NULL)) {
+		return -1;
+	}
+	int result = -1;
+	uint8_t converted[257];
+	int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg);
+	if(size>0) {
+		converted[0] = size>255 ? 255 : size;
+		if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
+			demo_append(conn, converted, converted[0]+1);
+			result = 0;
+		}
+	}
+	return result;
+}
+
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) {
+	if(log_badargs_if3(conn==NULL, playername==NULL, msg==NULL)) {
+		return -1;
+	}
+	uint8_t converted[257];
+	if(!format_chatmessage(converted, playername, msg)
+			&& !flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
+		demo_append(conn, converted, converted[0]+1);
+		return 0;
+	}
+	return -1;
+}
+
+int flib_gameconn_send_quit(flib_gameconn *conn) {
+	return flib_gameconn_send_cmd(conn, "efinish");
+}
+
+int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString) {
+	if(log_badargs_if2(conn==NULL, cmdString==NULL)) {
+		return -1;
+	}
+	int result = -1;
+	uint8_t converted[256];
+	size_t msglen = strlen(cmdString);
+	if(!log_e_if(msglen>255, "Message too long: %s", cmdString)) {
+		strcpy((char*)converted+1, cmdString);
+		converted[0] = msglen;
+		if(!flib_ipcbase_send_raw(conn->ipcBase, converted, msglen+1)) {
+			demo_append(conn, converted, msglen+1);
+			result = 0;
+		}
+	}
+	return result;
+}
+
+/**
+ * This macro generates a callback setter function. It uses the name of the callback to
+ * automatically generate the function name and the fields to set, so a consistent naming
+ * convention needs to be enforced (not that that is a bad thing). If null is passed as
+ * callback to the generated function, the defaultCb will be set instead (with conn
+ * as the context).
+ */
+#define GENERATE_CB_SETTER(cbName, cbParameterTypes, defaultCb) \
+	void flib_gameconn_##cbName(flib_gameconn *conn, void (*callback)cbParameterTypes, void *context) { \
+		if(!log_badargs_if(conn==NULL)) { \
+			conn->cbName##Cb = callback ? callback : &defaultCb; \
+			conn->cbName##Ctx = callback ? context : conn; \
+		} \
+	}
+
+/**
+ * Generate a callback setter function like GENERATE_CB_SETTER, and automatically generate a
+ * no-op callback function as well that is used as default.
+ */
+#define GENERATE_CB_SETTER_AND_DEFAULT(cbName, cbParameterTypes) \
+	static void _noop_callback_##cbName cbParameterTypes {} \
+	GENERATE_CB_SETTER(cbName, cbParameterTypes, _noop_callback_##cbName)
+
+GENERATE_CB_SETTER_AND_DEFAULT(onConnect, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onDisconnect, (void* context, int reason));
+GENERATE_CB_SETTER(onErrorMessage, (void* context, const char *msg), defaultCallback_onErrorMessage);
+GENERATE_CB_SETTER_AND_DEFAULT(onChat, (void* context, const char *msg, bool teamchat));
+GENERATE_CB_SETTER_AND_DEFAULT(onGameRecorded, (void *context, const uint8_t *record, size_t size, bool isSavegame));
+GENERATE_CB_SETTER_AND_DEFAULT(onEngineMessage, (void *context, const uint8_t *em, size_t size));
+
+#undef GENERATE_CB_SETTER_AND_DEFAULT
+#undef GENERATE_CB_SETTER
+
+static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
+	if(conn->state == AWAIT_CONNECTION) {
+		flib_ipcbase_accept(conn->ipcBase);
+		switch(flib_ipcbase_state(conn->ipcBase)) {
+		case IPC_CONNECTED:
+			{
+				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+				if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+					return;
+				} else {
+					demo_append(conn, configBuffer.data, configBuffer.size);
+					conn->state = CONNECTED;
+					conn->onConnectCb(conn->onConnectCtx);
+					if(conn->destroyRequested) {
+						return;
+					}
+				}
+			}
+			break;
+		case IPC_NOT_CONNECTED:
+			conn->state = FINISHED;
+			conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+			return;
+		default:
+			break;
+		}
+	}
+
+	if(conn->state == CONNECTED) {
+		uint8_t msgbuffer[257];
+		int len;
+		while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) {
+			if(len<2) {
+				flib_log_w("Received short message from IPC (<2 bytes)");
+				continue;
+			}
+			switch(msgbuffer[1]) {
+			case '?':	// Ping
+				// The pong is already part of the config message
+				break;
+			case 'C':	// Config query
+				// And we already send the config message on connecting.
+				break;
+			case 'E':	// Error message
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
+				}
+				break;
+			case 'i':	// Statistics
+				// TODO stats
+				break;
+			case 'Q':	// Game interrupted
+			case 'H':	// Game halted
+			case 'q':	// game finished
+				{
+					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
+					conn->disconnectReason = reason;
+					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
+					if(conn->demoBuffer) {
+						flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer);
+						demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D');
+						conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame);
+						if(conn->destroyRequested) {
+							return;
+						}
+					}
+					return;
+				}
+			case 's':	// Chat message
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					demo_append_chatmessage(conn, (char*)msgbuffer+2);
+
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
+				}
+				break;
+			case 'b':	// Teamchat message
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
+				}
+				break;
+			default:	// Engine message
+				demo_append(conn, msgbuffer, len);
+
+				conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len);
+				break;
+			}
+		}
+	}
+
+	if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) {
+		conn->state = FINISHED;
+		conn->onDisconnectCb(conn->onDisconnectCtx, conn->disconnectReason);
+	}
+}
+
+void flib_gameconn_tick(flib_gameconn *conn) {
+	if(!log_badargs_if(conn == NULL)
+			&& !log_w_if(conn->running, "Call to flib_gameconn_tick from a callback")
+			&& !log_w_if(conn->state == FINISHED, "We are already done.")) {
+		conn->running = true;
+		flib_gameconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_gameconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,179 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * This file contains functions for starting and interacting with a game run by the engine.
+ * The general usage is to first create a gameconn object by calling one of the flib_gameconn_create
+ * functions. That will cause the frontlib to listen on a random port which can be queried using
+ * flib_gameconn_getport(). You should also register your callback functions right at the start
+ * to ensure you don't miss any callbacks.
+ *
+ * Next, start the engine (that part is up to you) with the appropriate command line arguments
+ * for starting a game.
+ *
+ * In order to allow the gameconn to run, you should regularly call flib_gameconn_tick(), which
+ * performs network I/O and calls your callbacks on interesting events.
+ *
+ * Once the engine connects, the gameconn will send it the required commands for starting the
+ * game you requested in your flib_gameconn_create call.
+ *
+ * When the game is finished (or the connection is lost), you will receive the onDisconnect
+ * message. This is the signal to destroy the gameconn and stop calling tick().
+ */
+
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/*
+ * Different reasons for a disconnect. Only GAME_END_FINISHED signals a correctly completed game.
+ */
+#define GAME_END_FINISHED 0
+#define GAME_END_INTERRUPTED 1
+#define GAME_END_HALTED 2
+#define GAME_END_ERROR 3
+
+typedef struct _flib_gameconn flib_gameconn;
+
+/**
+ * Create a gameconn that will start a local or network game with the indicated configuration.
+ */
+flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame);
+
+/**
+ * Create a gameconn that will play back a demo.
+ */
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demoFileContent, size_t size);
+
+/**
+ * Create a gameconn that will continue from a saved game.
+ */
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *saveFileContent, size_t size);
+
+/**
+ * Create a gameconn that will start a campaign or training mission with the indicated script.
+ * seed is the random seed to use as entropy source (any string).
+ * script is the path and filename of a Campaign or Training script, relative to the Data directory
+ * (e.g. "Missions/Training/Basic_Training_-_Bazooka.lua")
+ */
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script);
+
+/**
+ * Release all resources of this gameconn, including the network connection, and free its memory.
+ * It is safe to call this function from a callback.
+ */
+void flib_gameconn_destroy(flib_gameconn *conn);
+
+/**
+ * Returns the port on which the gameconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_gameconn_getport(flib_gameconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_gameconn_tick(flib_gameconn *conn);
+
+/**
+ * Send an engine message to the engine. Only needed in net games, where you receive engine
+ * messages from the server and have to pass them here.
+ */
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len);
+
+/**
+ * Send an info message to the engine that will be displayed in the game's chatlog.
+ * The msgtype determines the color of the message;  in the QTFrontend, info messages and
+ * normal chat messages use 1, emote-messages (those starting with /me) use 2, and
+ * join/leave messages use 3. You should use flib_gameconn_send_chatmsg for chat messages
+ * though because it automatically formats /me messages.
+ *
+ * Generally only needed in net games.
+ */
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg);
+
+/**
+ * Send a chat message to be displayed in the game's chatlog. Messages starting with /me are
+ * automatically formatted correctly.
+ *
+ * Generally only needed in net games.
+ */
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
+
+/**
+ * Request the engine to stop the game (efinish).
+ * You can use this to shut down a game early without directly killing the engine process.
+ */
+int flib_gameconn_send_quit(flib_gameconn *conn);
+
+/**
+ * Send an arbitrary command to the engine, e.g. "eforcequit" to shut down the engine
+ * quickly. Commands prefixed with "e" will be processed by the engine's ProcessCommand
+ * method (with the e removed, so e.g. efinish will be parsed as finish).
+ */
+int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString);
+
+/**
+ * Expected callback signature: void handleConnect(void *context)
+ * The engine has successfully connected. You don't have to react to this in any way.
+ */
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context);
+
+/**
+ * Expected callback signature: void handleDisconnect(void *context, int reason)
+ * The connection to the engine was closed, either because the game has ended normally, or
+ * because it was interrupted/halted, or because of an error. The reason is provided as one
+ * of the GAME_END_xxx constants.
+ *
+ * You should destroy the gameconn and - in a netgame - notify the server that the game has ended.
+ */
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context);
+
+/**
+ * Expected callback signature: void handleErrorMessage(void* context, const char *msg)
+ * The engine sent an error message, you should probably display it to the user or at least log it.
+ */
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context);
+
+/**
+ * Expected callback signature: void handleChat(void* context, const char *msg, bool teamchat)
+ * The player entered a chat or teamchat message. In a netgame, you should send it on to the server.
+ */
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context);
+
+/**
+ * Expected callback signature: void handleGameRecorded(void *context, const uint8_t *record, size_t size, bool isSavegame)
+ * The game has stopped, and a demo or savegame is available. You can store it in a file and later pass it back
+ * to the engine to either watch a replay (if it's a demo) or to continue playing (if it's a savegame).
+ */
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, size_t size, bool isSavegame), void* context);
+
+/**
+ * Expected callback signature: void handleEngineMessage(void *context, const uint8_t *em, size_t size)
+ * The engine has generated a message with player input. In a netgame, you should send it on to the server.
+ */
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, size_t size), void* context);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,216 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "ipcbase.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * The receive buffer has to be able to hold any message that might be received. Normally
+ * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
+ * bitmap, 1 for the number of hogs which fit on the map).
+ *
+ * We don't need to worry about wasting a few kb though, and I like powers of two...
+ */
+struct _flib_ipcbase {
+	uint8_t readBuffer[8192];
+	int readBufferSize;
+
+	flib_acceptor *acceptor;
+	uint16_t port;
+
+	flib_tcpsocket *sock;
+};
+
+flib_ipcbase *flib_ipcbase_create() {
+	flib_ipcbase *result = flib_calloc(1, sizeof(flib_ipcbase));
+	flib_acceptor *acceptor = flib_acceptor_create(0);
+
+	if(!result || !acceptor) {
+		free(result);
+		flib_acceptor_close(acceptor);
+		return NULL;
+	}
+
+	result->acceptor = acceptor;
+	result->sock = NULL;
+	result->readBufferSize = 0;
+	result->port = flib_acceptor_listenport(acceptor);
+
+	flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
+	return result;
+}
+
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc) {
+	if(log_badargs_if(ipc==NULL)) {
+		return 0;
+	}
+	return ipc->port;
+}
+
+void flib_ipcbase_destroy(flib_ipcbase *ipc) {
+	if(ipc) {
+		flib_acceptor_close(ipc->acceptor);
+		flib_socket_close(ipc->sock);
+		if(ipc->sock) {
+			flib_log_d("IPC connection closed.");
+		}
+		free(ipc);
+	}
+}
+
+IpcState flib_ipcbase_state(flib_ipcbase *ipc) {
+	if(log_badargs_if(ipc==NULL)) {
+		return IPC_NOT_CONNECTED;
+	} else if(ipc->sock) {
+		return IPC_CONNECTED;
+	} else if(ipc->acceptor) {
+		return IPC_LISTENING;
+	} else {
+		return IPC_NOT_CONNECTED;
+	}
+}
+
+static void receiveToBuffer(flib_ipcbase *ipc) {
+	if(ipc->sock) {
+		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
+		if(size>=0) {
+			ipc->readBufferSize += size;
+		} else {
+			flib_log_d("IPC connection lost.");
+			flib_socket_close(ipc->sock);
+			ipc->sock = NULL;
+		}
+	}
+}
+
+static bool isMessageReady(flib_ipcbase *ipc) {
+	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		size_t msgStart = 0;
+		while(msgStart < len) {
+			uint8_t msglen = data[msgStart];
+			if(msgStart+msglen < len) {
+				flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
+			} else {
+				uint8_t msglen2 = len-msgStart-1;
+				flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
+			}
+			msgStart += (uint8_t)data[msgStart]+1;
+		}
+	}
+}
+
+static void logRecvMsg(const uint8_t *data) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		uint8_t msglen = data[0];
+		flib_log_d("[IPC IN][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+1);
+	}
+}
+
+static void popFromReadBuffer(flib_ipcbase *ipc, uint8_t *outbuf, size_t size) {
+	memcpy(outbuf, ipc->readBuffer, size);
+	memmove(ipc->readBuffer, ipc->readBuffer+size, ipc->readBufferSize-size);
+	ipc->readBufferSize -= size;
+}
+
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
+	if(log_badargs_if2(ipc==NULL, data==NULL)) {
+		return -1;
+	}
+
+	if(!isMessageReady(ipc)) {
+		receiveToBuffer(ipc);
+	}
+
+	if(isMessageReady(ipc)) {
+		int msgsize = ipc->readBuffer[0]+1;
+		popFromReadBuffer(ipc, data, msgsize);
+		logRecvMsg(data);
+		return msgsize;
+	} else if(!ipc->sock && ipc->readBufferSize>0) {
+		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
+		ipc->readBufferSize = 0;
+		return -1;
+	} else {
+		return -1;
+	}
+}
+
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data) {
+	if(log_badargs_if2(ipc==NULL, data==NULL)) {
+		return -1;
+	}
+
+	receiveToBuffer(ipc);
+
+	if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) {
+		popFromReadBuffer(ipc, data, IPCBASE_MAPMSG_BYTES);
+		return IPCBASE_MAPMSG_BYTES;
+	} else {
+		return -1;
+	}
+}
+
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) {
+	if(log_badargs_if2(ipc==NULL, data==NULL && len>0)
+			|| log_w_if(!ipc->sock, "flib_ipcbase_send_raw: Not connected.")) {
+		return -1;
+	}
+	if(flib_socket_send(ipc->sock, data, len) == len) {
+		logSentMsg(data, len);
+		return 0;
+	} else {
+		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
+		flib_socket_close(ipc->sock);
+		ipc->sock = NULL;
+		return -1;
+	}
+}
+
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len) {
+	if(log_badargs_if3(ipc==NULL, data==NULL && len>0, len>255)) {
+		return -1;
+	}
+
+	uint8_t sendbuf[256];
+	sendbuf[0] = len;
+	memcpy(sendbuf+1, data, len);
+	return flib_ipcbase_send_raw(ipc, sendbuf, len+1);
+}
+
+void flib_ipcbase_accept(flib_ipcbase *ipc) {
+	if(!log_badargs_if(ipc==NULL) && !ipc->sock && ipc->acceptor) {
+		ipc->sock = flib_socket_accept(ipc->acceptor, true);
+		if(ipc->sock) {
+			flib_log_d("IPC connection accepted.");
+			flib_acceptor_close(ipc->acceptor);
+			ipc->acceptor = NULL;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,105 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Low-level protocol support for the IPC connection to the engine.
+ */
+#ifndef IPCBASE_H_
+#define IPCBASE_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define IPCBASE_MAPMSG_BYTES 4097
+
+typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState;
+
+typedef struct _flib_ipcbase flib_ipcbase;
+
+/**
+ * Start an engine connection by listening on a random port. The selected port can
+ * be queried with flib_ipcbase_port and has to be passed to the engine.
+ *
+ * Returns NULL on error. Destroy the created object with flib_ipcbase_destroy.
+ *
+ * We stop accepting new connections once a connection has been established, so you
+ * need to create a new ipcbase in order to start a new connection.
+ */
+flib_ipcbase *flib_ipcbase_create();
+
+/**
+ * Return the listening port
+ */
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc);
+
+/**
+ * Free resources and close sockets. NULL safe.
+ */
+void flib_ipcbase_destroy(flib_ipcbase *ipc);
+
+/**
+ * Determine the current connection state
+ */
+IpcState flib_ipcbase_state(flib_ipcbase *ipc);
+
+/**
+ * Receive a single message (up to 256 bytes) and copy it into the data buffer.
+ * Returns the length of the received message, a negative value if no message could
+ * be read.
+ *
+ * The first byte of a message is its content length, which is one less than the returned
+ * value.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data);
+
+/**
+ * Try to receive 4097 bytes. This is the size of the reply the engine sends
+ * when successfully queried for map data. The first 4096 bytes are a bit-packed
+ * twocolor image of the map (256x128), the last byte is the number of hogs that
+ * fit on the map.
+ */
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data);
+
+/**
+ * Blocking send bytes over the socket. No message framing will be added.
+ * Returns 0 on success.
+ */
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len);
+
+/**
+ * Write a single message (up to 255 bytes) to the engine. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Calling this function in a state other than IPC_CONNECTED will fail immediately.
+ * Returns 0 on success.
+ */
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len);
+
+/**
+ * Try to accept a connection. Only has an effect in state IPC_LISTENING.
+ */
+void flib_ipcbase_accept(flib_ipcbase *ipc);
+
+#endif /* IPCBASE_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,316 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "ipcprotocol.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+#include "../md5/md5.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...) {
+	int result = -1;
+	if(!log_badargs_if2(vec==NULL, fmt==NULL)) {
+		// 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf
+		char msgbuffer[257];
+
+		// Format the message, leaving one byte at the start for the length
+		va_list argp;
+		va_start(argp, fmt);
+		int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp);
+		va_end(argp);
+
+		if(!log_e_if(msgSize > 255, "Message too long (%u bytes)", (unsigned)msgSize)
+				&& !log_e_if(msgSize < 0, "printf error")) {
+			// Add the length prefix
+			((uint8_t*)msgbuffer)[0] = msgSize;
+
+			// Append it to the vector
+			result = flib_vector_append(vec, msgbuffer, msgSize+1);
+		}
+	}
+	return result;
+}
+
+int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!log_badargs_if2(vec==NULL, map==NULL)) {
+		bool error = false;
+
+		if(map->mapgen == MAPGEN_NAMED) {
+			error |= log_e_if(!map->name, "Missing map name")
+					|| flib_ipc_append_message(tempvector, "emap %s", map->name);
+		}
+		if(!mappreview) {
+			error |= log_e_if(!map->theme, "Missing map theme")
+					|| flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+		}
+		error |= flib_ipc_append_seed(tempvector, map->seed);
+		error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
+		error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen);
+
+		if(map->mapgen == MAPGEN_MAZE) {
+			error |= flib_ipc_append_message(tempvector, "e$maze_size %i", map->mazeSize);
+		}
+		if(map->mapgen == MAPGEN_DRAWN) {
+			/*
+			 * We have to split the drawn map data into several edraw messages here because
+			 * it can be longer than the maximum message size.
+			 */
+			const char *edraw = "edraw ";
+			int edrawlen = strlen(edraw);
+			for(size_t offset=0; offset<map->drawDataSize; offset+=200) {
+				size_t bytesRemaining = map->drawDataSize-offset;
+				int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200;
+				uint8_t messagesize = edrawlen + fragmentsize;
+				error |= flib_vector_append(tempvector, &messagesize, 1);
+				error |= flib_vector_append(tempvector, edraw, edrawlen);
+				error |= flib_vector_append(tempvector, map->drawData+offset, fragmentsize);
+			}
+		}
+
+		if(!log_e_if(error, "Error generating map config")) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(tempvector);
+	return result;
+}
+
+int flib_ipc_append_seed(flib_vector *vec, const char *seed) {
+	if(log_badargs_if2(vec==NULL, seed==NULL)) {
+		return -1;
+	}
+	return flib_ipc_append_message(vec, "eseed %s", seed);
+}
+
+int flib_ipc_append_script(flib_vector *vec, const char *script) {
+	int result = -1;
+	if(!log_badargs_if2(vec==NULL, script==NULL)) {
+		result = flib_ipc_append_message(vec, "escript %s", script);
+	}
+	return result;
+}
+
+int flib_ipc_append_style(flib_vector *vec, const char *style) {
+	int result = -1;
+	char *copy = flib_strdupnull(style);
+	if(!log_badargs_if(vec==NULL) && copy) {
+		if(!strcmp("Normal", copy)) {
+			// "Normal" means no gametype script
+			// TODO if an empty script called "Normal" is added to the scripts directory this can be removed
+			result = 0;
+		} else {
+			size_t len = strlen(copy);
+			for(size_t i=0; i<len; i++) {
+				if(copy[i] == ' ') {
+					copy[i] = '_';
+				}
+			}
+
+			result = flib_ipc_append_message(vec, "escript %s%s.lua", MULTIPLAYER_SCRIPT_PATH, copy);
+		}
+	}
+	free(copy);
+	return result;
+}
+
+static uint32_t buildModFlags(const flib_scheme *scheme) {
+	uint32_t result = 0;
+	for(int i=0; i<flib_meta.modCount; i++) {
+		if(scheme->mods[i]) {
+			int bitmaskIndex = flib_meta.mods[i].bitmaskIndex;
+			result |= (UINT32_C(1) << bitmaskIndex);
+		}
+	}
+	return result;
+}
+
+int flib_ipc_append_gamescheme(flib_vector *vec, const flib_scheme *scheme) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!log_badargs_if2(vec==NULL, scheme==NULL) && tempvector) {
+		bool error = false;
+		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, buildModFlags(scheme));
+		for(int i=0; i<flib_meta.settingCount; i++) {
+			if(flib_meta.settings[i].engineCommand) {
+				int value = scheme->settings[i];
+				if(flib_meta.settings[i].maxMeansInfinity) {
+					value = value>=flib_meta.settings[i].max ? 9999 : value;
+				}
+				if(flib_meta.settings[i].times1000) {
+					value *= 1000;
+				}
+				error |= flib_ipc_append_message(tempvector, "%s %i", flib_meta.settings[i].engineCommand, value);
+			}
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(tempvector);
+	return result;
+}
+
+static int appendWeaponSet(flib_vector *vec, flib_weaponset *set) {
+	return flib_ipc_append_message(vec, "eammloadt %s", set->loadout)
+		|| flib_ipc_append_message(vec, "eammprob %s", set->crateprob)
+		|| flib_ipc_append_message(vec, "eammdelay %s", set->delay)
+		|| flib_ipc_append_message(vec, "eammreinf %s", set->crateammo);
+}
+
+static void calculateMd5Hex(const char *in, char out[33]) {
+	md5_state_t md5state;
+	uint8_t md5bytes[16];
+	md5_init(&md5state);
+	md5_append(&md5state, (unsigned char*)in, strlen(in));
+	md5_finish(&md5state, md5bytes);
+	for(int i=0;i<sizeof(md5bytes); i++) {
+		snprintf(out+i*2, 3, "%02x", (unsigned)md5bytes[i]);
+	}
+}
+
+static int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!log_badargs_if2(vec==NULL, team==NULL) && tempvector) {
+		bool error = false;
+
+		if(!perHogAmmo && !noAmmoStore) {
+			error = error
+					|| appendWeaponSet(tempvector, team->hogs[0].weaponset)
+					|| flib_ipc_append_message(tempvector, "eammstore");
+		}
+
+		char md5Hex[33];
+		calculateMd5Hex(team->ownerName ? team->ownerName : "", md5Hex);
+		if(team->colorIndex<0 || team->colorIndex>=flib_teamcolor_count) {
+			flib_log_e("Color index out of bounds for team %s: %i", team->name, team->colorIndex);
+			error = true;
+		} else {
+			error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", md5Hex, flib_teamcolors[team->colorIndex], team->name);
+		}
+
+		if(team->remoteDriven) {
+			error |= flib_ipc_append_message(tempvector, "erdriven");
+		}
+
+		error |= flib_ipc_append_message(tempvector, "egrave %s", team->grave);
+		error |= flib_ipc_append_message(tempvector, "efort %s", team->fort);
+		error |= flib_ipc_append_message(tempvector, "evoicepack %s", team->voicepack);
+		error |= flib_ipc_append_message(tempvector, "eflag %s", team->flag);
+
+		for(int i=0; i<team->bindingCount; i++) {
+			error |= flib_ipc_append_message(tempvector, "ebind %s %s", team->bindings[i].binding, team->bindings[i].action);
+		}
+
+		for(int i=0; i<team->hogsInGame; i++) {
+			if(perHogAmmo && !noAmmoStore) {
+				error |= appendWeaponSet(tempvector, team->hogs[i].weaponset);
+			}
+			error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
+			error |= flib_ipc_append_message(tempvector, "ehat %s", team->hogs[i].hat);
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(tempvector);
+	return result;
+}
+
+int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!log_badargs_if2(vec==NULL, setup==NULL) && tempvector) {
+		bool error = false;
+		bool perHogAmmo = false;
+		bool sharedAmmo = false;
+
+		error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL");
+		if(setup->map) {
+			error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
+		}
+		if(setup->style) {
+			error |= flib_ipc_append_style(tempvector, setup->style);
+		}
+		if(setup->gamescheme) {
+			error |= flib_ipc_append_gamescheme(tempvector, setup->gamescheme);
+			sharedAmmo = flib_scheme_get_mod(setup->gamescheme, "sharedammo");
+			// Shared ammo has priority over per-hog ammo
+			perHogAmmo = !sharedAmmo && flib_scheme_get_mod(setup->gamescheme, "perhogammo");
+		}
+		if(setup->teamlist->teams && setup->teamlist->teamCount>0) {
+			int *clanColors = flib_calloc(setup->teamlist->teamCount, sizeof(int));
+			if(!clanColors) {
+				error = true;
+			} else {
+				int clanCount = 0;
+				for(int i=0; !error && i<setup->teamlist->teamCount; i++) {
+					flib_team *team = setup->teamlist->teams[i];
+					// Find the clan index of this team (clans are identified by color).
+					bool newClan = false;
+					int clan = 0;
+					while(clan<clanCount && clanColors[clan] != team->colorIndex) {
+						clan++;
+					}
+					if(clan==clanCount) {
+						newClan = true;
+						clanCount++;
+						clanColors[clan] = team->colorIndex;
+					}
+
+					// If shared ammo is active, only add an ammo store for the first team in each clan.
+					bool noAmmoStore = sharedAmmo&&!newClan;
+					error |= flib_ipc_append_addteam(tempvector, setup->teamlist->teams[i], perHogAmmo, noAmmoStore);
+				}
+			}
+			free(clanColors);
+		}
+		error |= flib_ipc_append_message(tempvector, "!");
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,93 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef IPCPROTOCOL_H_
+#define IPCPROTOCOL_H_
+
+#include "../util/buffer.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/scheme.h"
+#include "../model/gamesetup.h"
+
+#include <stdbool.h>
+
+/**
+ * Create a message in the IPC protocol format and add it to
+ * the vector. Use a format string and extra parameters as with printf.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...);
+
+/**
+ * Append IPC messages to the buffer that configure the engine for
+ * this map.
+ *
+ * Unfortunately the engine needs a slightly different configuration
+ * for generating a map preview.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview);
+
+/**
+ * Append a seed message to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_seed(flib_vector *vec, const char *seed);
+
+/**
+ * Append a script to the buffer (e.g. "Missions/Training/Basic_Training_-_Bazooka.lua")
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_script(flib_vector *vec, const char *script);
+
+/**
+ * Append a game style to the buffer. (e.g. "Capture the Flag")
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_style(flib_vector *vec, const char *style);
+
+/**
+ * Append the game scheme to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_gamescheme(flib_vector *vec, const flib_scheme *scheme);
+
+/**
+ * Append the entire game config to the buffer (including the final "!" that marks the
+ * end of configuration data for the engine)
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame);
+
+#endif /* IPCPROTOCOL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,196 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "mapconn.h"
+#include "ipcbase.h"
+#include "ipcprotocol.h"
+
+#include "../util/logging.h"
+#include "../util/buffer.h"
+#include "../util/util.h"
+
+#include <stdlib.h>
+
+typedef enum {
+	AWAIT_CONNECTION,
+	AWAIT_REPLY,
+	AWAIT_CLOSE,
+	FINISHED
+} mapconn_state;
+
+struct _flib_mapconn {
+	uint8_t mapBuffer[IPCBASE_MAPMSG_BYTES];
+	flib_ipcbase *ipcBase;
+	flib_vector *configBuffer;
+
+	mapconn_state progress;
+
+	void (*onSuccessCb)(void*, const uint8_t*, int);
+	void *onSuccessCtx;
+
+	void (*onFailureCb)(void*, const char*);
+	void *onFailureCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void noop_handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {}
+static void noop_handleFailure(void *context, const char *errormessage) {}
+
+static void clearCallbacks(flib_mapconn *conn) {
+	conn->onSuccessCb = &noop_handleSuccess;
+	conn->onFailureCb = &noop_handleFailure;
+}
+
+static flib_vector *createConfigBuffer(const flib_map *mapdesc) {
+	flib_vector *result = NULL;
+	flib_vector *tempbuffer = flib_vector_create();
+	if(tempbuffer) {
+		bool error = false;
+		error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true);
+		error |= flib_ipc_append_message(tempbuffer, "!");
+		if(!error) {
+			result = tempbuffer;
+			tempbuffer = NULL;
+		}
+	}
+	flib_vector_destroy(tempbuffer);
+	return result;
+}
+
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc) {
+	if(log_badargs_if(mapdesc==NULL)) {
+		return NULL;
+	}
+	flib_mapconn *result = NULL;
+	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
+	if(tempConn) {
+		tempConn->ipcBase = flib_ipcbase_create();
+		tempConn->configBuffer = createConfigBuffer(mapdesc);
+		if(tempConn->ipcBase && tempConn->configBuffer) {
+			tempConn->progress = AWAIT_CONNECTION;
+			clearCallbacks(tempConn);
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_mapconn_destroy(tempConn);
+	return result;
+}
+
+void flib_mapconn_destroy(flib_mapconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_ipcbase_destroy(conn->ipcBase);
+			flib_vector_destroy(conn->configBuffer);
+			free(conn);
+		}
+	}
+}
+
+int flib_mapconn_getport(flib_mapconn *conn) {
+	if(log_badargs_if(conn==NULL)) {
+		return 0;
+	}
+	return flib_ipcbase_port(conn->ipcBase);
+}
+
+void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context) {
+	if(!log_badargs_if(conn==NULL)) {
+		conn->onSuccessCb = callback ? callback : &noop_handleSuccess;
+		conn->onSuccessCtx = context;
+	}
+}
+
+void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context) {
+	if(!log_badargs_if(conn==NULL)) {
+		conn->onFailureCb = callback ? callback : &noop_handleFailure;
+		conn->onFailureCtx = context;
+	}
+}
+
+static void flib_mapconn_wrappedtick(flib_mapconn *conn) {
+	if(conn->progress == AWAIT_CONNECTION) {
+		flib_ipcbase_accept(conn->ipcBase);
+		switch(flib_ipcbase_state(conn->ipcBase)) {
+		case IPC_CONNECTED:
+			{
+				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+				if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
+					conn->progress = FINISHED;
+					conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine.");
+					return;
+				} else {
+					conn->progress = AWAIT_REPLY;
+				}
+			}
+			break;
+		case IPC_NOT_CONNECTED:
+			conn->progress = FINISHED;
+			conn->onFailureCb(conn->onFailureCtx, "Engine connection closed unexpectedly.");
+			return;
+		default:
+			break;
+		}
+	}
+
+	if(conn->progress == AWAIT_REPLY) {
+		if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) {
+			conn->progress = AWAIT_CLOSE;
+		} else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
+			conn->progress = FINISHED;
+			conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
+			return;
+		}
+	}
+
+	if(conn->progress == AWAIT_CLOSE) {
+		// Just do throwaway reads so we find out when the engine disconnects
+		uint8_t buf[256];
+		flib_ipcbase_recv_message(conn->ipcBase, buf);
+		if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
+			conn->progress = FINISHED;
+			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
+			return;
+		}
+	}
+}
+
+void flib_mapconn_tick(flib_mapconn *conn) {
+	if(!log_badargs_if(conn==NULL)
+			&& !log_w_if(conn->running, "Call to flib_mapconn_tick from a callback")
+			&& !log_w_if(conn->progress == FINISHED, "We are already done.")) {
+		conn->running = true;
+		flib_mapconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_mapconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,116 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Functions for querying a map preview from the engine, which includes both a two-color image
+ * and the number of hogs this map is suitable for.
+ *
+ * The general usage is to first create a mapconn object by calling flib_mapconn_create.
+ * That will cause the frontlib to listen on a random port which can be queried using
+ * flib_mapconn_getport(). You should also register your callback functions right at the start
+ * to ensure you don't miss any callbacks.
+ *
+ * Next, start the engine (that part is up to you) with the appropriate command line arguments
+ * for a map preview request.
+ *
+ * In order to allow the mapconn to run, you should regularly call flib_mapconn_tick(), which
+ * performs network I/O and calls your callbacks if the map has been generated or an error
+ * has occurred. Once either the onSuccess or onFailure callback is called, you should destroy
+ * the mapconn and stop calling tick().
+ */
+
+#ifndef IPC_MAPCONN_H_
+#define IPC_MAPCONN_H_
+
+#include "../model/map.h"
+
+#include <stdint.h>
+
+#define MAPIMAGE_WIDTH 256
+#define MAPIMAGE_HEIGHT 128
+#define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT)
+
+typedef struct _flib_mapconn flib_mapconn;
+
+/**
+ * Start a new map rendering connection (mapconn). This means a listening socket
+ * will be started on a random unused port, waiting for a connection from the
+ * engine process. Once this connection is established, the required information
+ * will be sent to the engine, and the reply is read.
+ *
+ * The map must be a regular, maze or drawn map - for a preview of a named map,
+ * use the preview images in the map's directory, and for the hog count read the
+ * map information (e.g. using flib_mapcfg_read).
+ *
+ * No NULL parameters allowed, returns NULL on failure.
+ * Use flib_mapconn_destroy to free the returned object.
+ */
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc);
+
+/**
+ * Destroy the mapconn object. Passing NULL is allowed and does nothing.
+ * flib_mapconn_destroy may be called from inside a callback function.
+ */
+void flib_mapconn_destroy(flib_mapconn *conn);
+
+/**
+ * Returns the port on which the mapconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_mapconn_getport(flib_mapconn *conn);
+
+/**
+ * Set a callback which will receive the rendered map if the rendering succeeds.
+ *
+ * Expected callback signature:
+ * void handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs)
+ *
+ * The context passed to the callback is the same pointer you provided when
+ * registering the callback. bitmap is a pointer to a buffer of size MAPIMAGE_BYTES
+ * containing a bit-packed image of size MAPIMAGE_WIDTH * MAPIMAGE_HEIGHT.
+ * numHedgehogs is the number of hogs that fit on this map.
+ *
+ * The bitmap pointer passed to the callback belongs to the caller,
+ * so it should not be stored elsewhere. Note that it remains valid
+ * inside the callback method even if flib_mapconn_destroy is called.
+ */
+void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context);
+
+/**
+ * Set a callback which will receive an error message if rendering fails.
+ *
+ * Expected callback signature:
+ * void handleFailure(void *context, const char *errormessage)
+ *
+ * The context passed to the callback is the same pointer you provided when
+ * registering the callback.
+ *
+ * The error message passed to the callback belongs to the caller,
+ * so it should not be stored elsewhere. Note that it remains valid
+ * inside the callback method even if flib_mapconn_destroy is called.
+ */
+void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_mapconn_tick(flib_mapconn *conn);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/md5/md5.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,381 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+	either statically or dynamically; added missing #include <string.h>
+	in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+	type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+	unsigned in ANSI C, signed in traditional"; made test program
+	self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER	/* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+	/*
+	 * Determine dynamically whether this is a big-endian or
+	 * little-endian machine, since we can use a more efficient
+	 * algorithm on the latter.
+	 */
+	static const int w = 1;
+
+	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0		/* little-endian */
+	{
+	    /*
+	     * On little-endian machines, we can process properly aligned
+	     * data without copying it.
+	     */
+	    if (!((data - (const md5_byte_t *)0) & 3)) {
+		/* data are properly aligned */
+		X = (const md5_word_t *)data;
+	    } else {
+		/* not aligned */
+		memcpy(xbuf, data, 64);
+		X = xbuf;
+	    }
+	}
+#endif
+#if BYTE_ORDER == 0
+	else			/* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0		/* big-endian */
+	{
+	    /*
+	     * On big-endian machines, we must arrange the bytes in the
+	     * right order.
+	     */
+	    const md5_byte_t *xp = data;
+	    int i;
+
+#  if BYTE_ORDER == 0
+	    X = xbuf;		/* (dynamic only) */
+#  else
+#    define xbuf X		/* (static only) */
+#  endif
+	    for (i = 0; i < 16; ++i, xp += 4)
+		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+	}
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/md5/md5.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+	references to Ghostscript; clarified derivation from RFC 1321;
+	now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,55 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "gamesetup.h"
+#include "../util/util.h"
+
+#include <stdlib.h>
+
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup) {
+	if(gamesetup) {
+		free(gamesetup->style);
+		flib_scheme_destroy(gamesetup->gamescheme);
+		flib_map_destroy(gamesetup->map);
+		flib_teamlist_destroy(gamesetup->teamlist);
+		free(gamesetup);
+	}
+}
+
+flib_gamesetup *flib_gamesetup_copy(const flib_gamesetup *setup) {
+	if(!setup) {
+		return NULL;
+	}
+
+	flib_gamesetup *result = flib_calloc(1, sizeof(flib_gamesetup));
+	if(result) {
+		result->style = flib_strdupnull(setup->style);
+		result->gamescheme = flib_scheme_copy(setup->gamescheme);
+		result->map = flib_map_copy(setup->map);
+		result->teamlist = flib_teamlist_copy(setup->teamlist);
+		if((setup->style && !result->style)
+				|| (setup->gamescheme && !result->gamescheme)
+				|| (setup->map && !result->map)
+				|| (setup->teamlist && !result->teamlist)) {
+			flib_gamesetup_destroy(result);
+			result = NULL;
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,47 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * A complete game configuration that contains all settings the engine needs to start a
+ * local or networked game.
+ */
+
+#ifndef MODEL_GAMESETUP_H_
+#define MODEL_GAMESETUP_H_
+
+#include "scheme.h"
+#include "weapon.h"
+#include "map.h"
+#include "teamlist.h"
+
+typedef struct {
+    char *style;				// e.g. "Capture the Flag"
+    flib_scheme *gamescheme;
+    flib_map *map;
+	flib_teamlist *teamlist;
+} flib_gamesetup;
+
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup);
+
+/**
+ * Deep-copy of the flib_gamesetup.
+ */
+flib_gamesetup *flib_gamesetup_copy(const flib_gamesetup *gamesetup);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,110 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "map.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+
+flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter) {
+	if(log_badargs_if2(seed==NULL, theme==NULL)) {
+		return NULL;
+	}
+	flib_map newmap = {0};
+	newmap.mapgen = MAPGEN_REGULAR;
+	newmap.name = "+rnd+";
+	newmap.seed = (char*)seed;
+	newmap.theme = (char*)theme;
+	newmap.templateFilter = templateFilter;
+	return flib_map_copy(&newmap);
+}
+
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize) {
+	if(log_badargs_if2(seed==NULL, theme==NULL)) {
+		return NULL;
+	}
+	flib_map newmap = {0};
+	newmap.mapgen = MAPGEN_MAZE;
+	newmap.name = "+maze+";
+	newmap.seed = (char*)seed;
+	newmap.theme = (char*)theme;
+	newmap.mazeSize = mazeSize;
+	return flib_map_copy(&newmap);
+}
+
+flib_map *flib_map_create_named(const char *seed, const char *name) {
+	if(log_badargs_if2(seed==NULL, name==NULL)) {
+		return NULL;
+	}
+	flib_map newmap = {0};
+	newmap.mapgen = MAPGEN_NAMED;
+	newmap.name = (char*)name;
+	newmap.seed = (char*)seed;
+	return flib_map_copy(&newmap);
+}
+
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, size_t drawDataSize) {
+	if(log_badargs_if3(seed==NULL, theme==NULL, drawData==NULL)) {
+		return NULL;
+	}
+	flib_map newmap = {0};
+	newmap.mapgen = MAPGEN_DRAWN;
+	newmap.name = "+drawn+";
+	newmap.seed = (char*)seed;
+	newmap.theme = (char*)theme;
+	newmap.drawData = (uint8_t*) drawData;
+	newmap.drawDataSize = drawDataSize;
+	return flib_map_copy(&newmap);
+}
+
+flib_map *flib_map_copy(const flib_map *map) {
+	flib_map *result = NULL;
+	if(map) {
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		if(newmap) {
+			newmap->mapgen = map->mapgen;
+			newmap->drawDataSize = map->drawDataSize;
+			newmap->drawData = flib_bufdupnull(map->drawData, map->drawDataSize);
+			newmap->mazeSize = map->mazeSize;
+			newmap->name = flib_strdupnull(map->name);
+			newmap->seed = flib_strdupnull(map->seed);
+			newmap->templateFilter = map->templateFilter;
+			newmap->theme = flib_strdupnull(map->theme);
+			if((newmap->drawData || !map->drawData) && (newmap->name || !map->name) && (newmap->seed || !map->seed) && (newmap->theme || !map->theme)) {
+				result = newmap;
+				newmap = NULL;
+			}
+		}
+		flib_map_destroy(newmap);
+	}
+	return result;
+}
+
+void flib_map_destroy(flib_map *map) {
+	if(map) {
+		free(map->seed);
+		free(map->drawData);
+		free(map->name);
+		free(map->theme);
+		free(map);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,114 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef MODEL_MAP_H_
+#define MODEL_MAP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define MAPGEN_REGULAR 0
+#define MAPGEN_MAZE 1
+#define MAPGEN_DRAWN 2
+#define MAPGEN_NAMED 3
+
+#define TEMPLATEFILTER_ALL 0
+#define TEMPLATEFILTER_SMALL 1
+#define TEMPLATEFILTER_MEDIUM 2
+#define TEMPLATEFILTER_LARGE 3
+#define TEMPLATEFILTER_CAVERN 4
+#define TEMPLATEFILTER_WACKY 5
+
+#define MAZE_SIZE_SMALL_TUNNELS 0
+#define MAZE_SIZE_MEDIUM_TUNNELS 1
+#define MAZE_SIZE_LARGE_TUNNELS 2
+#define MAZE_SIZE_SMALL_ISLANDS 3
+#define MAZE_SIZE_MEDIUM_ISLANDS 4
+#define MAZE_SIZE_LARGE_ISLANDS 5
+
+/**
+ * Data structure for defining a map. This contains the whole recipe to
+ * exactly recreate a particular map.
+ *
+ * The required fields depend on the map generator, see the comments
+ * at the struct for details.
+ */
+typedef struct {
+	int mapgen;				// Always one of the MAPGEN_ constants
+	char *name;				// The name of the map for MAPGEN_NAMED (e.g. "Cogs"), otherwise one of "+rnd+", "+maze+" or "+drawn+".
+	char *seed;				// Used for all maps. This is a random seed for all (non-AI) entropy in the round. Typically a random UUID, but can be any string.
+	char *theme;			// Used for all maps. This is the name of a directory in Data/Themes (e.g. "Beach")
+	uint8_t *drawData;		// Used for MAPGEN_DRAWN
+	size_t drawDataSize;	// Used for MAPGEN_DRAWN
+	int templateFilter;		// Used for MAPGEN_REGULAR. One of the TEMPLATEFILTER_xxx constants.
+	int mazeSize;			// Used for MAPGEN_MAZE. One of the MAZE_SIZE_xxx constants.
+} flib_map;
+
+/**
+ * Create a generated map. theme should be the name of a
+ * directory in "Themes" and templateFilter should be one of the
+ * TEMPLATEFILTER_* constants, but this is not checked before
+ * passing it to the engine.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter);
+
+/**
+ * Create a generated maze-type map. theme should be the name of a
+ * directory in "Themes" and mazeSize should be one of the
+ * MAZE_SIZE_* constants, but this is not checked before
+ * passing it to the engine.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize);
+
+/**
+ * Create a map from the Maps-Directory. name should be the name of a
+ * directory in "Maps", but this is not checked before
+ * passing it to the engine. If this is a mission, the corresponding
+ * script is used automatically.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_named(const char *seed, const char *name);
+
+/**
+ * Create a hand-drawn map. Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, size_t drawDataSize);
+
+/**
+ * Create a deep copy of the map. Returns NULL on failure or if NULL was passed.
+ */
+flib_map *flib_map_copy(const flib_map *map);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_map_destroy(flib_map *map);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,64 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "mapcfg.h"
+
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+void removeNewline(char *str) {
+	for(;*str;str++) {
+		if(*str=='\n' || *str=='\r') {
+			*str = 0;
+			return;
+		}
+	}
+}
+
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out) {
+	int result = -1;
+	if(!log_badargs_if4(dataDirPath==NULL, mapname==NULL, out==NULL, flib_contains_dir_separator(mapname))) {
+		char *path = flib_asprintf("%sMaps/%s/map.cfg", dataDirPath, mapname);
+		if(path) {
+			FILE *file = fopen(path, "rb");
+			if(!log_e_if(!file, "Unable to open map config file %s", path)) {
+				if(!log_e_if(!fgets(out->theme, sizeof(out->theme), file), "Error reading theme from %s", path)) {
+					removeNewline(out->theme);
+					char buf[64];
+					if(fgets(buf, sizeof(buf), file)) {
+						removeNewline(buf);
+						errno = 0;
+						out->hogLimit = strtol(buf, NULL, 10);
+						result = !log_e_if(errno, "Invalid hoglimit in %s: %i", path, buf);
+					} else {
+						result = 0;
+					}
+				}
+				fclose(file);
+			}
+		}
+		free(path);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,38 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Data structure and functions for accessing the map.cfg of named maps.
+ */
+
+#ifndef MAPCFG_H_
+#define MAPCFG_H_
+
+typedef struct {
+	char theme[256];
+	int hogLimit;
+} flib_mapcfg;
+
+/**
+ * Read the map configuration for the map with this name.
+ * The dataDirPath must end in a path separator.
+ */
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out);
+
+#endif /* MAPCFG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/room.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,34 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "room.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+
+void flib_room_destroy(flib_room *room) {
+	if(room) {
+		free(room->map);
+		free(room->name);
+		free(room->owner);
+		free(room->scheme);
+		free(room->weapons);
+		free(room);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/room.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Models the room information for the lobby roomlist.
+ */
+
+#ifndef ROOM_H_
+#define ROOM_H_
+
+#include <stdbool.h>
+
+typedef struct {
+    bool inProgress;	// true if the game is running
+    char *name;
+    int playerCount;
+    int teamCount;
+    char *owner;
+    char *map;			// This is either a map name, or one of +rnd+, +maze+ or +drawn+.
+    char *scheme;
+    char *weapons;
+} flib_room;
+
+void flib_room_destroy();
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/scheme.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,95 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "scheme.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+flib_scheme *flib_scheme_create(const char *schemeName) {
+	flib_scheme *result = flib_calloc(1, sizeof(flib_scheme));
+	if(log_badargs_if(schemeName==NULL) || result==NULL) {
+		return NULL;
+	}
+
+	result->name = flib_strdupnull(schemeName);
+	result->mods = flib_calloc(flib_meta.modCount, sizeof(*result->mods));
+	result->settings = flib_calloc(flib_meta.settingCount, sizeof(*result->settings));
+
+	if(!result->mods || !result->settings || !result->name) {
+		flib_scheme_destroy(result);
+		return NULL;
+	}
+
+	for(int i=0; i<flib_meta.settingCount; i++) {
+		result->settings[i] = flib_meta.settings[i].def;
+	}
+	return result;
+}
+
+flib_scheme *flib_scheme_copy(const flib_scheme *scheme) {
+	flib_scheme *result = NULL;
+	if(scheme) {
+		result = flib_scheme_create(scheme->name);
+		if(result) {
+			memcpy(result->mods, scheme->mods, flib_meta.modCount * sizeof(*scheme->mods));
+			memcpy(result->settings, scheme->settings, flib_meta.settingCount * sizeof(*scheme->settings));
+		}
+	}
+	return result;
+}
+
+void flib_scheme_destroy(flib_scheme* scheme) {
+	if(scheme) {
+		free(scheme->mods);
+		free(scheme->settings);
+		free(scheme->name);
+		free(scheme);
+	}
+}
+
+bool flib_scheme_get_mod(const flib_scheme *scheme, const char *name) {
+	if(!log_badargs_if2(scheme==NULL, name==NULL)) {
+		for(int i=0; i<flib_meta.modCount; i++) {
+			if(!strcmp(flib_meta.mods[i].name, name)) {
+				return scheme->mods[i];
+			}
+		}
+		flib_log_e("Unable to find game mod %s", name);
+	}
+	return false;
+}
+
+int flib_scheme_get_setting(const flib_scheme *scheme, const char *name, int def) {
+	if(!log_badargs_if2(scheme==NULL, name==NULL)) {
+		for(int i=0; i<flib_meta.settingCount; i++) {
+			if(!strcmp(flib_meta.settings[i].name, name)) {
+				return scheme->settings[i];
+			}
+		}
+		flib_log_e("Unable to find game setting %s", name);
+	}
+	return def;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/scheme.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Data structures for game scheme information.
+ *
+ * The scheme consists of settings (integers) and mods (booleans). These are not fixed, but
+ * described in a "metascheme", which describes how each setting and mod is sent to the
+ * engine, and in which order they appear in the network protocol. The metascheme is defined
+ * in hwconsts.h
+ */
+
+#ifndef SCHEME_H_
+#define SCHEME_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "../hwconsts.h"
+
+/**
+ * The settings and mods arrays have the same number and order of elements
+ * as the corresponding arrays in the metascheme.
+ */
+typedef struct {
+    char *name;
+    int *settings;
+    bool *mods;
+} flib_scheme;
+
+/**
+ * Create a new configuration with everything set to default or false
+ * Returns NULL on error.
+ */
+flib_scheme *flib_scheme_create(const char *schemeName);
+
+/**
+ * Create a copy of the scheme. Returns NULL on error or if NULL was passed.
+ */
+flib_scheme *flib_scheme_copy(const flib_scheme *scheme);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_scheme_destroy(flib_scheme* scheme);
+
+/**
+ * Retrieve a mod setting by its name. If the mod is not found, logs an error and returns false.
+ */
+bool flib_scheme_get_mod(const flib_scheme *scheme, const char *name);
+
+/**
+ * Retrieve a game setting by its name. If the setting is not found, logs an error and returns def.
+ */
+int flib_scheme_get_setting(const flib_scheme *scheme, const char *name, int def);
+
+#endif /* SCHEME_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,216 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "schemelist.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/list.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+static char *makePrefixedName(int schemeIndex, const char *settingName) {
+	return flib_asprintf("%i\\%s", schemeIndex, settingName);
+}
+
+static int readSettingsFromIni(flib_ini *ini, flib_scheme *scheme, int index) {
+	bool error = false;
+	for(int i=0; i<flib_meta.settingCount && !error; i++) {
+		char *key = makePrefixedName(index, flib_meta.settings[i].name);
+		if(!key) {
+			error = true;
+		} else if(flib_ini_get_int_opt(ini, &scheme->settings[i], key, flib_meta.settings[i].def)) {
+			flib_log_e("Error reading setting %s in schemes file.", key);
+			error = true;
+		}
+		free(key);
+	}
+	return error;
+}
+
+static int readModsFromIni(flib_ini *ini, flib_scheme *scheme, int index) {
+	bool error = false;
+	for(int i=0; i<flib_meta.modCount && !error; i++) {
+		char *key = makePrefixedName(index, flib_meta.mods[i].name);
+		if(!key) {
+			error = true;
+		} else if(flib_ini_get_bool_opt(ini, &scheme->mods[i], key, false)) {
+			flib_log_e("Error reading mod %s in schemes file.", key);
+			error = true;
+		}
+		free(key);
+	}
+	return error;
+}
+
+static flib_scheme *readSchemeFromIni(flib_ini *ini, int index) {
+	flib_scheme *result = NULL;
+	char *schemeNameKey = makePrefixedName(index+1, "name");
+	if(schemeNameKey) {
+		char *schemeName = NULL;
+		if(!flib_ini_get_str_opt(ini, &schemeName, schemeNameKey, "Unnamed")) {
+			flib_scheme *tmpScheme = flib_scheme_create(schemeName);
+			if(tmpScheme) {
+				if(!readSettingsFromIni(ini, tmpScheme, index) && !readModsFromIni(ini, tmpScheme, index)) {
+					result = tmpScheme;
+					tmpScheme = NULL;
+				}
+			}
+			flib_scheme_destroy(tmpScheme);
+		}
+		free(schemeName);
+	}
+	free(schemeNameKey);
+	return result;
+}
+
+static flib_schemelist *fromIniHandleError(flib_schemelist *result, flib_ini *ini) {
+	flib_ini_destroy(ini);
+	flib_schemelist_destroy(result);
+	return NULL;
+}
+
+flib_schemelist *flib_schemelist_from_ini(const char *filename) {
+	if(log_badargs_if(filename==NULL)) {
+		return NULL;
+	}
+
+	flib_schemelist *list = NULL;
+	flib_ini *ini = flib_ini_load(filename);
+	if(!ini || flib_ini_enter_section(ini, "schemes")) {
+		flib_log_e("Missing file or missing section \"schemes\" in file %s.", filename);
+		return fromIniHandleError(list, ini);
+	}
+
+	list = flib_schemelist_create();
+	if(!list) {
+		return fromIniHandleError(list, ini);
+	}
+
+	int schemeCount = 0;
+	if(flib_ini_get_int(ini, &schemeCount, "size")) {
+		flib_log_e("Missing or malformed scheme count in config file %s.", filename);
+		return fromIniHandleError(list, ini);
+	}
+
+	for(int i=0; i<schemeCount; i++) {
+		flib_scheme *scheme = readSchemeFromIni(ini, i);
+		if(!scheme || flib_schemelist_insert(list, scheme, i)) {
+			flib_scheme_destroy(scheme);
+			flib_log_e("Error reading scheme %i from config file %s.", i, filename);
+			return fromIniHandleError(list, ini);
+		}
+	}
+
+
+	flib_ini_destroy(ini);
+	return list;
+}
+
+static int writeSchemeToIni(const flib_scheme *scheme, flib_ini *ini, int index) {
+	bool error = false;
+
+	char *key = makePrefixedName(index+1, "name");
+	error |= !key || flib_ini_set_str(ini, key, scheme->name);
+	free(key);
+
+	for(int i=0; i<flib_meta.modCount && !error; i++) {
+		char *key = makePrefixedName(index+1, flib_meta.mods[i].name);
+		error |= !key || flib_ini_set_bool(ini, key, scheme->mods[i]);
+		free(key);
+	}
+
+	for(int i=0; i<flib_meta.settingCount && !error; i++) {
+		char *key = makePrefixedName(index+1, flib_meta.settings[i].name);
+		error |= !key || flib_ini_set_int(ini, key, scheme->settings[i]);
+		free(key);
+	}
+	return error;
+}
+
+int flib_schemelist_to_ini(const char *filename, const flib_schemelist *schemes) {
+	int result = -1;
+	if(!log_badargs_if2(filename==NULL, schemes==NULL)) {
+		flib_ini *ini = flib_ini_create(NULL);
+		if(ini && !flib_ini_create_section(ini, "schemes")) {
+			bool error = false;
+			error |= flib_ini_set_int(ini, "size", schemes->schemeCount);
+			for(int i=0; i<schemes->schemeCount && !error; i++) {
+				error |= writeSchemeToIni(schemes->schemes[i], ini, i);
+			}
+
+			if(!error) {
+				result = flib_ini_save(ini, filename);
+			}
+		}
+		flib_ini_destroy(ini);
+	}
+	return result;
+}
+
+flib_schemelist *flib_schemelist_create() {
+	return flib_calloc(1, sizeof(flib_schemelist));
+}
+
+void flib_schemelist_destroy(flib_schemelist *list) {
+	if(list) {
+		for(int i=0; i<list->schemeCount; i++) {
+			flib_scheme_destroy(list->schemes[i]);
+		}
+		free(list->schemes);
+		free(list);
+	}
+}
+
+flib_scheme *flib_schemelist_find(flib_schemelist *list, const char *name) {
+	if(!log_badargs_if2(list==NULL, name==NULL)) {
+		for(int i=0; i<list->schemeCount; i++) {
+			if(!strcmp(name, list->schemes[i]->name)) {
+				return list->schemes[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+GENERATE_STATIC_LIST_INSERT(insertScheme, flib_scheme*)
+GENERATE_STATIC_LIST_DELETE(deleteScheme, flib_scheme*)
+
+int flib_schemelist_insert(flib_schemelist *list, flib_scheme *cfg, int pos) {
+	if(!log_badargs_if2(list==NULL, cfg==NULL)
+			&& !insertScheme(&list->schemes, &list->schemeCount, cfg, pos)) {
+		return 0;
+	}
+	return -1;
+}
+
+int flib_schemelist_delete(flib_schemelist *list, int pos) {
+	if(!log_badargs_if(list==NULL)) {
+		flib_scheme *elem = list->schemes[pos];
+		if(!deleteScheme(&list->schemes, &list->schemeCount, pos)) {
+			flib_scheme_destroy(elem);
+			return 0;
+		}
+	}
+	return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,79 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Functions for managing a list of schemes.
+ * This is in here because the scheme config file of the QtFrontend (which we are staying compatible with) contains
+ * all the schemes at once, so we need functions to work with a list like that.
+ */
+
+#ifndef SCHEMELIST_H_
+#define SCHEMELIST_H_
+
+#include "scheme.h"
+
+typedef struct {
+	int schemeCount;
+	flib_scheme **schemes;
+} flib_schemelist;
+
+/**
+ * Load a list of configurations from the ini file.
+ * Returns NULL on error.
+ */
+flib_schemelist *flib_schemelist_from_ini(const char *filename);
+
+/**
+ * Store the list of configurations to an ini file.
+ * Returns NULL on error.
+ */
+int flib_schemelist_to_ini(const char *filename, const flib_schemelist *config);
+
+/**
+ * Create an empty scheme list. Returns NULL on error.
+ */
+flib_schemelist *flib_schemelist_create();
+
+/**
+ * Insert a new scheme into the list at position pos, moving all higher schemes to make place.
+ * pos must be at least 0 (insert at the start) and at most list->schemeCount (insert at the end).
+ * Ownership of the scheme is transferred to the list.
+ * Returns 0 on success.
+ */
+int flib_schemelist_insert(flib_schemelist *list, flib_scheme *cfg, int pos);
+
+/**
+ * Delete a scheme from the list at position pos, moving down all higher schemes.
+ * The scheme is destroyed.
+ * Returns 0 on success.
+ */
+int flib_schemelist_delete(flib_schemelist *list, int pos);
+
+/**
+ * Find the scheme with a specific name
+ */
+flib_scheme *flib_schemelist_find(flib_schemelist *list, const char *name);
+
+/**
+ * Free this schemelist and all contained schemes
+ */
+void flib_schemelist_destroy(flib_schemelist *list);
+
+
+#endif /* SCHEMELIST_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,323 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "team.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+static flib_team *from_ini_handleError(flib_team *result, flib_ini *settingfile) {
+	flib_ini_destroy(settingfile);
+	flib_team_destroy(result);
+	return NULL;
+}
+
+flib_team *flib_team_from_ini(const char *filename) {
+	if(log_badargs_if(filename==NULL)) {
+		return NULL;
+	}
+
+	flib_team *result = flib_calloc(1, sizeof(flib_team));
+	flib_ini *ini = NULL;
+
+	if(!result) {
+		return from_ini_handleError(result, ini);
+	}
+
+	ini = flib_ini_load(filename);
+	if(!ini) {
+		flib_log_e("Error loading team file %s", filename);
+		return from_ini_handleError(result, ini);
+	}
+
+	if(flib_ini_enter_section(ini, "team")) {
+		flib_log_e("Missing section \"Team\" in team file %s", filename);
+		return from_ini_handleError(result, ini);
+	}
+	bool error = false;
+	error |= flib_ini_get_str(ini, &result->name, "name");
+	error |= flib_ini_get_str(ini, &result->grave, "grave");
+	error |= flib_ini_get_str(ini, &result->fort, "fort");
+	error |= flib_ini_get_str(ini, &result->voicepack, "voicepack");
+	error |= flib_ini_get_str(ini, &result->flag, "flag");
+	error |= flib_ini_get_int(ini, &result->rounds, "rounds");
+	error |= flib_ini_get_int(ini, &result->wins, "wins");
+	error |= flib_ini_get_int(ini, &result->campaignProgress, "campaignprogress");
+
+	int difficulty = 0;
+	error |= flib_ini_get_int(ini, &difficulty, "difficulty");
+
+	if(error) {
+		flib_log_e("Missing or malformed entry in section \"Team\" in file %s", filename);
+		return from_ini_handleError(result, ini);
+	}
+
+	for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+		char sectionName[32];
+		if(snprintf(sectionName, sizeof(sectionName), "hedgehog%i", i) <= 0) {
+			return from_ini_handleError(result, ini);
+		}
+		if(flib_ini_enter_section(ini, sectionName)) {
+			flib_log_e("Missing section \"%s\" in team file %s", sectionName, filename);
+			return from_ini_handleError(result, ini);
+		}
+		flib_hog *hog = &result->hogs[i];
+		error |= flib_ini_get_str(ini, &hog->name, "name");
+		error |= flib_ini_get_str(ini, &hog->hat, "hat");
+		error |= flib_ini_get_int(ini, &hog->rounds, "rounds");
+		error |= flib_ini_get_int(ini, &hog->kills, "kills");
+		error |= flib_ini_get_int(ini, &hog->deaths, "deaths");
+		error |= flib_ini_get_int(ini, &hog->suicides, "suicides");
+		result->hogs[i].difficulty = difficulty;
+		result->hogs[i].initialHealth = TEAM_DEFAULT_HEALTH;
+
+		if(error) {
+			flib_log_e("Missing or malformed entry in section \"%s\" in file %s", sectionName, filename);
+			return from_ini_handleError(result, ini);
+		}
+	}
+
+	if(!flib_ini_enter_section(ini, "binds")) {
+		result->bindingCount = flib_ini_get_keycount(ini);
+		if(result->bindingCount<0) {
+			flib_log_e("Error reading bindings from file %s", filename);
+			result->bindingCount = 0;
+		}
+		result->bindings = flib_calloc(result->bindingCount, sizeof(flib_binding));
+		if(!result->bindings) {
+			return from_ini_handleError(result, ini);
+		}
+		for(int i=0; i<result->bindingCount; i++) {
+			char *keyname = flib_ini_get_keyname(ini, i);
+			if(!keyname) {
+				error = true;
+			} else {
+				result->bindings[i].action = flib_urldecode(keyname);
+				error |= !result->bindings[i].action;
+				error |= flib_ini_get_str(ini, &result->bindings[i].binding, keyname);
+			}
+			free(keyname);
+		}
+	}
+
+	if(error) {
+		flib_log_e("Error reading team file %s", filename);
+		return from_ini_handleError(result, ini);
+	}
+
+	flib_ini_destroy(ini);
+	return result;
+}
+
+void flib_team_destroy(flib_team *team) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			free(team->hogs[i].name);
+			free(team->hogs[i].hat);
+			flib_weaponset_destroy(team->hogs[i].weaponset);
+		}
+		free(team->name);
+		free(team->grave);
+		free(team->fort);
+		free(team->voicepack);
+		free(team->flag);
+		if(team->bindings) {
+			for(int i=0; i<team->bindingCount; i++) {
+				free(team->bindings[i].action);
+				free(team->bindings[i].binding);
+			}
+		}
+		free(team->bindings);
+		free(team->ownerName);
+		free(team);
+	}
+}
+
+static int writeTeamSection(const flib_team *team, flib_ini *ini) {
+	if(flib_ini_create_section(ini, "team")) {
+		return -1;
+	}
+	bool error = false;
+	error |= flib_ini_set_str(ini, "name",  team->name);
+	error |= flib_ini_set_str(ini, "grave", team->grave);
+	error |= flib_ini_set_str(ini, "fort", team->fort);
+	error |= flib_ini_set_str(ini, "voicepack", team->voicepack);
+	error |= flib_ini_set_str(ini, "flag", team->flag);
+	error |= flib_ini_set_int(ini, "rounds", team->rounds);
+	error |= flib_ini_set_int(ini, "wins", team->wins);
+	error |= flib_ini_set_int(ini, "campaignprogress", team->campaignProgress);
+	error |= flib_ini_set_int(ini, "difficulty", team->hogs[0].difficulty);
+	return error;
+}
+
+static int writeHogSections(const flib_team *team, flib_ini *ini) {
+	for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+		const flib_hog *hog = &team->hogs[i];
+		char sectionName[32];
+		if(snprintf(sectionName, sizeof(sectionName), "hedgehog%i", i) <= 0) {
+			return -1;
+		}
+		if(flib_ini_create_section(ini, sectionName)) {
+			return -1;
+		}
+		bool error = false;
+		error |= flib_ini_set_str(ini, "name", hog->name);
+		error |= flib_ini_set_str(ini, "hat", hog->hat);
+		error |= flib_ini_set_int(ini, "rounds", hog->rounds);
+		error |= flib_ini_set_int(ini, "kills", hog->kills);
+		error |= flib_ini_set_int(ini, "deaths", hog->deaths);
+		error |= flib_ini_set_int(ini, "suicides", hog->suicides);
+		if(error) {
+			return error;
+		}
+	}
+	return 0;
+}
+
+static int writeBindingSection(const flib_team *team, flib_ini *ini) {
+	if(team->bindingCount == 0) {
+		return 0;
+	}
+	if(flib_ini_create_section(ini, "binds")) {
+		return -1;
+	}
+	for(int i=0; i<team->bindingCount; i++) {
+		bool error = false;
+		char *action = flib_urlencode(team->bindings[i].action);
+		if(action) {
+			error |= flib_ini_set_str(ini, action, team->bindings[i].binding);
+			free(action);
+		} else {
+			error = true;
+		}
+		if(error) {
+			return error;
+		}
+	}
+	return 0;
+}
+
+int flib_team_to_ini(const char *filename, const flib_team *team) {
+	int result = -1;
+	if(!log_badargs_if2(filename==NULL, team==NULL)) {
+		flib_ini *ini = flib_ini_create(filename);
+		bool error = false;
+		error |= writeTeamSection(team, ini);
+		error |= writeHogSections(team, ini);
+		error |= writeBindingSection(team, ini);
+		if(!error) {
+			result = flib_ini_save(ini, filename);
+		}
+		flib_ini_destroy(ini);
+	}
+	return result;
+}
+
+int flib_team_set_weaponset(flib_team *team, const flib_weaponset *set) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			flib_weaponset_destroy(team->hogs[i].weaponset);
+			team->hogs[i].weaponset = flib_weaponset_copy(set);
+			if(set && !team->hogs[i].weaponset) {
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+void flib_team_set_health(flib_team *team, int health) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			team->hogs[i].initialHealth = health;
+		}
+	}
+}
+
+static char *strdupWithError(const char *in, bool *error) {
+	char *out = flib_strdupnull(in);
+	if(in && !out) {
+		*error = true;
+	}
+	return out;
+}
+
+flib_team *flib_team_copy(const flib_team *team) {
+	flib_team *result = NULL;
+	if(team) {
+		flib_team *tmpTeam = flib_calloc(1, sizeof(flib_team));
+		if(tmpTeam) {
+			bool error = false;
+
+			for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+				tmpTeam->hogs[i].name = strdupWithError(team->hogs[i].name, &error);
+				tmpTeam->hogs[i].hat = strdupWithError(team->hogs[i].hat, &error);
+				tmpTeam->hogs[i].rounds = team->hogs[i].rounds;
+				tmpTeam->hogs[i].kills = team->hogs[i].kills;
+				tmpTeam->hogs[i].deaths = team->hogs[i].deaths;
+				tmpTeam->hogs[i].suicides = team->hogs[i].suicides;
+				tmpTeam->hogs[i].difficulty = team->hogs[i].difficulty;
+				tmpTeam->hogs[i].initialHealth = team->hogs[i].initialHealth;
+				tmpTeam->hogs[i].weaponset = flib_weaponset_copy(team->hogs[i].weaponset);
+				if(team->hogs[i].weaponset && !tmpTeam->hogs[i].weaponset) {
+					error = true;
+				}
+			}
+
+			tmpTeam->name = strdupWithError(team->name, &error);
+			tmpTeam->grave = strdupWithError(team->grave, &error);
+			tmpTeam->fort = strdupWithError(team->fort, &error);
+			tmpTeam->voicepack = strdupWithError(team->voicepack, &error);
+			tmpTeam->flag = strdupWithError(team->flag, &error);
+			tmpTeam->ownerName = strdupWithError(team->ownerName, &error);
+
+			tmpTeam->bindingCount = team->bindingCount;
+			if(team->bindings) {
+				tmpTeam->bindings = flib_calloc(team->bindingCount, sizeof(flib_binding));
+				if(tmpTeam->bindings) {
+					for(int i=0; i<tmpTeam->bindingCount; i++) {
+						tmpTeam->bindings[i].action = strdupWithError(team->bindings[i].action, &error);
+						tmpTeam->bindings[i].binding = strdupWithError(team->bindings[i].binding, &error);
+					}
+				} else {
+					error = true;
+				}
+			}
+
+			tmpTeam->rounds = team->rounds;
+			tmpTeam->wins = team->wins;
+			tmpTeam->campaignProgress = team->campaignProgress;
+
+			tmpTeam->colorIndex = team->colorIndex;
+			tmpTeam->hogsInGame = team->hogsInGame;
+			tmpTeam->remoteDriven = team->remoteDriven;
+
+			if(!error) {
+				result = tmpTeam;
+				tmpTeam = 0;
+			}
+		}
+		flib_team_destroy(tmpTeam);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,130 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * This file defines a data structure for a hedgewars team.
+ *
+ * Teams are used in several different contexts in Hedgewars, and some of these require
+ * extra information about teams. For example, the weaponset is important
+ * to the engine, but not for ini reading/writing, and with the team statistics it is the
+ * other way around. To keep things simple, the data structure can hold all information
+ * used in any context. On the downside, that means we can't use static typing to ensure
+ * that team information is "complete" for a particular purpose.
+ */
+#ifndef TEAM_H_
+#define TEAM_H_
+
+
+#include "weapon.h"
+#include "../hwconsts.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TEAM_DEFAULT_HEALTH 100
+
+/**
+ * Struct representing a single keybinding.
+ */
+typedef struct {
+	char *action;
+	char *binding;
+} flib_binding;
+
+typedef struct {
+	char *name;
+	char *hat;			// e.g. hair_yellow; References a .png file in Data/Graphics/Hats
+
+	// Statistics. They are irrelevant for the engine or server,
+	// but provided for ini reading/writing by the frontend.
+	int rounds;
+	int kills;
+	int deaths;
+	int suicides;
+
+	int difficulty;		// 0 = human, 1 = most difficult bot ... 5 = least difficult bot (somewhat counterintuitive)
+
+	// Transient setting used in game setup
+	int initialHealth;
+	flib_weaponset *weaponset;
+} flib_hog;
+
+typedef struct {
+	flib_hog hogs[HEDGEHOGS_PER_TEAM];
+	char *name;
+	char *grave;		// e.g. "Bone"; References a .png file in Data/Graphics/Graves
+	char *fort;			// e.g. "Castle"; References a series of files in Data/Forts
+	char *voicepack;	// e.g. "Classic"; References a directory in Data/Sounds/voices
+	char *flag;			// e.g. "hedgewars"; References a .png file in Data/Graphics/Flags
+
+	flib_binding *bindings;
+	int bindingCount;
+
+	// Statistics. They are irrelevant for the engine or server,
+	// but provided for ini reading/writing by the frontend.
+	int rounds;
+	int wins;
+	int campaignProgress;
+
+	// Transient settings used in game setup
+	int colorIndex;		// Index into a color table
+	int hogsInGame;		// The number of hogs that will actually play
+	bool remoteDriven;	// true for non-local teams in a network game
+	char *ownerName;	// Username of the owner of a team in a network game
+} flib_team;
+
+/**
+ * Free all memory associated with the team
+ */
+void flib_team_destroy(flib_team *team);
+
+/**
+ * Loads a team, returns NULL on error. Destroy this team using flib_team_destroy.
+ * This will not fill in the fields marked as "transient" in the structs above.
+ */
+flib_team *flib_team_from_ini(const char *filename);
+
+/**
+ * Write the team to an ini file. Attempts to retain extra ini settings
+ * that were already present. Note that not all fields of a team struct
+ * are stored in the ini, some are only used intermittently to store
+ * information about a team in the context of a game.
+ *
+ * The flib_team can handle "difficulty" on a per-hog basis, but it
+ * is only written per-team in the team file. The difficulty of the
+ * first hog is used for the entire team when writing.
+ */
+int flib_team_to_ini(const char *filename, const flib_team *team);
+
+/**
+ * Set the same weaponset for every hog in the team
+ */
+int flib_team_set_weaponset(flib_team *team, const flib_weaponset *set);
+
+/**
+ * Set the same initial health for every hog.
+ */
+void flib_team_set_health(flib_team *team, int health);
+
+/**
+ * Create a deep copy of a team. Returns NULL on failure.
+ */
+flib_team *flib_team_copy(const flib_team *team);
+
+#endif /* TEAM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,120 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "teamlist.h"
+
+#include "../util/util.h"
+#include "../util/list.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+flib_teamlist *flib_teamlist_create() {
+	return flib_calloc(1, sizeof(flib_teamlist));
+}
+
+void flib_teamlist_destroy(flib_teamlist *list) {
+	if(list) {
+		for(int i=0; i<list->teamCount; i++) {
+			flib_team_destroy(list->teams[i]);
+		}
+		free(list->teams);
+		free(list);
+	}
+}
+
+GENERATE_STATIC_LIST_INSERT(insertTeam, flib_team*)
+GENERATE_STATIC_LIST_DELETE(deleteTeam, flib_team*)
+
+static int findTeam(const flib_teamlist *list, const char *name) {
+	for(int i=0; i<list->teamCount; i++) {
+		if(!strcmp(name, list->teams[i]->name)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos) {
+	if(!log_badargs_if2(list==NULL, team==NULL)
+			&& !insertTeam(&list->teams, &list->teamCount, team, pos)) {
+		return 0;
+	}
+	return -1;
+}
+
+int flib_teamlist_delete(flib_teamlist *list, const char *name) {
+	int result = -1;
+	if(!log_badargs_if2(list==NULL, name==NULL)) {
+		int itemid = findTeam(list, name);
+		if(itemid>=0) {
+			flib_team *team = list->teams[itemid];
+			if(!deleteTeam(&list->teams, &list->teamCount, itemid)) {
+				flib_team_destroy(team);
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name) {
+	flib_team *result = NULL;
+	if(!log_badargs_if2(list==NULL, name==NULL)) {
+		int itemid = findTeam(list, name);
+		if(itemid>=0) {
+			result = list->teams[itemid];
+		}
+	}
+	return result;
+}
+
+void flib_teamlist_clear(flib_teamlist *list) {
+	if(!log_badargs_if(list==NULL)) {
+		for(int i=0; i<list->teamCount; i++) {
+			flib_team_destroy(list->teams[i]);
+		}
+		free(list->teams);
+		list->teams = NULL;
+		list->teamCount = 0;
+	}
+}
+
+flib_teamlist *flib_teamlist_copy(flib_teamlist *list) {
+	if(!list) {
+		return NULL;
+	}
+	flib_teamlist *result = flib_teamlist_create();
+	if(result) {
+		bool error = false;
+		for(int i=0; !error && i<list->teamCount; i++) {
+			flib_team *teamcopy = flib_team_copy(list->teams[i]);
+			if(!teamcopy || flib_teamlist_insert(result, teamcopy, i)) {
+				flib_team_destroy(teamcopy);
+				error = true;
+			}
+		}
+		if(error) {
+			flib_teamlist_destroy(result);
+			result = NULL;
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,61 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TEAMLIST_H_
+#define TEAMLIST_H_
+
+#include "team.h"
+
+typedef struct {
+	int teamCount;
+	flib_team **teams;
+} flib_teamlist;
+
+flib_teamlist *flib_teamlist_create();
+
+void flib_teamlist_destroy(flib_teamlist *list);
+
+/**
+ * Insert a team into the list. The list takes ownership of the team. Returns 0 on success.
+ */
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos);
+
+/**
+ * Delete the team with the name [name] from the list and destroys it.
+ * Returns 0 on success.
+ */
+int flib_teamlist_delete(flib_teamlist *list, const char *name);
+
+/**
+ * Returns the team with the name [name] from the list if it exists, NULL otherwise
+ */
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name);
+
+/**
+ * Removes all items from the list and destroys them.
+ */
+void flib_teamlist_clear(flib_teamlist *list);
+
+/**
+ * Create a copy of the list and all the teams it contains. Weaponsets are not copied, but
+ * kept as references
+ */
+flib_teamlist *flib_teamlist_copy(flib_teamlist *list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,234 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "weapon.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/list.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+static void setField(char field[WEAPONS_COUNT+1], const char *line, int lineLen, bool no9) {
+	if(lineLen>WEAPONS_COUNT) {
+		lineLen = WEAPONS_COUNT;
+	}
+
+	char min = '0';
+	char max = no9 ? '8' : '9';
+	for(int i=0; i<lineLen; i++) {
+		if(line[i] >= min && line[i] <= max) {
+			field[i] = line[i];
+		} else {
+			flib_log_w("Invalid character in weapon config string \"%.*s\", position %i", lineLen, line, i);
+			field[i] = '0';
+		}
+	}
+	for(int i=lineLen; i<WEAPONS_COUNT; i++) {
+		field[i] = '0';
+	}
+	field[WEAPONS_COUNT] = 0;
+}
+
+flib_weaponset *flib_weaponset_create(const char *name) {
+	flib_weaponset *result = NULL;
+	if(!log_badargs_if(name==NULL)) {
+		flib_weaponset *newSet = flib_calloc(1, sizeof(flib_weaponset));
+		if(newSet) {
+			newSet->name = flib_strdupnull(name);
+			if(newSet->name) {
+				setField(newSet->loadout, "", 0, false);
+				setField(newSet->crateprob, "", 0, false);
+				setField(newSet->crateammo, "", 0, false);
+				setField(newSet->delay, "", 0, false);
+				result = newSet;
+				newSet = NULL;
+			}
+		}
+		flib_weaponset_destroy(newSet);
+	}
+	return result;
+}
+
+void flib_weaponset_destroy(flib_weaponset *cfg) {
+	if(cfg) {
+		free(cfg->name);
+		free(cfg);
+	}
+}
+
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset) {
+	if(!weaponset) {
+		return NULL;
+	}
+
+	flib_weaponset *result = flib_weaponset_create(weaponset->name);
+	if(result) {
+		memcpy(result->loadout, weaponset->loadout, WEAPONS_COUNT+1);
+		memcpy(result->crateprob, weaponset->crateprob, WEAPONS_COUNT+1);
+		memcpy(result->delay, weaponset->delay, WEAPONS_COUNT+1);
+		memcpy(result->crateammo, weaponset->crateammo, WEAPONS_COUNT+1);
+	}
+
+	return result;
+}
+
+void flib_weaponsetlist_destroy(flib_weaponsetlist *list) {
+	if(list) {
+		for(int i=0; i<list->weaponsetCount; i++) {
+			flib_weaponset_destroy(list->weaponsets[i]);
+		}
+		free(list->weaponsets);
+		free(list);
+	}
+}
+
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring) {
+	flib_weaponset *result = NULL;
+	if(!log_badargs_if2(name==NULL, ammostring==NULL)) {
+		result = flib_weaponset_create(name);
+		if(result) {
+			int fieldlen = strlen(ammostring)/4;
+			setField(result->loadout, ammostring, fieldlen, false);
+			setField(result->crateprob, ammostring + fieldlen, fieldlen, true);
+			setField(result->delay, ammostring + 2*fieldlen, fieldlen, true);
+			setField(result->crateammo, ammostring + 3*fieldlen, fieldlen, true);
+		}
+	}
+	return result;
+}
+
+static int fillWeaponsetFromIni(flib_weaponsetlist *list, flib_ini *ini, int index) {
+	int result = -1;
+	char *keyname = flib_ini_get_keyname(ini, index);
+	char *decodedKeyname = flib_urldecode(keyname);
+	char *ammostring = NULL;
+	if(decodedKeyname && !flib_ini_get_str(ini, &ammostring, keyname)) {
+		flib_weaponset *set = flib_weaponset_from_ammostring(decodedKeyname, ammostring);
+		if(set) {
+			result = flib_weaponsetlist_insert(list, set, list->weaponsetCount);
+			if(result) {
+				flib_weaponset_destroy(set);
+			}
+		}
+	}
+	free(ammostring);
+	free(decodedKeyname);
+	free(keyname);
+	return result;
+}
+
+static int fillWeaponsetsFromIni(flib_weaponsetlist *list, flib_ini *ini) {
+	bool error = false;
+	int weaponsets = flib_ini_get_keycount(ini);
+
+	for(int i=0; i<weaponsets && !error; i++) {
+		error |= fillWeaponsetFromIni(list, ini, i);
+	}
+	return error;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_from_ini(const char *filename) {
+	flib_weaponsetlist *result = NULL;
+	if(!log_badargs_if(filename==NULL)) {
+		flib_ini *ini = flib_ini_load(filename);
+		if(!ini) {
+			flib_log_e("Missing file %s.", filename);
+		} else if(flib_ini_enter_section(ini, "General")) {
+			flib_log_e("Missing section \"General\" in file %s.", filename);
+		} else {
+			flib_weaponsetlist *tmpList = flib_weaponsetlist_create();
+			if(tmpList && !fillWeaponsetsFromIni(tmpList, ini)) {
+				result = tmpList;
+				tmpList = NULL;
+			}
+			flib_weaponsetlist_destroy(tmpList);
+		}
+		flib_ini_destroy(ini);
+	}
+	return result;
+}
+
+static bool needsEscape(char c) {
+	return !((c>='0' && c<='9') || (c>='a' && c <='z'));
+}
+
+static int writeWeaponsetToIni(flib_ini *ini, flib_weaponset *set) {
+	int result = -1;
+	char weaponstring[WEAPONS_COUNT*4+1];
+	strcpy(weaponstring, set->loadout);
+	strcat(weaponstring, set->crateprob);
+	strcat(weaponstring, set->delay);
+	strcat(weaponstring, set->crateammo);
+
+	char *escapedname = flib_urlencode_pred(set->name, needsEscape);
+	if(escapedname) {
+		result = flib_ini_set_str(ini, escapedname, weaponstring);
+	}
+	free(escapedname);
+	return result;
+}
+
+int flib_weaponsetlist_to_ini(const char *filename, const flib_weaponsetlist *list) {
+	int result = -1;
+	if(!log_badargs_if2(filename==NULL, list==NULL)) {
+		flib_ini *ini = flib_ini_create(NULL);
+		if(ini && !flib_ini_create_section(ini, "General")) {
+			bool error = false;
+			for(int i=0; i<list->weaponsetCount && !error; i++) {
+				error |= writeWeaponsetToIni(ini, list->weaponsets[i]);
+			}
+
+			if(!error) {
+				result = flib_ini_save(ini, filename);
+			}
+		}
+		flib_ini_destroy(ini);
+	}
+	return result;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_create() {
+	return flib_calloc(1, sizeof(flib_weaponsetlist));
+}
+
+GENERATE_STATIC_LIST_INSERT(insertWeaponset, flib_weaponset*)
+GENERATE_STATIC_LIST_DELETE(deleteWeaponset, flib_weaponset*)
+
+int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *set, int pos) {
+	if(!log_badargs_if2(list==NULL, set==NULL)
+			&& !insertWeaponset(&list->weaponsets, &list->weaponsetCount, set, pos)) {
+		return 0;
+	}
+	return -1;
+}
+
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos) {
+	if(!log_badargs_if(list==NULL)) {
+		flib_weaponset *elem = list->weaponsets[pos];
+		if(!deleteWeaponset(&list->weaponsets, &list->weaponsetCount, pos)) {
+			flib_weaponset_destroy(elem);
+			return 0;
+		}
+	}
+	return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,104 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef MODEL_WEAPON_H_
+#define MODEL_WEAPON_H_
+
+#include "../hwconsts.h"
+
+/**
+ * These values are all ASCII characters in the range '0'..'9'
+ * The fields are zero-terminated so they can easily be used as strings.
+ *
+ * For loadout, 9 means inifinite ammo.
+ * For the other setting, 9 is invalid.
+ */
+typedef struct {
+	char loadout[WEAPONS_COUNT+1];
+	char crateprob[WEAPONS_COUNT+1];
+	char crateammo[WEAPONS_COUNT+1];
+	char delay[WEAPONS_COUNT+1];
+	char *name;
+} flib_weaponset;
+
+typedef struct {
+	int weaponsetCount;
+	flib_weaponset **weaponsets;
+} flib_weaponsetlist;
+
+/**
+ * Returns a new weapon set, or NULL on error.
+ * name must not be NULL.
+ *
+ * The new weapon set is pre-filled with default
+ * settings (see hwconsts.h)
+ */
+flib_weaponset *flib_weaponset_create(const char *name);
+
+/**
+ * Free the memory used by this weaponset
+ */
+void flib_weaponset_destroy(flib_weaponset *weaponset);
+
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset);
+
+/**
+ * Create a weaponset from an ammostring. This format is used both in the ini files
+ * and in the net protocol.
+ */
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring);
+
+/**
+ * Load a list of weaponsets from the ini file.
+ * Returns NULL on error.
+ */
+flib_weaponsetlist *flib_weaponsetlist_from_ini(const char *filename);
+
+/**
+ * Store the list of weaponsets to an ini file.
+ * Returns NULL on error.
+ */
+int flib_weaponsetlist_to_ini(const char *filename, const flib_weaponsetlist *weaponsets);
+
+/**
+ * Create an empty weaponset list. Returns NULL on error.
+ */
+flib_weaponsetlist *flib_weaponsetlist_create();
+
+/**
+ * Release all memory associated with the weaponsetlist and release all contained weaponsets
+ */
+void flib_weaponsetlist_destroy(flib_weaponsetlist *list);
+
+/**
+ * Insert a new weaponset into the list at position pos, moving all higher weaponsets to make place.
+ * pos must be at least 0 (insert at the start) and at most list->weaponsetCount (insert at the end).
+ * Ownership of the weaponset is transferred to the list.
+ * Returns 0 on success.
+ */
+int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *weaponset, int pos);
+
+/**
+ * Delete a weaponset from the list at position pos, moving down all higher weaponsets.
+ * The weaponset is destroyed.
+ * Returns 0 on success.
+ */
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,259 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "netbase.h"
+#include "../util/buffer.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NET_READBUFFER_LIMIT (1024*1024)
+
+struct _flib_netbase {
+	flib_vector *readBuffer;
+	flib_tcpsocket *sock;
+};
+
+flib_netbase *flib_netbase_create(const char *server, uint16_t port) {
+	if(log_badargs_if2(server==NULL, port==0)) {
+		return NULL;
+	}
+
+	flib_netbase *result = NULL;
+	flib_netbase *newNet =  flib_calloc(1, sizeof(flib_netbase));
+
+	if(newNet) {
+		newNet->readBuffer = flib_vector_create();
+		newNet->sock = flib_socket_connect(server, port);
+		if(newNet->readBuffer && newNet->sock) {
+			flib_log_i("Connected to server %s:%u", server, (unsigned)port);
+			result = newNet;
+			newNet = NULL;
+		}
+	}
+	flib_netbase_destroy(newNet);
+
+	return result;
+}
+
+void flib_netbase_destroy(flib_netbase *net) {
+	if(net) {
+		flib_socket_close(net->sock);
+		flib_vector_destroy(net->readBuffer);
+		free(net);
+	}
+}
+
+bool flib_netbase_connected(flib_netbase *net) {
+	if(!log_badargs_if(net==NULL) && net->sock) {
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Parses and returns a message, and removes it from the vector.
+ */
+static flib_netmsg *parseMessage(flib_vector *vec) {
+	const uint8_t *partStart = flib_vector_data(vec);
+	const uint8_t *end = partStart+flib_vector_size(vec);
+	flib_netmsg *result = flib_netmsg_create();
+	if(!result) {
+		return NULL;
+	}
+
+	while(1) {
+		const uint8_t *partEnd = memchr(partStart, '\n', end-partStart);
+		if(!partEnd) {
+			// message incomplete
+			flib_netmsg_destroy(result);
+			return NULL;
+		} else if(partEnd-partStart == 0) {
+			// Zero-length part, message end marker. Remove the message from the vector.
+			uint8_t *vectorStart = flib_vector_data(vec);
+			size_t msgLen = partEnd+1-vectorStart;
+			memmove(vectorStart, partEnd+1, flib_vector_size(vec)-msgLen);
+			flib_vector_resize(vec, flib_vector_size(vec)-msgLen);
+			return result;
+		} else {
+			if(flib_netmsg_append_part(result, partStart, partEnd-partStart)) {
+				flib_netmsg_destroy(result);
+				return NULL;
+			}
+			partStart = partEnd+1; // Skip the '\n'
+		}
+	}
+	return NULL; // Never reached
+}
+
+/**
+ * Receive some bytes and add them to the buffer.
+ * Returns the number of bytes received.
+ * Automatically closes the socket if an error occurs
+ * and sets sock=NULL.
+ */
+static int receiveToBuffer(flib_netbase *net) {
+	uint8_t buffer[256];
+	if(!net->sock) {
+		return 0;
+	} else if(flib_vector_size(net->readBuffer) > NET_READBUFFER_LIMIT) {
+		flib_log_e("Net connection closed: Net message too big");
+		flib_socket_close(net->sock);
+		net->sock = NULL;
+		return 0;
+	} else {
+		int size = flib_socket_nbrecv(net->sock, buffer, sizeof(buffer));
+		if(size>=0 && !flib_vector_append(net->readBuffer, buffer, size)) {
+			return size;
+		} else {
+			flib_socket_close(net->sock);
+			net->sock = NULL;
+			return 0;
+		}
+	}
+}
+
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net) {
+	if(log_badargs_if(net==NULL)) {
+		return NULL;
+	}
+
+	flib_netmsg *msg;
+	while(!(msg=parseMessage(net->readBuffer))
+			&& receiveToBuffer(net)) {}
+
+	if(msg) {
+		return msg;
+	} else if(!net->sock && flib_vector_size(net->readBuffer)>0) {
+		// Connection is down and we didn't get a complete message, just flush the rest.
+		flib_vector_resize(net->readBuffer, 0);
+	}
+	return NULL;
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+		flib_log_d("[NET OUT][%03u]%*.*s",(unsigned)len, (unsigned)len, (unsigned)len, data);
+	}
+}
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len) {
+	if(log_badargs_if2(net==NULL, data==NULL && len>0)) {
+		return -1;
+	}
+	if(!net->sock) {
+		flib_log_w("flib_netbase_send_raw: Not connected.");
+		return -1;
+	}
+
+	if(flib_socket_send(net->sock, data, len) == len) {
+		logSentMsg(data, len);
+		return 0;
+	} else {
+		flib_log_w("Failed or incomplete write: net connection lost.");
+		flib_socket_close(net->sock);
+		net->sock = NULL;
+		return -1;
+	}
+}
+
+int flib_netbase_send_message(flib_netbase *net, const flib_netmsg *msg) {
+	if(log_badargs_if2(net==NULL, msg==NULL)) {
+		return -1;
+	}
+
+	size_t totalSize = 0;
+	for(int i=0; i<msg->partCount; i++) {
+		totalSize += strlen(msg->parts[i]) + 1;
+	}
+	totalSize++; // Last part ends in two '\n' instead of one
+
+	uint8_t *buffer = flib_malloc(totalSize);
+	if(!buffer) {
+		return -1;
+	}
+	size_t pos = 0;
+	for(int i=0; i<msg->partCount; i++) {
+		size_t partsize = strlen(msg->parts[i]);
+		memcpy(buffer+pos, msg->parts[i], partsize);
+		pos += partsize;
+		buffer[pos++] = '\n';
+	}
+	buffer[pos++] = '\n';
+	return flib_netbase_send_raw(net, buffer, pos);
+}
+
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...) {
+	int result = -1;
+	if(!log_badargs_if2(net==NULL, format==NULL)) {
+		va_list argp;
+		va_start(argp, format);
+		char *buffer = flib_vasprintf(format, argp);
+		if(buffer) {
+			result = flib_netbase_send_raw(net, buffer, strlen(buffer));
+		}
+		free(buffer);
+		va_end(argp);
+	}
+	return result;
+}
+
+flib_netmsg *flib_netmsg_create() {
+	flib_netmsg *result = flib_calloc(1, sizeof(flib_netmsg));
+	if(result) {
+		result->partCount = 0;
+		result->parts = NULL;
+		return result;
+	} else {
+		return NULL;
+	}
+}
+
+void flib_netmsg_destroy(flib_netmsg *msg) {
+	if(msg) {
+		for(int i=0; i<msg->partCount; i++) {
+			free(msg->parts[i]);
+		}
+		free(msg->parts);
+		free(msg);
+	}
+}
+
+int flib_netmsg_append_part(flib_netmsg *msg, const void *part, size_t partlen) {
+	int result = -1;
+	if(!log_badargs_if2(msg==NULL, part==NULL && partlen>0)) {
+		char **newParts = realloc(msg->parts, (msg->partCount+1)*sizeof(*msg->parts));
+		if(newParts) {
+			msg->parts = newParts;
+			msg->parts[msg->partCount] = flib_malloc(partlen+1);
+			if(msg->parts[msg->partCount]) {
+				memcpy(msg->parts[msg->partCount], part, partlen);
+				msg->parts[msg->partCount][partlen] = 0;
+				msg->partCount++;
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,91 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Low-level protocol support for the network connection
+ */
+
+#ifndef NETBASE_H_
+#define NETBASE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+struct _flib_netbase;
+typedef struct _flib_netbase flib_netbase;
+
+typedef struct {
+	int partCount;
+	char **parts;
+} flib_netmsg;
+
+/**
+ * Start a connection to the specified Hedgewars server.
+ *
+ * Returns NULL on error. Destroy the created object with flib_netconn_destroy.
+ */
+flib_netbase *flib_netbase_create(const char *server, uint16_t port);
+
+/**
+ * Free resources and close sockets.
+ */
+void flib_netbase_destroy(flib_netbase *net);
+
+/**
+ * Determine the current connection state. Starts out true, and turns to
+ * false when we are disconnected from the server.
+ */
+bool flib_netbase_connected(flib_netbase *net);
+
+/**
+ * Receive a new message and return it as a flib_netmsg. The netmsg has to be
+ * destroyed with flib_netmsg_destroy after use.
+ * Returns NULL if no message is available.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net);
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len);
+
+/**
+ * Write a single message to the server. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Returns a negative value on failure.
+ */
+int flib_netbase_send_message(flib_netbase *net, const flib_netmsg *msg);
+
+/**
+ * Send a message printf-style.
+ *
+ * flib_netbase_sendf(net, "%s\n\n", "TOGGLE_READY");
+ * flib_netbase_sendf(net, "%s\n%s\n%i\n\n", "CFG", "MAPGEN", MAPGEN_MAZE);
+ */
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...);
+
+flib_netmsg *flib_netmsg_create();
+void flib_netmsg_destroy(flib_netmsg *msg);
+int flib_netmsg_append_part(flib_netmsg *msg, const void *param, size_t len);
+
+#endif /* NETBASE_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,625 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com>
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "netconn_internal.h"
+#include "netprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../md5/md5.h"
+#include "../base64/base64.h"
+#include "../model/mapcfg.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+flib_netconn *flib_netconn_create(const char *playerName, const char *dataDirPath, const char *host, int port) {
+	flib_netconn *result = NULL;
+	if(!log_badargs_if4(playerName==NULL, host==NULL, port<1, port>65535)) {
+		flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
+		if(newConn) {
+			newConn->netBase = flib_netbase_create(host, port);
+			newConn->playerName = flib_strdupnull(playerName);
+			newConn->dataDirPath = flib_strdupnull(dataDirPath);
+
+			newConn->netconnState = NETCONN_STATE_CONNECTING;
+			newConn->isAdmin = false;
+
+			newConn->isChief = false;
+			newConn->map = flib_map_create_named("", "NoSuchMap");
+			newConn->pendingTeamlist.teamCount = 0;
+			newConn->pendingTeamlist.teams = NULL;
+			newConn->teamlist.teamCount = 0;
+			newConn->teamlist.teams = NULL;
+			newConn->scheme = NULL;
+			newConn->style = NULL;
+			newConn->weaponset = NULL;
+
+			newConn->running = false;
+			newConn->destroyRequested = false;
+			netconn_clearCallbacks(newConn);
+			if(newConn->netBase && newConn->playerName && newConn->dataDirPath && newConn->map) {
+				result = newConn;
+				newConn = NULL;
+			}
+		}
+		flib_netconn_destroy(newConn);
+	}
+	return result;
+}
+
+void flib_netconn_destroy(flib_netconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			netconn_clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_netbase_destroy(conn->netBase);
+			free(conn->playerName);
+			free(conn->dataDirPath);
+
+			flib_map_destroy(conn->map);
+			flib_teamlist_clear(&conn->pendingTeamlist);
+			flib_teamlist_clear(&conn->teamlist);
+			flib_scheme_destroy(conn->scheme);
+			free(conn->style);
+			flib_weaponset_destroy(conn->weaponset);
+
+			free(conn);
+		}
+	}
+}
+
+bool flib_netconn_is_chief(flib_netconn *conn) {
+	if(!log_badargs_if(conn==NULL) && conn->netconnState==NETCONN_STATE_ROOM) {
+		return conn->isChief;
+	}
+	return false;
+}
+
+const char *flib_netconn_get_playername(flib_netconn *conn) {
+	if(!log_badargs_if(conn==NULL)) {
+		return conn->playerName;
+	}
+	return NULL;
+}
+
+void netconn_leaveRoom(flib_netconn *conn) {
+	conn->netconnState = NETCONN_STATE_LOBBY;
+	conn->isChief = false;
+	flib_map_destroy(conn->map);
+	conn->map = flib_map_create_named("", "NoSuchMap");
+	flib_teamlist_clear(&conn->pendingTeamlist);
+	flib_teamlist_clear(&conn->teamlist);
+	flib_scheme_destroy(conn->scheme);
+	conn->scheme = NULL;
+	free(conn->style);
+	conn->style = NULL;
+	flib_weaponset_destroy(conn->weaponset);
+	conn->weaponset = NULL;
+}
+
+void netconn_setMap(flib_netconn *conn, const flib_map *map) {
+	flib_map *copy = flib_map_copy(map);
+	if(copy) {
+		flib_map_destroy(conn->map);
+		conn->map = copy;
+	}
+}
+
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
+	flib_weaponset *copy = flib_weaponset_copy(weaponset);
+	if(copy) {
+		flib_weaponset_destroy(conn->weaponset);
+		conn->weaponset = copy;
+	}
+}
+
+void netconn_setScript(flib_netconn *conn, const char *script) {
+	char *copy = flib_strdupnull(script);
+	if(copy) {
+		free(conn->style);
+		conn->style = copy;
+	}
+}
+
+void netconn_setScheme(flib_netconn *conn, const flib_scheme *scheme) {
+	flib_scheme *copy = flib_scheme_copy(scheme);
+	if(copy) {
+		flib_scheme_destroy(conn->scheme);
+		conn->scheme = copy;
+	}
+}
+
+flib_gamesetup *flib_netconn_create_gamesetup(flib_netconn *conn) {
+	flib_gamesetup *result = NULL;
+	if(!log_badargs_if(conn==NULL)) {
+		if(conn->teamlist.teamCount==0 || !conn->scheme || !conn->weaponset) {
+			flib_log_e("Incomplete room state");
+		} else {
+			flib_gamesetup stackSetup = {0};
+			stackSetup.gamescheme = conn->scheme;
+			stackSetup.map = conn->map;
+			stackSetup.style = conn->style;
+			stackSetup.teamlist = &conn->teamlist;
+			result = flib_gamesetup_copy(&stackSetup);
+			if(result) {
+				bool error = false;
+				for(int i=0; i<result->teamlist->teamCount; i++) {
+					if(flib_team_set_weaponset(result->teamlist->teams[i], conn->weaponset)) {
+						error = true;
+					}
+					flib_team_set_health(result->teamlist->teams[i], flib_scheme_get_setting(conn->scheme, "health", 100));
+				}
+				if(result->map->mapgen == MAPGEN_NAMED && result->map->name) {
+					flib_mapcfg mapcfg;
+					if(!flib_mapcfg_read(conn->dataDirPath, result->map->name, &mapcfg)) {
+						free(result->map->theme);
+						result->map->theme = flib_strdupnull(mapcfg.theme);
+						if(!result->map->theme) {
+							error = true;
+						}
+					} else {
+						flib_log_e("Unable to read map config for map %s", result->map->name);
+					}
+				}
+				if(error) {
+					flib_gamesetup_destroy(result);
+					result = NULL;
+				}
+			}
+		}
+	}
+	return result;
+}
+
+static void flib_netconn_wrappedtick(flib_netconn *conn) {
+	flib_netmsg *netmsg;
+	flib_netbase *net = conn->netBase;
+	bool exit = false;
+
+	while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
+		if(netmsg->partCount==0) {
+			flib_log_w("Empty server message");
+			continue;
+		}
+
+		if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+			char *buf = flib_join(netmsg->parts, netmsg->partCount, "|");
+			if(buf) {
+				flib_log_d("[Net In]%s", buf);
+			}
+			free(buf);
+		}
+
+		const char *cmd = netmsg->parts[0];
+
+	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
+	    	if(netmsg->partCount<2) {
+	    		flib_log_w("Net: Malformed NICK message");
+	    	} else {
+	    		char *nick = flib_strdupnull(netmsg->parts[1]);
+	    		if(nick) {
+					free(conn->playerName);
+					conn->playerName = nick;
+	    		} else {
+					conn->netconnState = NETCONN_STATE_DISCONNECTED;
+					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
+					exit = true;
+				}
+	    	}
+	    } else if (!strcmp(cmd, "PROTO")) {
+	        // The server just echoes this back apparently
+		} else if (!strcmp(cmd, "ERROR")) {
+			if (netmsg->partCount >= 2) {
+				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, netmsg->parts[1]);
+			} else {
+				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, "Unknown Error");
+			}
+		} else if(!strcmp(cmd, "WARNING")) {
+			if (netmsg->partCount >= 2) {
+				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, netmsg->parts[1]);
+			} else {
+				conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, "Unknown Warning");
+			}
+	    } else if(!strcmp(cmd, "CONNECTED")) {
+			if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
+				flib_log_w("Net: Server too old");
+				flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
+				conn->netconnState = NETCONN_STATE_DISCONNECTED;
+				conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_SERVER_TOO_OLD, "Server too old");
+				exit = true;
+			} else {
+				flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
+				flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
+			}
+		} else if(!strcmp(cmd, "PING")) {
+	        if (netmsg->partCount > 1) {
+	        	flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
+	        } else {
+	        	flib_netbase_sendf(net, "%s\n\n", "PONG");
+	        }
+	    } else if(!strcmp(cmd, "ROOMS")) {
+	        if(netmsg->partCount % 8 != 1) {
+	        	flib_log_w("Net: Malformed ROOMS message");
+	        } else {
+	        	int roomCount = netmsg->partCount/8;
+	        	flib_room **rooms = flib_room_array_from_netmsg(netmsg->parts+1, roomCount);
+	        	if(rooms) {
+	        		conn->onRoomlistCb(conn->onRoomlistCtx, (const flib_room**)rooms, roomCount);
+	        		for(int i=0; i<roomCount; i++) {
+	        			flib_room_destroy(rooms[i]);
+	        		}
+	        		free(rooms);
+	        	}
+	        }
+	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
+	        if(netmsg->partCount < 2) {
+	        	flib_log_w("Net: Empty SERVERMESSAGE message");
+	        } else {
+	        	conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_SERVERMESSAGE, netmsg->parts[1]);
+	        }
+	    } else if (!strcmp(cmd, "CHAT")) {
+	        if(netmsg->partCount < 3) {
+	        	flib_log_w("Net: Empty CHAT message");
+	        } else {
+	        	conn->onChatCb(conn->onChatCtx, netmsg->parts[1], netmsg->parts[2]);
+	        }
+	    } else if (!strcmp(cmd, "INFO")) {
+	        if(netmsg->partCount < 5) {
+	        	flib_log_w("Net: Malformed INFO message");
+	        } else {
+	        	char *joined = flib_join(netmsg->parts+1, netmsg->partCount-1, "\n");
+	        	if(joined) {
+	        		conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
+	        	}
+	        	free(joined);
+	        }
+	    } else if(!strcmp(cmd, "SERVER_VARS")) {
+	    	for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
+	    		conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
+	    	}
+	    } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
+	        if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
+	        	flib_log_w("Net: Malformed CLIENT_FLAGS message");
+	        } else {
+				const char *flags = netmsg->parts[1];
+				bool setFlag = flags[0] == '+';
+
+				for(int i=1; flags[i]; i++) {
+					switch(flags[i]) {
+					case 'r':
+						for(int j = 2; j < netmsg->partCount; ++j) {
+							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[j], setFlag);
+						}
+						break;
+					default:
+						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
+						break;
+					}
+				}
+	        }
+	    } else if (!strcmp(cmd, "ADD_TEAM")) {
+	        if(netmsg->partCount != 24 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad ADD_TEAM message");
+	        } else {
+	        	flib_team *team = flib_team_from_netmsg(netmsg->parts+1);
+	        	if(!team || flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount)) {
+					flib_team_destroy(team);
+					conn->netconnState = NETCONN_STATE_DISCONNECTED;
+					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
+					exit = true;
+	        	} else {
+	        		conn->onTeamAddCb(conn->onTeamAddCtx, team);
+	        	}
+	        }
+	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
+	        if(netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad REMOVETEAM message");
+	        } else {
+	        	flib_teamlist_delete(&conn->teamlist, netmsg->parts[1]);
+	        	conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
+	        }
+	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
+	    	netconn_leaveRoom(conn);
+	        conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
+	    } else if(!strcmp(cmd, "KICKED")) {
+	    	netconn_leaveRoom(conn);
+	    	conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
+	    } else if(!strcmp(cmd, "JOINED")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad JOINED message");
+	        } else {
+				for(int i = 1; i < netmsg->partCount; ++i)
+				{
+					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+					if (isMe) {
+						conn->netconnState = NETCONN_STATE_ROOM;
+						conn->onEnterRoomCb(conn->onEnterRoomCtx, conn->isChief);
+					}
+
+					conn->onRoomJoinCb(conn->onRoomJoinCtx, netmsg->parts[i]);
+				}
+	        }
+	    } else if(!strcmp(cmd, "LOBBY:JOINED")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad JOINED message");
+	        } else {
+				for(int i = 1; i < netmsg->partCount; ++i)
+				{
+					bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+					if (isMe && conn->netconnState == NETCONN_STATE_CONNECTING) {
+						conn->onConnectedCb(conn->onConnectedCtx);
+						conn->netconnState = NETCONN_STATE_LOBBY;
+					}
+					conn->onLobbyJoinCb(conn->onLobbyJoinCtx, netmsg->parts[i]);
+				}
+	        }
+	    } else if(!strcmp(cmd, "LEFT")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad LEFT message");
+	        } else {
+	        	conn->onRoomLeaveCb(conn->onRoomLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
+	        }
+	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
+	    	const char *subcmd = netmsg->parts[1];
+	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
+	    		flib_room *room = flib_room_from_netmsg(netmsg->parts+2);
+	    		if(room) {
+	    			conn->onRoomAddCb(conn->onRoomAddCtx, room);
+	    		}
+	    		flib_room_destroy(room);
+			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
+				flib_room *room = flib_room_from_netmsg(netmsg->parts+3);
+				if(room) {
+	    			conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], room);
+	    		}
+				flib_room_destroy(room);
+			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
+				conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
+			} else {
+				flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
+			}
+	    } else if(!strcmp(cmd, "LOBBY:LEFT")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad LOBBY:LEFT message");
+	        } else {
+	        	conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
+	        }
+	    } else if (!strcmp(cmd, "RUN_GAME")) {
+	        conn->onRunGameCb(conn->onRunGameCtx);
+	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
+	    	conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
+	    } else if (!strcmp(cmd, "NOTICE")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad NOTICE message");
+	        } else {
+				errno = 0;
+				long n = strtol(netmsg->parts[1], NULL, 10);
+				if(errno) {
+					flib_log_w("Net: Bad NOTICE message");
+				} else if(n==0) {
+					conn->onNickTakenCb(conn->onNickTakenCtx, conn->playerName);
+				} else {
+					flib_log_w("Net: Unknown NOTICE message: %l", n);
+				}
+	        }
+	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
+	        if (netmsg->partCount != 2 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	        } else {
+	        	flib_team *team = flib_team_copy(flib_teamlist_find(&conn->pendingTeamlist, netmsg->parts[1]));
+	        	if(team) {
+	        		flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount);
+	        		flib_teamlist_delete(&conn->pendingTeamlist, netmsg->parts[1]);
+	        	} else {
+	        		flib_log_e("Team accepted that was not requested: %s", netmsg->parts[1]);
+	        	}
+	        	conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
+	        }
+	    } else if (!strcmp(cmd, "CFG")) {
+	        if(netmsg->partCount < 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad CFG message");
+	        } else {
+	        	const char *subcmd = netmsg->parts[1];
+				if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == flib_meta.modCount + flib_meta.settingCount + 3) {
+					flib_scheme *cfg = flib_scheme_from_netmsg(netmsg->parts+2);
+					if(cfg) {
+						flib_scheme_destroy(conn->scheme);
+						conn->scheme = cfg;
+						conn->onSchemeChangedCb(conn->onSchemeChangedCtx, cfg);
+					} else {
+						flib_log_e("Error processing CFG SCHEME message");
+					}
+				} else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
+					flib_map *map = flib_map_from_netmsg(netmsg->parts+2);
+					if(map) {
+						flib_map_destroy(conn->map);
+						conn->map = map;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
+					} else {
+						flib_log_e("Error processing CFG FULLMAPCONFIG message");
+					}
+				} else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
+					char *mapname = flib_strdupnull(netmsg->parts[2]);
+					if(mapname) {
+						free(conn->map->name);
+						conn->map->name = mapname;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
+					} else {
+						flib_log_e("Error processing CFG MAP message");
+					}
+				} else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
+					char *themename = flib_strdupnull(netmsg->parts[2]);
+					if(themename) {
+						free(conn->map->theme);
+						conn->map->theme = themename;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
+					} else {
+						flib_log_e("Error processing CFG THEME message");
+					}
+				} else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
+					char *seed = flib_strdupnull(netmsg->parts[2]);
+					if(seed) {
+						free(conn->map->seed);
+						conn->map->seed = seed;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
+					} else {
+						flib_log_e("Error processing CFG SEED message");
+					}
+				} else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
+					conn->map->templateFilter = atoi(netmsg->parts[2]);
+					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
+				} else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
+					conn->map->mapgen = atoi(netmsg->parts[2]);
+					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
+				} else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
+					conn->map->mazeSize = atoi(netmsg->parts[2]);
+					conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
+				} else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
+					size_t drawnMapSize = 0;
+					uint8_t *drawnMapData = NULL;
+					if(!flib_drawnmapdata_from_netmsg(netmsg->parts[2], &drawnMapData, &drawnMapSize)) {
+						free(conn->map->drawData);
+						conn->map->drawData = drawnMapData;
+						conn->map->drawDataSize = drawnMapSize;
+						conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
+					} else {
+						flib_log_e("Error processing CFG DRAWNMAP message");
+					}
+				} else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
+					netconn_setScript(conn, netmsg->parts[2]);
+					conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
+				} else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
+					flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
+					if(weapons) {
+						flib_weaponset_destroy(conn->weaponset);
+						conn->weaponset = weapons;
+						conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
+					} else {
+						flib_log_e("Error processing CFG AMMO message");
+					}
+				} else {
+					flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
+				}
+	        }
+	    } else if (!strcmp(cmd, "HH_NUM")) {
+	        if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad HH_NUM message");
+	        } else {
+	        	int hogs = atoi(netmsg->parts[2]);
+	        	if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
+	        		flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
+	        	} else {
+	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+	        		if(team) {
+	        			team->hogsInGame = hogs;
+	        		} else {
+	        			flib_log_e("HH_NUM message for unknown team %s", netmsg->parts[1]);
+	        		}
+	        		conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
+	        	}
+	        }
+	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
+	        if (netmsg->partCount != 3 || conn->netconnState!=NETCONN_STATE_ROOM) {
+	            flib_log_w("Net: Bad TEAM_COLOR message");
+	        } else {
+	        	long color;
+	        	if(sscanf(netmsg->parts[2], "%lu", &color) && color>=0 && color<flib_teamcolor_count) {
+	        		flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+	        		if(team) {
+	        			team->colorIndex = color;
+	        		} else {
+	        			flib_log_e("TEAM_COLOR message for unknown team %s", netmsg->parts[1]);
+	        		}
+	        		conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], color);
+	        	} else {
+	        		flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
+	        	}
+	        }
+	    } else if (!strcmp(cmd, "EM")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad EM message");
+	        } else {
+	        	for(int i = 1; i < netmsg->partCount; ++i) {
+					char *out = NULL;
+					size_t outlen;
+					bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
+					if(ok && outlen) {
+						conn->onEngineMessageCb(conn->onEngineMessageCtx, (uint8_t*)out, outlen);
+					} else {
+						flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
+					}
+					free(out);
+	        	}
+	        }
+	    } else if (!strcmp(cmd, "BYE")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad BYE message");
+	        } else {
+				conn->netconnState = NETCONN_STATE_DISCONNECTED;
+				if (!strcmp(netmsg->parts[1], "Authentication failed")) {
+					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_AUTH_FAILED, netmsg->parts[1]);
+				} else {
+					conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
+				}
+				exit = true;
+	        }
+	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
+	    	conn->onAdminAccessCb(conn->onAdminAccessCtx);
+	    	conn->isAdmin = true;
+	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
+	        } else {
+	        	conn->isChief = strcmp("0", netmsg->parts[1]);
+	        	conn->onRoomChiefStatusCb(conn->onRoomChiefStatusCtx, conn->isChief);
+	        }
+	    } else {
+	    	flib_log_w("Unknown server command: %s", cmd);
+	    }
+		flib_netmsg_destroy(netmsg);
+	}
+
+	if(!exit && !conn->destroyRequested && !flib_netbase_connected(net)) {
+		conn->netconnState = NETCONN_STATE_DISCONNECTED;
+		conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_CONNLOST, "Connection lost");
+	}
+}
+
+void flib_netconn_tick(flib_netconn *conn) {
+	if(!log_badargs_if(conn==NULL)
+			&& !log_w_if(conn->running, "Call to flib_netconn_tick from a callback")
+			&& !log_w_if(conn->netconnState == NETCONN_STATE_DISCONNECTED, "We are already done.")) {
+		conn->running = true;
+		flib_netconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_netconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,646 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * This file contains functions for communicating with a Hedgewars server to chat, prepare and play
+ * rounds of Hedgewars.
+ *
+ * To use this, first create a netconn object by calling flib_netconn_create. This will start the
+ * connection to the game server (which might fail right away, the function returns null then). You
+ * should also register your callback functions right at the start to ensure you don't miss any
+ * callbacks.
+ *
+ * In order to allow the netconn to run, you should regularly call flib_netconn_tick(), which
+ * performs network I/O and calls your callbacks on interesting events.
+ *
+ * When the connection is closed, you will receive the onDisconnect callback. This is the signal to
+ * destroy the netconn and stop calling tick().
+ *
+ * The connection process lasts from the time you create the netconn until you receive the
+ * onConnected callback (or onDisconnected in case something goes wrong). During that time, you
+ * might receive the onNickTaken and onPasswordRequest callbacks; see their description for more
+ * information on how to handle them. You could also receive other callbacks during connecting (e.g.
+ * about the room list), but it should be safe to ignore them.
+ *
+ * Once you are connected, you are in the lobby, and you can enter rooms and leave them again. The
+ * room and lobby states have different protocols, so many commands only work in either one or the
+ * other. If you are in a room you might also be in a game, but most of the functions behave the
+ * same ingame as in a room.
+ *
+ * The state changes from lobby to room when the server tells you that you just entered one, which
+ * will also trigger the onEnterRoom callback. This usually happens in reply to either a joinRoom,
+ * createRoom or playerFollow command.
+ *
+ * The state changes back to lobby when the room is dissolved, when you are kicked from the room, or
+ * when you actively leave the room using flib_netconn_send_leaveRoom. The first two events will
+ * trigger the onLeaveRoom callback.
+ */
+
+#ifndef NETCONN_H_
+#define NETCONN_H_
+
+#include "../model/gamesetup.h"
+#include "../model/scheme.h"
+#include "../model/room.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define NETCONN_STATE_CONNECTING 0
+#define NETCONN_STATE_LOBBY 1
+#define NETCONN_STATE_ROOM 2
+#define NETCONN_STATE_DISCONNECTED 10
+
+#define NETCONN_DISCONNECT_NORMAL 0				// The connection was closed normally
+#define NETCONN_DISCONNECT_SERVER_TOO_OLD 1		// The server has a lower protocol version than we do
+#define NETCONN_DISCONNECT_AUTH_FAILED 2		// You sent a password with flib_netconn_send_password that was not accepted
+#define NETCONN_DISCONNECT_CONNLOST 3			// The network connection was lost
+#define NETCONN_DISCONNECT_INTERNAL_ERROR 100	// Something went wrong in frontlib itself
+
+#define NETCONN_ROOMLEAVE_ABANDONED 0			// The room was closed because the chief left
+#define NETCONN_ROOMLEAVE_KICKED 1				// You have been kicked from the room
+
+#define NETCONN_MSG_TYPE_PLAYERINFO 0			// A response to flib_netconn_send_playerInfo
+#define NETCONN_MSG_TYPE_SERVERMESSAGE 1		// The welcome message when connecting to the lobby
+#define NETCONN_MSG_TYPE_WARNING 2				// A general warning message
+#define NETCONN_MSG_TYPE_ERROR 3				// A general error message
+
+#define NETCONN_MAPCHANGE_FULL 0
+#define NETCONN_MAPCHANGE_MAP 1
+#define NETCONN_MAPCHANGE_MAPGEN 2
+#define NETCONN_MAPCHANGE_DRAWNMAP 3
+#define NETCONN_MAPCHANGE_MAZE_SIZE 4
+#define NETCONN_MAPCHANGE_TEMPLATE 5
+#define NETCONN_MAPCHANGE_THEME 6
+#define NETCONN_MAPCHANGE_SEED 7
+
+typedef struct _flib_netconn flib_netconn;
+
+/**
+ * Create a new netplay connection with these parameters.
+ * The path to the data directory must end with a path delimiter (e.g. C:\Games\Hedgewars\Data\)
+ */
+flib_netconn *flib_netconn_create(const char *playerName, const char *dataDirPath, const char *host, int port);
+void flib_netconn_destroy(flib_netconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_netconn_tick(flib_netconn *conn);
+
+/**
+ * Are you currently the owner of this room? The return value only makes sense in
+ * NETCONN_STATE_ROOM and NETCONN_STATE_INGAME states.
+ */
+bool flib_netconn_is_chief(flib_netconn *conn);
+
+/**
+ * Returns the playername. This is *probably* the one provided on creation, but if that name was
+ * already taken, a different one could have been set by the onNickTaken callback or its default
+ * implementation.
+ */
+const char *flib_netconn_get_playername(flib_netconn *conn);
+
+/**
+ * Generate a game setup from the current room state.
+ * Returns NULL if the room state does not contain enough information for a complete game setup,
+ * or if an error occurs.
+ *
+ * The new gamesetup must be destroyed with flib_gamesetup_destroy().
+ */
+flib_gamesetup *flib_netconn_create_gamesetup(flib_netconn *conn);
+
+
+
+
+// Send functions needed when connecting and disconnecting
+
+	/**
+	 * Request a different nickname.
+	 * This function only makes sense in reaction to an onNickTaken callback, because the netconn
+	 * automatically requests the nickname you provide on creation, and once the server accepts the
+	 * nickname it can no longer be changed.
+	 */
+	int flib_netconn_send_nick(flib_netconn *conn, const char *nick);
+
+	/**
+	 * Send the password in reply to a password request.
+	 * If the server does not accept the password, you will be disconnected
+	 * (NETCONN_DISCONNECT_AUTH_FAILED)
+	 */
+	int flib_netconn_send_password(flib_netconn *conn, const char *passwd);
+
+	/**
+	 * Tell the server that you want to leave. If successful, the server will disconnect you.
+	 */
+	int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg);
+
+
+// Send functions that make sense both in the lobby and in rooms
+
+	/**
+	 * Send a chat message. This message is either sent to the lobby or the room, depending on
+	 * whether you are in a room at the moment. The message is not echoed back to you.
+	 */
+	int flib_netconn_send_chat(flib_netconn *conn, const char *chat);
+
+	/**
+	 * Kick a player. This has different meanings in the lobby and in a room;
+	 * In the lobby, it will kick the player from the server, and you need to be a server admin to
+	 * do it. In a room, it will kick the player from the room, and you need to be room chief.
+	 */
+	int flib_netconn_send_kick(flib_netconn *conn, const char *playerName);
+
+	/**
+	 * Request information about a player (e.g. current room, version, partial IP). If the action
+	 * succeeds, you will receive an onMessage callback with NETCONN_MSG_TYPE_PLAYERINFO containing
+	 * the requested information.
+	 */
+	int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName);
+
+
+// Send functions that only make sense in the lobby
+
+	/**
+	 * Request an update of the room list. Only makes sense when in lobby state.
+	 * If the action succeeds, you will receive an onRoomlist callback containing the current room
+	 * data.
+	 */
+	int flib_netconn_send_request_roomlist(flib_netconn *conn);
+
+	/**
+	 * Join a room as guest (not chief). Only makes sense when in lobby state. If the action
+	 * succeeds, you will receive an onEnterRoom callback with chief=false.
+	 */
+	int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room);
+
+	/**
+	 * Follow a player. Only valid in the lobby. If the player is in a room (or in a game), this
+	 * command is analogous to calling flib_netconn_send_joinRoom with that room.
+	 */
+	int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName);
+
+	/**
+	 * Create and join a new room. Only makes sense when in lobby state. If the action succeeds,
+	 * you will receive an onEnterRoom callback with chief=true.
+	 */
+	int flib_netconn_send_createRoom(flib_netconn *conn, const char *room);
+
+	/**
+	 * Ban a player. You need to be in the lobby and a server admin for this to work.
+	 */
+	int flib_netconn_send_ban(flib_netconn *conn, const char *playerName);
+
+	/**
+	 * Does something administrator-y. At any rate you need to be an administrator and in the lobby
+	 * to use this command.
+	 */
+	int flib_netconn_send_clearAccountsCache(flib_netconn *conn);
+
+	/**
+	 * Sets a server variable to the indicated value. Only makes sense if you are server admin and
+	 * in the lobby. Known variables are MOTD_NEW, MOTD_OLD and LATEST_PROTO. MOTD_OLD is shown to
+	 * players with older protocol versions, to inform them that they might want to update.
+	 */
+	int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value);
+
+	/**
+	 * Queries all server variables. Only makes sense if you are server admin and in the lobby.
+	 * If the action succeeds, you will receive several onServerVar callbacks with the
+	 * current values of all server variables.
+	 */
+	int flib_netconn_send_getServerVars(flib_netconn *conn);
+
+
+// Send functions that only make sense in a room
+
+	/**
+	 * Leave the room for the lobby. Only makes sense in room state. msg can be NULL if you don't
+	 * want to send a message. The server always accepts a part message, so once you send it off,
+	 * you can just assume that you are back in the lobby.
+	 */
+	int flib_netconn_send_leaveRoom(flib_netconn *conn, const char *msg);
+
+	/**
+	 * Change your "ready" status in the room. Only makes sense when in room state. If the action
+	 * succeeds, you will receive an onReadyState callback containing the change.
+	 */
+	int flib_netconn_send_toggleReady(flib_netconn *conn);
+
+	/**
+	 * Add a team to the current room. Apart from the "fixed" team information, this also includes
+	 * the color, but not the number of hogs. Only makes sense when in room state. If the action
+	 * succeeds, you will receive an onTeamAccepted callback with the name of the team.
+	 *
+	 * Notes: Technically, sending a color here is the only way for a non-chief to set the color of
+	 * her own team. The server remembers this color and even generates a separate teamColor message
+	 * to inform everyone of it. However, at the moment the frontends generally override this color
+	 * with one they choose themselves in order to deal with unfortunate behavior of the QtFrontend,
+	 * which always sends color index 0 when adding a team but thinks that the team has a random
+	 * color. The chief always sends a new color in order to bring the QtFrontend back into sync.
+	 */
+	int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team);
+
+	/**
+	 * Remove the team with the name teamname. Only makes sense when in room state.
+	 * The server does not send a reply on success.
+	 */
+	int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname);
+
+
+// Send functions that only make sense in a room and if you are room chief
+
+	/**
+	 * Rename the current room. Only makes sense in room state and if you are chief. If the action
+	 * succeeds, you (and everyone else on the server) will receive an onRoomUpdate message
+	 * containing the change.
+	 */
+	int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName);
+
+	/**
+	 * Set the number of hogs for a team. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount);
+
+	/**
+	 * Set the teamcolor of a team. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex);
+
+	/**
+	 * Set the weaponset for the room. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+
+	/**
+	 * Set the map for the room. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_map(flib_netconn *conn, const flib_map *map);
+
+	/**
+	 * Set the mapname. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName);
+
+	/**
+	 * Set the map generator (regular, maze, drawn, named). Only makes sense in room state and if
+	 * you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen);
+
+	/**
+	 * Set the map template for regular maps. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter);
+
+	/**
+	 * Set the maze template (maze size) for mazes. Only makes sense in room state and if you are
+	 * chief. The server does not send a reply.
+	 */
+	int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize);
+
+	/**
+	 * Set the seed for the map. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed);
+
+	/**
+	 * Set the theme for the map. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme);
+
+	/**
+	 * Set the draw data for the drawn map. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size);
+
+	/**
+	 * Set the script (game style). Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_script(flib_netconn *conn, const char *scriptName);
+
+	/**
+	 * Set the scheme. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_scheme(flib_netconn *conn, const flib_scheme *scheme);
+
+	/**
+	 * Signal that you want to start the game. Only makes sense in room state and if you are chief.
+	 * The server will check whether all players are ready and whether it believes the setup makes
+	 * sense (e.g. more than one clan). If the server is satisfied, you will receive an onRunGame
+	 * callback (all other clients in the room are notified the same way). Otherwise the server
+	 * might answer with a warning, or might not answer at all.
+	 */
+	int flib_netconn_send_startGame(flib_netconn *conn);
+
+	/**
+	 * Allow/forbid players to join the room. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn);
+
+	/**
+	 * Allow/forbid adding teams to the room. Only makes sense in room state and if you are chief.
+	 * The server does not send a reply.
+	 */
+	int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn);
+
+
+// Send functions that are only needed for running a game
+
+	/**
+	 * Send a teamchat message, forwarded from the engine. Only makes sense ingame.
+	 * The server does not send a reply. In contrast to a Chat message, the server
+	 * automatically converts this into an engine message and passes it on to the other
+	 * clients.
+	 */
+	int flib_netconn_send_teamchat(flib_netconn *conn, const char *msg);
+
+	/**
+	 * Send an engine message. Only makes sense when ingame. In a networked game, you have to pass
+	 * all the engine messages from the engine here, and they will be spread to all other clients
+	 * in the game to keep the game in sync.
+	 */
+	int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size);
+
+	/**
+	 * Inform the server that the round has ended. Call this when the engine has disconnected,
+	 * passing 1 if the round ended normally, 0 otherwise.
+	 */
+	int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError);
+
+
+
+
+
+// Callbacks that are important for connecting/disconnecting
+
+	/**
+	 * onNickTaken is called when connecting to the server, if it turns out that there is already a
+	 * player with the same nick.
+	 * In order to proceed, a new nickname needs to be sent to the server using
+	 * flib_netconn_send_nick() (or of course you can bail out and send a QUIT).
+	 * If you don't set a callback, the netconn will automatically react by generating a new name.
+	 */
+	void flib_netconn_onNickTaken(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+
+	/**
+	 * When connecting with a registered nickname, the server will ask for a password before
+	 * admitting you in. This callback is called when that happens. As a reaction, you can send the
+	 * password using flib_netconn_send_password. If you don't register a callback, the default
+	 * behavior is to just quit in a way that will cause a disconnect with
+	 * NETCONN_DISCONNECT_AUTH_FAILED.
+	 *
+	 * You can't just choose a new nickname when you receive this callback, because at that point
+	 * the server has already accepted your nick.
+	 */
+	void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+
+	/**
+	 * This is called when the server has accepted our nickname (and possibly password) and we have
+	 * entered the lobby.
+	 */
+	void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context), void* context);
+
+	/**
+	 * This is always the last callback (unless the netconn is destroyed early), and the netconn
+	 * should be destroyed when it is received. The reason for the disconnect is passed as one of
+	 * the NETCONN_DISCONNECT_ constants. Sometimes a message is included as well, but that
+	 * parameter might also be NULL.
+	 */
+	void flib_netconn_onDisconnected(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void* context);
+
+
+// Callbacks that make sense in most situations
+
+	/**
+	 * Callback for several informational messages that should be displayed to the user
+	 * (e.g. in the chat window), but do not require a reaction. If a game is running, you might
+	 * want to redirect some of these messages to the engine as well so the user will see them.
+	 */
+	void flib_netconn_onMessage(flib_netconn *conn, void (*callback)(void *context, int msgtype, const char *msg), void* context);
+
+	/**
+	 * We received a chat message. Where this message belongs depends on the current state
+	 * (lobby/room). If a game is running the message should be passed to the engine.
+	 */
+	void flib_netconn_onChat(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *msg), void* context);
+
+	/**
+	 * Callbacks for incremental room list updates. They will fire whenever these events occur,
+	 * even before you first query the actual roomlist - so be sure not to blindly reference your
+	 * room list in these callbacks. The server currently only sends updates when a room changes
+	 * its name, so in order to update other room information you need to query the roomlist again
+	 * (see send_request_roomlist / onRoomlist).
+	 */
+	void flib_netconn_onRoomAdd(flib_netconn *conn, void (*callback)(void *context, const flib_room *room), void* context);
+	void flib_netconn_onRoomDelete(flib_netconn *conn, void (*callback)(void *context, const char *name), void* context);
+	void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_room *room), void* context);
+
+	/**
+	 * Callbacks for players joining or leaving the lobby. In contrast to the roomlist updates, you
+	 * will get a JOIN callback for every player already on the server when you join (and there is
+	 * no direct way to query the current playerlist)
+	 *
+	 * NOTE: partMessage may be NULL.
+	 */
+	void flib_netconn_onLobbyJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+	void flib_netconn_onLobbyLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
+
+
+// Callbacks that happen only in response to specific requests
+
+	/**
+	 * Response to flib_netconn_send_request_roomlist().
+	 * The rooms array contains the current state of all rooms on the server.
+	 */
+	void flib_netconn_onRoomlist(flib_netconn *conn, void (*callback)(void *context, const flib_room **rooms, int roomCount), void* context);
+
+	/**
+	 * Response to flib_netconn_send_joinRoom, flib_netconn_send_playerFollow or
+	 * flib_netconn_send_createRoom.
+	 *
+	 * You just left the lobby and entered a room.
+	 * If chief is true, you can and should send a full configuration for the room now. This
+	 * consists of ammo, scheme, script and map, where map apparently has to come last.
+	 */
+	void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context);
+
+	/**
+	 * Response to flib_netconn_send_addTeam.
+	 * The server might reject your team for several reasons, e.g. because it has the same name as
+	 * an existing team, or because the room chief restricted adding new teams. If the team is
+	 * accepted by the server, this callback is fired.
+	 *
+	 * If you are the room chief, you are expected to provide the hog count for your own team now
+	 * using flib_netconn_send_teamHogCount. The color of the team is already set to the one you
+	 * provided in addTeam.
+	 */
+	void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *team), void *context);
+
+	/**
+	 * When you query the server vars with flib_netconn_send_getServerVars (only works as admin),
+	 * the server replies with a list of them. This callback is called for each entry in that list.
+	 */
+	void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context);
+
+
+// Callbacks that are only relevant in a room
+
+	/**
+	 * This callback informs about changes to your room chief status, i.e. whether you are allowed
+	 * to modify the current room. Generally when you create a room you start out being room chief,
+	 * and when you join an existing room you are not. However, if the original chief leaves a room,
+	 * the server can choose a new chief, and if that happens this callback is called with the new
+	 * status.
+	 *
+	 * Note: This callback does not automatically fire when joining a room. You can always query the
+	 * current chief status using flib_netconn_is_chief().
+	 */
+	void flib_netconn_onRoomChiefStatus(flib_netconn *conn, void (*callback)(void *context, bool chief), void* context);
+
+	/**
+	 * One of the players in the room (possibly you) changed their ready state.
+	 */
+	void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context);
+
+	/**
+	 * You just left a room and entered the lobby again.
+	 * reason is one of the NETCONN_ROOMLEAVE_ constants (usually a kick).
+	 * This will not be called when you actively leave a room using PART.
+	 * Don't confuse with onRoomLeave, which indicates that *someone else* left the room.
+	 */
+	void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context);
+
+	/**
+	 * Someone joined or left the room you are currently in.
+	 * Analogous to onLobbyJoin/leave, you will receive the join callback for all players that are
+	 * already in the room when you join, including for yourself (this is actually how it is
+	 * determined that you joined a room).
+	 *
+	 * However, you will *not* receive onRoomLeave messages for everyone when you leave the room.
+	 */
+	void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+	void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
+
+	/**
+	 * A new team was added to the room. The person who adds a team does NOT receive this callback
+	 * (he gets onTeamAccepted instead).
+	 *
+	 * The team does not contain bindings, stats, weaponset, color or the number of hogs (but it is
+	 * assumed to be the default of 4).
+	 *
+	 * If you receive this message and you are the room chief, you may want to send a color and hog
+	 * count for this team using flib_netconn_send_teamHogCount / teamColor for QtFrontend
+	 * compatibility.
+	 *
+	 * The server currently sends another message with the color of the team to the same recipients
+	 * as this teamAdd message, which will trigger an onTeamColorChanged callback. See the
+	 * description of flib_netconn_send_addTeam for more information.
+	 */
+	void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, const flib_team *team), void *context);
+
+	/**
+	 * A team was removed from the room. The person who removed the team will not receive this
+	 * callback.
+	 */
+	void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context);
+
+	/**
+	 * The number of hogs in a team has been changed by the room chief. If you are the chief and
+	 * change the number of hogs yourself, you will not receive this callback.
+	 */
+	void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context);
+
+	/**
+	 * The color of a team has been set or changed. The client who set or changed the color will
+	 * not receive this callback.
+	 *
+	 * Normally, only the chief can change the color of a team. However, this message is also
+	 * generated when a team is added, so you can receive it even as chief.
+	 */
+	void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int colorIndex), void *context);
+
+	/**
+	 * The room chief has changed the game scheme (or you just joined a room).
+	 * You will not receive this callback if you changed the scheme yourself.
+	 */
+	void flib_netconn_onSchemeChanged(flib_netconn *conn, void (*callback)(void *context, const flib_scheme *scheme), void *context);
+
+	/**
+	 * The room chief has changed the map (or you just joined a room). Only non-chiefs receive these
+	 * messages.
+	 *
+	 * To reduce the number of callback functions, the netconn keeps track of the current map
+	 * settings and always passes the entire current map config, but informs the callee about what
+	 * has changed (see the NETCONN_MAPCHANGE_ constants).
+	 *
+	 * Caution: Due to the way the protocol works, the map might not be complete at this point if it
+	 * is a hand-drawn map, because the "full" map config does not include the drawn map data.
+	 */
+	void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context);
+
+	/**
+	 * The room chief has changed the game style (or you just joined a room). If you are the chief
+	 * and change the style yourself, you will not receive this callback.
+	 */
+	void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context);
+
+	/**
+	 * The room chief has changed the weaponset (or you just joined a room). If you are the chief
+	 * and change the weaponset yourself, you will not receive this callback.
+	 */
+	void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, const flib_weaponset *weaponset), void *context);
+
+	/**
+	 * The game is starting. Fire up the engine and join in!
+	 * You can let the netconn generate the right game setup using flib_netconn_create_gamesetup
+	 */
+	void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+	/**
+	 * You are in a room, a game is in progress, and the server is sending you the new input for the
+	 * engine to keep up to date with the current happenings. Pass it on to the engine using
+	 * flib_gameconn_send_enginemsg.
+	 */
+	void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const uint8_t *message, size_t size), void *context);
+
+
+// Callbacks only needed for admin stuffs
+
+	/**
+	 * This callback is called if the server informs us that we have admin rights.
+	 */
+	void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_callbacks.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,153 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "netconn_internal.h"
+
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) {
+	flib_log_i("Net: [%i] %s", msgtype, msg);
+}
+
+static void defaultCallback_onChat(void *context, const char *nick, const char *msg) {
+	flib_log_i("%s: %s", nick, msg);
+}
+
+// Change the name by suffixing it with a number. If it already ends in a number, increase that number by 1.
+static void defaultCallback_onNickTaken(void *context, const char *requestedNick) {
+	flib_netconn *conn = context;
+	size_t namelen = strlen(requestedNick);
+	int digits = 0;
+	while(digits<namelen && isdigit(requestedNick[namelen-1-digits])) {
+		digits++;
+	}
+	long suffix = 0;
+	if(digits>0) {
+		suffix = atol(requestedNick+namelen-digits)+1;
+	}
+	char *newPlayerName = flib_asprintf("%.*s%li", namelen-digits, requestedNick, suffix);
+	if(newPlayerName) {
+		flib_netconn_send_nick(conn, newPlayerName);
+	} else {
+		flib_netconn_send_quit(conn, "Nick already taken.");
+	}
+	free(newPlayerName);
+}
+
+// Default behavior: Quit
+static void defaultCallback_onPasswordRequest(void *context, const char *requestedNick) {
+	flib_netconn_send_quit((flib_netconn*)context, "Authentication failed");
+}
+
+void netconn_clearCallbacks(flib_netconn *conn) {
+	flib_netconn_onMessage(conn, NULL, NULL);
+	flib_netconn_onConnected(conn, NULL, NULL);
+	flib_netconn_onDisconnected(conn, NULL, NULL);
+	flib_netconn_onRoomlist(conn, NULL, NULL);
+	flib_netconn_onRoomAdd(conn, NULL, NULL);
+	flib_netconn_onRoomDelete(conn, NULL, NULL);
+	flib_netconn_onRoomUpdate(conn, NULL, NULL);
+	flib_netconn_onChat(conn, NULL, NULL);
+	flib_netconn_onLobbyJoin(conn, NULL, NULL);
+	flib_netconn_onLobbyLeave(conn, NULL, NULL);
+	flib_netconn_onRoomJoin(conn, NULL, NULL);
+	flib_netconn_onRoomLeave(conn, NULL, NULL);
+	flib_netconn_onNickTaken(conn, NULL, NULL);
+	flib_netconn_onPasswordRequest(conn, NULL, NULL);
+	flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
+	flib_netconn_onReadyState(conn, NULL, NULL);
+	flib_netconn_onEnterRoom(conn, NULL, NULL);
+	flib_netconn_onLeaveRoom(conn, NULL, NULL);
+	flib_netconn_onTeamAdd(conn, NULL, NULL);
+	flib_netconn_onTeamDelete(conn, NULL, NULL);
+	flib_netconn_onRunGame(conn, NULL, NULL);
+	flib_netconn_onTeamAccepted(conn, NULL, NULL);
+	flib_netconn_onHogCountChanged(conn, NULL, NULL);
+	flib_netconn_onTeamColorChanged(conn, NULL, NULL);
+	flib_netconn_onEngineMessage(conn, NULL, NULL);
+	flib_netconn_onSchemeChanged(conn, NULL, NULL);
+	flib_netconn_onMapChanged(conn, NULL, NULL);
+	flib_netconn_onScriptChanged(conn, NULL, NULL);
+	flib_netconn_onWeaponsetChanged(conn, NULL, NULL);
+	flib_netconn_onAdminAccess(conn, NULL, NULL);
+	flib_netconn_onServerVar(conn, NULL, NULL);
+}
+
+/**
+ * This macro generates a callback setter function. It uses the name of the callback to
+ * automatically generate the function name and the fields to set, so a consistent naming
+ * convention needs to be enforced (not that that is a bad thing). If null is passed as
+ * callback to the generated function, the defaultCb will be set instead (with conn
+ * as the context).
+ */
+#define GENERATE_CB_SETTER(cbName, cbParameterTypes, defaultCb) \
+	void flib_netconn_##cbName(flib_netconn *conn, void (*callback)cbParameterTypes, void *context) { \
+		if(!log_badargs_if(conn==NULL)) { \
+			conn->cbName##Cb = callback ? callback : &defaultCb; \
+			conn->cbName##Ctx = callback ? context : conn; \
+		} \
+	}
+
+/**
+ * Generate a callback setter function like GENERATE_CB_SETTER, and automatically generate a
+ * no-op callback function as well that is used as default.
+ */
+#define GENERATE_CB_SETTER_AND_DEFAULT(cbName, cbParameterTypes) \
+	static void _noop_callback_##cbName cbParameterTypes {} \
+	GENERATE_CB_SETTER(cbName, cbParameterTypes, _noop_callback_##cbName)
+
+GENERATE_CB_SETTER(onMessage, (void *context, int msgtype, const char *msg), defaultCallback_onMessage);
+GENERATE_CB_SETTER_AND_DEFAULT(onConnected, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onDisconnected, (void *context, int reason, const char *message));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomlist, (void *context, const flib_room **rooms, int roomCount));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomAdd, (void *context, const flib_room *room));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomDelete, (void *context, const char *name));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomUpdate, (void *context, const char *oldName, const flib_room *room));
+GENERATE_CB_SETTER(onChat, (void *context, const char *nick, const char *msg), defaultCallback_onChat);
+GENERATE_CB_SETTER_AND_DEFAULT(onLobbyJoin, (void *context, const char *nick));
+GENERATE_CB_SETTER_AND_DEFAULT(onLobbyLeave, (void *context, const char *nick, const char *partMsg));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomJoin, (void *context, const char *nick));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomLeave, (void *context, const char *nick, const char *partMessage));
+GENERATE_CB_SETTER(onNickTaken, (void *context, const char *nick), defaultCallback_onNickTaken);
+GENERATE_CB_SETTER(onPasswordRequest, (void *context, const char *nick), defaultCallback_onPasswordRequest);
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomChiefStatus, (void *context, bool chief));
+GENERATE_CB_SETTER_AND_DEFAULT(onReadyState, (void *context, const char *nick, bool ready));
+GENERATE_CB_SETTER_AND_DEFAULT(onEnterRoom, (void *context, bool chief));
+GENERATE_CB_SETTER_AND_DEFAULT(onLeaveRoom, (void *context, int reason, const char *message));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamAdd, (void *context, const flib_team *team));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamDelete, (void *context, const char *teamname));
+GENERATE_CB_SETTER_AND_DEFAULT(onRunGame, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamAccepted, (void *context, const char *teamName));
+GENERATE_CB_SETTER_AND_DEFAULT(onHogCountChanged, (void *context, const char *teamName, int hogs));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamColorChanged, (void *context, const char *teamName, int colorIndex));
+GENERATE_CB_SETTER_AND_DEFAULT(onEngineMessage, (void *context, const uint8_t *message, size_t size));
+GENERATE_CB_SETTER_AND_DEFAULT(onSchemeChanged, (void *context, const flib_scheme *scheme));
+GENERATE_CB_SETTER_AND_DEFAULT(onMapChanged, (void *context, const flib_map *map, int changetype));
+GENERATE_CB_SETTER_AND_DEFAULT(onScriptChanged, (void *context, const char *script));
+GENERATE_CB_SETTER_AND_DEFAULT(onWeaponsetChanged, (void *context, const flib_weaponset *weaponset));
+GENERATE_CB_SETTER_AND_DEFAULT(onAdminAccess, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onServerVar, (void *context, const char *name, const char *value));
+
+#undef GENERATE_CB_SETTER_AND_DEFAULT
+#undef GENERATE_CB_SETTER
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_internal.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,158 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Common definitions needed by netconn functions, to allow splitting them into several files.
+ */
+
+#ifndef NETCONN_INTERNAL_H_
+#define NETCONN_INTERNAL_H_
+
+#include "netconn.h"
+#include "netbase.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/weapon.h"
+#include "../model/room.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct _flib_netconn {
+	flib_netbase *netBase;
+	char *playerName;
+	char *dataDirPath;
+
+	int netconnState;			// One of the NETCONN_STATE constants
+	bool isAdmin;				// Player is server administrator
+
+	bool isChief;				// Player can modify the current room
+	flib_map *map;
+	flib_teamlist pendingTeamlist;
+	flib_teamlist teamlist;
+	flib_scheme *scheme;
+	char *style;
+	flib_weaponset *weaponset;
+
+	void (*onMessageCb)(void *context, int msgtype, const char *msg);
+	void *onMessageCtx;
+
+	void (*onConnectedCb)(void *context);
+	void *onConnectedCtx;
+
+	void (*onDisconnectedCb)(void *context, int reason, const char *message);
+	void *onDisconnectedCtx;
+
+	void (*onRoomlistCb)(void *context, const flib_room **rooms, int roomCount);
+	void *onRoomlistCtx;
+
+	void (*onRoomAddCb)(void *context, const flib_room *room);
+	void *onRoomAddCtx;
+
+	void (*onRoomDeleteCb)(void *context, const char *name);
+	void *onRoomDeleteCtx;
+
+	void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_room *room);
+	void *onRoomUpdateCtx;
+
+	void (*onChatCb)(void *context, const char *nick, const char *msg);
+	void *onChatCtx;
+
+	void (*onLobbyJoinCb)(void *context, const char *nick);
+	void *onLobbyJoinCtx;
+
+	void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage);
+	void *onLobbyLeaveCtx;
+
+	void (*onRoomJoinCb)(void *context, const char *nick);
+	void *onRoomJoinCtx;
+
+	void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage);
+	void *onRoomLeaveCtx;
+
+	void (*onNickTakenCb)(void *context, const char *nick);
+	void *onNickTakenCtx;
+
+	void (*onPasswordRequestCb)(void *context, const char *nick);
+	void *onPasswordRequestCtx;
+
+	void (*onRoomChiefStatusCb)(void *context, bool isChief);
+	void *onRoomChiefStatusCtx;
+
+	void (*onReadyStateCb)(void *context, const char *nick, bool ready);
+	void *onReadyStateCtx;
+
+	void (*onEnterRoomCb)(void *context, bool chief);
+	void *onEnterRoomCtx;
+
+	void (*onLeaveRoomCb)(void *context, int reason, const char *message);
+	void *onLeaveRoomCtx;
+
+	void (*onTeamAddCb)(void *context, const flib_team *team);
+	void *onTeamAddCtx;
+
+	void (*onTeamDeleteCb)(void *context, const char *teamname);
+	void *onTeamDeleteCtx;
+
+	void (*onRunGameCb)(void *context);
+	void *onRunGameCtx;
+
+	void (*onTeamAcceptedCb)(void *context, const char *teamName);
+	void *onTeamAcceptedCtx;
+
+	void (*onHogCountChangedCb)(void *context, const char *teamName, int hogs);
+	void *onHogCountChangedCtx;
+
+	void (*onTeamColorChangedCb)(void *context, const char *teamName, int colorIndex);
+	void *onTeamColorChangedCtx;
+
+	void (*onEngineMessageCb)(void *context, const uint8_t *message, size_t size);
+	void *onEngineMessageCtx;
+
+	void (*onSchemeChangedCb)(void *context, const flib_scheme *scheme);
+	void *onSchemeChangedCtx;
+
+	void (*onMapChangedCb)(void *context, const flib_map *map, int changetype);
+	void *onMapChangedCtx;
+
+	void (*onScriptChangedCb)(void *context, const char *script);
+	void *onScriptChangedCtx;
+
+	void (*onWeaponsetChangedCb)(void *context, const flib_weaponset *weaponset);
+	void *onWeaponsetChangedCtx;
+
+	void (*onAdminAccessCb)(void *context);
+	void *onAdminAccessCtx;
+
+	void (*onServerVarCb)(void *context, const char *name, const char *value);
+	void *onServerVarCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+void netconn_clearCallbacks(flib_netconn *conn);
+void netconn_leaveRoom(flib_netconn *conn);
+void netconn_setMap(flib_netconn *conn, const flib_map *map);
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+void netconn_setScript(flib_netconn *conn, const char *script);
+void netconn_setScheme(flib_netconn *conn, const flib_scheme *scheme);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_send.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,495 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "netconn_internal.h"
+
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/buffer.h"
+#include "../md5/md5.h"
+#include "../base64/base64.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+// cmdname is always given as literal from functions in this file, so it is never null.
+static int sendVoid(flib_netconn *conn, const char *cmdname) {
+	if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname);
+}
+
+// Testing for !*str prevents sending 0-length parameters (they trip up the protocol)
+static int sendStr(flib_netconn *conn, const char *cmdname, const char *str) {
+	if(log_e_if(!conn || flib_strempty(str), "Invalid parameter sending %s command", cmdname)) {
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", cmdname, str);
+}
+
+static int sendInt(flib_netconn *conn, const char *cmdname, int param) {
+	if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n%i\n\n", cmdname, param);
+}
+
+int flib_netconn_send_nick(flib_netconn *conn, const char *nick) {
+	int result = -1;
+	if(!log_badargs_if2(conn==NULL, flib_strempty(nick))) {
+		char *tmpName = flib_strdupnull(nick);
+		if(tmpName) {
+			if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
+				free(conn->playerName);
+				conn->playerName = tmpName;
+				tmpName = NULL;
+				result = 0;
+			}
+		}
+		free(tmpName);
+	}
+	return result;
+}
+
+int flib_netconn_send_password(flib_netconn *conn, const char *passwd) {
+	int result = -1;
+	if(!log_badargs_if2(conn==NULL, passwd==NULL)) {
+		md5_state_t md5state;
+		uint8_t md5bytes[16];
+		char md5hex[33];
+		md5_init(&md5state);
+		md5_append(&md5state, (unsigned char*)passwd, strlen(passwd));
+		md5_finish(&md5state, md5bytes);
+		for(int i=0;i<sizeof(md5bytes); i++) {
+			// Needs to be lowercase - server checks case sensitive
+			snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]);
+		}
+		result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex);
+	}
+	return result;
+}
+
+int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) {
+	return sendStr(conn, "QUIT", (quitmsg && *quitmsg) ? quitmsg : "User quit");
+}
+
+int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
+	if(!flib_strempty(chat)) {
+		return sendStr(conn, "CHAT", chat);
+	}
+	return 0;
+}
+
+int flib_netconn_send_kick(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "KICK", playerName);
+}
+
+int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "INFO", playerName);
+}
+
+int flib_netconn_send_request_roomlist(flib_netconn *conn) {
+	return sendVoid(conn, "LIST");
+}
+
+int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) {
+	if(!sendStr(conn, "JOIN_ROOM", room)) {
+		conn->isChief = false;
+		return 0;
+	}
+	return -1;
+}
+
+int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "FOLLOW", playerName);
+}
+
+int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) {
+	if(!sendStr(conn, "CREATE_ROOM", room)) {
+		conn->isChief = true;
+		return 0;
+	}
+	return -1;
+}
+
+int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) {
+	return sendStr(conn, "BAN", playerName);
+}
+
+int flib_netconn_send_clearAccountsCache(flib_netconn *conn) {
+	return sendVoid(conn, "CLEAR_ACCOUNTS_CACHE");
+}
+
+int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value) {
+	if(log_badargs_if3(conn==NULL, flib_strempty(name), flib_strempty(value))) {
+		return -1;
+	}
+	return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value);
+}
+
+int flib_netconn_send_getServerVars(flib_netconn *conn) {
+	return sendVoid(conn, "GET_SERVER_VAR");
+}
+int flib_netconn_send_leaveRoom(flib_netconn *conn, const char *str) {
+	int result = -1;
+	if(conn->netconnState==NETCONN_STATE_ROOM) {
+		result = (str && *str) ? sendStr(conn, "PART", str) : sendVoid(conn, "PART");
+		if(!result) {
+			netconn_leaveRoom(conn);
+		}
+	}
+	return result;
+}
+
+int flib_netconn_send_toggleReady(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_READY");
+}
+
+static void addTeamToPendingList(flib_netconn *conn, const flib_team *team) {
+	flib_team *teamcopy = flib_team_copy(team);
+	if(teamcopy) {
+		teamcopy->remoteDriven = false;
+		free(teamcopy->ownerName);
+		teamcopy->ownerName = flib_strdupnull(conn->playerName);
+		if(teamcopy->ownerName) {
+			flib_teamlist_delete(&conn->pendingTeamlist, team->name);
+			if(!flib_teamlist_insert(&conn->pendingTeamlist, teamcopy, 0)) {
+				teamcopy = NULL;
+			}
+		}
+	}
+	flib_team_destroy(teamcopy);
+}
+
+int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team) {
+	int result = -1;
+	if(!log_badargs_if2(conn==NULL, team==NULL)) {
+		bool missingInfo = flib_strempty(team->name) || flib_strempty(team->grave) || flib_strempty(team->fort) || flib_strempty(team->voicepack) || flib_strempty(team->flag);
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			missingInfo |= flib_strempty(team->hogs[i].name) || flib_strempty(team->hogs[i].hat);
+		}
+		if(!log_e_if(missingInfo, "Incomplete team definition")) {
+			flib_vector *vec = flib_vector_create();
+			if(vec) {
+				bool error = false;
+				error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%i\n%s\n%s\n%s\n%s\n%i\n", team->name, team->colorIndex, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty);
+				for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+					error |= flib_vector_appendf(vec, "%s\n%s\n", team->hogs[i].name, team->hogs[i].hat);
+				}
+				error |= flib_vector_appendf(vec, "\n");
+				if(!error && !flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec))) {
+					addTeamToPendingList(conn, team);
+					result = 0;
+				}
+			}
+			flib_vector_destroy(vec);
+		}
+	}
+	return result;
+}
+
+int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) {
+	flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+	if(team && !team->remoteDriven && !sendStr(conn, "REMOVE_TEAM", teamname)) {
+		flib_teamlist_delete(&conn->teamlist, teamname);
+		return 0;
+	}
+	return -1;
+}
+
+int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) {
+	return sendStr(conn, "ROOM_NAME", roomName);
+}
+
+int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount) {
+	if(!log_badargs_if5(conn==NULL, flib_strempty(teamname), hogcount<1, hogcount>HEDGEHOGS_PER_TEAM, !conn->isChief)
+			&& !flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount)) {
+		flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+		if(team) {
+			team->hogsInGame = hogcount;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex) {
+	if(!log_badargs_if3(conn==NULL, flib_strempty(teamname), !conn->isChief)
+			&& !flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%i\n\n", teamname, colorIndex)) {
+		flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+		if(team) {
+			team->colorIndex = colorIndex;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
+	if(!log_badargs_if3(conn==NULL, weaponset==NULL, flib_strempty(weaponset->name))) {
+		char ammostring[WEAPONS_COUNT*4+1];
+		strcpy(ammostring, weaponset->loadout);
+		strcat(ammostring, weaponset->crateprob);
+		strcat(ammostring, weaponset->delay);
+		strcat(ammostring, weaponset->crateammo);
+		if(conn->isChief) {
+			if(!flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring)) {
+				netconn_setWeaponset(conn, weaponset);
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_map(flib_netconn *conn, const flib_map *map) {
+	if(log_badargs_if2(conn==NULL, map==NULL)) {
+		return -1;
+	}
+	bool error = false;
+
+	if(map->seed) {
+		error |= flib_netconn_send_mapSeed(conn, map->seed);
+	}
+	error |= flib_netconn_send_mapTemplate(conn, map->templateFilter);
+	if(map->theme) {
+		error |= flib_netconn_send_mapTheme(conn, map->theme);
+	}
+	error |= flib_netconn_send_mapGen(conn, map->mapgen);
+	error |= flib_netconn_send_mapMazeSize(conn, map->mazeSize);
+	if(map->drawData && map->drawDataSize>0) {
+		error |= flib_netconn_send_mapDrawdata(conn, map->drawData, map->drawDataSize);
+	}
+	// Name is sent last, because the QtFrontend uses this to update its preview, and to show/hide
+	// certain fields
+	if(map->name) {
+		error |= flib_netconn_send_mapName(conn, map->name);
+	}
+	return error;
+}
+
+int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) {
+	if(log_badargs_if2(conn==NULL, mapName==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendStr(conn, "CFG\nMAP", mapName)) {
+			char *copy = flib_strdupnull(mapName);
+			if(copy) {
+				free(conn->map->name);
+				conn->map->name = copy;
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) {
+	if(log_badargs_if(conn==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendInt(conn, "CFG\nMAPGEN", mapGen)) {
+			conn->map->mapgen = mapGen;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) {
+	if(log_badargs_if(conn==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendInt(conn, "CFG\nTEMPLATE", templateFilter)) {
+			conn->map->templateFilter = templateFilter;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) {
+	if(log_badargs_if(conn==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendInt(conn, "CFG\nMAZE_SIZE", mazeSize)) {
+			conn->map->mazeSize = mazeSize;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) {
+	if(log_badargs_if2(conn==NULL, seed==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendStr(conn, "CFG\nSEED", seed)) {
+			char *copy = flib_strdupnull(seed);
+			if(copy) {
+				free(conn->map->seed);
+				conn->map->seed = copy;
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) {
+	if(log_badargs_if2(conn==NULL, theme==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendStr(conn, "CFG\nTHEME", theme)) {
+			char *copy = flib_strdupnull(theme);
+			if(copy) {
+				free(conn->map->theme);
+				conn->map->theme = copy;
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size) {
+	int result = -1;
+	if(!log_badargs_if3(conn==NULL, drawData==NULL && size>0, size>SIZE_MAX/2) && conn->isChief) {
+		uLongf zippedSize = compressBound(size);
+		uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header
+		if(zipped) {
+			// Create the QCompress size header (uint32 big endian)
+			zipped[0] = (size>>24) & 0xff;
+			zipped[1] = (size>>16) & 0xff;
+			zipped[2] = (size>>8) & 0xff;
+			zipped[3] = (size) & 0xff;
+
+			if(compress(zipped+4, &zippedSize, drawData, size) != Z_OK) {
+				flib_log_e("Error compressing drawn map data.");
+			} else {
+				char *base64encout = NULL;
+				base64_encode_alloc((const char*)zipped, zippedSize+4, &base64encout);
+				if(!base64encout) {
+					flib_log_e("Error base64-encoding drawn map data.");
+				} else {
+					result = flib_netbase_sendf(conn->netBase, "CFG\nDRAWNMAP\n%s\n\n", base64encout);
+				}
+				free(base64encout);
+			}
+		}
+		free(zipped);
+	}
+
+	if(!result) {
+		uint8_t *copy = flib_bufdupnull(drawData, size);
+		if(copy) {
+			free(conn->map->drawData);
+			conn->map->drawData = copy;
+			conn->map->drawDataSize = size;
+		}
+	}
+	return result;
+}
+
+int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) {
+	if(log_badargs_if2(conn==NULL, scriptName==NULL)) {
+		return -1;
+	}
+	if(conn->isChief) {
+		if(!sendStr(conn, "CFG\nSCRIPT", scriptName)) {
+			netconn_setScript(conn, scriptName);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int flib_netconn_send_scheme(flib_netconn *conn, const flib_scheme *scheme) {
+	int result = -1;
+	if(!log_badargs_if3(conn==NULL, scheme==NULL, flib_strempty(scheme->name)) && conn->isChief) {
+		flib_vector *vec = flib_vector_create();
+		if(vec) {
+			bool error = false;
+			error |= flib_vector_appendf(vec, "CFG\nSCHEME\n%s\n", scheme->name);
+			for(int i=0; i<flib_meta.modCount; i++) {
+				error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false");
+			}
+			for(int i=0; i<flib_meta.settingCount; i++) {
+				error |= flib_vector_appendf(vec, "%i\n", scheme->settings[i]);
+			}
+			error |= flib_vector_appendf(vec, "\n");
+			if(!error) {
+				result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec));
+			}
+		}
+		flib_vector_destroy(vec);
+	}
+
+	if(!result) {
+		netconn_setScheme(conn, scheme);
+	}
+	return result;
+}
+
+int flib_netconn_send_startGame(flib_netconn *conn) {
+	return sendVoid(conn, "START_GAME");
+}
+
+int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_RESTRICT_JOINS");
+}
+
+int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn) {
+	return sendVoid(conn, "TOGGLE_RESTRICT_TEAMS");
+}
+
+int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) {
+	if(!flib_strempty(chat)) {
+		return sendStr(conn, "TEAMCHAT", chat);
+	}
+	return 0;
+}
+
+int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size) {
+	int result = -1;
+	if(!log_badargs_if2(conn==NULL, message==NULL && size>0)) {
+		char *base64encout = NULL;
+		base64_encode_alloc((const char*)message, size, &base64encout);
+		if(base64encout) {
+			result = flib_netbase_sendf(conn->netBase, "EM\n%s\n\n", base64encout);
+		}
+		free(base64encout);
+	}
+	return result;
+}
+
+int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) {
+	return sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,192 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "netprotocol.h"
+
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include "../base64/base64.h"
+
+#include <zlib.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static int fillTeamFromMsg(flib_team *team, char **parts) {
+	team->name = flib_strdupnull(parts[0]);
+	team->grave = flib_strdupnull(parts[1]);
+	team->fort = flib_strdupnull(parts[2]);
+	team->voicepack = flib_strdupnull(parts[3]);
+	team->flag = flib_strdupnull(parts[4]);
+	team->ownerName = flib_strdupnull(parts[5]);
+	if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag || !team->ownerName) {
+		return -1;
+	}
+
+	errno = 0;
+	long difficulty = strtol(parts[6], NULL, 10);
+	if(errno) {
+		return -1;
+	}
+
+	for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+		flib_hog *hog = &team->hogs[i];
+		hog->difficulty = difficulty;
+		hog->name = flib_strdupnull(parts[7+2*i]);
+		hog->hat = flib_strdupnull(parts[8+2*i]);
+		if(!hog->name || !hog->hat) {
+			return -1;
+		}
+	}
+
+	// Set some default assumptions as well
+	team->colorIndex = DEFAULT_COLOR_INDEX;
+	team->hogsInGame = DEFAULT_HEDGEHOG_COUNT;
+	team->remoteDriven = true;
+	return 0;
+}
+
+flib_team *flib_team_from_netmsg(char **parts) {
+	flib_team *result = NULL;
+	flib_team *tmpTeam = flib_calloc(1, sizeof(flib_team));
+	if(tmpTeam) {
+		if(!fillTeamFromMsg(tmpTeam, parts)) {
+			result = tmpTeam;
+			tmpTeam = NULL;
+		} else {
+			flib_log_e("Error parsing team from net.");
+		}
+	}
+	flib_team_destroy(tmpTeam);
+	return result;
+}
+
+flib_scheme *flib_scheme_from_netmsg(char **parts) {
+	flib_scheme *result = flib_scheme_create(parts[0]);
+	if(result) {
+		for(int i=0; i<flib_meta.modCount; i++) {
+			result->mods[i] = !strcmp(parts[i+1], "true");
+		}
+		for(int i=0; i<flib_meta.settingCount; i++) {
+			result->settings[i] = atoi(parts[i+flib_meta.modCount+1]);
+		}
+	}
+	return result;
+}
+
+flib_map *flib_map_from_netmsg(char **parts) {
+	flib_map *result = flib_map_create_named(parts[3], parts[0]);
+	if(result) {
+		result->mapgen = atoi(parts[1]);
+		result->mazeSize = atoi(parts[2]);
+		result->templateFilter = atoi(parts[4]);
+	}
+	return result;
+}
+
+int flib_drawnmapdata_from_netmsg(char *netmsg, uint8_t** outbuf, size_t *outlen) {
+	int result = -1;
+
+	// First step: base64 decoding
+	char *base64decout = NULL;
+	size_t base64declen;
+	bool ok = base64_decode_alloc(netmsg, strlen(netmsg), &base64decout, &base64declen);
+	if(ok && base64declen>3) {
+		// Second step: unzip with the QCompress header. That header is just a big-endian
+		// uint32 indicating the length of the uncompressed data.
+		uint8_t *ubyteBuf = (uint8_t*)base64decout;
+		uint32_t unzipLen =
+				(((uint32_t)ubyteBuf[0])<<24)
+				+ (((uint32_t)ubyteBuf[1])<<16)
+				+ (((uint32_t)ubyteBuf[2])<<8)
+				+ ubyteBuf[3];
+		if(unzipLen==0) {
+			*outbuf = NULL;
+			*outlen = 0;
+			result = 0;
+		} else {
+			uint8_t *out = flib_malloc(unzipLen);
+			if(out) {
+				uLongf actualUnzipLen = unzipLen;
+				int resultcode = uncompress(out, &actualUnzipLen, (Bytef*)(base64decout+4), base64declen-4);
+				if(resultcode == Z_OK) {
+					*outbuf = out;
+					*outlen = actualUnzipLen;
+					out = NULL;
+					result = 0;
+				} else {
+					flib_log_e("Uncompressing drawn map failed. Code: %i", resultcode);
+				}
+			}
+			free(out);
+		}
+	} else {
+		flib_log_e("base64 decoding of drawn map failed.");
+	}
+	free(base64decout);
+	return result;
+}
+
+flib_room *flib_room_from_netmsg(char **params) {
+	flib_room *result = NULL;
+	flib_room *tmpRoom = flib_calloc(1, sizeof(flib_room));
+	if(tmpRoom) {
+		tmpRoom->inProgress = !strcmp(params[0], "True");
+		tmpRoom->name = flib_strdupnull(params[1]);
+		tmpRoom->playerCount = atoi(params[2]);
+		tmpRoom->teamCount = atoi(params[3]);
+		tmpRoom->owner = flib_strdupnull(params[4]);
+		tmpRoom->map = flib_strdupnull(params[5]);
+		tmpRoom->scheme = flib_strdupnull(params[6]);
+		tmpRoom->weapons = flib_strdupnull(params[7]);
+		if(tmpRoom->name && tmpRoom->owner && tmpRoom->map && tmpRoom->scheme && tmpRoom->weapons) {
+			result = tmpRoom;
+			tmpRoom = NULL;
+		}
+	}
+	flib_room_destroy(tmpRoom);
+	return result;
+}
+
+int fillRoomArray(flib_room **array, char **params, int count) {
+	for(int i=0; i<count; i++) {
+		array[i] = flib_room_from_netmsg(params + 8*i);
+		if(!array[i]) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+flib_room **flib_room_array_from_netmsg(char **params, int count) {
+	flib_room **result = flib_calloc(count, sizeof(flib_room*));
+	if(result) {
+		if(fillRoomArray(result, params, count)) {
+			for(int i=0; i<count; i++) {
+				flib_room_destroy(result[i]);
+			}
+			free(result);
+			result = NULL;
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,66 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef NETPROTOCOL_H_
+#define NETPROTOCOL_H_
+
+#include "../model/team.h"
+#include "../model/scheme.h"
+#include "../model/map.h"
+#include "../model/room.h"
+
+#include <stddef.h>
+
+/**
+ * Create a new team from this 23-part net message
+ */
+flib_team *flib_team_from_netmsg(char **parts);
+
+/**
+ * Create a new scheme from this net message, which must have
+ * meta->modCount+meta->settingCount+1 parts.
+ */
+flib_scheme *flib_scheme_from_netmsg(char **parts);
+
+/**
+ * Create a new map from this five-part netmsg
+ */
+flib_map *flib_map_from_netmsg(char **parts);
+
+/**
+ * Decode the drawn map data from this netmessage line.
+ *
+ * The data is first base64 decoded and then quncompress()ed.
+ * The return value is a newly allocated byte buffer, the length
+ * is written to the variable pointed to by outlen.
+ * Returns NULL on error.
+ */
+int flib_drawnmapdata_from_netmsg(char *netmsg, uint8_t **outbuf, size_t *outlen);
+
+/**
+ * Create a new room from this 8-part net message
+ */
+flib_room *flib_room_from_netmsg(char **params);
+
+/**
+ * Create an array of count rooms from count*8 netmessage parts
+ */
+flib_room **flib_room_array_from_netmsg(char **params, int count);
+
+#endif /* NETPROTOCOL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,191 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "socket.h"
+#include "util/logging.h"
+#include "util/util.h"
+#include <stdlib.h>
+#include <SDL_net.h>
+#include <time.h>
+
+struct _flib_tcpsocket {
+	TCPsocket sock;
+	SDLNet_SocketSet sockset;
+};
+
+struct _flib_acceptor {
+	TCPsocket sock;
+	uint16_t port;
+};
+
+static uint32_t getPeerIp(TCPsocket sock) {
+	IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
+	return SDLNet_Read32(&addr->host);
+}
+
+static bool connectionIsLocal(TCPsocket sock) {
+	return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
+}
+
+static flib_tcpsocket *createSocket(TCPsocket sdlsock) {
+	flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket));
+	if(result) {
+		result->sock = sdlsock;
+		result->sockset = SDLNet_AllocSocketSet(1);
+
+		if(!result->sockset) {
+			flib_log_e("Can't allocate socket: Out of memory!");
+			SDLNet_FreeSocketSet(result->sockset);
+			free(result);
+			result = NULL;
+		} else {
+			SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
+		}
+	}
+	return result;
+}
+
+TCPsocket listen(uint16_t port) {
+	IPaddress addr;
+	addr.host = INADDR_ANY;
+	SDLNet_Write16(port, &addr.port);
+	TCPsocket sock = SDLNet_TCP_Open(&addr);
+	if(!sock) {
+		flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
+	}
+	return sock;
+}
+
+flib_acceptor *flib_acceptor_create(uint16_t port) {
+	flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor));
+	if(result) {
+		if(port > 0) {
+			result->port = port;
+			result->sock = listen(result->port);
+		} else {
+			/* SDL_net does not seem to have a way to listen on a random unused port
+			   and find out which port that is, so let's try to find one ourselves. */
+			srand(time(NULL));
+			for(int i=0; !result->sock && i<1000; i++) {
+				// IANA suggests using ports in the range 49152-65535 for things like this
+				result->port = 49152+(rand()%(65535-49152));
+				result->sock = listen(result->port);
+			}
+		}
+		if(!result->sock) {
+			flib_log_e("Failed to create acceptor.");
+			free(result);
+			result = NULL;
+		}
+	}
+	return result;
+}
+
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) {
+	if(!acceptor) {
+		flib_log_e("Call to flib_acceptor_listenport with acceptor==null");
+		return 0;
+	}
+	return acceptor->port;
+}
+
+void flib_acceptor_close(flib_acceptor *acceptor) {
+	if(acceptor) {
+		SDLNet_TCP_Close(acceptor->sock);
+		free(acceptor);
+	}
+}
+
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) {
+	flib_tcpsocket *result = NULL;
+	if(!acceptor) {
+		flib_log_e("Call to flib_socket_accept with acceptor==null");
+	} else {
+		TCPsocket sock = NULL;
+		while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
+			if(localOnly && !connectionIsLocal(sock)) {
+				flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock)));
+			} else {
+				result = createSocket(sock);
+			}
+			if(!result) {
+				SDLNet_TCP_Close(sock);
+			}
+		}
+	}
+	return result;
+}
+
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) {
+	flib_tcpsocket *result = NULL;
+	if(!host || port==0) {
+		flib_log_e("Invalid parameter in flib_socket_connect");
+	} else {
+		IPaddress ip;
+		if(SDLNet_ResolveHost(&ip,host,port)==-1) {
+		   flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		} else {
+			TCPsocket sock=SDLNet_TCP_Open(&ip);
+			if(!sock) {
+				flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
+			} else {
+				result = createSocket(sock);
+				if(result) {
+					sock = NULL;
+				}
+			}
+			SDLNet_TCP_Close(sock);
+		}
+	}
+	return result;
+}
+
+void flib_socket_close(flib_tcpsocket *sock) {
+	if(sock) {
+		SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
+		SDLNet_TCP_Close(sock->sock);
+		SDLNet_FreeSocketSet(sock->sockset);
+		free(sock);
+	}
+}
+
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen) {
+	if(!sock || (maxlen>0 && !data)) {
+		flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null");
+		return -1;
+	}
+	int readySockets = SDLNet_CheckSockets(sock->sockset, 0);
+	if(readySockets>0) {
+		int size = SDLNet_TCP_Recv(sock->sock, data, maxlen);
+		return size>0 ? size : -1;
+	} else if(readySockets==0) {
+		return 0;
+	} else {
+		flib_log_e("Error in select system call: %s", SDLNet_GetError());
+		return -1;
+	}
+}
+
+int flib_socket_send(flib_tcpsocket *sock, const void *data, int len) {
+	if(!sock || (len>0 && !data)) {
+		flib_log_e("Call to flib_socket_send with sock==null or data==null");
+		return -1;
+	}
+	return SDLNet_TCP_Send(sock->sock, data, len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,91 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/*
+ * Sockets for TCP networking.
+ *
+ * This layer offers some functionality over what SDL_net offers directly: listening
+ * sockets (called acceptors here) can be bound to port 0, which will make them listen
+ * on a random unused port, if one can be found. To support this feature, you can also
+ * query the local port that an acceptor is listening on.
+ *
+ * Further, we support nonblocking reads here.
+ */
+
+#ifndef SOCKET_H_
+#define SOCKET_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct _flib_tcpsocket flib_tcpsocket;
+typedef struct _flib_acceptor flib_acceptor;
+
+/**
+ * Create a new acceptor which will listen for incoming TCP connections
+ * on the given port. If port is 0, this will listen on a random
+ * unused port which can then be queried with flib_acceptor_listenport.
+ *
+ * Returns NULL on error.
+ */
+flib_acceptor *flib_acceptor_create(uint16_t port);
+
+/**
+ * Return the port on which the acceptor is listening.
+ */
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor);
+
+/**
+ * Close the acceptor and free its memory. NULL-safe.
+ */
+void flib_acceptor_close(flib_acceptor *acceptor);
+
+/**
+ * Try to accept a connection from an acceptor (listening socket).
+ * if localOnly is true, this will only accept connections which came from 127.0.0.1
+ * Returns NULL if nothing can be accepted.
+ */
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly);
+
+/**
+ * Try to connect to the server at the given address.
+ */
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port);
+
+/**
+ * Close the socket and free its memory. NULL-safe.
+ */
+void flib_socket_close(flib_tcpsocket *socket);
+
+/**
+ * Attempt to receive up to maxlen bytes from the socket, but does not
+ * block if nothing is available.
+ * Returns the ammount of data received, 0 if there was nothing to receive,
+ * or a negative number if the connection was closed or an error occurred.
+ */
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen);
+
+/**
+ * Blocking send all the data in the data buffer. Returns the actual ammount
+ * of data sent, or a negative value on error. If the value returned here
+ * is less than len, either the connection closed or an error occurred.
+ */
+int flib_socket_send(flib_tcpsocket *sock, const void *data, int len);
+
+#endif /* SOCKET_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,177 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "buffer.h"
+#include "logging.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#define MIN_VECTOR_CAPACITY 16
+
+struct _flib_vector {
+	void *data;
+	size_t size;
+	size_t capacity;
+};
+
+flib_vector *flib_vector_create() {
+	flib_vector *result = NULL;
+	flib_vector *tmpVector = flib_calloc(1, sizeof(flib_vector));
+	if(tmpVector) {
+		tmpVector->data = flib_malloc(MIN_VECTOR_CAPACITY);
+		if(tmpVector->data) {
+			tmpVector->size = 0;
+			tmpVector->capacity = MIN_VECTOR_CAPACITY;
+			result = tmpVector;
+			tmpVector = NULL;
+		}
+	}
+	flib_vector_destroy(tmpVector);
+	return result;
+}
+
+void flib_vector_destroy(flib_vector *vec) {
+	if(vec) {
+		free(vec->data);
+		free(vec);
+	}
+}
+
+static int setCapacity(flib_vector *vec, size_t newCapacity) {
+	if(newCapacity == vec->capacity) {
+		return 0;
+	}
+	void *newData = realloc(vec->data, newCapacity);
+	if(newData) {
+		vec->data = newData;
+		vec->capacity = newCapacity;
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static int allocateExtraCapacity(flib_vector *vec, size_t extraCapacity) {
+	if(extraCapacity <= SIZE_MAX - vec->capacity) {
+		return setCapacity(vec, vec->capacity + extraCapacity);
+	} else {
+		return -1;
+	}
+}
+
+int flib_vector_resize(flib_vector *vec, size_t newSize) {
+	if(log_badargs_if(vec==NULL)) {
+		return -1;
+	}
+
+	if(vec->capacity < newSize) {
+		// Resize exponentially for constant amortized time,
+		// But at least by as much as we need of course
+		size_t extraCapacity = (vec->capacity)/2;
+		size_t minExtraCapacity = newSize - vec->capacity;
+		if(extraCapacity < minExtraCapacity) {
+			extraCapacity = minExtraCapacity;
+		}
+
+		if(allocateExtraCapacity(vec, extraCapacity)) {
+			allocateExtraCapacity(vec, minExtraCapacity);
+		}
+	} else if(vec->capacity/2 > newSize) {
+		size_t newCapacity = newSize+newSize/4;
+		if(newCapacity < MIN_VECTOR_CAPACITY) {
+			newCapacity = MIN_VECTOR_CAPACITY;
+		}
+		setCapacity(vec, newCapacity);
+	}
+
+	if(vec->capacity >= newSize) {
+		vec->size = newSize;
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+int flib_vector_append(flib_vector *vec, const void *data, size_t len) {
+	if(!log_badargs_if2(vec==NULL, data==NULL && len>0)
+			&& !log_oom_if(len > SIZE_MAX-vec->size)) {
+		size_t oldSize = vec->size;
+		if(!log_oom_if(flib_vector_resize(vec, vec->size+len))) {
+			memmove(((uint8_t*)vec->data) + oldSize, data, len);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int flib_vector_appendf(flib_vector *vec, const char *fmt, ...) {
+	int result = -1;
+	if(!log_badargs_if2(vec==NULL, fmt==NULL)) {
+		va_list argp;
+		va_start(argp, fmt);
+		char *formatted = flib_vasprintf(fmt, argp);
+		va_end(argp);
+
+
+		if(formatted) {
+			size_t len = strlen(formatted);
+			result = flib_vector_append(vec, formatted, len);
+		}
+	}
+	return result;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector *vec) {
+	if(log_badargs_if(vec==NULL)) {
+		flib_buffer result = {NULL, 0};
+		return result;
+	} else {
+		flib_buffer result = {vec->data, vec->size};
+		return result;
+	}
+}
+
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec) {
+	if(log_badargs_if(vec==NULL)) {
+		flib_constbuffer result = {NULL, 0};
+		return result;
+	} else {
+		flib_constbuffer result = {vec->data, vec->size};
+		return result;
+	}
+}
+
+void *flib_vector_data(flib_vector *vec) {
+	if(log_badargs_if(vec==NULL)) {
+		return NULL;
+	} else {
+		return vec->data;
+	}
+}
+
+size_t flib_vector_size(flib_vector *vec) {
+	if(log_badargs_if(vec==NULL)) {
+		return 0;
+	} else {
+		return vec->size;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,104 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * A simple struct to hold both the pointer to an array and its size,
+ * for e.g. conveniently returning it from a function.
+ *
+ * Convention: Size is zero iff data is a NULL pointer.
+ */
+typedef struct {
+	void *data;
+	size_t size;
+} flib_buffer;
+
+/**
+ * Just like flib_buffer, but the contents are not supposed to be modified.
+ */
+typedef struct {
+	const void *data;
+	size_t size;
+} flib_constbuffer;
+
+/**
+ * Simple variable-capacity data structure that can be efficiently appended to.
+ */
+typedef struct _flib_vector flib_vector;
+
+/**
+ * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
+ * May return NULL if memory runs out.
+ */
+flib_vector *flib_vector_create();
+
+/**
+ * Free the memory of this vector
+ */
+void flib_vector_destroy(flib_vector *vec);
+
+/**
+ * Resize the vector. This changes the size, and ensures the capacity is large enough to
+ * for the new size. Can also free memory if the new size is smaller. There is no guarantee
+ * about the contents of extra memory.
+ */
+int flib_vector_resize(flib_vector *vec, size_t newSize);
+
+/**
+ * Append the provided data to the end of the vector, enlarging it as required.
+ * The vector remains unchanged if appending fails.
+ * Returns 0 on success.
+ */
+int flib_vector_append(flib_vector *vec, const void *data, size_t len);
+
+/**
+ * Append data from a format string to the buffer (without trailing 0)
+ * Returns 0 on success.
+ */
+int flib_vector_appendf(flib_vector *vec, const char *template, ...);
+
+/**
+ * Return a pointer to the current data buffer of the vector. This pointer can
+ * become invalid if the vector size or capacity is changed.
+ */
+void *flib_vector_data(flib_vector *vec);
+
+/**
+ * Return the current size of the vector.
+ */
+size_t flib_vector_size(flib_vector *vec);
+
+/**
+ * Return a buffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_buffer flib_vector_as_buffer(flib_vector *vec);
+
+/**
+ * Return a constbuffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,321 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "inihelper.h"
+#include "../iniparser/dictionary.h"
+#include "../iniparser/iniparser.h"
+
+#include "logging.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdarg.h>
+
+struct _flib_ini {
+	dictionary *inidict;
+	char *currentSection;
+};
+
+static char *createDictKey(const char *sectionName, const char *keyName) {
+	return flib_asprintf("%s:%s", sectionName, keyName);
+}
+
+/**
+ * Turns a string into a lowercase string, in-place.
+ */
+static void strToLower(char *str) {
+	if(str) {
+		while(*str) {
+			*str = tolower(*str);
+			str++;
+		}
+	}
+}
+
+flib_ini *flib_ini_create(const char *filename) {
+	flib_ini *result = NULL;
+	flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
+	if(tmpIni) {
+		if(filename) {
+			tmpIni->inidict = iniparser_load(filename);
+		}
+		if(!tmpIni->inidict) {
+			tmpIni->inidict = dictionary_new(0);
+		}
+		if(tmpIni->inidict) {
+			result = tmpIni;
+			tmpIni = NULL;
+		}
+	}
+	flib_ini_destroy(tmpIni);
+	return result;
+}
+
+flib_ini *flib_ini_load(const char *filename) {
+	flib_ini *result = NULL;
+	if(!log_badargs_if(filename==NULL)) {
+		flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
+		if(tmpIni) {
+			tmpIni->inidict = iniparser_load(filename);
+			if(tmpIni->inidict) {
+				result = tmpIni;
+				tmpIni = NULL;
+			}
+		}
+		flib_ini_destroy(tmpIni);
+	}
+	return result;
+}
+
+int flib_ini_save(flib_ini *ini, const char *filename) {
+	int result = INI_ERROR_OTHER;
+	if(!log_badargs_if2(ini==NULL, filename==NULL)) {
+		FILE *file = fopen(filename, "wb");
+		if(!file) {
+			flib_log_e("Error opening file \"%s\" for writing.", filename);
+		} else {
+			iniparser_dump_ini(ini->inidict, file);
+			if(fclose(file)) {
+				flib_log_e("Write error on ini file \"%s\"", filename);
+			} else {
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+void flib_ini_destroy(flib_ini *ini) {
+	if(ini) {
+		if(ini->inidict) {
+			iniparser_freedict(ini->inidict);
+		}
+		free(ini->currentSection);
+		free(ini);
+	}
+}
+
+int flib_ini_enter_section(flib_ini *ini, const char *section) {
+	int result = INI_ERROR_OTHER;
+	if(ini) {
+		free(ini->currentSection);
+		ini->currentSection = NULL;
+	}
+	if(!log_badargs_if2(ini==NULL, section==NULL)) {
+		if(!iniparser_find_entry(ini->inidict, section)) {
+			flib_log_d("Ini section %s not found", section);
+			result = INI_ERROR_NOTFOUND;
+		} else {
+			ini->currentSection = flib_strdupnull(section);
+			if(ini->currentSection) {
+				// Usually iniparser ignores case, but some section-handling functions don't,
+				// so we set it to lowercase manually
+				strToLower(ini->currentSection);
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+int flib_ini_create_section(flib_ini *ini, const char *section) {
+	int result = INI_ERROR_OTHER;
+	if(!log_badargs_if2(ini==NULL, section==NULL)) {
+		result = flib_ini_enter_section(ini, section);
+		if(result == INI_ERROR_NOTFOUND) {
+			if(iniparser_set(ini->inidict, section, NULL)) {
+				flib_log_e("Error creating ini section %s", section);
+				result = INI_ERROR_OTHER;
+			} else {
+				result = flib_ini_enter_section(ini, section);
+			}
+		}
+	}
+	return result;
+}
+
+/**
+ * The result is an internal string of the iniparser, don't free it.
+ */
+static char *findValue(dictionary *dict, const char *section, const char *key) {
+	char *result = NULL;
+	char *dictKey = createDictKey(section, key);
+	if(dictKey) {
+		result = iniparser_getstring(dict, dictKey, NULL);
+	}
+	free(dictKey);
+	return result;
+}
+
+int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key) {
+	char *tmpValue = NULL;
+	int result = flib_ini_get_str_opt(ini, &tmpValue, key, NULL);
+	if(result==0) {
+		if(tmpValue == NULL) {
+			result = INI_ERROR_NOTFOUND;
+		} else {
+			*outVar = tmpValue;
+			tmpValue = NULL;
+		}
+	}
+	free(tmpValue);
+	return result;
+}
+
+int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def) {
+	int result = INI_ERROR_OTHER;
+	if(!log_badargs_if4(ini==NULL, ini->currentSection==NULL, outVar==NULL, key==NULL)) {
+		const char *value = findValue(ini->inidict, ini->currentSection, key);
+		if(!value) {
+			value = def;
+		}
+		char *valueDup = flib_strdupnull(value);
+		if(valueDup || !def) {
+			*outVar = valueDup;
+			result = 0;
+		}
+	}
+	return result;
+}
+
+int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key) {
+	char *tmpValue = NULL;
+	int result = flib_ini_get_str(ini, &tmpValue, key);
+	if(result==0) {
+		errno = 0;
+		long val = strtol(tmpValue, NULL, 10);
+		if(errno!=0 || val<INT_MIN || val>INT_MAX) {
+			flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", ini->currentSection, key, tmpValue);
+			result = INI_ERROR_FORMAT;
+		} else {
+			*outVar = val;
+		}
+	}
+	free(tmpValue);
+	return result;
+}
+
+int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def) {
+	int tmpValue;
+	int result = flib_ini_get_int(ini, &tmpValue, key);
+	if(result == 0) {
+		*outVar = tmpValue;
+	} else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
+		*outVar = def;
+		result = 0;
+	}
+	return result;
+}
+
+int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key) {
+	char *tmpValue = NULL;
+	int result = flib_ini_get_str(ini, &tmpValue, key);
+	if(result==0) {
+		bool trueval = strchr("1tTyY", tmpValue[0]);
+		bool falseval = strchr("0fFnN", tmpValue[0]);
+		if(!trueval && !falseval) {
+			flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", ini->currentSection, key, tmpValue);
+			result = INI_ERROR_FORMAT;
+		} else {
+			*outVar = trueval;
+		}
+	}
+	free(tmpValue);
+	return result;
+}
+
+int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def) {
+	bool tmpValue;
+	int result = flib_ini_get_bool(ini, &tmpValue, key);
+	if(result == 0) {
+		*outVar = tmpValue;
+	} else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
+		*outVar = def;
+		result = 0;
+	}
+	return result;
+}
+
+int flib_ini_set_str(flib_ini *ini, const char *key, const char *value) {
+	int result = INI_ERROR_OTHER;
+	if(!log_badargs_if4(ini==NULL, ini->currentSection==NULL, key==NULL, value==NULL)) {
+		char *dictKey = createDictKey(ini->currentSection, key);
+		if(dictKey) {
+			result = iniparser_set(ini->inidict, dictKey, value);
+			if(result) {
+				flib_log_e("Error setting ini entry %s to %s", dictKey, value);
+			}
+		}
+		free(dictKey);
+	}
+	return result;
+}
+
+int flib_ini_set_int(flib_ini *ini, const char *key, int value) {
+	int result = INI_ERROR_OTHER;
+	char *strvalue = flib_asprintf("%i", value);
+	if(strvalue) {
+		result = flib_ini_set_str(ini, key, strvalue);
+	}
+	free(strvalue);
+	return result;
+}
+
+int flib_ini_set_bool(flib_ini *ini, const char *key, bool value) {
+	return flib_ini_set_str(ini, key, value ? "true" : "false");
+}
+
+int flib_ini_get_sectioncount(flib_ini *ini) {
+	if(!log_badargs_if(ini==NULL)) {
+		return iniparser_getnsec(ini->inidict);
+	}
+	return INI_ERROR_OTHER;
+}
+
+char *flib_ini_get_sectionname(flib_ini *ini, int number) {
+	if(!log_badargs_if2(ini==NULL, number<0)) {
+		return flib_strdupnull(iniparser_getsecname(ini->inidict, number));
+	}
+	return NULL;
+}
+
+int flib_ini_get_keycount(flib_ini *ini) {
+	if(!log_badargs_if2(ini==NULL, ini->currentSection==NULL)) {
+		return iniparser_getsecnkeys(ini->inidict, ini->currentSection);
+	}
+	return INI_ERROR_OTHER;
+}
+
+char *flib_ini_get_keyname(flib_ini *ini, int number) {
+	char *result = NULL;
+	if(!log_badargs_if3(ini==NULL, ini->currentSection==NULL, number<0)) {
+		int keyCount = iniparser_getsecnkeys(ini->inidict, ini->currentSection);
+		char **keys = iniparser_getseckeys(ini->inidict, ini->currentSection);
+		if(keys && keyCount>number) {
+			// The keys are in the format section:key, so we have to skip the section and colon.
+			result = flib_strdupnull(keys[number]+strlen(ini->currentSection)+1);
+		}
+		free(keys);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,178 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Convenience interface for ini reading/writing.
+ *
+ * We currently use iniparser in the background, but using its interface directly is a bit verbose.
+ * This module is supposed to 1. make ini reading and writing a bit more convenient, and 2. hide
+ * the iniparser dependency so it can at need be easily replaced.
+ */
+
+#ifndef INIHELPER_H_
+#define INIHELPER_H_
+
+#include <stdbool.h>
+
+#define INI_ERROR_NOTFOUND -1
+#define INI_ERROR_FORMAT -2
+#define INI_ERROR_OTHER -100
+
+typedef struct _flib_ini flib_ini;
+
+/**
+ * Create a new ini data structure, pre-filled with the contents of
+ * the file "filename" if it exists. If filename is null, or the file
+ * is not found, an empty ini will be created. However, if an error
+ * occurs while reading the ini file (or any other error), null
+ * is returned.
+ *
+ * This behavior is useful for modifying an existing ini file without
+ * discarding unknown keys.
+ */
+flib_ini *flib_ini_create(const char *filename);
+
+/**
+ * Similar to flib_ini_create, but fails if the file is not found
+ * or if filename is null.
+ */
+flib_ini *flib_ini_load(const char *filename);
+
+/**
+ * Store the ini to the file "filename", overwriting
+ * the previous contents. Returns 0 on success.
+ */
+int flib_ini_save(flib_ini *ini, const char *filename);
+
+void flib_ini_destroy(flib_ini *ini);
+
+/**
+ * Enter the section with the specified name. Returns 0 on
+ * success, INI_ERROR_NOTFOUND if the section does not exist
+ * and a different value if another error occurs.
+ * If an error occurs, there is no current section.
+ *
+ * The section name should only consist of letters and
+ * numbers.
+ */
+int flib_ini_enter_section(flib_ini *ini, const char *section);
+
+/**
+ * Creates and enters the section with the specified name. Simply
+ * enters the section if it exists already. Returns 0 on success
+ * and a different value if another error occurs.
+ * If an error occurs, there is no current section.
+ */
+int flib_ini_create_section(flib_ini *ini, const char *section);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a newly allocated string. Returns 0 on success, INI_ERROR_NOTFOUND
+ * if the key was not found and a different value for other errors,
+ * e.g. if there is no current section.
+ */
+int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a newly allocated string. If the key is not found, the default
+ * value will be used instead. Returns 0 on success.
+ */
+int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as an int. Returns 0 on success, INI_ERROR_NOTFOUND
+ * if the key was not found, INI_ERROR_FORMAT if it was found but
+ * could not be converted to an int, and a different value for other
+ * errors, e.g. if there is no current section.
+ */
+int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as an int. If the key is not found, the default value will be used instead.
+ * Returns 0 on success, INI_ERROR_FORMAT if the value was found but
+ * could not be converted to int, and another value otherwise.
+ */
+int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a bool. Treats everything beginning with "Y", "T" or "1" as true,
+ * everything starting with "N", "F" or "1" as false.
+ *
+ * Returns 0 on success, INI_ERROR_NOTFOUND if the key was not found,
+ * INI_ERROR_FORMAT if the value could not be interpreted as boolean,
+ * and another value otherwise.
+ */
+int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a bool. If the key is not found, the default value will be
+ * used instead. Returns 0 on success, INI_ERROR_FORMAT if the
+ * value could not be interpreted as boolean, and another value otherwise.
+ */
+int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_str(flib_ini *ini, const char *key, const char *value);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_int(flib_ini *ini, const char *key, int value);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_bool(flib_ini *ini, const char *key, bool value);
+
+/**
+ * Returns the number of sections in the ini file, or a negative value on error.
+ */
+int flib_ini_get_sectioncount(flib_ini *ini);
+
+/**
+ * Returns the name of the section, or NULL on error. The returned string must
+ * be free()d.
+ *
+ * Note: There is no guarantee that the order of the sections
+ * will remain stable if the ini is modified.
+ */
+char *flib_ini_get_sectionname(flib_ini *ini, int number);
+
+/**
+ * Returns the number of keys in the current section, or -1 on error.
+ */
+int flib_ini_get_keycount(flib_ini *ini);
+
+/**
+ * Returns the name of the key in the current section, or NULL on error.
+ * The returned string must be free()d.
+ *
+ * Note: There is no guarantee that the order of the keys in a section
+ * will remain stable if the ini is modified.
+ */
+char *flib_ini_get_keyname(flib_ini *ini, int number);
+
+#endif /* INIHELPER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/list.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,80 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * Simple dynamic array manipulation functions.
+ */
+
+#ifndef LIST_H_
+#define LIST_H_
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+#include "logging.h"
+
+/**
+ * Generate a static function that inserts a new value into a heap array of the given type,
+ * using realloc and memmove to increase the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / increment).
+ * The function returns 0 on success and leaves the array unchanged on error.
+ */
+#define GENERATE_STATIC_LIST_INSERT(fname, type) \
+	static int fname(type **listptr, int *listSizePtr, type element, int pos) { \
+		int result = -1; \
+		if(!log_badargs_if4(listptr==NULL, listSizePtr==NULL, pos < 0, pos > *listSizePtr)) { \
+			type *newList = flib_realloc(*listptr, ((*listSizePtr)+1)*sizeof(type)); \
+			if(newList) { \
+				memmove(newList + (pos+1), newList + pos, ((*listSizePtr)-pos)*sizeof(type)); \
+				newList[pos] = element; \
+				(*listSizePtr)++; \
+				*listptr = newList; \
+				result = 0; \
+			} \
+		} \
+		return result; \
+	}
+
+/**
+ * Generate a static function that deletes a value from a heap array of the given type,
+ * using realloc and memmove to decrease the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / decrement).
+ * The function returns 0 on success and leaves the array unchanged on error.
+ */
+#define GENERATE_STATIC_LIST_DELETE(fname, type) \
+	static int fname(type **listPtr, int *listSizePtr, int pos) { \
+		int result = -1; \
+		if(!log_badargs_if4(listPtr==NULL, listSizePtr==NULL, pos < 0, pos >= *listSizePtr)) { \
+			memmove((*listPtr) + pos, (*listPtr) + (pos+1), ((*listSizePtr)-(pos+1))*sizeof(type)); \
+			(*listSizePtr)--; \
+			\
+			size_t newCharSize = (*listSizePtr)*sizeof(type); \
+			type *newList = flib_realloc((*listPtr), newCharSize); \
+			if(newList || newCharSize==0) { \
+				(*listPtr) = newList; \
+			} /* If the realloc fails, just keep using the old buffer...*/ \
+			result = 0; \
+		} \
+		return result; \
+	}
+
+#endif /* LIST_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,145 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "logging.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+static int flib_loglevel = FLIB_LOGLEVEL_INFO;
+static FILE *flib_logfile = NULL;
+void (*flib_logCallback)(int level, const char *msg) = NULL;
+
+char* flib_format_ip(uint32_t numip) {
+	static char ip[16];
+	snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
+	return ip;
+}
+
+static inline FILE *flib_log_getfile() {
+	if(flib_logfile==NULL) {
+		return stdout;
+	} else {
+		return flib_logfile;
+	}
+}
+
+static int log_time(char *buffer) {
+    time_t timer;
+    struct tm* tm_info;
+
+    time(&timer);
+    tm_info = localtime(&timer);
+
+    return strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
+}
+
+static char getPrefix(int level) {
+	switch(level) {
+	case FLIB_LOGLEVEL_ERROR: return 'E';
+	case FLIB_LOGLEVEL_WARNING: return 'W';
+	case FLIB_LOGLEVEL_INFO: return 'I';
+	case FLIB_LOGLEVEL_DEBUG: return 'D';
+	default: return '?';
+	}
+}
+
+static void _flib_vflog(const char *func, int level, const char *fmt, va_list args) {
+	if(level >= flib_loglevel) {
+		char logbuffer[1024];
+		logbuffer[0] = getPrefix(level);
+		logbuffer[1] = ' ';
+
+		int pos = 2;
+
+		int len = log_time(logbuffer+pos);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		len = snprintf(logbuffer+pos, sizeof(logbuffer)-pos, " [%-30s] ", func);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		len = vsnprintf(logbuffer+pos, sizeof(logbuffer)-pos, fmt, args);
+		if(len>=0) {
+			pos += len;
+			if(pos>sizeof(logbuffer)-1) pos = sizeof(logbuffer)-1;
+		} else {
+			return;
+		}
+
+		if(flib_logCallback != NULL) {
+			flib_logCallback(level, logbuffer);
+		} else {
+			FILE *logfile = flib_log_getfile();
+			fputs(logbuffer, logfile);
+			fputc('\n', logfile);
+			fflush(logfile);
+		}
+	}
+}
+
+void _flib_flog(const char *func, int level, const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	_flib_vflog(func, level, fmt, argp);
+	va_end(argp);
+}
+
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...) {
+	if(!cond) {
+		va_list argp;
+		va_start(argp, fmt);
+		_flib_vflog(func, level, fmt, argp);
+		va_end(argp);
+	}
+	return !cond;
+}
+
+int flib_log_getLevel() {
+	return flib_loglevel;
+}
+
+void flib_log_setLevel(int level) {
+	flib_loglevel = level;
+}
+
+void flib_log_setFile(FILE *file) {
+	flib_logfile = file;
+	flib_logCallback = NULL;
+}
+
+bool flib_log_isActive(int level) {
+	return level >= flib_log_getLevel();
+}
+
+void flib_log_setCallback(void (*logCallback)(int level, const char *msg)) {
+	flib_logCallback = logCallback;
+	flib_logfile = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,106 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#define FLIB_LOGLEVEL_ALL -100
+#define FLIB_LOGLEVEL_DEBUG -1
+#define FLIB_LOGLEVEL_INFO 0
+#define FLIB_LOGLEVEL_WARNING 1
+#define FLIB_LOGLEVEL_ERROR 2
+#define FLIB_LOGLEVEL_NONE 100
+
+/**
+ * Returns a pointer to a static buffer, don't free or store.
+ */
+char* flib_format_ip(uint32_t numip);
+
+/**
+ * Evaluates the expression cond. If it is true, a formatted error will be logged.
+ * Returns true if an error is logged, false otherwise (i.e. the boolean value of the argument)
+ * Usage: log_e_if(errorHasHappened, "Format string", formatArg, ...);
+ */
+#define log_e_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_ERROR, !(bool)(cond), __VA_ARGS__)
+#define log_w_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_WARNING, !(bool)(cond), __VA_ARGS__)
+
+/**
+ * Helper macros for log_badargs_if
+ * The t parameters are the textual representation of the c parameters. They need to be passed
+ * explicitly, to prevent them from being expanded in prescan.
+ */
+#define _flib_lbi(c1,t1) log_e_if(c1, "Invalid Argument (%s)", t1)
+#define _flib_lbi2(c1,t1,c2,t2) (_flib_lbi(c1,t1) || _flib_lbi(c2,t2))
+#define _flib_lbi3(c1,t1,c2,t2,c3,t3) (_flib_lbi(c1,t1) || _flib_lbi2(c2,t2,c3,t3))
+#define _flib_lbi4(c1,t1,c2,t2,c3,t3,c4,t4) (_flib_lbi(c1,t1) || _flib_lbi3(c2,t2,c3,t3,c4,t4))
+#define _flib_lbi5(c1,t1,c2,t2,c3,t3,c4,t4,c5,t5) (_flib_lbi(c1,t1) || _flib_lbi4(c2,t2,c3,t3,c4,t4,c5,t5))
+#define _flib_lbi6(c1,t1,c2,t2,c3,t3,c4,t4,c5,t5,c6,t6) (_flib_lbi(c1,t1) || _flib_lbi5(c2,t2,c3,t3,c4,t4,c5,t5,c6,t6))
+
+/**
+ * These macros log an "Invalid Argument" error for the first of their arguments that evaluates to true.
+ * The text of the argument is included in the log message.
+ * The expression returns true if any of its arguments is true (i.e. if an argument error was logged).
+ *
+ * For example, log_badargs_if(x==NULL) will log "Invalid Argument (x==NULL)" and return true if x is NULL.
+ */
+#define log_badargs_if(c1) _flib_lbi(c1,#c1)
+#define log_badargs_if2(c1, c2) _flib_lbi2(c1,#c1,c2,#c2)
+#define log_badargs_if3(c1, c2, c3) _flib_lbi3(c1,#c1,c2,#c2,c3,#c3)
+#define log_badargs_if4(c1, c2, c3, c4) _flib_lbi4(c1,#c1,c2,#c2,c3,#c3,c4,#c4)
+#define log_badargs_if5(c1, c2, c3, c4, c5) _flib_lbi5(c1,#c1,c2,#c2,c3,#c3,c4,#c4,c5,#c5)
+#define log_badargs_if6(c1, c2, c3, c4, c5, c6) _flib_lbi6(c1,#c1,c2,#c2,c3,#c3,c4,#c4,c5,#c5,c6,#c6)
+
+#define log_oom_if(cond) log_e_if(cond, "Out of Memory")
+
+#define flib_log_e(...) _flib_flog(__func__, FLIB_LOGLEVEL_ERROR, __VA_ARGS__)
+#define flib_log_w(...) _flib_flog(__func__, FLIB_LOGLEVEL_WARNING, __VA_ARGS__)
+#define flib_log_i(...) _flib_flog(__func__, FLIB_LOGLEVEL_INFO, __VA_ARGS__)
+#define flib_log_d(...) _flib_flog(__func__, FLIB_LOGLEVEL_DEBUG, __VA_ARGS__)
+
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...);
+void _flib_flog(const char *func, int level, const char *fmt, ...);
+
+/**
+ * Only log messages that are at least the indicated level
+ */
+void flib_log_setLevel(int level);
+int flib_log_getLevel();
+
+/**
+ * Log to the indicated file. You can pass NULL to log to stdout.
+ * This overrides setCallback and vice versa.
+ */
+void flib_log_setFile(FILE *logfile);
+
+/**
+ * Returns whether messages of this level are logged at the moment.
+ */
+bool flib_log_isActive(int level);
+
+/**
+ * Allows logging through an arbitrary callback function. Useful for integrating into an
+ * existing logging system. This overrides setFile and vice versa.
+ */
+void flib_log_setCallback(void (*logCallback)(int level, const char *msg));
+
+#endif /* LOGGING_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,212 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "util.h"
+#include "logging.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <limits.h>
+
+char *flib_asprintf(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	char *result = flib_vasprintf(fmt, argp);
+	va_end(argp);
+	return result;
+}
+
+char *flib_vasprintf(const char *fmt, va_list args) {
+	char *result = NULL;
+	if(!log_badargs_if(fmt==NULL)) {
+		int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;					// Figure out how much memory we need,
+		if(!log_e_if(requiredSize<0, "Error formatting string with template \"%s\"", fmt)) {
+			char *tmpbuf = flib_malloc(requiredSize);						// allocate it
+			if(tmpbuf && vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) {	// and then do the actual formatting.
+				result = tmpbuf;
+				tmpbuf = NULL;
+			}
+			free(tmpbuf);
+		}
+	}
+	return result;
+}
+
+char *flib_join(char **parts, int partCount, const char *delimiter) {
+	char *result = NULL;
+	if(!log_badargs_if2(parts==NULL, delimiter==NULL)) {
+		size_t totalSize = 1;
+		size_t delimLen = strlen(delimiter);
+		for(int i=0; i<partCount; i++) {
+			totalSize += strlen(parts[i]) + delimLen;
+		}
+		result = flib_malloc(totalSize);
+
+		if(result) {
+			size_t outpos = 0;
+			for(int i=0; i<partCount; i++) {
+				if(i>0) {
+					strcpy(result+outpos, delimiter);
+					outpos += delimLen;
+				}
+				strcpy(result+outpos, parts[i]);
+				outpos += strlen(parts[i]);
+			}
+		}
+	}
+	return result;
+}
+
+char *flib_strdupnull(const char *str) {
+	return str==NULL ? NULL : flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+	void *result = NULL;
+	if(!log_badargs_if(buf==NULL && size>0)) {
+		result = flib_malloc(size);
+		if(result) {
+			memcpy(result, buf, size);
+		}
+	}
+	return result;
+}
+
+void *flib_malloc(size_t size) {
+	void *result = malloc(size);
+	if(!result && size>0) {
+		flib_log_e("Out of memory trying to malloc %zu bytes.", size);
+	}
+	return result;
+}
+
+void *flib_calloc(size_t count, size_t elementsize) {
+	void *result = calloc(count, elementsize);
+	if(!result && count>0 && elementsize>0) {
+		flib_log_e("Out of memory trying to calloc %zu objects of %zu bytes each.", count, elementsize);
+	}
+	return result;
+}
+
+void *flib_realloc(void *ptr, size_t size) {
+	void *result = realloc(ptr, size);
+	if(!result && size>0) {
+		flib_log_e("Out of memory trying to realloc %zu bytes.", size);
+	}
+	return result;
+}
+
+static bool isAsciiAlnum(char c) {
+	return (c>='0' && c<='9') || (c>='a' && c <='z') || (c>='A' && c <='Z');
+}
+
+char *flib_urlencode(const char *inbuf) {
+	return flib_urlencode_pred(inbuf, isAsciiAlnum);
+}
+
+static size_t countCharsToEscape(const char *inbuf, bool (*needsEscaping)(char c)) {
+	size_t result = 0;
+	for(const char *c=inbuf; *c; c++) {
+		if(needsEscaping(*c)) {
+			result++;
+		}
+	}
+	return result;
+}
+
+char *flib_urlencode_pred(const char *inbuf, bool (*needsEscaping)(char c)) {
+	char *result = NULL;
+	if(inbuf && !log_badargs_if(needsEscaping == NULL)) {
+		size_t insize = strlen(inbuf);
+		if(!log_e_if(insize > SIZE_MAX/4, "String too long: %zu bytes.", insize)) {
+			size_t escapeCount = countCharsToEscape(inbuf, needsEscaping);
+			result = flib_malloc(insize + escapeCount*2 + 1);
+		}
+		if(result) {
+			char *out = result;
+			for(const char *in = inbuf; *in; in++) {
+				if(!needsEscaping(*in)) {
+					*out = *in;
+					out++;
+				} else {
+					snprintf(out, 4, "%%%02x", (unsigned)(*(uint8_t*)in));
+					out += 3;
+				}
+			}
+			*out = 0;
+		}
+	}
+	return result;
+}
+
+char *flib_urldecode(const char *inbuf) {
+	if(!inbuf) {
+		return NULL;
+	}
+	char *outbuf = flib_malloc(strlen(inbuf)+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
+            char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
+            outbuf[outpos++] = strtol(temp, NULL, 16);
+            inpos += 3;
+        } else {
+        	outbuf[outpos++] = inbuf[inpos++];
+        }
+    }
+    outbuf[outpos] = 0;
+    char *shrunk = realloc(outbuf, outpos+1);
+    return shrunk ? shrunk : outbuf;
+}
+
+bool flib_contains_dir_separator(const char *str) {
+	if(!log_badargs_if(!str)) {
+		for(;*str;str++) {
+			if(*str=='\\' || *str=='/') {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool flib_strempty(const char *str) {
+	return !str || !*str;
+}
+
+int flib_gets(char *str, size_t strlen) {
+	if(fgets(str, strlen, stdin)) {
+		for(char *s=str; *s; s++) {
+			if(*s=='\r' || *s=='\n') {
+				*s = 0;
+				break;
+			}
+		}
+		return 0;
+	}
+	return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h	Mon Aug 27 17:40:16 2012 +0200
@@ -0,0 +1,122 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef FLIB_UTIL_H_
+#define FLIB_UTIL_H_
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+/**
+ * Prints a format string to a newly allocated buffer of the required size.
+ * Parameters are like those for printf. Returns NULL on error.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_asprintf(const char *fmt, ...);
+
+/**
+ * Exactly as flib_asprintf, but accepts a va_list.
+ */
+char *flib_vasprintf(const char *fmt, va_list args);
+
+/**
+ * Creates a new string (that must be freed) containing all parts
+ * joined together, with the specified delimiter between each.
+ */
+char *flib_join(char **parts, int partCount, const char *delimiter);
+
+/**
+ * Return a duplicate of the provided string, or NULL if an error
+ * occurs or if str is already NULL.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_strdupnull(const char *str);
+
+/**
+ * Return a duplicate of the provided buffer, or NULL if an error
+ * occurs or if buf is already NULL or if size is 0.
+ *
+ * Returned buffer must be free()d
+ */
+void *flib_bufdupnull(const void *buf, size_t size);
+
+/**
+ * Simple malloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like malloc.
+ */
+void *flib_malloc(size_t size);
+
+/**
+ * Simple calloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like calloc.
+ */
+void *flib_calloc(size_t count, size_t elementsize);
+
+/**
+ * Simple realloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like realloc.
+ */
+void *flib_realloc(void *ptr, size_t size);
+
+/**
+ * Replace all non-alphanumeric and non-ascii bytes with escape
+ * sequences in the form %XX. Does not modify the original string,
+ * but returns a newly allocated one that must be free()d. Returns
+ * null on failure or if null was passed as argument.
+ *
+ * This should work fine with all ASCII-based charsets including UTF-8.
+ */
+char *flib_urlencode(const char *str);
+
+/**
+ * Replace some bytes with escape sequences in the form %XX.
+ * Does not modify the original string, but returns a newly allocated
+ * one that must be free()d.
+ *
+ * All bytes for which the predicate function returns true are escaped.
+ *
+ * Returns null on failure or if null was passed as argument.
+ */
+char *flib_urlencode_pred(const char *str, bool (*needsEscaping)(char c));
+
+/**
+ * Replace escape sequences of the form %XX with their byte values.
+ * Does not modify the original string, but returns a newly allocated
+ * one that must be free()d. Returns null on failure or if null was
+ * passed as argument.
+ */
+char *flib_urldecode(const char *str);
+
+/**
+ * Figure out if the string contains / or \. Useful in routines that
+ * construct filenames.
+ */
+bool flib_contains_dir_separator(const char *str);
+
+/**
+ * Returns true if str is either NULL or points to a 0-length string
+ */
+bool flib_strempty(const char *str);
+
+int flib_gets(char *str, size_t strlen);
+
+#endif