Merge
authorMedo <smaxein@googlemail.com>
Tue, 19 Jun 2012 21:20:08 +0200
changeset 7267 710f3ced8934
parent 7234 613998625a3c (diff)
parent 7265 3f96073156e1 (current diff)
child 7269 5b0aeef8ba2a
Merge
QTfrontend/CMakeLists.txt
QTfrontend/game.cpp
share/hedgewars/Data/Scripts/plist/Balanced Random Weapon.plist
share/hedgewars/Data/Scripts/plist/Capture the Flag.plist
share/hedgewars/Data/Scripts/plist/Highlander.plist
share/hedgewars/Data/Scripts/plist/No Jumping.plist
share/hedgewars/Data/Scripts/plist/Normal.plist
share/hedgewars/Data/Scripts/plist/Racer.plist
share/hedgewars/Data/Scripts/plist/Random Weapon.plist
share/hedgewars/Data/Scripts/plist/Space Invasion.plist
share/hedgewars/Data/Scripts/plist/The Specialists.plist
share/hedgewars/Data/Scripts/plist/Tumbler.plist
--- a/.hgignore	Tue Jun 19 01:20:19 2012 +0400
+++ b/.hgignore	Tue Jun 19 21:20:08 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	Tue Jun 19 01:20:19 2012 +0400
+++ b/QTfrontend/CMakeLists.txt	Tue Jun 19 21:20:08 2012 +0200
@@ -130,7 +130,6 @@
     achievements.h
     binds.h
     ui_hwform.h
-    KB.h
     hwconsts.h
     sdlkeys.h
     )
--- a/QTfrontend/game.cpp	Tue Jun 19 01:20:19 2012 +0400
+++ b/QTfrontend/game.cpp	Tue Jun 19 21:20:08 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	Tue Jun 19 01:20:19 2012 +0400
+++ b/QTfrontend/net/newnetclient.h	Tue Jun 19 21:20:08 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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/Android Support library/NOTICE.txt	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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
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/ini4j-0.5.2.jar has changed
--- a/project_files/Android-build/SDL-android-project/res/raw/basicflags.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/basicsettings.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,150 @@
+[DamagePercent]
+checkOverMax=false
+times1000=false
+command=e$damagepct
+default=100
+image=Damage
+max=300
+min=10
+title=Damage Modifier
+
+[TurnTime]
+checkOverMax=true
+times1000=true
+command=e$turntime
+default=45
+image=Time
+max=100
+min=1
+title=Turn Time
+
+[InitialHealth]
+checkOverMax=false
+times1000=false
+command=inithealth
+default=200
+image=Health
+max=200
+min=50
+title=Initial Health
+
+[SuddenDeathTimeout]
+checkOverMax=true
+times1000=false
+command=e$sd_turns
+default=15
+image=SuddenDeath
+max=50
+min=0
+title=Sudden Death Timeout
+
+[CrateDropTurns]
+checkOverMax=false
+times1000=false
+command=e$casefreq
+default=5
+image=Box
+max=9
+min=0
+title=Crate Drop Turns
+
+[MinesTime]
+checkOverMax=false
+times1000=true
+command=e$minestime
+default=3
+image=Time
+max=5
+min=-1
+title=Mines Time
+
+[MinesNumber]
+checkOverMax=false
+times1000=false
+command=e$minesnum
+default=4
+image=Mine
+max=80
+min=0
+title=Mines Number
+
+[MinesDudPercent]
+checkOverMax=false
+times1000=false
+command=e$minedudpct
+default=0
+image=Dud
+max=100
+min=0
+title=Dud Mines Probability (%)
+
+[Explosives]
+checkOverMax=false
+times1000=false
+command=e$explosives
+default=2
+image=Damage
+max=40
+min=0
+title=Explosives
+
+[HealthCratePercent]
+checkOverMax=false
+times1000=false
+command=e$healthprob
+default=35
+image=Health
+max=100
+min=0
+title=Health Kit Probability (%)
+
+[HealthCrateHP]
+checkOverMax=false
+times1000=false
+command=e$hcaseamount
+default=25
+image=Health
+max=200
+min=0
+title=Health Amount in Kit
+
+[SuddenDeathWaterRise]
+checkOverMax=false
+times1000=false
+command=e$waterrise
+default=47
+image=SuddenDeath
+max=100
+min=0
+title=Water Rise Amount
+
+[SuddenDeathHealthDecrease]
+checkOverMax=false
+times1000=false
+command=e$healthdec
+default=5
+image=SuddenDeath
+max=100
+min=0
+title=Health Decrease
+
+[RopeLengthPercent]
+checkOverMax=false
+times1000=false
+command=e$ropepct
+default=100
+image=Rope
+max=999
+min=25
+title=Rope Length (%)
+
+[GetAwayTimePercent]
+checkOverMax=false
+times1000=false
+command=e$getawaytime
+default=100
+image=Time
+max=999
+min=0
+title=Get Away Time (%)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/gamemods.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,74 @@
+[SolidLand]
+bitmaskIndex=2
+
+[Border]
+bitmaskIndex=3
+
+[DivideTeams]
+bitmaskIndex=4
+
+[LowGravity]
+bitmaskIndex=5
+
+[LaserSight]
+bitmaskIndex=6
+
+[Invulnerable]
+bitmaskIndex=7
+
+[ResetHealth]
+bitmaskIndex=8
+
+[Vampiric]
+bitmaskIndex=9
+
+[Karma]
+bitmaskIndex=10
+
+[Artillery]
+bitmaskIndex=11
+
+[Forts]
+bitmaskIndex=12
+
+[RandomOrder]
+bitmaskIndex=13
+
+[King]
+bitmaskIndex=14
+
+[PlaceHog]
+bitmaskIndex=15
+
+[SharedAmmo]
+bitmaskIndex=16
+
+[DisableGirders]
+bitmaskIndex=17
+
+[DisableLandObjects]
+bitmaskIndex=18
+
+[AISurvival]
+bitmaskIndex=19
+
+[InfAttack]
+bitmaskIndex=20
+
+[ResetWeps]
+bitmaskIndex=21
+
+[PerHogAmmo]
+bitmaskIndex=22
+
+[DisableWind]
+bitmaskIndex=23
+
+[MoreWind]
+bitmaskIndex=24
+
+[TagTeam]
+bitmaskIndex=25
+
+[BottomBorder]
+bitmaskIndex=26
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_barrelmayhem.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Barrel Mayhem
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=30
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=0
+MinesTime=0
+MinesNumber=0
+MinesDudPercent=0
+Explosives=80
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_barrelmayhem.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_cleanslate.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Clean Slate
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=45
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=4
+MinesDudPercent=0
+Explosives=2
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=true
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=false
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=true
+ResetWeps=true
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_cleanslate.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_default_scheme.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Default
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=45
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=4
+MinesDudPercent=0
+Explosives=2
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=false
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_default_scheme.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_fortmode.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Fort Mode
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=45
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=0
+MinesDudPercent=0
+Explosives=0
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=true
+LowGravity=true
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=true
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=false
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_fortmode.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_kingmode.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=King Mode
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=45
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=4
+MinesDudPercent=0
+Explosives=2
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=true
+PlaceHog=false
+SharedAmmo=false
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_kingmode.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_minefield.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Minefield
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=30
+InitialHealth=50
+SuddenDeathTimeout=15
+CrateDropTurns=0
+MinesTime=0
+MinesNumber=80
+MinesDudPercent=0
+Explosives=0
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=true
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_minefield.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_promode.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Pro Mode
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=15
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=0
+MinesTime=3
+MinesNumber=0
+MinesDudPercent=0
+Explosives=2
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_promode.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_shoppa.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Shoppa
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=30
+InitialHealth=100
+SuddenDeathTimeout=50
+CrateDropTurns=1
+MinesTime=3
+MinesNumber=0
+MinesDudPercent=0
+Explosives=0
+HealthCratePercent=0
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=true
+DivideTeams=true
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=true
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_shoppa.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_thinkingwithportals.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Thinking with Portals
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=45
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=2
+MinesTime=3
+MinesNumber=5
+MinesDudPercent=0
+Explosives=5
+HealthCratePercent=25
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=true
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=false
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_thinkingwithportals.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_timeless.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Timeless
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=9999
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=5
+MinesDudPercent=10
+Explosives=2
+HealthCratePercent=35
+HealthCrateHP=30
+SuddenDeathWaterRise=0
+SuddenDeathHealthDecrease=0
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=false
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=false
+DisableLandObjects=false
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=true
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_timeless.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/scheme_tunnelhogs.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,46 @@
+[Scheme]
+name=Tunnelhogs
+
+[BasicSettings]
+DamagePercent=100
+TurnTime=30
+InitialHealth=100
+SuddenDeathTimeout=15
+CrateDropTurns=5
+MinesTime=3
+MinesNumber=10
+MinesDudPercent=10
+Explosives=10
+HealthCratePercent=35
+HealthCrateHP=25
+SuddenDeathWaterRise=47
+SuddenDeathHealthDecrease=5
+RopeLengthPercent=100
+GetAwayTimePercent=100
+
+[GameMods]
+SolidLand=false
+Border=false
+DivideTeams=true
+LowGravity=false
+LaserSight=false
+Invulnerable=false
+ResetHealth=false
+Vampiric=false
+Karma=false
+Artillery=false
+Forts=false
+RandomOrder=true
+King=false
+PlaceHog=false
+SharedAmmo=true
+DisableGirders=true
+DisableLandObjects=true
+AISurvival=false
+InfAttack=false
+ResetWeps=false
+PerHogAmmo=false
+DisableWind=false
+MoreWind=false
+TagTeam=false
+BottomBorder=false
--- a/project_files/Android-build/SDL-android-project/res/raw/scheme_tunnelhogs.xml	Tue Jun 19 01:20:19 2012 +0400
+++ /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
--- a/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Tue Jun 19 21:20:08 2012 +0200
@@ -2,7 +2,8 @@
 <resources>
 
 <array name="schemes">
-	<item>@raw/basicflags</item>
+	<item>@raw/basicsettings</item>
+    <item>@raw/gamemods</item>
 	<item>@raw/scheme_default_scheme</item>
 	<item>@raw/scheme_barrelmayhem</item>
 	<item>@raw/scheme_cleanslate</item>
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Tue Jun 19 21:20:08 2012 +0200
@@ -1,7 +1,8 @@
 /*
  * 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
@@ -18,322 +19,167 @@
 
 package org.hedgewars.hedgeroid.Datastructures;
 
-import java.io.BufferedReader;
+import java.util.Map;
+
 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 java.util.List;
+import java.util.Map.Entry;
+import java.util.TreeMap;
 
 import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
+import org.ini4j.Ini;
+import org.ini4j.InvalidFileFormatException;
+import org.ini4j.Profile.Section;
 
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 public class Scheme implements Parcelable, Comparable<Scheme>{
-
 	public static final String DIRECTORY_SCHEME = "schemes";
+	private static final Map<String, BasicSettingMeta> basicSettingsMeta = new TreeMap<String, BasicSettingMeta>();
+	private static final Map<String, GameModMeta> gameModsMeta = new TreeMap<String, GameModMeta>();
 
-	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){
+	private final String name;
+	private final int gamemod;
+	private final Map<String, Integer> basic = new TreeMap<String, Integer>();
+		
+	public Scheme(String _name, Map<String, Integer> _basic, int _gamemod) {
 		name = _name;
 		gamemod = _gamemod;
-		basic = _basic;
+		basic.putAll(_basic);
 	}
 	
 	public Scheme(Parcel in){
-		readFromParcel(in);
+		name = in.readString();
+		gamemod = in.readInt();
+		in.readMap(basic, Integer.class.getClassLoader());
 	}
 
-	public void sendToEngine(EngineProtocolNetwork epn)throws IOException{ 
+	public int getHealth() {
+		return basic.get("InitialHealth");
+	}
+	
+	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);
+		for(Map.Entry<String, Integer> entry : basic.entrySet()) {
+			BasicSettingMeta basicflag = basicSettingsMeta.get(entry.getKey());
 			
-			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;
+			//Health is a special case, it doesn't need to be send 				                             
+			//to the engine yet, we'll do that with the other HH info
+			if(!basicflag.command.equals("inithealth")){
+				epn.sendToEngine(String.format("%s %d", basicflag.command, entry.getValue()));
 			}
-			
-			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 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[]{};
+	public static List<Scheme> getSchemes(Context c) throws IllegalArgumentException {
+		File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME);
+		File[] files = schemeDir.listFiles(new FilenameFilter() {
+			public boolean accept(File dir, String filename) {
+				return filename.toLowerCase().startsWith("scheme_");
+			}
+		});
+		if(files == null) files = new File[0];
 		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;
+		List<Scheme> schemes = new ArrayList<Scheme>();
 
-				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;
+		for(File file : files) {
+			try {
+				Ini ini = new Ini(file);
+				
+				String name = ini.get("Scheme", "name");
+				if(name==null) {
+					name = file.getName();
+				}
+				Section basicSettingsSection = ini.get("BasicSettings");
+				Section gameModsSection = ini.get("GameMods");
+				if(basicSettingsSection == null || gameModsSection == null) {
+					Log.e(Scheme.class.getCanonicalName(), "Scheme file "+file+" is missing the BasicSettings or GameMods section - skipping.");
+					continue;
+				}
+				
+				Map<String, Integer> basicSettings = new TreeMap<String, Integer>();
+				for(Entry<String, BasicSettingMeta> entry : basicSettingsMeta.entrySet()) {
+					String key = entry.getKey();
+					BasicSettingMeta settingMeta = entry.getValue();
+					Integer value = null;
+					if(basicSettingsSection.containsKey(key)) {
+						try {
+							value = Integer.valueOf(basicSettingsSection.get(key));						
+						} catch (NumberFormatException e) {
+							// ignore
+						}
 					}
-					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();
+					
+					if(value==null) {
+						Log.w(Scheme.class.getCanonicalName(), "Scheme file "+file+" setting "+key+" is missing or invalid, using default.");
+						value = settingMeta.def;
+					}
+					
+					if(settingMeta.checkOverMax) {
+						value = Math.min(value, settingMeta.max);
+					}
+					if(settingMeta.times1000) {
+						value *= 1000;
+					}
+					
+					basicSettings.put(key, value);						
+				}
+				
+				int gamemods = 0;
+				for(Entry<String, GameModMeta> entry : gameModsMeta.entrySet()) {
+					String key = entry.getKey();
+					GameModMeta modMeta = entry.getValue();
+					if(Boolean.parseBoolean(gameModsSection.get(key))) {
+						gamemods |= (1 << modMeta.bitmaskIndex);
+					}
+				}
+				
+				schemes.add(new Scheme(name, basicSettings, gamemods));
+			} catch (InvalidFileFormatException e) {
+				throw new RuntimeException(e);
+			} catch (IOException e) {
+				throw new RuntimeException(e);
+			}
 		}
-		return new ArrayList<Scheme>();//TODO handle correctly
+		return schemes;
 	}
 	
-	private static FilenameFilter fnf = new FilenameFilter(){
-		public boolean accept(File dir, String filename) {
-			return filename.toLowerCase().startsWith("scheme_");
-		}
-	};
-
 	/**
-	 * 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
+	 * This method will parse the basic flags from a prespecified ini file.
+	 * In the future we could use one provided by the Data folder.
 	 */
-	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
+	public static void parseConfiguration(Context c) {
+		File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME);
+		File settingsFile = new File(schemeDir, "basicsettings");
+		File gameModsFile = new File(schemeDir, "gamemods");
+		
+		try {
+			Ini ini = new Ini(settingsFile);
+			for(Entry<String, Section> sectionEntry : ini.entrySet()) {
+				basicSettingsMeta.put(sectionEntry.getKey(), new BasicSettingMeta(sectionEntry.getValue()));
 			}
-			eventType = getEventType(xmlPuller);//</flag>
+			
+			ini = new Ini(gameModsFile);
+			for(Entry<String, Section> sectionEntry : ini.entrySet()) {
+				gameModsMeta.put(sectionEntry.getKey(), new GameModMeta(sectionEntry.getValue()));
+			}
+		} catch (InvalidFileFormatException e) {
+			throw new RuntimeException(e);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
 		}
-
-		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() {
@@ -343,13 +189,7 @@
 	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());
+		dest.writeMap(basic);
 	}
 
 	public static final Parcelable.Creator<Scheme> CREATOR = new Parcelable.Creator<Scheme>() {
@@ -366,3 +206,60 @@
 		return name.compareTo(another.name);
 	}
 }
+
+class BasicSettingMeta {
+	final String command;
+	final String title;
+	final int def;
+	final int min;
+	final int max;
+	final boolean times1000;
+	final boolean checkOverMax;
+	
+	public BasicSettingMeta(Ini.Section section) {
+		command = getRequired(section, "command");
+		title = section.get("title", "");
+		def = Integer.parseInt(getRequired(section, "default"));
+		min = Integer.parseInt(getRequired(section, "min"));
+		max = Integer.parseInt(getRequired(section, "max"));
+		times1000 = Boolean.parseBoolean(section.get("times1000", "false"));
+		checkOverMax = Boolean.parseBoolean(section.get("checkOverMax", "false"));
+	}
+	
+	private String getRequired(Ini.Section section, String key) {
+		String result = section.get(key);
+		if(result==null) {
+			throw new IllegalArgumentException("basicsettings.ini, section "+section.getName()+" is missing required setting "+key+".");
+		}
+		return result;
+	}
+
+	@Override
+	public String toString() {
+		return String
+				.format("BasicSettingMeta [command=%s, title=%s, def=%s, min=%s, max=%s, times1000=%s, checkOverMax=%s]",
+						command, title, def, min, max, times1000, checkOverMax);
+	}
+}
+
+// TODO: Extend with additional metadata
+class GameModMeta {
+	final int bitmaskIndex;
+	
+	public GameModMeta(Ini.Section section) {
+		bitmaskIndex = Integer.parseInt(getRequired(section, "bitmaskIndex"));
+	}
+	
+	private String getRequired(Ini.Section section, String key) {
+		String result = section.get(key);
+		if(result==null) {
+			throw new IllegalArgumentException("gamemods.ini, section "+section.getName()+" is missing required setting "+key+".");
+		}
+		return result;
+	}
+
+	@Override
+	public String toString() {
+		return String.format("GameModMeta [bitmaskIndex=%s]", bitmaskIndex);
+	}
+}
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Tue Jun 19 21:20:08 2012 +0200
@@ -88,6 +88,7 @@
 		readFromParcel(in);
 	}
 
+	@Override
 	public boolean equals(Object o){
 		if(super.equals(o)) return true;
 		else if(o instanceof Team){
@@ -103,6 +104,19 @@
 			return false;
 		}
 	}
+	
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		result = prime * result + ((grave == null) ? 0 : grave.hashCode());
+		result = prime * result + ((flag == null) ? 0 : flag.hashCode());
+		result = prime * result + ((voice == null) ? 0 : voice.hashCode());
+		result = prime * result + ((fort == null) ? 0 : fort.hashCode());
+		result = prime * result + ((hash == null) ? 0 : hash.hashCode());
+		return result;
+	}
 
 	public void setRandomColor(int[] illegalcolors){
 		Integer[] colorsToPickFrom = TEAM_COLORS;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Tue Jun 19 21:20:08 2012 +0200
@@ -107,7 +107,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/DownloadListActivity.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java	Tue Jun 19 21:20:08 2012 +0200
@@ -6,7 +6,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/DownloadPackage.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java	Tue Jun 19 21:20:08 2012 +0200
@@ -29,7 +29,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;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java	Tue Jun 19 21:20:08 2012 +0200
@@ -79,7 +79,7 @@
 		weapon.sendToEngine(epn, teamCount);
 		
 		for(Team t : teams){
-			if(t != null)t.sendToEngine(epn, teamCount, scheme.health);
+			if(t != null)t.sendToEngine(epn, teamCount, scheme.getHealth());
 		}
 	}
 	
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Tue Jun 19 21:20:08 2012 +0200
@@ -26,7 +26,6 @@
 import android.app.ProgressDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Tue Jun 19 21:20:08 2012 +0200
@@ -580,34 +580,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;		
 
-			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 +628,5 @@
 					event.values[2] / SensorManager.GRAVITY_EARTH);
 		}
 	}
-
 }
 
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java	Tue Jun 19 21:20:08 2012 +0200
@@ -29,11 +29,9 @@
 
 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;
@@ -56,7 +54,7 @@
 	public void onCreate(Bundle savedInstanceState){
 		super.onCreate(savedInstanceState);
 
-		Scheme.parseBasicFlags(this);
+		Scheme.parseConfiguration(this);
 		config = new GameConfig();
 
 		setContentView(R.layout.starting_game);
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Tue Jun 19 21:20:08 2012 +0200
@@ -311,7 +311,7 @@
 			imgFort.setImageDrawable(fortIconDrawable);
 			scroller.fullScroll(ScrollView.FOCUS_DOWN);// Scroll the scrollview
 			// to the bottom, work
-			// around for scollview
+			// around for scrollview
 			// invalidation (scrolls
 			// back to top)
 		}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java	Tue Jun 19 01:20:19 2012 +0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java	Tue Jun 19 21:20:08 2012 +0200
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Build;
@@ -54,6 +55,7 @@
 		return getCachePath(c) + ROOT_DIR;
 	}
 
+	@TargetApi(8)
 	static class FroyoSDCardDir{
 		public static String getDownloadPath(Context c){
 			File f =  c.getExternalCacheDir();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,36 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include <SDL.h>
+#include <SDL_net.h>
+
+static int flib_initflags;
+
+int flib_init(int flags) {
+	flib_initflags = flags;
+
+	flib_log_d("Initializing frontlib");
+	if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+		if(SDL_Init(0)==-1) {
+		    flib_log_e("Error in SDL_Init: %s", SDL_GetError());
+		    return -1;
+		}
+	}
+
+	if(SDLNet_Init()==-1) {
+		flib_log_e("Error in SDLNet_Init: %s", SDLNet_GetError());
+		if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+			SDL_Quit();
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+void flib_quit() {
+	flib_log_d("Shutting down frontlib");
+	SDLNet_Quit();
+	if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+		SDL_Quit();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,31 @@
+/*
+ * 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_
+
+#define FRONTLIB_SDL_ALREADY_INITIALIZED 1
+
+/**
+ * Call this function before anything else in this library.
+ *
+ * If the calling program uses SDL, it needs to call SDL_Init before initializing
+ * this library and then pass FRONTLIB_SDL_ALREADY_INITIALIZED as flag to this function.
+ *
+ * Otherwise, pass 0 to let this library handle SDL_Init an SDL_Quit itself.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int flib_init(int flags);
+
+/**
+ * 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.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,19 @@
+/**
+ * This file contains important constants which might need to be changed to adapt to
+ * changes in the engine or protocols.
+ */
+
+#ifndef HWCONSTS_H_
+#define HWCONSTS_H_
+
+#define HEDGEHOGS_PER_TEAM 8
+#define NETGAME_DEFAULT_PORT 46631
+#define PROTOCOL_VERSION 42
+#define MIN_SERVER_VERSION 1
+
+#define GAMEMOD_PERHOGAMMO_MASKBIT 22
+#define GAMEMOD_SHAREDAMMO_MASKBIT 16
+
+#define WEAPONS_COUNT 55
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/LICENSE	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,414 @@
+#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;
+
+	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, int size, bool isSavegame);
+	void *onGameRecordedCtx;
+
+	void (*onEngineMessageCb)(void *context, const uint8_t *em, int size);
+	void *onEngineMessageCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onConnect(void* context) {}
+static void defaultCallback_onDisconnect(void* context, int reason) {}
+static void defaultCallback_onErrorMessage(void* context, const char *msg) {
+	flib_log_w("Error from engine (no callback set): %s", msg);
+}
+static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
+static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
+static void defaultCallback_onEngineMessage(void *context, const uint8_t *em, int size) {}
+
+static void clearCallbacks(flib_gameconn *conn) {
+	conn->onConnectCb = &defaultCallback_onConnect;
+	conn->onDisconnectCb = &defaultCallback_onDisconnect;
+	conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
+	conn->onChatCb = &defaultCallback_onChat;
+	conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
+	conn->onEngineMessageCb = &defaultCallback_onEngineMessage;
+}
+
+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;
+			clearCallbacks(tempConn);
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame) {
+	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)) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, demo, size) == size) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, save, size) == size) {
+			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(!conn) {
+		flib_log_e("null parameter in flib_gameconn_getport");
+		return 0;
+	} else {
+		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) < 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(size>0) {
+		buffer[0] = size>255 ? 255 : size;
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+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, uint8_t *data, int len) {
+	int result = -1;
+	if(!conn || (!data && len>0)) {
+		flib_log_e("null parameter in flib_gameconn_send_enginemsg");
+	} else if(!flib_ipcbase_send_raw(conn->ipcBase, data, len)) {
+		demo_append(conn, data, len);
+		result = 0;
+	}
+	return result;
+}
+
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) {
+	int result = -1;
+	if(!conn || !msg) {
+		flib_log_e("null parameter in flib_gameconn_send_textmsg");
+	} else {
+		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) {
+	int result = -1;
+	uint8_t converted[257];
+	if(!conn || !playername || !msg) {
+		flib_log_e("null parameter in flib_gameconn_send_chatmsg");
+	} else if(format_chatmessage(converted, playername, msg)) {
+		flib_log_e("Error formatting message in flib_gameconn_send_chatmsg");
+	} else if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
+		demo_append(conn, converted, converted[0]+1);
+		result = 0;
+	}
+	return result;
+}
+
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onConnect");
+	} else {
+		conn->onConnectCb = callback ? callback : &defaultCallback_onConnect;
+		conn->onConnectCtx = context;
+	}
+}
+
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onDisconnect");
+	} else {
+		conn->onDisconnectCb = callback ? callback : &defaultCallback_onDisconnect;
+		conn->onDisconnectCtx = context;
+	}
+}
+
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onErrorMessage");
+	} else {
+		conn->onErrorMessageCb = callback ? callback : &defaultCallback_onErrorMessage;
+		conn->onErrorMessageCtx = context;
+	}
+}
+
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onChat");
+	} else {
+		conn->onChatCb = callback ? callback : &defaultCallback_onChat;
+		conn->onChatCtx = context;
+	}
+}
+
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onGameRecorded");
+	} else {
+		conn->onGameRecordedCb = callback ? callback : &defaultCallback_onGameRecorded;
+		conn->onGameRecordedCtx = context;
+	}
+}
+
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onEngineMessage");
+	} else {
+		conn->onEngineMessageCb = callback ? callback : &defaultCallback_onEngineMessage;
+		conn->onEngineMessageCtx = context;
+	}
+}
+
+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;
+					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;
+						}
+					}
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
+					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, GAME_END_ERROR);
+	}
+}
+
+void flib_gameconn_tick(flib_gameconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_gameconn_tick from a callback");
+	} else if(conn->state == FINISHED) {
+		flib_log_w("Call to flib_gameconn_tick, but we are already done.");
+	} else {
+		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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,79 @@
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define GAME_END_FINISHED 0
+#define GAME_END_INTERRUPTED 1
+#define GAME_END_HALTED 2
+#define GAME_END_ERROR 3
+
+struct _flib_gameconn;
+typedef struct _flib_gameconn flib_gameconn;
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame);
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size);
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size);
+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);
+
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len);
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg);
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
+
+/**
+ * handleConnect(void *context)
+ */
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context);
+
+/**
+ * handleDisconnect(void *context, int reason)
+ */
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context);
+
+/**
+ * Receives error messages sent by the engine
+ * handleErrorMessage(void* context, const char *msg)
+ */
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context);
+
+/**
+ * handleChat(void* context, const char *msg, bool teamchat)
+ */
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context);
+
+/**
+ * Called when the game ends
+ * handleGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame)
+ */
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context);
+
+/**
+ * Called when the game ends
+ * TODO handleStats(???)
+ */
+
+/**
+ * ...needs to be passed on to the server in a net game
+ * handleEngineMessage(void *context, const uint8_t *em, int size)
+ */
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
+
+// TODO efinish
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,200 @@
+#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(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_port");
+		return 0;
+	}
+	return ipc->port;
+}
+
+void flib_ipcbase_destroy(flib_ipcbase *ipc) {
+	if(ipc) {
+		flib_acceptor_close(ipc->acceptor);
+		flib_socket_close(ipc->sock);
+		free(ipc);
+	}
+}
+
+IpcState flib_ipcbase_state(flib_ipcbase *ipc) {
+	if(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_state");
+		return IPC_NOT_CONNECTED;
+	} else if(ipc->sock) {
+		return IPC_CONNECTED;
+	} else if(ipc->acceptor) {
+		return IPC_LISTENING;
+	} else {
+		return IPC_NOT_CONNECTED;
+	}
+}
+
+static bool isMessageReady(flib_ipcbase *ipc) {
+	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+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_socket_close(ipc->sock);
+			ipc->sock = NULL;
+		}
+	}
+}
+
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
+	if(!ipc || !data) {
+		flib_log_e("null parameter in flib_ipcbase_recv_message");
+		return -1;
+	}
+
+	if(!isMessageReady(ipc)) {
+		receiveToBuffer(ipc);
+	}
+
+	if(isMessageReady(ipc)) {
+		int msgsize = ipc->readBuffer[0]+1;
+		memcpy(data, ipc->readBuffer, msgsize);
+		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
+		ipc->readBufferSize -= msgsize;
+		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(!ipc || !data) {
+		flib_log_e("null parameter in flib_ipcbase_recv_map");
+		return -1;
+	}
+
+	receiveToBuffer(ipc);
+
+	if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) {
+		memcpy(data, ipc->readBuffer, IPCBASE_MAPMSG_BYTES);
+		memmove(ipc->readBuffer, ipc->readBuffer+IPCBASE_MAPMSG_BYTES, ipc->readBufferSize-IPCBASE_MAPMSG_BYTES);
+		return IPCBASE_MAPMSG_BYTES;
+	} else {
+		return -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;
+		}
+	}
+}
+
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) {
+	if(!ipc || (!data && len>0)) {
+		flib_log_e("null parameter in flib_ipcbase_send_raw");
+		return -1;
+	}
+	if(!ipc->sock) {
+		flib_log_w("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(!ipc || (!data && len>0)) {
+		flib_log_e("null parameter in flib_ipcbase_send_message");
+		return -1;
+	} else if(len>255) {
+		flib_log_e("Overlong message (%zu bytes) in flib_ipcbase_send_message", len);
+		return -1;
+	}
+
+	uint8_t sendbuf[256];
+	sendbuf[0] = len;
+	memcpy(sendbuf+1, data, len);
+	return flib_ipcbase_send_raw(ipc, sendbuf, len+1);
+}
+
+int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data) {
+	return flib_ipcbase_send_message(ipc, data, strlen(data));
+}
+
+void flib_ipcbase_accept(flib_ipcbase *ipc) {
+	if(!ipc) {
+		flib_log_e("null parameter in flib_ipcbase_accept");
+	} else if(!ipc->sock && ipc->acceptor) {
+		ipc->sock = flib_socket_accept(ipc->acceptor, true);
+		if(ipc->sock) {
+			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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+struct _flib_ipcbase;
+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();
+
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc);
+
+/**
+ * Free resources and close sockets.
+ */
+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);
+
+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 a negative value on failure.
+ */
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len);
+
+/**
+ * Convenience function for sending a 0-delimited string.
+ */
+int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data);
+
+/**
+ * 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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,269 @@
+#include "ipcprotocol.h"
+#include "../util/util.h"
+#include "../util/logging.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(!vec || !fmt) {
+		flib_log_e("null parameter in flib_ipc_appendmessage");
+	} else {
+		// 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(msgSize > 255) {
+			flib_log_e("Message too long (%u bytes) in flib_ipc_appendmessage", (unsigned)msgSize);
+		} else if(msgSize<0) {
+			flib_log_e("printf error in flib_ipc_appendmessage");
+		} else {
+			// Add the length prefix
+			((uint8_t*)msgbuffer)[0] = msgSize;
+
+			// Append it to the vector
+			if(flib_vector_append(vec, msgbuffer, msgSize+1) == msgSize+1) {
+				result = 0;
+			}
+		}
+	}
+	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(!vec || !map) {
+		flib_log_e("null parameter in flib_ipc_append_mapconf");
+	} else if(tempvector) {
+		bool error = false;
+
+		if(map->mapgen == MAPGEN_NAMED) {
+			error |= flib_ipc_append_message(tempvector, "emap %s", map->name);
+		}
+		if(map->theme && !mappreview) {
+			error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+		}
+		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(int offset=0; offset<map->drawDataSize; offset+=200) {
+				int bytesRemaining = map->drawDataSize-offset;
+				int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200;
+				uint8_t messagesize = edrawlen + fragmentsize;
+				error |= (flib_vector_append(tempvector, &messagesize, 1) != 1);
+				error |= (flib_vector_append(tempvector, edraw, edrawlen) != edrawlen);
+				error |= (flib_vector_append(tempvector, map->drawData+offset, fragmentsize) != fragmentsize);
+			}
+		}
+
+		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) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(tempvector);
+	return result;
+}
+
+int flib_ipc_append_seed(flib_vector *vec, const char *seed) {
+	if(!vec || !seed) {
+		flib_log_e("null parameter in flib_ipc_append_seed");
+		return -1;
+	} else {
+		return flib_ipc_append_message(vec, "eseed %s", seed);
+	}
+}
+
+int flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *scheme) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!vec || !scheme) {
+		flib_log_e("null parameter in flib_ipc_append_gamescheme");
+	} else if(tempvector) {
+		const flib_cfg_meta *meta = scheme->meta;
+		bool error = false;
+		uint32_t gamemods = 0;
+		for(int i=0; i<meta->modCount; i++) {
+			if(scheme->mods[i]) {
+				gamemods |= (1<<meta->mods[i].bitmaskIndex);
+			}
+		}
+		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, gamemods);
+		for(int i=0; i<meta->settingCount; i++) {
+			int value = scheme->settings[i];
+			if(meta->settings[i].maxMeansInfinity) {
+				value = value>=meta->settings[i].max ? 9999 : value;
+			}
+			if(meta->settings[i].times1000) {
+				value *= 1000;
+			}
+			error |= flib_ipc_append_message(tempvector, "%s %i", 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) == 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);
+}
+
+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(!vec || !team) {
+		flib_log_e("invalid parameter in flib_ipc_append_addteam");
+	} else if(tempvector) {
+		bool error = false;
+
+		if(!perHogAmmo && !noAmmoStore) {
+			error |= appendWeaponSet(tempvector, team->hogs[0].weaponset);
+			error |= flib_ipc_append_message(tempvector, "eammstore");
+		}
+
+		char *hash = team->hash ? team->hash : "00000000000000000000000000000000";
+		error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, team->color, 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) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(tempvector);
+	return result;
+}
+
+static bool getGameMod(const flib_cfg *conf, int maskbit) {
+	for(int i=0; i<conf->meta->modCount; i++) {
+		if(conf->meta->mods[i].bitmaskIndex == maskbit) {
+			return conf->mods[i];
+		}
+	}
+	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
+	return false;
+}
+
+int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame) {
+	int result = -1;
+	flib_vector *tempvector = flib_vector_create();
+	if(!vec || !setup) {
+		flib_log_e("null parameter in flib_ipc_append_fullconfig");
+	} else if(tempvector) {
+		bool error = false;
+		bool perHogAmmo = false;
+		bool sharedAmmo = false;
+
+		error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL");
+		error |= flib_ipc_append_seed(vec, setup->seed);
+		if(setup->map) {
+			error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
+		}
+		if(setup->script) {
+			error |= flib_ipc_append_message(tempvector, "escript %s", setup->script);
+		}
+		if(setup->gamescheme) {
+			error |= flib_ipc_append_gamescheme(tempvector, setup->gamescheme);
+			sharedAmmo = getGameMod(setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
+			// Shared ammo has priority over per-hog ammo
+			perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
+		}
+		if(setup->teams && setup->teamcount>0) {
+			uint32_t *clanColors = flib_calloc(setup->teamcount, sizeof(uint32_t));
+			if(!clanColors) {
+				error = true;
+			} else {
+				int clanCount = 0;
+				for(int i=0; i<setup->teamcount; i++) {
+					flib_team *team = setup->teams[i];
+					bool newClan = false;
+
+					// Find the clan index of this team (clans are identified by color).
+					// The upper 8 bits (alpha) are ignored in the engine as well.
+					uint32_t color = team->color&UINT32_C(0x00ffffff);
+					int clan = 0;
+					while(clan<clanCount && clanColors[clan] != color) {
+						clan++;
+					}
+					if(clan==clanCount) {
+						newClan = true;
+						clanCount++;
+						clanColors[clan] = color;
+					}
+
+					// 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->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) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,53 @@
+#ifndef IPCPROTOCOL_H_
+#define IPCPROTOCOL_H_
+
+#include "../util/buffer.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/cfg.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 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_cfg *cfg);
+
+int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore);
+
+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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,175 @@
+#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,
+	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(char *seed, flib_map *mapdesc) {
+	flib_vector *result = NULL;
+	flib_vector *tempbuffer = flib_vector_create();
+	if(tempbuffer) {
+		bool error = false;
+		error |= flib_ipc_append_seed(tempbuffer, seed);
+		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(char *seed, flib_map *mapdesc) {
+	flib_mapconn *result = NULL;
+	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
+	if(tempConn) {
+		tempConn->ipcBase = flib_ipcbase_create();
+		tempConn->configBuffer = createConfigBuffer(seed, 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(!conn) {
+		flib_log_e("null parameter in flib_mapconn_getport");
+		return 0;
+	} else {
+		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(!conn) {
+		flib_log_e("null parameter in flib_mapconn_onSuccess");
+	} else {
+		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(!conn) {
+		flib_log_e("null parameter in flib_mapconn_onError");
+	} else {
+		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 = FINISHED;
+			conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
+			return;
+		} else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
+			conn->progress = FINISHED;
+			conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
+			return;
+		}
+	}
+}
+
+void flib_mapconn_tick(flib_mapconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_mapconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_mapconn_tick from a callback");
+	} else if(conn->progress == FINISHED) {
+		flib_log_w("Call to flib_mapconn_tick, but we are already done. Best destroy your flib_mapconn object in the callbacks.");
+	} else {
+		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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,78 @@
+#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)
+
+struct _flib_mapconn;
+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.
+ *
+ * No NULL parameters allowed, returns NULL on failure.
+ * Use flib_mapconn_destroy to free the returned object.
+ */
+flib_mapconn *flib_mapconn_create(char *seed, 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.
+ * You can pass callback=NULL to unset a callback.
+ *
+ * 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.
+ * You can pass callback=NULL to unset a callback.
+ *
+ * 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/model/cfg.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,196 @@
+#include "cfg.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+static void flib_cfg_meta_destroy(flib_cfg_meta *cfg) {
+	if(cfg) {
+		if(cfg->settings) {
+			for(int i=0; i<cfg->settingCount; i++) {
+				free(cfg->settings[i].name);
+				free(cfg->settings[i].engineCommand);
+			}
+			free(cfg->settings);
+		}
+		if(cfg->mods) {
+			for(int i=0; i<cfg->modCount; i++) {
+				free(cfg->mods[i].name);
+			}
+			free(cfg->mods);
+		}
+		free(cfg);
+	}
+}
+
+static void flib_cfg_destroy(flib_cfg* cfg) {
+	if(cfg) {
+		flib_cfg_meta_release(cfg->meta);
+		free(cfg->mods);
+		free(cfg->settings);
+		free(cfg->schemeName);
+		free(cfg);
+	}
+}
+
+static flib_cfg_meta *flib_cfg_meta_from_ini_handleError(flib_cfg_meta *result, flib_ini *ini) {
+	flib_cfg_meta_destroy(result);
+	flib_ini_destroy(ini);
+	return NULL;
+}
+
+static int readMetaSettingSections(flib_ini *ini, flib_cfg_meta *result, int limit) {
+	while(result->settingCount<limit) {
+		char sectionName[32];
+		if(snprintf(sectionName, sizeof(sectionName), "setting%i", result->settingCount) <= 0) {
+			return -1;
+		}
+		if(!flib_ini_enter_section(ini, sectionName)) {
+			flib_cfg_setting_meta *metasetting = &result->settings[result->settingCount];
+			result->settingCount++;
+
+			bool error = false;
+			error |= flib_ini_get_str(ini, &metasetting->name, "name");
+			error |= flib_ini_get_str_opt(ini, &metasetting->engineCommand, "command", NULL);
+			error |= flib_ini_get_bool(ini, &metasetting->times1000, "times1000");
+			error |= flib_ini_get_bool(ini, &metasetting->maxMeansInfinity, "maxmeansinfinity");
+			error |= flib_ini_get_int(ini, &metasetting->min, "min");
+			error |= flib_ini_get_int(ini, &metasetting->max, "max");
+			error |= flib_ini_get_int(ini, &metasetting->def, "default");
+			if(error) {
+				flib_log_e("Missing or malformed ini parameter in metaconfig, section %s", sectionName);
+				return -1;
+			}
+		} else {
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static int readMetaModSections(flib_ini *ini, flib_cfg_meta *result, int limit) {
+	while(result->modCount<limit) {
+		char sectionName[32];
+		if(snprintf(sectionName, sizeof(sectionName), "mod%i", result->modCount) <= 0) {
+			return -1;
+		}
+		if(!flib_ini_enter_section(ini, sectionName)) {
+			flib_cfg_mod_meta *metamod = &result->mods[result->modCount];
+			result->modCount++;
+
+			bool error = false;
+			error |= flib_ini_get_str(ini, &metamod->name, "name");
+			error |= flib_ini_get_int(ini, &metamod->bitmaskIndex, "bitmaskIndex");
+			if(error) {
+				flib_log_e("Missing or malformed ini parameter in metaconfig, section %s", sectionName);
+				return -1;
+			}
+		} else {
+			return 0;
+		}
+	}
+	return 0;
+}
+
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *filename) {
+	if(!filename) {
+		flib_log_e("null parameter in flib_cfg_meta_from_ini");
+		return NULL;
+	}
+	flib_cfg_meta *result = flib_cfg_meta_retain(flib_calloc(1, sizeof(flib_cfg_meta)));
+	flib_ini *ini = flib_ini_load(filename);
+
+	if(!result || !ini) {
+		return flib_cfg_meta_from_ini_handleError(result, ini);
+	}
+
+	// We're overallocating here for simplicity
+	int sectionCount = flib_ini_get_sectioncount(ini);
+	result->settingCount = 0;
+	result->modCount = 0;
+	result->settings = flib_calloc(sectionCount, sizeof(flib_cfg_setting_meta));
+	result->mods = flib_calloc(sectionCount, sizeof(flib_cfg_mod_meta));
+
+	if(!result->settings || !result->mods) {
+		return flib_cfg_meta_from_ini_handleError(result, ini);
+	}
+
+	if(readMetaSettingSections(ini, result, sectionCount) || readMetaModSections(ini, result, sectionCount)) {
+		return flib_cfg_meta_from_ini_handleError(result, ini);
+	}
+
+	if(result->settingCount+result->modCount != sectionCount) {
+		flib_log_e("Unknown or non-contiguous sections headers in metaconfig.");
+		return flib_cfg_meta_from_ini_handleError(result, ini);
+	}
+
+	flib_ini_destroy(ini);
+	return result;
+}
+
+flib_cfg_meta *flib_cfg_meta_retain(flib_cfg_meta *metainfo) {
+	if(metainfo) {
+		flib_retain(&metainfo->_referenceCount, "flib_cfg_meta");
+	}
+	return metainfo;
+}
+
+void flib_cfg_meta_release(flib_cfg_meta *cfg) {
+	if(cfg && flib_release(&cfg->_referenceCount, "flib_cfg_meta")) {
+		flib_cfg_meta_destroy(cfg);
+	}
+}
+
+flib_cfg *flib_cfg_create(flib_cfg_meta *meta, const char *schemeName) {
+	flib_cfg *result = flib_cfg_retain(flib_calloc(1, sizeof(flib_cfg)));
+	if(!meta || !result || !schemeName) {
+		flib_log_e("null parameter in flib_cfg_create");
+		return NULL;
+	}
+
+	result->meta = flib_cfg_meta_retain(meta);
+	result->schemeName = flib_strdupnull(schemeName);
+	result->mods = flib_calloc(meta->modCount, sizeof(*result->mods));
+	result->settings = flib_calloc(meta->settingCount, sizeof(*result->settings));
+
+	if(!result->mods || !result->settings || !result->schemeName) {
+		flib_cfg_destroy(result);
+		return NULL;
+	}
+
+	for(int i=0; i<meta->settingCount; i++) {
+		result->settings[i] = meta->settings[i].def;
+	}
+	return result;
+}
+
+flib_cfg *flib_cfg_copy(flib_cfg *cfg) {
+	flib_cfg *result = NULL;
+	if(cfg) {
+		result = flib_cfg_create(cfg->meta, cfg->schemeName);
+		if(result) {
+			memcpy(result->mods, cfg->mods, cfg->meta->modCount * sizeof(*cfg->mods));
+			memcpy(result->settings, cfg->settings, cfg->meta->settingCount * sizeof(*cfg->settings));
+		}
+	}
+	return result;
+}
+
+flib_cfg *flib_cfg_retain(flib_cfg *cfg) {
+	if(cfg) {
+		flib_retain(&cfg->_referenceCount, "flib_cfg");
+	}
+	return cfg;
+}
+
+void flib_cfg_release(flib_cfg *cfg) {
+	if(cfg && flib_release(&cfg->_referenceCount, "flib_cfg")) {
+		flib_cfg_destroy(cfg);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/cfg.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,85 @@
+/**
+ * Data structures for game scheme information.
+ */
+
+#ifndef CFG_H_
+#define CFG_H_
+
+#include <stdbool.h>
+
+// TODO: cfg/config -> scheme
+
+typedef struct {
+    char *name;
+    char *engineCommand;
+    bool maxMeansInfinity;
+    bool times1000;
+    int min;
+    int max;
+    int def;
+} flib_cfg_setting_meta;
+
+typedef struct {
+    char *name;
+    int bitmaskIndex;
+} flib_cfg_mod_meta;
+
+typedef struct {
+	int _referenceCount;
+	int settingCount;
+	int modCount;
+    flib_cfg_setting_meta *settings;
+    flib_cfg_mod_meta *mods;
+} flib_cfg_meta;
+
+typedef struct {
+	int _referenceCount;
+    flib_cfg_meta *meta;
+
+    char *schemeName;
+    int *settings;
+    bool *mods;
+} flib_cfg;
+
+/**
+ * Read the meta-configuration from a .ini file (e.g. which settings exist,
+ * what are their defaults etc.)
+ *
+ * Returns the meta-configuration or NULL.
+ */
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *filename);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_cfg_meta *flib_cfg_meta_retain(flib_cfg_meta *metainfo);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_cfg_meta_release(flib_cfg_meta *metainfo);
+
+/**
+ * Create a new configuration with everything set to default or false
+ * Returns NULL on error.
+ */
+flib_cfg *flib_cfg_create(flib_cfg_meta *meta, const char *schemeName);
+
+/**
+ * Create a copy of the scheme. Returns NULL on error or if NULL was passed.
+ */
+flib_cfg *flib_cfg_copy(flib_cfg *cfg);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_cfg *flib_cfg_retain(flib_cfg *cfg);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_cfg_release(flib_cfg* cfg);
+
+#endif /* CFG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,2 @@
+#include "gamesetup.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,25 @@
+/**
+ * A complete game configuration that contains all settings for a
+ * local or networked game.
+ *
+ * It should be noted that the meta-configuration is not included.
+ */
+
+#ifndef MODEL_GAMESETUP_H_
+#define MODEL_GAMESETUP_H_
+
+#include "cfg.h"
+#include "weapon.h"
+#include "map.h"
+#include "team.h"
+
+typedef struct {
+    char *seed;						// required
+    char *script;					// optional
+    flib_cfg *gamescheme;			// optional
+    flib_map *map;					// optional
+	flib_team **teams;				// optional
+	int teamcount;
+} flib_gamesetup;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,113 @@
+#include "map.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+#include "../util/refcounter.h"
+
+#include <stdlib.h>
+
+static void flib_map_destroy(flib_map *map) {
+	if(map) {
+		free(map->drawData);
+		free(map->name);
+		free(map->theme);
+		free(map);
+	}
+}
+
+flib_map *flib_map_create_regular(const char *theme, int templateFilter) {
+	flib_map *result = NULL;
+	if(!theme) {
+		flib_log_e("null parameter in flib_map_create_regular");
+	} else {
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		if(newmap) {
+			newmap->_referenceCount = 1;
+			newmap->mapgen = MAPGEN_REGULAR;
+			newmap->templateFilter = templateFilter;
+			newmap->theme = flib_strdupnull(theme);
+			if(newmap->theme) {
+				result = newmap;
+				newmap = NULL;
+			}
+		}
+		flib_map_destroy(newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_create_maze(const char *theme, int mazeSize) {
+	flib_map *result = NULL;
+	if(!theme) {
+		flib_log_e("null parameter in flib_map_create_maze");
+	} else {
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		if(newmap) {
+			newmap->_referenceCount = 1;
+			newmap->mapgen = MAPGEN_MAZE;
+			newmap->mazeSize = mazeSize;
+			newmap->theme = flib_strdupnull(theme);
+			if(newmap->theme) {
+				result = newmap;
+				newmap = NULL;
+			}
+		}
+		flib_map_destroy(newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_create_named(const char *name) {
+	flib_map *result = NULL;
+	if(!name) {
+		flib_log_e("null parameter in flib_map_create_named");
+	} else {
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		if(newmap) {
+			newmap->_referenceCount = 1;
+			newmap->mapgen = MAPGEN_NAMED;
+			newmap->name = flib_strdupnull(name);
+			if(newmap->name) {
+				result = newmap;
+				newmap = NULL;
+			}
+		}
+		flib_map_destroy(newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize) {
+	flib_map *result = NULL;
+	if(!theme || !drawData) {
+		flib_log_e("null parameter in flib_map_create_named");
+	} else {
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
+		if(newmap) {
+			newmap->_referenceCount = 1;
+			newmap->mapgen = MAPGEN_DRAWN;
+			newmap->drawData = flib_bufdupnull(drawData, drawDataSize);
+			newmap->drawDataSize = drawDataSize;
+			if(newmap->drawData) {
+				result = newmap;
+				newmap = NULL;
+			}
+		}
+		flib_map_destroy(newmap);
+	}
+	return result;
+}
+
+flib_map *flib_map_retain(flib_map *map) {
+	if(map) {
+		flib_retain(&map->_referenceCount, "flib_map");
+	}
+	return map;
+}
+
+void flib_map_release(flib_map *map) {
+	if(map && flib_release(&map->_referenceCount, "flib_map")) {
+		flib_map_destroy(map);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,94 @@
+/**
+ * Data structure for defining a map. Note that most maps also depend on the
+ * random seed passed to the engine, if you store that in addition to the
+ * flib_map structure you have the whole recipe to exactly recreate a particular
+ * map. For named maps, you also need the corresponding files.
+ */
+
+#ifndef MODEL_MAP_H_
+#define MODEL_MAP_H_
+
+#include <stdint.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
+
+typedef struct {
+	int _referenceCount;
+	int mapgen;				// Always one of the MAPGEN_ constants
+	char *theme;			// Used for all except MAPGEN_NAMED
+	char *name;				// Used for MAPGEN_NAMED
+	uint8_t *drawData;		// Used for MAPGEN_DRAWN
+	int drawDataSize;		// Used for MAPGEN_DRAWN
+	int templateFilter;		// Used for MAPGEN_REGULAR
+	int mazeSize;			// Used for MAPGEN_MAZE
+} 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 *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 *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 *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 *theme, const uint8_t *drawData, int drawDataSize);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_map *flib_map_retain(flib_map *map);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_map_release(flib_map *map);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,229 @@
+#include "schemelist.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+static void flib_schemelist_destroy(flib_schemelist *list) {
+	if(list) {
+		for(int i=0; i<list->schemeCount; i++) {
+			flib_cfg_release(list->schemes[i]);
+		}
+		free(list);
+	}
+}
+
+static char *makePrefixedName(int schemeIndex, const char *settingName) {
+	return flib_asprintf("%i\\%s", schemeIndex, settingName);
+}
+
+static int readSettingsFromIni(flib_ini *ini, flib_cfg *scheme, int index) {
+	flib_cfg_meta *meta = scheme->meta;
+	bool error = false;
+	for(int i=0; i<meta->settingCount && !error; i++) {
+		char *key = makePrefixedName(index, meta->settings[i].name);
+		if(!key) {
+			error = true;
+		} else if(flib_ini_get_int_opt(ini, &scheme->settings[i], key, 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_cfg *scheme, int index) {
+	flib_cfg_meta *meta = scheme->meta;
+	bool error = false;
+	for(int i=0; i<meta->modCount && !error; i++) {
+		char *key = makePrefixedName(index, 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_cfg *readSchemeFromIni(flib_cfg_meta *meta, flib_ini *ini, int index) {
+	flib_cfg *result = NULL;
+	char *schemeNameKey = makePrefixedName(index+1, "name");
+	if(schemeNameKey) {
+		char *schemeName = NULL;
+		if(!flib_ini_get_str_opt(ini, &schemeName, schemeNameKey, "Unnamed")) {
+			flib_cfg *scheme = flib_cfg_create(meta, schemeName);
+			if(scheme) {
+				if(!readSettingsFromIni(ini, scheme, index) && !readModsFromIni(ini, scheme, index)) {
+					result = flib_cfg_retain(scheme);
+				}
+			}
+			flib_cfg_release(scheme);
+		}
+		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(flib_cfg_meta *meta, const char *filename) {
+	flib_schemelist *list = NULL;
+	if(!meta || !filename) {
+		flib_log_e("null parameter in flib_schemelist_from_ini");
+		return 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_cfg *scheme = readSchemeFromIni(meta, ini, i);
+		if(!scheme || flib_schemelist_insert(list, scheme, i)) {
+			flib_cfg_release(scheme);
+			flib_log_e("Error reading scheme %i from config file %s.", i, filename);
+			return fromIniHandleError(list, ini);
+		}
+		flib_cfg_release(scheme);
+	}
+
+
+	flib_ini_destroy(ini);
+	return list;
+}
+
+static int writeSchemeToIni(flib_cfg *scheme, flib_ini *ini, int index) {
+	flib_cfg_meta *meta = scheme->meta;
+	bool error = false;
+
+	char *key = makePrefixedName(index+1, "name");
+	error |= !key || flib_ini_set_str(ini, key, scheme->schemeName);
+	free(key);
+
+	for(int i=0; i<meta->modCount && !error; i++) {
+		char *key = makePrefixedName(index+1, meta->mods[i].name);
+		error |= !key || flib_ini_set_bool(ini, key, scheme->mods[i]);
+		free(key);
+	}
+
+	for(int i=0; i<meta->settingCount && !error; i++) {
+		char *key = makePrefixedName(index+1, 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(!filename || !schemes) {
+		flib_log_e("null parameter in flib_schemelist_to_ini");
+	} else {
+		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_schemelist_retain(flib_calloc(1, sizeof(flib_schemelist)));
+}
+
+flib_schemelist *flib_schemelist_retain(flib_schemelist *list) {
+	if(list) {
+		flib_retain(&list->_referenceCount, "flib_schemelist");
+	}
+	return list;
+}
+
+void flib_schemelist_release(flib_schemelist *list) {
+	if(list && flib_release(&list->_referenceCount, "flib_schemelist")) {
+		flib_schemelist_destroy(list);
+	}
+}
+
+flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name) {
+	if(list && name) {
+		for(int i=0; i<list->schemeCount; i++) {
+			if(!strcmp(name, list->schemes[i]->schemeName)) {
+				return list->schemes[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+int flib_schemelist_insert(flib_schemelist *list, flib_cfg *cfg, int pos) {
+	int result = -1;
+	if(!list || !cfg || pos < 0 || pos > list->schemeCount) {
+		flib_log_e("Invalid parameter in flib_schemelist_insert");
+	} else {
+		flib_cfg **newSchemes = flib_realloc(list->schemes, (list->schemeCount+1)*sizeof(*list->schemes));
+		if(newSchemes) {
+			list->schemes = newSchemes;
+			memmove(list->schemes+pos+1, list->schemes+pos, (list->schemeCount-pos)*sizeof(*list->schemes));
+			list->schemes[pos] = flib_cfg_retain(cfg);
+			list->schemeCount++;
+			result = 0;
+		}
+	}
+	return result;
+}
+
+int flib_schemelist_delete(flib_schemelist *list, int pos) {
+	int result = -1;
+	if(!list || pos < 0 || pos >= list->schemeCount) {
+		flib_log_e("Invalid parameter in flib_schemelist_delete");
+	} else {
+		flib_cfg_release(list->schemes[pos]);
+		memmove(list->schemes+pos, list->schemes+pos+1, (list->schemeCount-(pos+1))*sizeof(*list->schemes));
+		list->schemes[list->schemeCount-1] = NULL;
+		list->schemeCount--;
+
+		// If the realloc fails, just keep using the old buffer...
+		flib_cfg **newSchemes = flib_realloc(list->schemes, list->schemeCount*sizeof(*list->schemes));
+		if(newSchemes || list->schemeCount==1) {
+			list->schemes = newSchemes;
+		}
+		result = 0;
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,67 @@
+/**
+ * Functions for managing a list of schemes.
+ * This is in here because the scheme config file of the QtFrontend (which we are staying compatble 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 "cfg.h"
+
+typedef struct {
+	int _referenceCount;
+	int schemeCount;
+	flib_cfg **schemes;
+} flib_schemelist;
+
+/**
+ * Load a list of configurations from the ini file.
+ * Returns NULL on error.
+ */
+flib_schemelist *flib_schemelist_from_ini(flib_cfg_meta *meta, 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).
+ * The scheme is retained automatically.
+ * Returns 0 on success.
+ */
+int flib_schemelist_insert(flib_schemelist *list, flib_cfg *cfg, int pos);
+
+/**
+ * Delete a cfg from the list at position pos, moving down all higher schemes.
+ * The scheme is released automatically.
+ * Returns 0 on success.
+ */
+int flib_schemelist_delete(flib_schemelist *list, int pos);
+
+/**
+ * Find the scheme with a specific name
+ */
+flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_schemelist *flib_schemelist_retain(flib_schemelist *list);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_schemelist_release(flib_schemelist *list);
+
+
+#endif /* SCHEMELIST_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,223 @@
+#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) {
+	flib_team *result = flib_calloc(1, sizeof(flib_team));
+	flib_ini *ini = NULL;
+
+	if(!filename) {
+		flib_log_e("null parameter in flib_team_from_ini");
+		return from_ini_handleError(result, ini);
+	}
+
+	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;
+}
+
+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(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(!filename || !team) {
+		flib_log_e("null parameter in flib_team_to_ini");
+	} else {
+		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;
+}
+
+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_release(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->hash);
+		free(team);
+	}
+}
+
+void flib_team_set_weaponset(flib_team *team, flib_weaponset *set) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			flib_weaponset_release(team->hogs[i].weaponset);
+			team->hogs[i].weaponset = flib_weaponset_retain(set);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,95 @@
+#ifndef TEAM_H_
+#define TEAM_H_
+
+#include "weapon.h"
+#include "../hwconsts.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TEAM_DEFAULT_HOGNAME "Hog"
+#define TEAM_DEFAULT_HAT "NoHat"
+#define TEAM_DEFAULT_DIFFICULTY 0
+#define TEAM_DEFAULT_HEALTH 100
+
+// TODO default bindings?
+
+typedef struct {
+	char *action;
+	char *binding;
+} flib_binding;
+
+typedef struct {
+	char *name;
+	char *hat;
+
+	// 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;
+
+	// 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;
+	char *fort;
+	char *voicepack;
+	char *flag;
+
+	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
+	uint32_t color;
+	int hogsInGame;
+	bool remoteDriven;
+	char *hash; // TODO calculate at the appropriate time... i.e. before trying to send the config to the engine
+} flib_team;
+
+/**
+ * Returns a new team, or NULL on error. name must not be NULL.
+ *
+ * The new team is pre-filled with default settings (see hwconsts.h)
+ */
+flib_team *flib_team_create(const char *name);
+
+/**
+ * Loads a team, returns NULL on error.
+ */
+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
+ */
+void flib_team_set_weaponset(flib_team *team, flib_weaponset *set);
+
+void flib_team_destroy(flib_team *team);
+
+#endif /* TEAM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,238 @@
+#include "weapon.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+static void flib_weaponset_destroy(flib_weaponset *cfg) {
+	if(cfg) {
+		free(cfg->name);
+		free(cfg);
+	}
+}
+
+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(!name) {
+		flib_log_e("null parameter in flib_weaponset_create_str");
+	} else {
+		flib_weaponset *newSet = flib_weaponset_retain(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 = flib_weaponset_retain(newSet);
+			}
+		}
+		flib_weaponset_release(newSet);
+	}
+	return result;
+}
+
+flib_weaponset *flib_weaponset_retain(flib_weaponset *weaponset) {
+	if(weaponset) {
+		flib_retain(&weaponset->_referenceCount, "flib_weaponset");
+	}
+	return weaponset;
+}
+
+void flib_weaponset_release(flib_weaponset *weaponset) {
+	if(weaponset && flib_release(&weaponset->_referenceCount, "flib_weaponset")) {
+		flib_weaponset_destroy(weaponset);
+	}
+}
+
+static void flib_weaponsetlist_destroy(flib_weaponsetlist *list) {
+	if(list) {
+		for(int i=0; i<list->weaponsetCount; i++) {
+			flib_weaponset_release(list->weaponsets[i]);
+		}
+		free(list);
+	}
+}
+
+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);
+
+	if(decodedKeyname) {
+		flib_weaponset *set = flib_weaponset_create(decodedKeyname);
+		if(set) {
+			char *value = NULL;
+			if(!flib_ini_get_str(ini, &value, keyname)) {
+				int fieldlen = strlen(value)/4;
+				setField(set->loadout, value, fieldlen, false);
+				setField(set->crateprob, value+1*fieldlen, fieldlen, true);
+				setField(set->delay, value+2*fieldlen, fieldlen, true);
+				setField(set->crateammo, value+3*fieldlen, fieldlen, true);
+				result = flib_weaponsetlist_insert(list, set, list->weaponsetCount);
+			}
+			free(value);
+		}
+		flib_weaponset_release(set);
+	}
+
+	free(keyname);
+	free(decodedKeyname);
+	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(!filename) {
+		flib_log_e("null parameter in flib_weaponsetlist_from_ini");
+	} else {
+		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 *list = flib_weaponsetlist_create();
+			if(list) {
+				if(!fillWeaponsetsFromIni(list, ini)) {
+					result = flib_weaponsetlist_retain(list);
+				}
+			}
+			flib_weaponsetlist_release(list);
+		}
+		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(!filename || !list) {
+		flib_log_e("null parameter in flib_weaponsetlist_to_ini");
+	} else {
+		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_weaponsetlist_retain(flib_calloc(1, sizeof(flib_weaponsetlist)));
+}
+
+int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *weaponset, int pos) {
+	int result = -1;
+	if(!list || !weaponset || pos < 0 || pos > list->weaponsetCount) {
+		flib_log_e("Invalid parameter in flib_weaponsetlist_insert");
+	} else {
+		flib_weaponset **newSets = flib_realloc(list->weaponsets, (list->weaponsetCount+1)*sizeof(*list->weaponsets));
+		if(newSets) {
+			list->weaponsets = newSets;
+			memmove(list->weaponsets+pos+1, list->weaponsets+pos, (list->weaponsetCount-pos)*sizeof(*list->weaponsets));
+			list->weaponsets[pos] = flib_weaponset_retain(weaponset);
+			list->weaponsetCount++;
+			result = 0;
+		}
+	}
+	return result;
+}
+
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos) {
+	int result = -1;
+	if(!list || pos < 0 || pos >= list->weaponsetCount) {
+		flib_log_e("Invalid parameter in flib_weaponsetlist_delete");
+	} else {
+		flib_weaponset_release(list->weaponsets[pos]);
+		memmove(list->weaponsets+pos, list->weaponsets+pos+1, (list->weaponsetCount-(pos+1))*sizeof(*list->weaponsets));
+		list->weaponsets[list->weaponsetCount-1] = NULL;
+		list->weaponsetCount--;
+
+		// If the realloc fails, just keep using the old buffer...
+		flib_weaponset **newSets = flib_realloc(list->weaponsets, list->weaponsetCount*sizeof(*list->weaponsets));
+		if(newSets || list->weaponsetCount==1) {
+			list->weaponsets = newSets;
+		}
+		result = 0;
+	}
+	return result;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_retain(flib_weaponsetlist *list) {
+	if(list) {
+		flib_retain(&list->_referenceCount, "flib_weaponsetlist");
+	}
+	return list;
+}
+
+void flib_weaponsetlist_release(flib_weaponsetlist *list) {
+	if(list && flib_release(&list->_referenceCount, "flib_weaponsetlist")) {
+		flib_weaponsetlist_destroy(list);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,90 @@
+#ifndef MODEL_WEAPON_H_
+#define MODEL_WEAPON_H_
+
+#include "../hwconsts.h"
+
+/**
+ * These values are all in the range 0..9
+ *
+ * For loadout, 9 means inifinite ammo.
+ * For the other setting, 9 is invalid.
+ */
+typedef struct {
+	int _referenceCount;
+	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 _referenceCount;
+	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);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_weaponset *flib_weaponset_retain(flib_weaponset *weaponset);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_weaponset_release(flib_weaponset *weaponset);
+
+/**
+ * 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();
+
+/**
+ * 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).
+ * The weaponset is retained automatically.
+ * 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 released automatically.
+ * Returns 0 on success.
+ */
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_weaponsetlist *flib_weaponsetlist_retain(flib_weaponsetlist *list);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_weaponsetlist_release(flib_weaponsetlist *list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,248 @@
+#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) {
+	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(!net) {
+		flib_log_e("null parameter in flib_netbase_connected");
+		return false;
+	} else if(net->sock) {
+		return true;
+	} else {
+		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(!net) {
+		flib_log_e("null parameter in flib_netbase_recv_message");
+		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(!net || (!data && len>0)) {
+		flib_log_e("null parameter in flib_netbase_send_raw");
+		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, flib_netmsg *msg) {
+	if(!net || !msg) {
+		flib_log_e("null parameter in flib_netbase_send_message");
+		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(!net || !format) {
+		flib_log_e("null parameter in flib_netbase_sendf");
+	} else {
+		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(!msg) {
+		flib_log_e("null parameter in flib_netmsg_append_part");
+	} else {
+		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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * 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, 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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,469 @@
+/*
+ * 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.h"
+#include "netbase.h"
+#include "netprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+struct _flib_netconn {
+	flib_netbase *netBase;
+	char *playerName;
+
+	int netconnState;	// One of the NETCONN_STATE constants
+
+	void (*onErrorCb)(void* context, int errorCode, const char *errorMsg);
+	void *onErrorCtx;
+
+	void (*onConnectedCb)(void *context, const char *serverMessage);
+	void *onConnectedCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onError(void* context, int errorCode, const char *errormsg) {}
+static void defaultCallback_onConnected(void *context, const char *serverMessage) {}
+
+static void clearCallbacks(flib_netconn *conn) {
+	conn->onErrorCb = &defaultCallback_onError;
+	conn->onConnectedCb = &defaultCallback_onConnected;
+}
+
+
+flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port) {
+	flib_netconn *result = NULL;
+	flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
+	if(newConn) {
+		newConn->netconnState = NETCONN_STATE_AWAIT_CONNECTED;
+		newConn->running = false;
+		newConn->destroyRequested = false;
+		clearCallbacks(newConn);
+		newConn->netBase = flib_netbase_create(host, port);
+		newConn->playerName = flib_strdupnull(playerName);
+		if(newConn->netBase && newConn->playerName) {
+			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.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_netbase_destroy(conn->netBase);
+			free(conn->playerName);
+			free(conn);
+		}
+	}
+}
+
+void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errorMsg), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onError");
+	} else {
+		conn->onErrorCb = callback ? callback : &defaultCallback_onError;
+		conn->onErrorCtx = context;
+	}
+}
+
+void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_onConnected");
+	} else {
+		conn->onConnectedCb = callback ? callback : &defaultCallback_onConnected;
+		conn->onConnectedCtx = context;
+	}
+}
+
+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;
+		}
+
+		const char *cmd = netmsg->parts[0];
+
+	    if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
+	    	free(conn->playerName);
+	    	conn->playerName = flib_strdupnull(netmsg->parts[1]);
+	    	if(!conn->playerName) {
+	    		// TODO handle error
+	    	}
+	    	// TODO callback?
+	    } else if (!strcmp(cmd, "PROTO")) {
+	        // The server just echoes this back apparently
+		} else if (!strcmp(cmd, "ERROR")) {
+			// TODO: onErrorMessage?
+			if (netmsg->partCount == 2) {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
+			} else {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Error");
+			}
+		} else if(!strcmp(cmd, "WARNING")) {
+			// TODO: onWarnMessage?
+			if (netmsg->partCount == 2) {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]);
+			} else {
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Warning");
+			}
+	    } else if(!strcmp(cmd, "CONNECTED")) {
+			if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
+				flib_log_w("Server too old");
+				flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
+				// TODO actually disconnect?
+				conn->netconnState = NETCONN_STATE_DISCONNECTED;
+				conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_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);
+				conn->netconnState = NETCONN_STATE_LOBBY;
+			}
+		} 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 {
+	        	// TODO
+				//QStringList tmp = lst;
+				//tmp.removeFirst();
+				//m_roomsListModel->setRoomsList(tmp);
+	        }
+	    } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
+	        if(netmsg->partCount < 2) {
+	        	flib_log_w("Net: Empty SERVERMESSAGE message");
+	        } else {
+	        	// TODO
+	        	// emit serverMessage(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "CHAT")) {
+	        if(netmsg->partCount < 3) {
+	        	flib_log_w("Net: Empty CHAT message");
+	        } else {
+	        	// TODO
+				// if (netClientState == InLobby)
+				// 	emit chatStringLobby(lst[1], HWProto::formatChatMsgForFrontend(lst[2]));
+				// else
+				//	emit chatStringFromNet(HWProto::formatChatMsg(lst[1], lst[2]));
+	        }
+	    } else if (!strcmp(cmd, "INFO")) {
+	        if(netmsg->partCount < 5) {
+	        	flib_log_w("Net: Malformed INFO message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				if (netClientState == InLobby)
+//					emit chatStringLobby(tmp.join("\n").prepend('\x01'));
+//				else
+//					emit chatStringFromNet(tmp.join("\n").prepend('\x01'));
+	        }
+	    } else if(!strcmp(cmd, "SERVER_VARS")) {
+	    	// TODO
+//	        QStringList tmp = lst;
+//	        tmp.removeFirst();
+//	        while (tmp.size() >= 2)
+//	        {
+//	            if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]);
+//	            else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]);
+//	            else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt());
+//
+//	            tmp.removeFirst();
+//	            tmp.removeFirst();
+//	        }
+	    } 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) {
+							if (!strcmp(conn->playerName, netmsg->parts[i])) {
+								// TODO
+								// if (isChief && !setFlag) ToggleReady();
+								// else emit setMyReadyStatus(setFlag);
+							}
+							// TODO
+							// emit setReadyStatus(lst[i], 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) {
+	            flib_log_w("Net: Bad ADD_TEAM message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				emit AddNetTeam(tmp);
+	        }
+	    } else if (!strcmp(cmd, "REMOVE_TEAM")) {
+	        if(netmsg->partCount != 2) {
+	            flib_log_w("Net: Bad REMOVETEAM message");
+	        } else {
+	        	// TODO
+	        	// emit RemoveNetTeam(HWTeam(lst[1]));
+	        }
+	    } else if(!strcmp(cmd, "ROOMABANDONED")) {
+	        conn->netconnState = NETCONN_STATE_LOBBY;
+//	        TODO
+//	        askRoomsList();
+//	        emit LeftRoom(tr("Room destroyed"));
+	    } else if(!strcmp(cmd, "KICKED")) {
+	    	conn->netconnState = NETCONN_STATE_LOBBY;
+//	    	TODO
+//	        askRoomsList();
+//	        emit LeftRoom(tr("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;
+//						TODO
+//						emit EnteredGame();
+//						emit roomMaster(isChief);
+//						if (isChief)
+//							emit configAsked();
+					}
+
+//					TODO
+//					emit nickAdded(lst[i], isChief && !isMe));
+//					emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[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_LOBBY;
+						// TODO
+//						RawSendNet(QString("LIST"));
+//						emit connected();
+					}
+					// TODO
+//					emit nickAddedLobby(lst[i], false);
+//					emit chatStringLobby(lst[i], tr("%1 *** %2 has joined").arg('\x03').arg("|nick|"));
+				}
+	        }
+	    } else if(!strcmp(cmd, "LEFT")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad LEFT message");
+	        } else {
+	        	// TODO
+//				emit nickRemoved(lst[1]);
+//				if (netmsg->partCount < 3)
+//					emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+//				else
+//					emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2]));
+	        }
+	    } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
+	    	const char *subcmd = netmsg->parts[1];
+	    	if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
+	    		// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//
+//				m_roomsListModel->addRoom(tmp);
+			} else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
+				// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//
+//				QString roomName = tmp.takeFirst();
+//				m_roomsListModel->updateRoom(roomName, tmp);
+			} else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
+				// TODO
+				// m_roomsListModel->removeRoom(lst[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 {
+	        	// TODO
+//				emit nickRemovedLobby(lst[1]);
+//				if (netmsg->partCount < 3)
+//					emit chatStringLobby(tr("%1 *** %2 has left").arg('\x03').arg(lst[1]));
+//				else
+//					emit chatStringLobby(lst[1], tr("%1 *** %2 has left (%3)").arg('\x03').arg("|nick|", lst[2]));
+	        }
+	    } else if (!strcmp(cmd, "RUN_GAME")) {
+	        conn->netconnState = NETCONN_STATE_INGAME;
+	        // TODO
+	        // emit AskForRunGame();
+	    } else if (!strcmp(cmd, "ASKPASSWORD")) {
+	    	// TODO
+	        // emit AskForPassword(mynick);
+	    } 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 {
+					// TODO
+					// handleNotice(n);
+				}
+	        }
+	    } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
+	        if (netmsg->partCount != 2) {
+	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	        } else {
+	        	// TODO
+	        	// emit TeamAccepted(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "CFG")) {
+	        if(netmsg->partCount < 3) {
+	            flib_log_w("Net: Bad CFG message");
+	        } else {
+	        	// TODO
+//				QStringList tmp = lst;
+//				tmp.removeFirst();
+//				tmp.removeFirst();
+//				if (lst[1] == "SCHEME")
+//					emit netSchemeConfig(tmp);
+//				else
+//					emit paramChanged(lst[1], tmp);
+	        }
+	    } else if (!strcmp(cmd, "HH_NUM")) {
+	        if (netmsg->partCount != 3) {
+	            flib_log_w("Net: Bad TEAM_ACCEPTED message");
+	        } else {
+	        	// TODO
+//				HWTeam tmptm(lst[1]);
+//				tmptm.setNumHedgehogs(lst[2].toUInt());
+//				emit hhnumChanged(tmptm);
+	        }
+	    } else if (!strcmp(cmd, "TEAM_COLOR")) {
+	        if (netmsg->partCount != 3) {
+	            flib_log_w("Net: Bad TEAM_COLOR message");
+	        } else {
+	        	// TODO
+//				HWTeam tmptm(lst[1]);
+//				tmptm.setColor(lst[2].toInt());
+//				emit teamColorChanged(tmptm);
+	        }
+	    } else if (!strcmp(cmd, "EM")) {
+	        if(netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad EM message");
+	        } else {
+	        	// TODO
+//				for(int i = 1; i < netmsg->partCount; ++i) {
+//					QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
+//					emit FromNet(em);
+//				}
+	        }
+	    } else if (!strcmp(cmd, "BYE")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad BYE message");
+	        } else {
+				if (!strcmp(netmsg->parts[1], "Authentication failed")) {
+					// TODO
+					//emit AuthFailed();
+				}
+				// TODO
+//				m_game_connected = false;
+//				Disconnect();
+//				emit disconnected(lst[1]);
+	        }
+	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
+	    	// TODO
+	        // emit adminAccess(true);
+	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
+	        if (netmsg->partCount < 2) {
+	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
+	        } else {
+	        	// TODO
+//				isChief = (lst[1] != "0");
+//				emit roomMaster(isChief);
+	        }
+	    } else {
+	    	flib_log_w("Unknown server command: %s", cmd);
+	    }
+		flib_netmsg_destroy(netmsg);
+	}
+}
+
+void flib_netconn_tick(flib_netconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_netconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_netconn_tick from a callback");
+	} else if(conn->netconnState == NETCONN_STATE_DISCONNECTED) {
+		flib_log_w("Call to flib_netconn_tick, but we are already done.");
+	} else {
+		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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,45 @@
+#ifndef NETCONN_H_
+#define NETCONN_H_
+
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define NETCONN_STATE_AWAIT_CONNECTED 0
+#define NETCONN_STATE_LOBBY 1
+#define NETCONN_STATE_ROOM 2
+#define NETCONN_STATE_INGAME 3
+#define NETCONN_STATE_DISCONNECTED 10
+
+#define NETCONN_ERROR_SERVER_TOO_OLD 1
+#define NETCONN_ERROR_FROM_SERVER 2
+
+struct _flib_netconn;
+typedef struct _flib_netconn flib_netconn;
+
+flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port);
+void flib_netconn_destroy(flib_netconn *conn);
+
+/**
+ * This is called when we can't stay connected due to a problem, e.g. because the
+ * server version is too old, or we are unexpectedly disconnected.
+ *
+ * Once this callback has been called, you should destroy the flib_netconn.
+ */
+void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errormsg), void* context);
+
+/**
+ * This is called when we receive a CONNECTED message from the server, which should be the first
+ * message arriving from the server.
+ */
+void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_netconn_tick(flib_netconn *conn);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,7 @@
+#ifndef NETPROTOCOL_H_
+#define NETPROTOCOL_H_
+
+
+
+
+#endif /* NETPROTOCOL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/resources/metasettings.ini	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,234 @@
+[mod0]
+name=fortsmode
+bitmaskindex=12
+
+[mod1]
+name=divteams
+bitmaskindex=4
+
+[mod2]
+name=solidland
+bitmaskindex=2
+
+[mod3]
+name=border
+bitmaskindex=3
+
+[mod4]
+name=lowgrav
+bitmaskindex=5
+
+[mod5]
+name=laser
+bitmaskindex=6
+
+[mod6]
+name=invulnerability
+bitmaskindex=7
+
+[mod7]
+name=resethealth
+bitmaskindex=8
+
+[mod8]
+name=vampiric
+bitmaskindex=9
+
+[mod9]
+name=karma
+bitmaskindex=10
+
+[mod10]
+name=artillery
+bitmaskindex=11
+
+[mod11]
+name=randomorder
+bitmaskindex=13
+
+[mod12]
+name=king
+bitmaskindex=14
+
+[mod13]
+name=placehog
+bitmaskindex=15
+
+[mod14]
+name=sharedammo
+bitmaskindex=16
+
+[mod15]
+name=disablegirders
+bitmaskindex=17
+
+[mod16]
+name=disablelandobjects
+bitmaskindex=18
+
+[mod17]
+name=aisurvival
+bitmaskindex=19
+
+[mod18]
+name=infattack
+bitmaskindex=20
+
+[mod19]
+name=resetweps
+bitmaskindex=21
+
+[mod20]
+name=perhogammo
+bitmaskindex=22
+
+[mod21]
+name=disablewind
+bitmaskindex=23
+
+[mod22]
+name=morewind
+bitmaskindex=24
+
+[mod23]
+name=tagteam
+bitmaskindex=25
+
+[mod24]
+name=bottomborder
+bitmaskindex=26
+
+
+[setting0]
+name=damagefactor
+times1000=false
+command=e$damagepct
+maxmeansinfinity=false
+min=10
+max=300
+default=100
+
+[setting1]
+name=turntime
+times1000=true
+command=e$turntime
+maxmeansinfinity=true
+min=1
+max=9999
+default=45
+
+[setting2]
+name=health
+times1000=false
+maxmeansinfinity=false
+min=50
+max=200
+default=100
+
+[setting3]
+name=suddendeath
+times1000=false
+command=e$sd_turns
+maxmeansinfinity=true
+min=0
+max=50
+default=15
+
+[setting4]
+name=caseprobability
+times1000=false
+command=e$casefreq
+maxmeansinfinity=false
+min=0
+max=9
+default=5
+
+[setting5]
+name=minestime
+times1000=true
+command=e$minestime
+maxmeansinfinity=false
+min=-1
+max=5
+default=3
+
+[setting6]
+name=minesnum
+times1000=false
+command=e$minesnum
+maxmeansinfinity=false
+min=0
+max=80
+default=4
+
+[setting7]
+name=minedudpct
+times1000=false
+command=e$minedudpct
+maxmeansinfinity=false
+min=0
+max=100
+default=0
+
+[setting8]
+name=explosives
+times1000=false
+command=e$explosives
+maxmeansinfinity=false
+min=0
+max=40
+default=2
+
+[setting9]
+name=healthprobability
+times1000=false
+command=e$healthprob
+maxmeansinfinity=false
+min=0
+max=100
+default=35
+
+[setting10]
+name=healthcaseamount
+times1000=false
+command=e$hcaseamount
+maxmeansinfinity=false
+min=0
+max=200
+default=25
+
+[setting11]
+name=waterrise
+times1000=false
+command=e$waterrise
+maxmeansinfinity=false
+min=0
+max=100
+default=47
+
+[setting12]
+name=healthdecrease
+times1000=false
+command=e$healthdec
+maxmeansinfinity=false
+min=0
+max=100
+default=5
+
+[setting13]
+name=ropepct
+times1000=false
+command=e$ropepct
+maxmeansinfinity=false
+min=25
+max=999
+default=100
+
+[setting14]
+name=getawaytime
+times1000=false
+command=e$getawaytime
+maxmeansinfinity=false
+min=0
+max=999
+default=100
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,180 @@
+#include "socket.h"
+#include "util/logging.h"
+#include "util/util.h"
+#include <stdlib.h>
+#include <SDL_net.h>
+#include <time.h>
+
+typedef struct _flib_tcpsocket {
+	TCPsocket sock;
+	SDLNet_SocketSet sockset;
+} _flib_tcpsocket;
+
+typedef struct _flib_acceptor {
+	TCPsocket sock;
+	uint16_t port;
+} _flib_acceptor;
+
+static uint32_t get_peer_ip(TCPsocket sock) {
+	IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
+	return SDLNet_Read32(&addr->host);
+}
+
+static bool connection_is_local(TCPsocket sock) {
+	return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
+}
+
+static flib_tcpsocket *flib_socket_create(TCPsocket sdlsock) {
+	flib_tcpsocket *result = flib_calloc(1, sizeof(_flib_tcpsocket));
+	if(!result) {
+		return NULL;
+	}
+	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);
+		return NULL;
+	}
+
+	SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
+	return result;
+}
+
+flib_acceptor *flib_acceptor_create(uint16_t port) {
+	flib_acceptor *result = flib_calloc(1, sizeof(_flib_acceptor));
+	if(!result) {
+		return NULL;
+	}
+
+	IPaddress addr;
+	addr.host = INADDR_ANY;
+
+	if(port > 0) {
+		result->port = port;
+		SDLNet_Write16(port, &addr.port);
+		result->sock = SDLNet_TCP_Open(&addr);
+		if(result->sock) {
+			return result;
+		} else {
+			flib_log_e("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
+			free(result);
+			return NULL;
+		}
+	} 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));
+		rand();
+		for(int i=0; i<1000; i++) {
+			// IANA suggests using ports in the range 49152-65535 for things like this
+			result->port = 49152+(rand()%(65535-49152));
+			SDLNet_Write16(result->port, &addr.port);
+			result->sock = SDLNet_TCP_Open(&addr);
+			if(result->sock) {
+				return result;
+			} else {
+				flib_log_w("Unable to listen on port %u: %s", (unsigned)result->port, SDLNet_GetError());
+			}
+		}
+		flib_log_e("Unable to listen on a random unused port.");
+		free(result);
+		return NULL;
+	}
+}
+
+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) {
+	if(!acceptor) {
+		flib_log_e("Call to flib_socket_accept with acceptor==null");
+		return NULL;
+	}
+	flib_tcpsocket *result = NULL;
+	TCPsocket sock = NULL;
+	while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
+		if(localOnly && !connection_is_local(sock)) {
+			flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock)));
+			SDLNet_TCP_Close(sock);
+		} else {
+			result = flib_socket_create(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 = flib_socket_create(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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * 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>
+
+struct _flib_tcpsocket;
+typedef struct _flib_tcpsocket flib_tcpsocket;
+
+struct _flib_acceptor;
+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.
+ *
+ * Can return 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.
+ * If the acceptor is already NULL, nothing happens.
+ */
+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.
+ * If the socket is already NULL, nothing happens.
+ */
+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);
+
+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/test.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,244 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "util/buffer.h"
+#include "model/map.h"
+#include "model/weapon.h"
+#include "model/schemelist.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+#include "net/netbase.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+
+// Callback function that will be called when the map is rendered
+static void handleMapSuccess(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++) {
+		for(int x=0; x<MAPIMAGE_WIDTH; x++) {
+			int pixelnum = x + y*MAPIMAGE_WIDTH;
+			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+			printf(pixel ? "#" : " ");
+		}
+		printf("\n");
+	}
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn **connptr = context;
+	flib_gameconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+	fwrite(record, 1, size, file);
+	fclose(file);
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+	flib_log_e("Map rendering failed: %s", errormessage);
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void startEngineMap(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s %i landpreview", enginePath, configPath, port);
+	system(commandbuffer);
+}
+
+static void startEngineGame(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	const char *dataPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars\\Data";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", enginePath, configPath, port, dataPath);
+	flib_log_d("Starting engine with CMD: %s", commandbuffer);
+	system(commandbuffer);
+}
+
+void testMapPreview() {
+	// Create a map description and check that there was no error
+	flib_map *map = flib_map_create_maze("Jungle", MAZE_SIZE_SMALL_TUNNELS);
+	assert(map);
+
+	// Create a new connection to the engine and check that there was no error
+	flib_mapconn *mapConnection = flib_mapconn_create("This is the seed value", map);
+	assert(mapConnection);
+
+	// We don't need the map description anymore
+	flib_map_release(map);
+	map = NULL;
+
+	// Register the callback functions
+	flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+	flib_mapconn_onSuccess(mapConnection, &handleMapSuccess, &mapConnection);
+
+	// Start the engine process and tell it which port the frontlib is listening on
+	startEngineMap(flib_mapconn_getport(mapConnection));
+
+	// Usually, flib_mapconn_tick will be called in an event loop that runs several
+	// times per second. It handles I/O operations and progress, and calls
+	// callbacks when something interesting happens.
+	while(mapConnection) {
+		flib_mapconn_tick(mapConnection);
+	}
+}
+
+void testGame() {
+	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("metasettings.ini");
+	assert(metaconf);
+	flib_weaponset *weapons = flib_weaponset_create("Defaultweaps");
+	flib_schemelist *schemelist = flib_schemelist_from_ini(metaconf, "schemes.ini");
+
+	flib_gamesetup setup;
+	setup.gamescheme = flib_schemelist_find(schemelist, "Default");
+	setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+	setup.seed = "asparagus";
+	setup.script = NULL;
+	setup.teamcount = 2;
+	setup.teams = calloc(2, sizeof(flib_team*));
+	setup.teams[0] = calloc(1, sizeof(flib_team));
+	setup.teams[0]->color = 0xffff0000;
+	setup.teams[0]->flag = "australia";
+	setup.teams[0]->fort = "Plane";
+	setup.teams[0]->grave = "Bone";
+	setup.teams[0]->hogsInGame = 2;
+	setup.teams[0]->name = "Team Awesome";
+	setup.teams[0]->voicepack = "British";
+	setup.teams[0]->hogs[0].difficulty = 2;
+	setup.teams[0]->hogs[0].hat = "NoHat";
+	setup.teams[0]->hogs[0].initialHealth = 100;
+	setup.teams[0]->hogs[0].name = "Harry 120";
+	setup.teams[0]->hogs[1].difficulty = 2;
+	setup.teams[0]->hogs[1].hat = "chef";
+	setup.teams[0]->hogs[1].initialHealth = 100;
+	setup.teams[0]->hogs[1].name = "Chefkoch";
+	setup.teams[1] = flib_team_from_ini("Cave Dwellers.hwt");
+	setup.teams[1]->color = 0xFF0000F0;
+	setup.teams[1]->hogsInGame = 8;
+	flib_team_set_weaponset(setup.teams[0], weapons);
+	flib_team_set_weaponset(setup.teams[1], weapons);
+	flib_weaponset_release(weapons);
+
+	flib_gameconn *gameconn = flib_gameconn_create("Medo42", &setup, false);
+	assert(gameconn);
+
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	//flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+void testDemo() {
+	FILE *demofile = fopen("testdemo.42.hwd", "rb");
+	assert(demofile);
+	flib_vector *vec = flib_vector_create();
+	uint8_t demobuf[512];
+	int len;
+	while((len=fread(demobuf, 1, 512, demofile))>0) {
+		flib_vector_append(vec, demobuf, len);
+	}
+	fclose(demofile);
+	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+	flib_gameconn *gameconn = flib_gameconn_create_playdemo(constbuf.data, constbuf.size);
+	flib_vector_destroy(vec);
+	assert(gameconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+void testSave() {
+	FILE *demofile = fopen("testsave.42.hws", "rb");
+	assert(demofile);
+	flib_vector *vec = flib_vector_create();
+	uint8_t demobuf[512];
+	int len;
+	while((len=fread(demobuf, 1, 512, demofile))>0) {
+		flib_vector_append(vec, demobuf, len);
+	}
+	fclose(demofile);
+	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+	flib_gameconn *gameconn = flib_gameconn_create_loadgame("Medo42", constbuf.data, constbuf.size);
+	flib_vector_destroy(vec);
+	assert(gameconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+int main(int argc, char *argv[]) {
+	flib_init(0);
+	flib_log_setLevel(FLIB_LOGLEVEL_ALL);
+
+	//testMapPreview();
+	//testDemo();
+	//testSave();
+	//testGame();
+
+	flib_netbase *conn = flib_netbase_create("140.247.62.101", 46631);
+
+	while(flib_netbase_connected(conn)) {
+		flib_netmsg *msg = flib_netbase_recv_message(conn);
+		if(msg && msg->partCount>0) {
+			flib_log_i("[NET IN] %s", msg->parts[0]);
+			for(int i=1; i<msg->partCount; i++) {
+				flib_log_i("[NET IN][-] %s", msg->parts[i]);
+			}
+			if(!strcmp(msg->parts[0], "CONNECTED")) {
+				flib_netmsg *nickmsg = flib_netmsg_create();
+				flib_netmsg_append_part(nickmsg, "NICK", 4);
+				flib_netmsg_append_part(nickmsg, "Medo42_frontlib", 15);
+				flib_netmsg *protomsg = flib_netmsg_create();
+				flib_netmsg_append_part(protomsg, "PROTO", 5);
+				flib_netmsg_append_part(protomsg, "42", 2);
+				flib_netbase_send_message(conn, nickmsg);
+				flib_netbase_send_message(conn, protomsg);
+				flib_netmsg_destroy(nickmsg);
+				flib_netmsg_destroy(protomsg);
+			}
+			if(!strcmp(msg->parts[0], "SERVER_MESSAGE")) {
+				flib_netmsg *quitmsg = flib_netmsg_create();
+				flib_netmsg_append_part(quitmsg, "QUIT", 4);
+				flib_netmsg_append_part(quitmsg, "Just testing", 12);
+				flib_netbase_send_message(conn, quitmsg);
+				flib_netmsg_destroy(quitmsg);
+			}
+		}
+		flib_netmsg_destroy(msg);
+	}
+
+	flib_quit();
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,154 @@
+#include "buffer.h"
+#include "logging.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#define MIN_VECTOR_CAPACITY 16
+
+typedef struct _flib_vector {
+	void *data;
+	size_t size;
+	size_t capacity;
+} _flib_vector;
+
+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(!vec) {
+		flib_log_e("null parameter in flib_vector_resize");
+		return -1;
+	}
+
+	if(vec->capacity < newSize) {
+		// Resize exponentially for constant amortized time,
+		// But at least by as much as we need of course,
+		// and be extra careful with integer overflows...
+		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(!vec) {
+		flib_log_e("null parameter in flib_vector_append");
+		return 0;
+	}
+
+	if(len > SIZE_MAX-vec->size) {
+		return 0;
+	}
+
+	size_t oldSize = vec->size;
+	if(flib_vector_resize(vec, vec->size+len)) {
+		return 0;
+	}
+
+	memmove(((uint8_t*)vec->data) + oldSize, data, len);
+	return len;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_as_buffer");
+		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(!vec) {
+		flib_log_e("null parameter in flib_vector_as_constbuffer");
+		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(!vec) {
+		flib_log_e("null parameter in flib_vector_data");
+		return NULL;
+	} else {
+		return vec->data;
+	}
+}
+
+size_t flib_vector_size(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_size");
+		return 0;
+	} else {
+		return vec->size;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,80 @@
+#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.
+ */
+struct _flib_vector;
+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.
+ * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
+ * The vector remains unchanged if appending fails.
+ */
+int flib_vector_append(flib_vector *vec, const void *data, size_t len);
+
+/**
+ * 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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,320 @@
+#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(!filename) {
+		flib_log_e("null parameter in flib_ini_load");
+	} else {
+		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(!ini || !filename) {
+		flib_log_e("null parameter in flib_ini_save");
+	} else {
+		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(!ini || !section) {
+		flib_log_e("null parameter in flib_ini_enter_section");
+	} else {
+		if(!iniparser_find_entry(ini->inidict, 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(!ini || !section) {
+		flib_log_e("null parameter in flib_ini_create_section");
+	} else {
+		result = flib_ini_enter_section(ini, section);
+		if(result == INI_ERROR_NOTFOUND) {
+			if(iniparser_set(ini->inidict, section, NULL)) {
+				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(!ini || !outVar || !key || !ini->currentSection) {
+		flib_log_e("null parameter or no current section in flib_ini_get_str_opt");
+	} else {
+		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(!ini || !key || !value || !ini->currentSection) {
+		flib_log_e("null parameter or no current section in flib_ini_set_str");
+	} else {
+		char *dictKey = createDictKey(ini->currentSection, key);
+		if(dictKey) {
+			result = iniparser_set(ini->inidict, 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) {
+	int result = INI_ERROR_OTHER;
+	if(!ini) {
+		flib_log_e("null parameter in flib_ini_get_sectioncount");
+	} else {
+		result = iniparser_getnsec(ini->inidict);
+	}
+	return result;
+}
+
+char *flib_ini_get_sectionname(flib_ini *ini, int number) {
+	char *result = NULL;
+	if(!ini || number<0) {
+		flib_log_e("bad parameter in flib_ini_get_sectionname");
+	} else {
+		result = flib_strdupnull(iniparser_getsecname(ini->inidict, number));
+	}
+	return result;
+}
+
+int flib_ini_get_keycount(flib_ini *ini) {
+	int result = INI_ERROR_OTHER;
+	if(!ini || !ini->currentSection) {
+		flib_log_e("null parameter or no current section in flib_ini_get_keycount");
+	} else {
+		result = iniparser_getsecnkeys(ini->inidict, ini->currentSection);
+	}
+	return result;
+}
+
+char *flib_ini_get_keyname(flib_ini *ini, int number) {
+	char *result = NULL;
+	if(!ini || number<0 || !ini->currentSection) {
+		flib_log_e("bad parameter or no current section in flib_ini_get_keyname");
+	} else {
+		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	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,160 @@
+/**
+ * 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
+
+struct _flib_ini;
+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/logging.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,91 @@
+#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;
+
+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 void log_time() {
+    time_t timer;
+    char buffer[25];
+    struct tm* tm_info;
+
+    time(&timer);
+    tm_info = localtime(&timer);
+
+    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
+    fprintf(flib_log_getfile(), "%s", buffer);
+}
+
+static void flib_vflog(const char *prefix, int level, const char *fmt, va_list args) {
+	FILE *logfile = flib_log_getfile();
+	if(level >= flib_loglevel) {
+		fprintf(logfile, "%s ", prefix);
+		log_time(logfile);
+		fprintf(logfile, "  ", prefix);
+		vfprintf(logfile, fmt, args);
+		fprintf(logfile, "\n");
+		fflush(logfile);
+	}
+}
+
+void flib_log_e(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog("E", FLIB_LOGLEVEL_ERROR, fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_w(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog("W", FLIB_LOGLEVEL_WARNING, fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_i(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog("I", FLIB_LOGLEVEL_INFO, fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_d(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog("D", FLIB_LOGLEVEL_DEBUG, fmt, argp);
+	va_end(argp);
+}
+
+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;
+}
+
+bool flib_log_isActive(int level) {
+	return level >= flib_log_getLevel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,30 @@
+#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);
+
+void flib_log_e(const char *fmt, ...);
+void flib_log_w(const char *fmt, ...);
+void flib_log_i(const char *fmt, ...);
+void flib_log_d(const char *fmt, ...);
+
+int flib_log_getLevel();
+void flib_log_setLevel(int level);
+void flib_log_setFile(FILE *logfile);
+bool flib_log_isActive(int level);
+
+#endif /* LOGGING_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/refcounter.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,51 @@
+/**
+ * Helper functions for reference counted structs.
+ *
+ * We don't have enough of them to justify going crazy with macros, but I still prefer
+ * to have the logic in one place.
+ *
+ * In particular, these functions handle counter overflow in a sensible way
+ * (log and leak).
+ */
+
+#ifndef REFCOUNTER_H_
+#define REFCOUNTER_H_
+
+#include "logging.h"
+#include <stdbool.h>
+
+static inline void flib_retain(int *referenceCountPtr, const char *objName) {
+	if(!referenceCountPtr || !objName) {
+		flib_log_e("null parameter to flib_retain");
+	} else {
+		if((*referenceCountPtr)  >= 0) {
+			(*referenceCountPtr)++;
+			flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr));
+		}
+		if((*referenceCountPtr) < 0) {
+			flib_log_e("Memory leak: Reference count overflow in %s object!", objName);
+		}
+	}
+}
+
+/**
+ * Returns true if the struct should be freed.
+ */
+static inline bool flib_release(int *referenceCountPtr, const char *objName) {
+	bool result = false;
+	if(!referenceCountPtr) {
+		flib_log_e("null parameter to flib_release");
+	} else if((*referenceCountPtr) > 0) {
+		if(--(*referenceCountPtr) == 0) {
+			flib_log_d("releasing and destroying %s", objName);
+			result = true;
+		} else {
+			flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr));
+		}
+	} else if((*referenceCountPtr) == 0) {
+		flib_log_e("Attempt to release a %s with zero references!", objName);
+	}
+	return result;
+}
+
+#endif /* REFCOUNTER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,142 @@
+#include "util.h"
+#include "logging.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.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(!fmt) {
+		flib_log_e("null parameter in flib_vasprintf");
+	} else {
+		int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;					// Figure out how much memory we need,
+		if(requiredSize<0) {
+			flib_log_e("Error formatting string with template \"%s\" in flib_vasprintf", fmt);
+		} else {
+			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_strdupnull(const char *str) {
+	if(!str) {
+		return NULL;
+	}
+	return flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+	if(!buf || size==0) {
+		return NULL;
+	}
+	void *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);
+}
+
+char *flib_urlencode_pred(const char *inbuf, bool (*needsEscaping)(char c)) {
+	if(!inbuf) {
+		return NULL;
+	}
+	size_t insize = strlen(inbuf);
+	if(insize > SIZE_MAX/4) {
+		flib_log_e("String too long in flib_urlencode: %zu bytes.", insize);
+		return NULL;
+	}
+
+	char *outbuf = flib_malloc(insize*3+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(!needsEscaping(inbuf[inpos])) {
+        	outbuf[outpos++] = inbuf[inpos++];
+        } else {
+            if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+            	flib_log_e("printf error in flib_urlencode");
+            	free(outbuf);
+            	return NULL;
+            }
+            inpos++;
+            outpos += 3;
+        }
+    }
+    outbuf[outpos] = 0;
+    char *shrunk = realloc(outbuf, outpos+1);
+    return shrunk ? shrunk : outbuf;
+}
+
+char *flib_urldecode(const char *inbuf) {
+	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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h	Tue Jun 19 21:20:08 2012 +0200
@@ -0,0 +1,84 @@
+#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);
+
+/**
+ * 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);
+
+#endif