--- a/.hgignore Sun Jun 10 14:36:40 2012 -0400
+++ b/.hgignore Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/QTfrontend/CMakeLists.txt Mon Jun 11 00:06:22 2012 +0200
@@ -128,7 +128,6 @@
achievements.h
binds.h
ui_hwform.h
- KB.h
hwconsts.h
sdlkeys.h
)
--- a/QTfrontend/game.cpp Sun Jun 10 14:36:40 2012 -0400
+++ b/QTfrontend/game.cpp Mon Jun 11 00:06:22 2012 +0200
@@ -27,7 +27,6 @@
#include "gameuiconfig.h"
#include "gamecfgwidget.h"
#include "teamselect.h"
-#include "KB.h"
#include "proto.h"
#include <QTextStream>
@@ -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 Sun Jun 10 14:36:40 2012 -0400
+++ b/QTfrontend/net/newnetclient.h Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,195 @@
+Notice for all the files in this folder.
+------------------------------------------------------------
+
+
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/ini4j/LICENSE.txt Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/Licenses/ini4j/NOTICE.txt Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Mon Jun 11 00:06:22 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 Sun Jun 10 14:36:40 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,115 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "model/map.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+
+#include <SDL.h>
+#include <SDL_net.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+
+static int flib_initflags;
+
+int flib_init(int flags) {
+ flib_initflags = flags;
+
+ 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() {
+ SDLNet_Quit();
+ if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+ SDL_Quit();
+ }
+}
+
+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);
+}
+
+int main(int argc, char *argv[]) {
+ flib_init(0);
+
+ flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
+ assert(metaconf);
+ flib_gamesetup setup;
+ setup.gamescheme = flib_cfg_from_ini(metaconf, "scheme_shoppa.ini");
+ setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+ setup.seed = "apsfooasdgnds";
+ setup.script = NULL;
+ setup.teamcount = 2;
+ setup.teams = calloc(2, 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].weaponset = flib_weaponset_create("Defaultweaps");
+ 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].color = 0xff0000ff;
+ setup.teams[1].flag = "germany";
+ setup.teams[1].fort = "Cake";
+ setup.teams[1].grave = "Cherry";
+ setup.teams[1].hogsInGame = 2;
+ setup.teams[1].name = "The Krauts";
+ setup.teams[1].voicepack = "Pirate";
+ setup.teams[1].weaponset = flib_weaponset_create("Defaultweaps");
+ setup.teams[1].hogs[0].difficulty = 0;
+ setup.teams[1].hogs[0].hat = "quotecap";
+ setup.teams[1].hogs[0].initialHealth = 100;
+ setup.teams[1].hogs[0].name = "Quote";
+ setup.teams[1].hogs[1].difficulty = 0;
+ setup.teams[1].hogs[1].hat = "chef";
+ setup.teams[1].hogs[1].initialHealth = 100;
+ setup.teams[1].hogs[1].name = "Chefkoch2";
+
+ flib_gameconn *gameconn = flib_gameconn_create("Medo42", metaconf, &setup, false);
+ assert(gameconn);
+
+ flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+ flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+
+ while(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+ flib_log_i("Shutting down...");
+ flib_quit();
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.h Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,21 @@
+/**
+ * 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 GAMEMOD_PERHOGAMMO_MASKBIT 22
+#define GAMEMOD_SHAREDAMMO_MASKBIT 16
+
+#define WEAPONS_COUNT 55
+#define AMMOLINE_DEFAULT_QT "9391929422199121032235111001201000000211110101011111011"
+#define AMMOLINE_DEFAULT_PROB "0405040541600655546554464776576666666155510101115411011"
+#define AMMOLINE_DEFAULT_DELAY "0000000000000205500000040007004000000000220000000600000"
+#define AMMOLINE_DEFAULT_CRATE "1311110312111111123114111111111111111211111101111111010"
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/LICENSE Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,23 @@
+Copyright (c) 2000-2012 by Nicolas Devillard.
+MIT License
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/VERSION Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,2 @@
+This is version 3.1 of the iniparser library developed by N. Devillard.
+See http://ndevilla.free.fr/iniparser/ for details and new versions.
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/dictionary.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.c
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ 1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ 128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, int size)
+{
+ void * newptr ;
+
+ newptr = calloc(2*size, 1);
+ if (newptr==NULL) {
+ return NULL ;
+ }
+ memcpy(newptr, ptr, size);
+ free(ptr);
+ return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+ char * t ;
+ if (!s)
+ return NULL ;
+ t = (char*)malloc(strlen(s)+1) ;
+ if (t) {
+ strcpy(t,s);
+ }
+ return t ;
+}
+
+/*---------------------------------------------------------------------------
+ Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+ int len ;
+ unsigned hash ;
+ int i ;
+
+ len = strlen(key);
+ for (hash=0, i=0 ; i<len ; i++) {
+ hash += (unsigned)key[i] ;
+ hash += (hash<<10);
+ hash ^= (hash>>6) ;
+ }
+ hash += (hash <<3);
+ hash ^= (hash >>11);
+ hash += (hash <<15);
+ return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size)
+{
+ dictionary * d ;
+
+ /* If no size was specified, allocate space for DICTMINSZ */
+ if (size<DICTMINSZ) size=DICTMINSZ ;
+
+ if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+ return NULL;
+ }
+ d->size = size ;
+ d->val = (char **)calloc(size, sizeof(char*));
+ d->key = (char **)calloc(size, sizeof(char*));
+ d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+ return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+ int i ;
+
+ if (d==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]!=NULL)
+ free(d->key[i]);
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ }
+ free(d->val);
+ free(d->key);
+ free(d->hash);
+ free(d);
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+ unsigned hash ;
+ int i ;
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ return d->val[i] ;
+ }
+ }
+ }
+ return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+ int i ;
+ unsigned hash ;
+
+ if (d==NULL || key==NULL) return -1 ;
+
+ /* Compute hash for this key */
+ hash = dictionary_hash(key) ;
+ /* Find if value is already in dictionary */
+ if (d->n>0) {
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (hash==d->hash[i]) { /* Same hash value */
+ if (!strcmp(key, d->key[i])) { /* Same key */
+ /* Found a value: modify and return */
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ /* Value has been modified: return */
+ return 0 ;
+ }
+ }
+ }
+ }
+ /* Add a new value */
+ /* See if dictionary needs to grow */
+ if (d->n==d->size) {
+
+ /* Reached maximum size: reallocate dictionary */
+ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ;
+ d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ;
+ d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+ if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+ /* Cannot grow dictionary */
+ return -1 ;
+ }
+ /* Double size */
+ d->size *= 2 ;
+ }
+
+ /* Insert key in the first empty slot. Start at d->n and wrap at
+ d->size. Because d->n < d->size this will necessarily
+ terminate. */
+ for (i=d->n ; d->key[i] ; ) {
+ if(++i == d->size) i = 0;
+ }
+ /* Copy key */
+ d->key[i] = xstrdup(key);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ d->hash[i] = hash;
+ d->n ++ ;
+ return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+ unsigned hash ;
+ int i ;
+
+ if (key == NULL) {
+ return;
+ }
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ /* Found key */
+ break ;
+ }
+ }
+ }
+ if (i>=d->size)
+ /* Key not found */
+ return ;
+
+ free(d->key[i]);
+ d->key[i] = NULL ;
+ if (d->val[i]!=NULL) {
+ free(d->val[i]);
+ d->val[i] = NULL ;
+ }
+ d->hash[i] = 0 ;
+ d->n -- ;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+ int i ;
+
+ if (d==NULL || out==NULL) return ;
+ if (d->n<1) {
+ fprintf(out, "empty dictionary\n");
+ return ;
+ }
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]) {
+ fprintf(out, "%20s\t[%s]\n",
+ d->key[i],
+ d->val[i] ? d->val[i] : "UNDEF");
+ }
+ }
+ return ;
+}
+
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+ dictionary * d ;
+ char * val ;
+ int i ;
+ char cval[90] ;
+
+ /* Allocate dictionary */
+ printf("allocating...\n");
+ d = dictionary_new(0);
+
+ /* Set values in dictionary */
+ printf("setting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_set(d, cval, "salut");
+ }
+ printf("getting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ val = dictionary_get(d, cval, DICT_INVALID_KEY);
+ if (val==DICT_INVALID_KEY) {
+ printf("cannot get value for key [%s]\n", cval);
+ }
+ }
+ printf("unsetting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_unset(d, cval);
+ }
+ if (d->n != 0) {
+ printf("error deleting values\n");
+ }
+ printf("deallocating...\n");
+ dictionary_del(d);
+ return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/dictionary.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,165 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.h
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*---------------------------------------------------------------------------
+ New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dictionary object
+
+ This object contains a list of string/string associations. Each
+ association is identified by a unique string key. Looking up values
+ in the dictionary is speeded up by the use of a (hopefully collision-free)
+ hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+ int n ; /** Number of entries in dictionary */
+ int size ; /** Storage size */
+ char ** val ; /** List of string values */
+ char ** key ; /** List of string keys */
+ unsigned * hash ; /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+ Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/iniparser.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,748 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.c
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ (1024)
+#define INI_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+ LINE_UNPROCESSED,
+ LINE_ERROR,
+ LINE_EMPTY,
+ LINE_COMMENT,
+ LINE_SECTION,
+ LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Convert a string to lowercase.
+ @param s String to convert.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string
+ containing a lowercased version of the input string. Do not free
+ or modify the returned string! Since the returned string is statically
+ allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+ static char l[ASCIILINESZ+1];
+ int i ;
+
+ if (s==NULL) return NULL ;
+ memset(l, 0, ASCIILINESZ+1);
+ i=0 ;
+ while (s[i] && i<ASCIILINESZ) {
+ l[i] = (char)tolower((int)s[i]);
+ i++ ;
+ }
+ l[ASCIILINESZ]=(char)0;
+ return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Remove blanks at the beginning and the end of a string.
+ @param s String to parse.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string,
+ which is identical to the input string, except that all blank
+ characters at the end and the beg. of the string have been removed.
+ Do not free or modify the returned string! Since the returned string
+ is statically allocated, it will be modified at each function call
+ (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(const char * s)
+{
+ static char l[ASCIILINESZ+1];
+ char * last ;
+
+ if (s==NULL) return NULL ;
+
+ while (isspace((int)*s) && *s) s++;
+ memset(l, 0, ASCIILINESZ+1);
+ strcpy(l, s);
+ last = l + strlen(l);
+ while (last > l) {
+ if (!isspace((int)*(last-1)))
+ break ;
+ last -- ;
+ }
+ *last = (char)0;
+ return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+ int i ;
+ int nsec ;
+
+ if (d==NULL) return -1 ;
+ nsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ nsec ++ ;
+ }
+ }
+ return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+ int i ;
+ int foundsec ;
+
+ if (d==NULL || n<0) return NULL ;
+ foundsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ foundsec++ ;
+ if (foundsec>n)
+ break ;
+ }
+ }
+ if (foundsec<=n) {
+ return NULL ;
+ }
+ return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+ int i ;
+
+ if (d==NULL || f==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (d->val[i]!=NULL) {
+ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ } else {
+ fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+ }
+ }
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+ int i ;
+ int nsec ;
+ char * secname ;
+
+ if (d==NULL || f==NULL) return ;
+
+ nsec = iniparser_getnsec(d);
+ if (nsec<1) {
+ /* No section in file: dump all keys as they are */
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+ }
+ return ;
+ }
+ for (i=0 ; i<nsec ; i++) {
+ secname = iniparser_getsecname(d, i) ;
+ iniparser_dumpsection_ini(d, secname, f) ;
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+{
+ int j ;
+ char keym[ASCIILINESZ+1];
+ int seclen ;
+
+ if (d==NULL || f==NULL) return ;
+ if (! iniparser_find_entry(d, s)) return ;
+
+ seclen = (int)strlen(s);
+ fprintf(f, "\n[%s]\n", s);
+ sprintf(keym, "%s:", s);
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ fprintf(f,
+ "%-30s = %s\n",
+ d->key[j]+seclen+1,
+ d->val[j] ? d->val[j] : "");
+ }
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+ int seclen, nkeys ;
+ char keym[ASCIILINESZ+1];
+ int j ;
+
+ nkeys = 0;
+
+ if (d==NULL) return nkeys;
+ if (! iniparser_find_entry(d, s)) return nkeys;
+
+ seclen = (int)strlen(s);
+ sprintf(keym, "%s:", s);
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1))
+ nkeys++;
+ }
+
+ return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+ char **keys;
+
+ int i, j ;
+ char keym[ASCIILINESZ+1];
+ int seclen, nkeys ;
+
+ keys = NULL;
+
+ if (d==NULL) return keys;
+ if (! iniparser_find_entry(d, s)) return keys;
+
+ nkeys = iniparser_getsecnkeys(d, s);
+
+ keys = (char**) malloc(nkeys*sizeof(char*));
+
+ seclen = (int)strlen(s);
+ sprintf(keym, "%s:", s);
+
+ i = 0;
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ keys[i] = d->key[j];
+ i++;
+ }
+ }
+
+ return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+ char * lc_key ;
+ char * sval ;
+
+ if (d==NULL || key==NULL)
+ return def ;
+
+ lc_key = strlwc(key);
+ sval = dictionary_get(d, lc_key, def);
+ return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+ char * c ;
+ int ret ;
+
+ c = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (c==INI_INVALID_KEY) return notfound ;
+ if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+ ret = 1 ;
+ } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+ ret = 0 ;
+ } else {
+ ret = notfound ;
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+ dictionary * ini,
+ const char * entry
+)
+{
+ int found=0 ;
+ if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+ found = 1 ;
+ }
+ return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+ return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+ dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Load a single line from an INI file
+ @param input_line Input line, may be concatenated multi-line input
+ @param section Output space to store section
+ @param key Output space to store key
+ @param value Output space to store value
+ @return line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+ const char * input_line,
+ char * section,
+ char * key,
+ char * value)
+{
+ line_status sta ;
+ char line[ASCIILINESZ+1];
+ int len ;
+
+ strcpy(line, strstrip(input_line));
+ len = (int)strlen(line);
+
+ sta = LINE_UNPROCESSED ;
+ if (len<1) {
+ /* Empty line */
+ sta = LINE_EMPTY ;
+ } else if (line[0]=='#' || line[0]==';') {
+ /* Comment line */
+ sta = LINE_COMMENT ;
+ } else if (line[0]=='[' && line[len-1]==']') {
+ /* Section name */
+ sscanf(line, "[%[^]]", section);
+ strcpy(section, strstrip(section));
+ strcpy(section, strlwc(section));
+ sta = LINE_SECTION ;
+ } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+ || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
+ || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
+ /* Usual key=value, with or without comments */
+ strcpy(key, strstrip(key));
+ strcpy(key, strlwc(key));
+ strcpy(value, strstrip(value));
+ /*
+ * sscanf cannot handle '' or "" as empty values
+ * this is done here
+ */
+ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+ value[0]=0 ;
+ }
+ sta = LINE_VALUE ;
+ } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+ || sscanf(line, "%[^=] %[=]", key, value) == 2) {
+ /*
+ * Special cases:
+ * key=
+ * key=;
+ * key=#
+ */
+ strcpy(key, strstrip(key));
+ strcpy(key, strlwc(key));
+ value[0]=0 ;
+ sta = LINE_VALUE ;
+ } else {
+ /* Generate syntax error */
+ sta = LINE_ERROR ;
+ }
+ return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+ FILE * in ;
+
+ char line [ASCIILINESZ+1] ;
+ char section [ASCIILINESZ+1] ;
+ char key [ASCIILINESZ+1] ;
+ char tmp [ASCIILINESZ+1] ;
+ char val [ASCIILINESZ+1] ;
+
+ int last=0 ;
+ int len ;
+ int lineno=0 ;
+ int errs=0;
+
+ dictionary * dict ;
+
+ if ((in=fopen(ininame, "r"))==NULL) {
+ fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+ return NULL ;
+ }
+
+ dict = dictionary_new(0) ;
+ if (!dict) {
+ fclose(in);
+ return NULL ;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+ memset(section, 0, ASCIILINESZ);
+ memset(key, 0, ASCIILINESZ);
+ memset(val, 0, ASCIILINESZ);
+ last=0 ;
+
+ while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+ lineno++ ;
+ len = (int)strlen(line)-1;
+ if (len==0)
+ continue;
+ /* Safety check against buffer overflows */
+ if (line[len]!='\n') {
+ fprintf(stderr,
+ "iniparser: input line too long in %s (%d)\n",
+ ininame,
+ lineno);
+ dictionary_del(dict);
+ fclose(in);
+ return NULL ;
+ }
+ /* Get rid of \n and spaces at end of line */
+ while ((len>=0) &&
+ ((line[len]=='\n') || (isspace(line[len])))) {
+ line[len]=0 ;
+ len-- ;
+ }
+ /* Detect multi-line */
+ if (line[len]=='\\') {
+ /* Multi-line value */
+ last=len ;
+ continue ;
+ } else {
+ last=0 ;
+ }
+ switch (iniparser_line(line, section, key, val)) {
+ case LINE_EMPTY:
+ case LINE_COMMENT:
+ break ;
+
+ case LINE_SECTION:
+ errs = dictionary_set(dict, section, NULL);
+ break ;
+
+ case LINE_VALUE:
+ sprintf(tmp, "%s:%s", section, key);
+ errs = dictionary_set(dict, tmp, val) ;
+ break ;
+
+ case LINE_ERROR:
+ fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+ ininame,
+ lineno);
+ fprintf(stderr, "-> %s\n", line);
+ errs++ ;
+ break;
+
+ default:
+ break ;
+ }
+ memset(line, 0, ASCIILINESZ);
+ last=0;
+ if (errs<0) {
+ fprintf(stderr, "iniparser: memory allocation failure\n");
+ break ;
+ }
+ }
+ if (errs) {
+ dictionary_del(dict);
+ dict = NULL ;
+ }
+ fclose(in);
+ return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+ dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/iniparser.h Mon Jun 11 00:06:22 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/demo.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,71 @@
+#include "demo.h"
+#include "../util/logging.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static int demo_record(flib_vector demoBuffer, const void *data, size_t len) {
+ if(flib_vector_append(demoBuffer, data, len) < len) {
+ flib_log_e("Error recording demo: Out of memory.");
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName) {
+ if(!demoBuffer || !message || !playerName) {
+ flib_log_e("Call to flib_demo_record_from_engine with demoBuffer==null or message==null or playerName==null");
+ return -1;
+ }
+
+ if(strchr("?CEiQqHb", message[1])) {
+ return 0; // Those message types are not recorded in a demo.
+ }
+
+ if(message[1] == 's') {
+ if(message[0] >= 3) {
+ // Chat messages are reformatted to make them look as if they were received, not sent.
+ // Get the actual chat message as C string
+ char chatMsg[256];
+ memcpy(chatMsg, message+2, message[0]-3);
+ chatMsg[message[0]-3] = 0;
+
+ // If the message starts with /me, it will be displayed differently.
+ char converted[257];
+ bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4);
+ const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s ";
+ int size = snprintf(converted+1, 256, template, playerName, chatMsg);
+ if(size>0) {
+ converted[0] = size>255 ? 255 : size;
+ return demo_record(demoBuffer, converted, converted[0]+1);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0; // Malformed chat message is no reason to abort...
+ }
+ } else {
+ return demo_record(demoBuffer, message, message[0]+1);
+ }
+}
+
+int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len) {
+ if(!demoBuffer || (len>0 && !message)) {
+ flib_log_e("Call to flib_demo_record_to_engine with demoBuffer==null or message==null");
+ return -1;
+ }
+ return demo_record(demoBuffer, message, len);
+}
+
+void flib_demo_replace_gamemode(flib_buffer buf, char gamemode) {
+ size_t msgStart = 0;
+ char *data = (char*)buf.data;
+ while(msgStart+2 < buf.size) {
+ if(!memcmp(data+msgStart, "\x02T", 2)) {
+ data[msgStart+2] = gamemode;
+ }
+ msgStart += (uint8_t)data[msgStart]+1;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/demo.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,30 @@
+/**
+ * Demo recording functions. Only used by the ipc game code.
+ */
+
+#ifndef DEMO_H_
+#define DEMO_H_
+
+#include "../util/buffer.h"
+
+/**
+ * Record a message sent from the engine to the frontend.
+ * Returns 0 for OK, a negative value on error.
+ * Don't pass NULL.
+ */
+int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName);
+
+/**
+ * Record a message sent from the frontend to the engine.
+ * Returns 0 for OK, a negative value on error.
+ * Don't pass NULL.
+ */
+int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len);
+
+/**
+ * Replace game mode messages ("TL", "TD", "TS", "TN") in the recording to mirror
+ * the intended use. Pass 'S' for savegames, 'D' for demos.
+ */
+void flib_demo_replace_gamemode(flib_buffer buf, char gamemode);
+
+#endif /* DEMO_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,348 @@
+#include "gameconn.h"
+#include "ipcconn.h"
+#include "ipcprotocol.h"
+#include "../util/logging.h"
+#include "../hwconsts.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef enum {
+ AWAIT_CONNECTION,
+ CONNECTED,
+ FINISHED
+} gameconn_state;
+
+struct _flib_gameconn {
+ flib_ipcconn connection;
+ flib_vector configBuffer;
+
+ 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 (*onNetMessageCb)(void *context, const uint8_t *em, int size);
+ void *onNetMessageCtx;
+
+ 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_onNetMessage(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->onNetMessageCb = &defaultCallback_onNetMessage;
+}
+
+static bool getGameMod(flib_cfg_meta *meta, flib_cfg *conf, int maskbit) {
+ for(int i=0; i<meta->modCount; i++) {
+ if(meta->mods[i].bitmaskIndex == maskbit) {
+ return conf->mods[i];
+ }
+ }
+ flib_log_e("Unable to find game mod with mask bit %i", maskbit);
+ return false;
+}
+
+static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+ bool error = false;
+ bool perHogAmmo = false;
+ bool sharedAmmo = false;
+
+ error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
+ error |= flib_ipc_append_seed(configBuffer, setup->seed);
+ if(setup->map) {
+ error |= flib_ipc_append_mapconf(configBuffer, setup->map, false);
+ }
+ if(setup->script) {
+ error |= flib_ipc_append_message(configBuffer, "escript %s", setup->script);
+ }
+ if(setup->gamescheme) {
+ error |= flib_ipc_append_gamescheme(configBuffer, setup->gamescheme, metaconf);
+ perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
+ sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
+ }
+ if(setup->teams) {
+ for(int i=0; i<setup->teamcount; i++) {
+ error |= flib_ipc_append_addteam(configBuffer, &setup->teams[i], perHogAmmo, sharedAmmo);
+ }
+ }
+ error |= flib_ipc_append_message(configBuffer, "!");
+ return error ? -1 : 0;
+}
+
+static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
+ flib_gameconn *result = NULL;
+ flib_gameconn *tempConn = calloc(1, sizeof(flib_gameconn));
+ if(tempConn) {
+ tempConn->connection = flib_ipcconn_create(record, playerName);
+ tempConn->configBuffer = flib_vector_create();
+ if(tempConn->connection && tempConn->configBuffer) {
+ 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_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+ flib_gameconn *result = NULL;
+ flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
+ if(tempConn) {
+ if(fillConfigBuffer(tempConn->configBuffer, playerName, metaconf, setup, netgame) == 0) {
+ 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_ipcconn_destroy(&conn->connection);
+ flib_vector_destroy(&conn->configBuffer);
+ 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_ipcconn_port(conn->connection);
+ }
+}
+
+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_onNetMessage(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_onNetMessage");
+ } else {
+ conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage;
+ conn->onNetMessageCtx = context;
+ }
+}
+
+static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
+ if(conn->state == AWAIT_CONNECTION) {
+ flib_ipcconn_accept(conn->connection);
+ switch(flib_ipcconn_state(conn->connection)) {
+ case IPC_CONNECTED:
+ {
+ flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+ if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+ return;
+ } else {
+ 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_ipcconn_recv_message(conn->connection, msgbuffer))>=0) {
+ if(len<2) {
+ flib_log_w("Received short message from IPC (<2 bytes)");
+ continue;
+ }
+ switch(msgbuffer[1]) {
+ case '?':
+ // The pong is already part of the config message
+ break;
+ case 'C':
+ // And we already send the config message on connecting.
+ break;
+ case 'E':
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
+ }
+ break;
+ case 'i':
+ // TODO stats
+ break;
+ case 'Q':
+ case 'H':
+ case 'q':
+ {
+ 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;
+ flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
+ if(record.size) {
+ conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
+ if(conn->destroyRequested) {
+ return;
+ }
+ }
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, reason);
+ return;
+ }
+ case 's':
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
+ }
+ break;
+ case 'b':
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
+ }
+ break;
+ default:
+ conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
+ break;
+ }
+ }
+ }
+
+ if(flib_ipcconn_state(conn->connection) == 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,81 @@
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../util/buffer.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_cfg_meta *metaconf, 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);
+
+// TODO: Not needed yet, only for netgames
+/*
+flib_gameconn_send_enginemsg(flib_gameconn conn, uint8_t *data, int len);
+flib_gameconn_send_textmsg(flib_gameconn conn, int msgtype, const char *msg);
+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_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcconn.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,237 @@
+#include "ipcconn.h"
+#include "demo.h"
+#include "../util/logging.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...
+ */
+typedef struct _flib_ipcconn {
+ uint8_t readBuffer[8192];
+ char playerName[256];
+
+ int readBufferSize;
+
+ flib_acceptor acceptor;
+ uint16_t port;
+
+ flib_tcpsocket sock;
+ flib_vector demoBuffer;
+} _flib_ipcconn;
+
+flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) {
+ flib_ipcconn result = malloc(sizeof(_flib_ipcconn));
+ flib_acceptor acceptor = flib_acceptor_create(0);
+
+ if(!result || !acceptor) {
+ flib_log_e("Can't create ipcconn.");
+ free(result);
+ flib_acceptor_close(&acceptor);
+ return NULL;
+ }
+
+ result->acceptor = acceptor;
+ result->sock = NULL;
+ result->readBufferSize = 0;
+ result->port = flib_acceptor_listenport(acceptor);
+
+ if(localPlayerName) {
+ strncpy(result->playerName, localPlayerName, 255);
+ } else {
+ strncpy(result->playerName, "Player", 255);
+ }
+
+ if(recordDemo) {
+ result->demoBuffer = flib_vector_create();
+ }
+
+ flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
+ return result;
+}
+
+uint16_t flib_ipcconn_port(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_port with ipc==null");
+ return 0;
+ }
+ return ipc->port;
+}
+
+void flib_ipcconn_destroy(flib_ipcconn *ipcptr) {
+ if(!ipcptr) {
+ flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null");
+ } else if(*ipcptr) {
+ flib_ipcconn ipc = *ipcptr;
+ flib_acceptor_close(&ipc->acceptor);
+ flib_socket_close(&ipc->sock);
+ flib_vector_destroy(&ipc->demoBuffer);
+ free(ipc);
+ *ipcptr = NULL;
+ }
+}
+
+IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_state with ipc==null");
+ return IPC_NOT_CONNECTED;
+ } else if(ipc->sock) {
+ return IPC_CONNECTED;
+ } else if(ipc->acceptor) {
+ return IPC_LISTENING;
+ } else {
+ return IPC_NOT_CONNECTED;
+ }
+}
+
+static bool isMessageReady(flib_ipcconn ipc) {
+ return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+static void receiveToBuffer(flib_ipcconn 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);
+ }
+ }
+}
+
+int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null");
+ return -1;
+ }
+
+ if(!isMessageReady(ipc)) {
+ receiveToBuffer(ipc);
+ }
+
+ if(isMessageReady(ipc)) {
+ if(ipc->demoBuffer) {
+ if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) {
+ flib_log_w("Stopping demo recording due to an error.");
+ flib_vector_destroy(&ipc->demoBuffer);
+ }
+ }
+ 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_ipcconn_recv_map(flib_ipcconn ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("Call to flib_ipcconn_recv_map with ipc==null or data==null");
+ return -1;
+ }
+
+ receiveToBuffer(ipc);
+
+ if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) {
+ memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES);
+ memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES);
+ return IPCCONN_MAPMSG_BYTES;
+ } else {
+ return -1;
+ }
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+ if(flib_log_getLevel() > 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_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len) {
+ if(!ipc || (!data && len>0)) {
+ flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null");
+ return -1;
+ }
+ if(!ipc->sock) {
+ flib_log_w("flib_ipcconn_send_raw: Not connected.");
+ return -1;
+ }
+
+ if(flib_socket_send(ipc->sock, data, len) == len) {
+ logSentMsg(data, len);
+ if(ipc->demoBuffer) {
+ if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) {
+ flib_log_w("Stopping demo recording due to an error.");
+ flib_vector_destroy(&ipc->demoBuffer);
+ }
+ }
+ return 0;
+ } else {
+ flib_log_w("Failed or incomplete ICP write: engine connection lost.");
+ flib_socket_close(&ipc->sock);
+ return -1;
+ }
+}
+
+int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) {
+ if(!ipc || (!data && len>0) || len>255) {
+ flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255");
+ return -1;
+ }
+
+ uint8_t sendbuf[256];
+ sendbuf[0] = len;
+ memcpy(sendbuf+1, data, len);
+ return flib_ipcconn_send_raw(ipc, sendbuf, len+1);
+}
+
+int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
+ return flib_ipcconn_send_message(ipc, data, strlen(data));
+}
+
+void flib_ipcconn_accept(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_accept with ipc==null");
+ } else if(!ipc->sock && ipc->acceptor) {
+ ipc->sock = flib_socket_accept(ipc->acceptor, true);
+ if(ipc->sock) {
+ flib_acceptor_close(&ipc->acceptor);
+ }
+ }
+}
+
+flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_getrecord with ipc==null");
+ }
+ if(!ipc || !ipc->demoBuffer) {
+ flib_constbuffer result = {NULL, 0};
+ return result;
+ }
+ flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D');
+ return flib_vector_as_constbuffer(ipc->demoBuffer);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcconn.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,108 @@
+/*
+ * Low-level protocol support for the IPC connection to the engine.
+ */
+
+#ifndef IPCCONN_H_
+#define IPCCONN_H_
+
+#include "../util/buffer.h"
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define IPCCONN_MAPMSG_BYTES 4097
+
+typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState;
+
+struct _flib_ipcconn;
+typedef struct _flib_ipcconn *flib_ipcconn;
+
+/**
+ * TODO move demo recording up by one layer?
+ *
+ * Start an engine connection by listening on a random port. The selected port can
+ * be queried with flib_ipcconn_port and has to be passed to the engine.
+ *
+ * The parameter "recordDemo" can be used to control whether demo recording should
+ * be enabled for this connection. The localPlayerName is needed for demo
+ * recording purposes.
+ *
+ * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy.
+ *
+ * We stop accepting new connections once a connection has been established, so you
+ * need to create a new ipcconn in order to start a new connection.
+ */
+flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName);
+
+uint16_t flib_ipcconn_port(flib_ipcconn ipc);
+
+/**
+ * Free resources, close sockets, and set the pointer to NULL.
+ */
+void flib_ipcconn_destroy(flib_ipcconn *ipcptr);
+
+/**
+ * Determine the current connection state
+ */
+IpcConnState flib_ipcconn_state(flib_ipcconn 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_ipcconn_recv_message(flib_ipcconn 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_ipcconn_recv_map(flib_ipcconn ipc, void *data);
+
+int flib_ipcconn_send_raw(flib_ipcconn 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_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len);
+
+/**
+ * Convenience function for sending a 0-delimited string.
+ */
+int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data);
+
+/**
+ * Call regularly to allow background work to proceed
+ */
+void flib_ipcconn_accept(flib_ipcconn ipc);
+
+/**
+ * Get a record of the connection. This should be called after
+ * the connection is closed and all messages have been received.
+ *
+ * If demo recording was not enabled, or if the recording failed for some reason,
+ * the buffer will be empty.
+ *
+ * If save=true is passed, the result will be a savegame, otherwise it will be a
+ * demo.
+ *
+ * The buffer is only valid until flib_ipcconn_getsave is called again or the ipcconn
+ * is destroyed.
+ */
+flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save);
+
+#endif /* IPCCONN_H_ */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,181 @@
+#include "ipcprotocol.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.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, 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, flib_cfg *scheme, flib_cfg_meta *meta) {
+ int result = -1;
+ flib_vector tempvector = flib_vector_create();
+ if(!vec || !scheme || !meta) {
+ flib_log_e("null parameter in flib_ipc_append_gamescheme");
+ } else if(tempvector) {
+ 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].checkOverMax) {
+ value = value>meta->settings[i].max ? meta->settings[i].max : 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;
+}
+
+// FIXME shared ammo will break per-team ammo
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo) {
+ int result = -1;
+ flib_vector tempvector = flib_vector_create();
+ if(!vec || !team || !team->weaponset) {
+ flib_log_e("invalid parameter in flib_ipc_append_addteam");
+ } else if(tempvector) {
+ bool error = false;
+ error |= flib_ipc_append_message(tempvector, "eammloadt %s", team->weaponset->loadout);
+ error |= flib_ipc_append_message(tempvector, "eammprob %s", team->weaponset->crateprob);
+ error |= flib_ipc_append_message(tempvector, "eammdelay %s", team->weaponset->delay);
+ error |= flib_ipc_append_message(tempvector, "eammreinf %s", team->weaponset->crateammo);
+ if(!perHogAmmo) {
+ 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);
+
+ // TODO bindings
+
+ for(int i=0; i<team->hogsInGame; i++) {
+ 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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,50 @@
+#ifndef IPCPROTOCOL_H_
+#define IPCPROTOCOL_H_
+
+#include "../util/buffer.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/cfg.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, 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, flib_cfg *seed, flib_cfg_meta *meta);
+
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo);
+
+#endif /* IPCPROTOCOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,174 @@
+#include "mapconn.h"
+#include "ipcconn.h"
+#include "ipcprotocol.h"
+
+#include "../util/logging.h"
+#include "../util/buffer.h"
+
+#include <stdlib.h>
+
+typedef enum {
+ AWAIT_CONNECTION,
+ AWAIT_REPLY,
+ FINISHED
+} mapconn_state;
+
+struct _flib_mapconn {
+ uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
+ flib_ipcconn connection;
+ 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 = calloc(1, sizeof(flib_mapconn));
+ if(tempConn) {
+ tempConn->connection = flib_ipcconn_create(false, "Player");
+ tempConn->configBuffer = createConfigBuffer(seed, mapdesc);
+ if(tempConn->connection && 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_ipcconn_destroy(&conn->connection);
+ 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_ipcconn_port(conn->connection);
+ }
+}
+
+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_ipcconn_accept(conn->connection);
+ switch(flib_ipcconn_state(conn->connection)) {
+ case IPC_CONNECTED:
+ {
+ flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+ if(flib_ipcconn_send_raw(conn->connection, 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_ipcconn_recv_map(conn->connection, conn->mapBuffer) >= 0) {
+ conn->progress = FINISHED;
+ conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCCONN_MAPMSG_BYTES-1]);
+ return;
+ } else if(flib_ipcconn_state(conn->connection) != 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 Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,211 @@
+#include "cfg.h"
+
+#include "../iniparser/iniparser.h"
+#include "../iniparser/dictionary.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <stdio.h>
+
+static void freeCfgMeta(flib_cfg_meta *cfg) {
+ if(cfg) {
+ if(cfg->settings) {
+ for(int i=0; i<cfg->settingCount; i++) {
+ free(cfg->settings[i].iniName);
+ free(cfg->settings[i].title);
+ free(cfg->settings[i].engineCommand);
+ free(cfg->settings[i].image);
+ }
+ free(cfg->settings);
+ }
+ if(cfg->mods) {
+ for(int i=0; i<cfg->modCount; i++) {
+ free(cfg->mods[i].iniName);
+ }
+ free(cfg->mods);
+ }
+ free(cfg);
+ }
+}
+
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *settingpath, const char *modpath) {
+ if(!settingpath || !modpath) {
+ return NULL;
+ }
+ flib_cfg_meta *result = calloc(1, sizeof(flib_cfg_meta));
+ dictionary *settingfile = iniparser_load(settingpath);
+ dictionary *modfile = iniparser_load(modpath);
+
+ if(!result || !settingfile || !modfile) {
+ goto handleError;
+ }
+
+ result->settingCount = iniparser_getnsec(settingfile);
+ result->modCount = iniparser_getnsec(modfile);
+ result->settings = calloc(result->settingCount, sizeof(flib_cfg_setting_meta));
+ result->mods = calloc(result->modCount, sizeof(flib_cfg_mod_meta));
+
+ if(!result->settings || !result->mods) {
+ goto handleError;
+ }
+
+ for(int i=0; i<result->settingCount; i++) {
+ char *sectionName = iniparser_getsecname(settingfile, i);
+ if(!sectionName) {
+ goto handleError;
+ }
+
+ bool error = false;
+ result->settings[i].iniName = flib_strdupnull(sectionName);
+ result->settings[i].title = inihelper_getstringdup(settingfile, &error, sectionName, "title");
+ result->settings[i].engineCommand = inihelper_getstringdup(settingfile, &error, sectionName, "command");
+ result->settings[i].image = inihelper_getstringdup(settingfile, &error, sectionName, "image");
+ result->settings[i].checkOverMax = inihelper_getbool(settingfile, &error, sectionName, "checkOverMax");
+ result->settings[i].times1000 = inihelper_getbool(settingfile, &error, sectionName, "times1000");
+ result->settings[i].min = inihelper_getint(settingfile, &error, sectionName, "min");
+ result->settings[i].max = inihelper_getint(settingfile, &error, sectionName, "max");
+ result->settings[i].def = inihelper_getint(settingfile, &error, sectionName, "default");
+ if(error) {
+ flib_log_e("Missing or malformed ini parameter in file %s, section %s", settingpath, sectionName);
+ goto handleError;
+ }
+ }
+
+ for(int i=0; i<result->modCount; i++) {
+ char *sectionName = iniparser_getsecname(modfile, i);
+ if(!sectionName) {
+ goto handleError;
+ }
+
+ bool error = false;
+ result->mods[i].iniName = flib_strdupnull(sectionName);
+ result->mods[i].bitmaskIndex = inihelper_getint(modfile, &error, sectionName, "bitmaskIndex");
+ if(error) {
+ flib_log_e("Missing or malformed ini parameter in file %s, section %s", modpath, sectionName);
+ goto handleError;
+ }
+ }
+
+ iniparser_freedict(settingfile);
+ iniparser_freedict(modfile);
+ return result;
+
+ handleError:
+ freeCfgMeta(result);
+ iniparser_freedict(settingfile);
+ iniparser_freedict(modfile);
+ return NULL;
+}
+
+void flib_cfg_meta_destroy(flib_cfg_meta *metainfo) {
+ freeCfgMeta(metainfo);
+}
+
+flib_cfg *flib_cfg_create(const flib_cfg_meta *meta, const char *schemeName) {
+ flib_cfg *result = calloc(1, sizeof(flib_cfg));
+ if(!meta || !result || !schemeName) {
+ return NULL;
+ }
+
+ result->modCount = meta->modCount;
+ result->settingCount = meta->settingCount;
+ result->schemeName = flib_strdupnull(schemeName);
+ result->mods = calloc(meta->modCount, sizeof(*result->mods));
+ result->settings = 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_from_ini_handleError(flib_cfg *result, dictionary *settingfile) {
+ iniparser_freedict(settingfile);
+ flib_cfg_destroy(result);
+ return NULL;
+}
+
+flib_cfg *flib_cfg_from_ini(const flib_cfg_meta *meta, const char *filename) {
+ if(!meta || !filename) {
+ return NULL;
+ }
+ dictionary *settingfile = iniparser_load(filename);
+ if(!settingfile) {
+ return NULL;
+ }
+
+ bool error = false;
+ char *schemename = inihelper_getstring(settingfile, &error, "Scheme", "name");
+ if(!schemename) {
+ return flib_cfg_from_ini_handleError(NULL, settingfile);
+ }
+
+ flib_cfg *result = flib_cfg_create(meta, schemename);
+
+ for(int i=0; i<meta->settingCount; i++) {
+ char *key = inihelper_createDictKey("BasicSettings", meta->settings[i].iniName);
+ if(!key) {
+ return flib_cfg_from_ini_handleError(result, settingfile);
+ }
+ result->settings[i] = iniparser_getint(settingfile, key, meta->settings[i].def);
+ free(key);
+ }
+ for(int i=0; i<meta->modCount; i++) {
+ char *key = inihelper_createDictKey("GameMods", meta->mods[i].iniName);
+ if(!key) {
+ return flib_cfg_from_ini_handleError(result, settingfile);
+ }
+ result->mods[i] = iniparser_getboolean(settingfile, key, false);
+ free(key);
+ }
+ iniparser_freedict(settingfile);
+ return result;
+}
+
+int flib_cfg_to_ini(const flib_cfg_meta *meta, const char *filename, const flib_cfg *config) {
+ int result = -1;
+ if(meta && filename && config && config->modCount==meta->modCount && config->settingCount==meta->settingCount) {
+ dictionary *dict = dictionary_new(0);
+ if(dict) {
+ bool error = false;
+ // Add the sections
+ error |= iniparser_set(dict, "Scheme", NULL);
+ error |= iniparser_set(dict, "BasicSettings", NULL);
+ error |= iniparser_set(dict, "GameMods", NULL);
+
+ // Add the values
+ error |= inihelper_setstr(dict, "Scheme", "name", config->schemeName);
+ for(int i=0; i<config->settingCount; i++) {
+ error |= inihelper_setint(dict, "BasicSettings", meta->settings[i].iniName, config->settings[i]);
+ }
+ for(int i=0; i<config->modCount; i++) {
+ error |= inihelper_setbool(dict, "GameMods", meta->mods[i].iniName, config->mods[i]);
+ }
+ if(!error) {
+ FILE *inifile = fopen(filename, "wb");
+ if(inifile) {
+ iniparser_dump_ini(dict, inifile);
+ fclose(inifile);
+ result = 0;
+ }
+ }
+ dictionary_del(dict);
+ }
+ }
+ return result;
+}
+
+void flib_cfg_destroy(flib_cfg* cfg) {
+ if(cfg) {
+ free(cfg->mods);
+ free(cfg->settings);
+ free(cfg->schemeName);
+ free(cfg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/cfg.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,55 @@
+/**
+ * Data structures for game scheme information.
+ *
+ * Important conventions:
+ * - All data structures own what they point to.
+ * - Strings are never null pointers.
+ */
+
+#ifndef CFG_H_
+#define CFG_H_
+
+#include <stdbool.h>
+
+typedef struct {
+ char *iniName;
+ char *title;
+ char *engineCommand;
+ char *image;
+ int netplayIndex;
+ bool checkOverMax;
+ bool times1000;
+ int def;
+ int min;
+ int max;
+} flib_cfg_setting_meta;
+
+typedef struct {
+ char *iniName;
+ int bitmaskIndex;
+} flib_cfg_mod_meta;
+
+typedef struct {
+ int settingCount;
+ int modCount;
+ flib_cfg_setting_meta *settings;
+ flib_cfg_mod_meta *mods;
+} flib_cfg_meta;
+
+typedef struct {
+ int settingCount;
+ int modCount;
+ char *schemeName;
+ int *settings;
+ bool *mods;
+} flib_cfg;
+
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *settingpath, const char *modpath);
+void flib_cfg_meta_destroy(flib_cfg_meta *metainfo);
+
+flib_cfg *flib_cfg_create(const flib_cfg_meta *meta, const char *schemeName);
+flib_cfg *flib_cfg_from_ini(const flib_cfg_meta *meta, const char *filename);
+int flib_cfg_to_ini(const flib_cfg_meta *meta, const char *filename, const flib_cfg *config);
+void flib_cfg_destroy(flib_cfg* cfg);
+
+#endif /* CFG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 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 Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,95 @@
+#include "map.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+
+flib_map *flib_map_create_regular(const char *theme, int templateFilter) {
+ flib_map *result = NULL;
+ if(!theme) {
+ flib_log_e("null parameter in flib_map_create_regular");
+ } else {
+ flib_map *newmap = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ 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 = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ 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 = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ 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 = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ 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;
+}
+
+void flib_map_destroy(flib_map *map) {
+ if(map) {
+ free(map->drawData);
+ free(map->name);
+ free(map->theme);
+ free(map);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,87 @@
+/**
+ * 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 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);
+
+/**
+ * Free the memory taken up by the map. Passing NULL is allowed and does nothing.
+ */
+void flib_map_destroy(flib_map *map);
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,1 @@
+#include "team.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,51 @@
+#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
+
+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 deaths;
+ int kills;
+ int suicides;
+
+ // These settings are sometimes used on a per-team basis.
+ int difficulty;
+ int initialHealth;
+} flib_hog;
+
+typedef struct {
+ flib_hog hogs[HEDGEHOGS_PER_TEAM];
+ char *name;
+ char *grave;
+ char *fort;
+ char *voicepack;
+ char *flag;
+
+ // TODO binds
+
+ // Transient settings used in game setup
+ uint32_t color;
+ int hogsInGame;
+ bool remoteDriven;
+ char *hash;
+
+ // This setting is sometimes used on a per-game basis.
+ flib_weaponset *weaponset;
+} flib_team;
+
+#endif /* TEAM_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,128 @@
+#include "weapon.h"
+
+#include "../iniparser/iniparser.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+int set_field(char field[WEAPONS_COUNT+1], const char *line, bool no9) {
+ // Validate the new string
+ for(int i=0; i<WEAPONS_COUNT && line[i]; i++) {
+ if(line[i] < '0' || line[i] > '9' || (no9 && line[i] == '9')) {
+ flib_log_e("Invalid character in weapon config string \"%.*s\", position %i", WEAPONS_COUNT, line, i);
+ return -1;
+ }
+ }
+
+ bool lineEnded = false;
+ for(int i=0; i<WEAPONS_COUNT; i++) {
+ if(!lineEnded && !line[i]) {
+ flib_log_w("Incomplete weapon config line \"%s\", filling with zeroes.", line);
+ lineEnded = true;
+ }
+ if(lineEnded) {
+ field[i] = '0';
+ } else {
+ field[i] = line[i];
+ }
+ }
+ field[WEAPONS_COUNT] = 0;
+ return 0;
+}
+
+static flib_weaponset *flib_weaponset_create_str(const char *name, const char *loadoutStr, const char *crateProbStr, const char *crateAmmoStr, const char *delayStr) {
+ flib_weaponset *result = NULL;
+ if(!name || !loadoutStr || !crateProbStr || !crateAmmoStr || !delayStr) {
+ flib_log_e("null parameter in flib_weaponset_create_str");
+ } else {
+ flib_weaponset *newSet = calloc(1, sizeof(flib_weaponset));
+ char *nameCopy = flib_strdupnull(name);
+ if(newSet && nameCopy) {
+ newSet->name = nameCopy;
+ nameCopy = NULL;
+ bool error = false;
+ error |= set_field(newSet->loadout, loadoutStr, false);
+ error |= set_field(newSet->crateprob, crateProbStr, false);
+ error |= set_field(newSet->crateammo, crateAmmoStr, false);
+ error |= set_field(newSet->delay, delayStr, false);
+ if(!error) {
+ result = newSet;
+ newSet = NULL;
+ }
+ }
+ free(nameCopy);
+ flib_weaponset_destroy(newSet);
+ }
+ return result;
+}
+
+void flib_weaponset_destroy(flib_weaponset *cfg) {
+ if(cfg) {
+ free(cfg->name);
+ free(cfg);
+ }
+}
+
+flib_weaponset *flib_weaponset_create(const char *name) {
+ return flib_weaponset_create_str(name, AMMOLINE_DEFAULT_QT, AMMOLINE_DEFAULT_PROB, AMMOLINE_DEFAULT_CRATE, AMMOLINE_DEFAULT_DELAY);
+}
+
+flib_weaponset *flib_weaponset_from_ini(const char *filename) {
+ flib_weaponset *result = NULL;
+ if(!filename) {
+ flib_log_e("null parameter in flib_weaponset_from_ini");
+ } else {
+ dictionary *settingfile = iniparser_load(filename);
+ if(!settingfile) {
+ flib_log_e("Error loading weapon scheme file %s", filename);
+ } else {
+ bool error = false;
+ char *name = inihelper_getstring(settingfile, &error, "weaponset", "name");
+ char *loadout = inihelper_getstring(settingfile, &error, "weaponset", "loadout");
+ char *crateprob = inihelper_getstring(settingfile, &error, "weaponset", "crateprob");
+ char *crateammo = inihelper_getstring(settingfile, &error, "weaponset", "crateammo");
+ char *delay = inihelper_getstring(settingfile, &error, "weaponset", "delay");
+ if(error) {
+ flib_log_e("Missing key in weapon scheme file %s", filename);
+ } else {
+ result = flib_weaponset_create_str(name, loadout, crateprob, crateammo, delay);
+ }
+ }
+ iniparser_freedict(settingfile);
+ }
+ return result;
+}
+
+int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set) {
+ int result = -1;
+ if(!filename || !set) {
+ flib_log_e("null parameter in flib_weaponset_to_ini");
+ } else {
+ dictionary *dict = dictionary_new(0);
+ if(dict) {
+ bool error = false;
+ // Add the sections
+ error |= iniparser_set(dict, "weaponset", NULL);
+
+ // Add the values
+ error |= inihelper_setstr(dict, "weaponset", "name", set->name);
+ error |= inihelper_setstr(dict, "weaponset", "loadout", set->loadout);
+ error |= inihelper_setstr(dict, "weaponset", "crateprob", set->crateprob);
+ error |= inihelper_setstr(dict, "weaponset", "crateammo", set->crateammo);
+ error |= inihelper_setstr(dict, "weaponset", "delay", set->delay);
+ if(!error) {
+ FILE *inifile = fopen(filename, "wb");
+ if(inifile) {
+ iniparser_dump_ini(dict, inifile);
+ fclose(inifile);
+ result = 0;
+ }
+ }
+ dictionary_del(dict);
+ }
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,32 @@
+#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 might actually be invalid, it's not possible to set more than 8 in the QtFrontend. (TODO)
+ */
+typedef struct {
+ char loadout[WEAPONS_COUNT+1];
+ char crateprob[WEAPONS_COUNT+1];
+ char crateammo[WEAPONS_COUNT+1];
+ char delay[WEAPONS_COUNT+1];
+ char *name;
+} flib_weaponset;
+
+/**
+ * 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);
+flib_weaponset *flib_weaponset_from_ini(const char *filename);
+int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set);
+void flib_weaponset_destroy(flib_weaponset *set);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,164 @@
+#include "socket.h"
+#include "util/logging.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 = malloc(sizeof(_flib_tcpsocket));
+ if(!result) {
+ flib_log_e("Can't allocate socket: Out of memory!");
+ 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 = malloc(sizeof(_flib_acceptor));
+ if(!result) {
+ flib_log_e("Can't allocate acceptor: Out of memory!");
+ 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 *acceptorptr) {
+ if(!acceptorptr) {
+ flib_log_e("Call to flib_acceptor_close with acceptorptr==null");
+ } else if(*acceptorptr) {
+ SDLNet_TCP_Close((*acceptorptr)->sock);
+ free(*acceptorptr);
+ *acceptorptr = NULL;
+ }
+}
+
+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;
+}
+
+void flib_socket_close(flib_tcpsocket *sockptr) {
+ if(!sockptr) {
+ flib_log_e("Call to flib_socket_close with sockptr==null");
+ } else if(*sockptr) {
+ flib_tcpsocket sock = *sockptr;
+ SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
+ SDLNet_TCP_Close(sock->sock);
+ SDLNet_FreeSocketSet(sock->sockset);
+ free(sock);
+ *sockptr = NULL;
+ }
+}
+
+int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen) {
+ if(!sock || (maxlen>0 && !data)) {
+ flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null");
+ return -1;
+ }
+ int readySockets = SDLNet_CheckSockets(sock->sockset, 0);
+ if(readySockets>0) {
+ int size = SDLNet_TCP_Recv(sock->sock, data, maxlen);
+ return size>0 ? size : -1;
+ } else if(readySockets==0) {
+ return 0;
+ } else {
+ flib_log_e("Error in select system call: %s", SDLNet_GetError());
+ return -1;
+ }
+}
+
+int flib_socket_send(flib_tcpsocket sock, const void *data, int len) {
+ if(!sock || (len>0 && !data)) {
+ flib_log_e("Call to flib_socket_send with sock==null or data==null");
+ return -1;
+ }
+ return SDLNet_TCP_Send(sock->sock, data, len);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,67 @@
+/*
+ * 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, free its memory and set it to NULL.
+ * If the acceptor is already NULL, nothing happens.
+ */
+void flib_acceptor_close(flib_acceptor *acceptorptr);
+
+/**
+ * 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);
+
+/**
+ * Close the socket, free its memory and set it to NULL.
+ * 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/util/buffer.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,90 @@
+#include "buffer.h"
+#include "logging.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+typedef struct _flib_vector {
+ void *data;
+ size_t size;
+ size_t capacity;
+} _flib_vector;
+
+flib_vector flib_vector_create() {
+ flib_vector result = malloc(sizeof(_flib_vector));
+ if(result == NULL) {
+ return NULL;
+ }
+ result->data = malloc(16);
+ if(result->data == NULL) {
+ free(result);
+ return NULL;
+ }
+ result->size = 0;
+ result->capacity = 16;
+ return result;
+}
+
+void flib_vector_destroy(flib_vector *vec) {
+ if(vec && *vec) {
+ free((*vec)->data);
+ free(*vec);
+ *vec = NULL;
+ }
+}
+
+static void try_realloc(flib_vector vec, size_t newCapacity) {
+ void *newData = realloc(vec->data, newCapacity);
+ if(newData) {
+ vec->data = newData;
+ vec->capacity = newCapacity;
+ }
+}
+
+static size_t getFreeCapacity(flib_vector vec) {
+ return vec->capacity - vec->size;
+}
+
+int flib_vector_append(flib_vector vec, const void *data, size_t len) {
+ if(getFreeCapacity(vec) < len) {
+ // 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 = len - getFreeCapacity(vec);
+ if(extraCapacity < minExtraCapacity) {
+ extraCapacity = minExtraCapacity;
+ }
+
+ if(extraCapacity <= SIZE_MAX - vec->capacity) {
+ try_realloc(vec, vec->capacity+extraCapacity);
+ }
+
+ // Check if we were able to resize.
+ // If not, try to allocate at least what we need...
+ if(getFreeCapacity(vec) < len) {
+ try_realloc(vec, vec->capacity+minExtraCapacity);
+
+ // Still not working? Then we fail.
+ if(getFreeCapacity(vec) < len) {
+ return 0;
+ }
+ }
+ }
+
+ memmove(vec->data + vec->size, data, len);
+ vec->size += len;
+ return len;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector vec) {
+ flib_buffer result = {vec->data, vec->size};
+ return result;
+}
+
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
+ flib_constbuffer result = {vec->data, vec->size};
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,58 @@
+#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 (opaque type).
+ */
+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 and set it to NULL.
+ */
+void flib_vector_destroy(flib_vector *vec);
+
+/**
+ * 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 an out of memory situation occurs.
+ */
+int flib_vector_append(flib_vector vec, const void *data, size_t len);
+
+/**
+ * Return a buffer or constbuffer 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);
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,168 @@
+#include "inihelper.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>
+
+static bool keychar_needs_urlencoding(char c) {
+ return !isalnum(c);
+}
+
+char *inihelper_urlencode(const char *inbuf) {
+ if(!inbuf) {
+ return NULL;
+ }
+ size_t insize = strlen(inbuf);
+ if(insize > SIZE_MAX/4) {
+ return NULL;
+ }
+
+ char *outbuf = malloc(insize*3+1);
+ if(!outbuf) {
+ return NULL;
+ }
+
+ size_t inpos = 0, outpos = 0;
+ while(inbuf[inpos]) {
+ if(!keychar_needs_urlencoding(inbuf[inpos])) {
+ outbuf[outpos++] = inbuf[inpos++];
+ } else {
+ if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+ free(outbuf);
+ return NULL;
+ }
+ inpos++;
+ outpos += 3;
+ }
+ }
+ outbuf[outpos] = 0;
+ return outbuf;
+}
+
+char *inihelper_urldecode(const char *inbuf) {
+ char *outbuf = 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;
+ return outbuf;
+}
+
+char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
+ if(!sectionName || !keyName) {
+ return NULL;
+ }
+ return flib_asprintf("%s:%s", sectionName, keyName);
+}
+
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+ if(!inifile || !sectionName || !keyName) {
+ *error = true;
+ return NULL;
+ }
+ char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+ if(!extendedkey) {
+ *error = true;
+ return NULL;
+ }
+ char *result = iniparser_getstring(inifile, extendedkey, NULL);
+ free(extendedkey);
+ if(!result) {
+ flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
+ *error = true;
+ }
+ return result;
+}
+
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+ return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
+}
+
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+ char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+ if(!value) {
+ return 0;
+ } else {
+ errno = 0;
+ long val = strtol(value, NULL, 10);
+ if(errno!=0) {
+ *error = true;
+ return 0;
+ }
+ if(val<INT_MIN || val>INT_MAX) {
+ *error = true;
+ return 0;
+ }
+ return (int)val;
+ }
+}
+
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+ char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+ if(!value) {
+ return false;
+ } else {
+ bool trueval = strchr("1tTyY", value[0]);
+ bool falseval = strchr("0fFnN", value[0]);
+ if(!trueval && !falseval) {
+ *error = true;
+ return false;
+ } else {
+ return trueval;
+ }
+ }
+}
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) {
+ int result = -1;
+ if(!dict || !sectionName || !keyName || !value) {
+ flib_log_e("inihelper_setstr called with bad parameters");
+ } else {
+ char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+ if(extendedkey) {
+ result = iniparser_set(dict, extendedkey, value);
+ free(extendedkey);
+ }
+ }
+ return result;
+}
+
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) {
+ int result = -1;
+ if(!dict || !sectionName || !keyName) {
+ flib_log_e("inihelper_setint called with bad parameters");
+ } else {
+ char *strvalue = flib_asprintf("%i", value);
+ if(strvalue) {
+ result = inihelper_setstr(dict, sectionName, keyName, strvalue);
+ free(strvalue);
+ }
+ }
+ return result;
+}
+
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) {
+ int result = -1;
+ if(!dict || !sectionName || !keyName) {
+ flib_log_e("inihelper_setint called with bad parameters");
+ } else {
+ result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,54 @@
+/**
+ * Some helper functions for working with the iniparser functions - in particular,
+ * for interoperability with the ini format used by the QtSettings class.
+ */
+
+#ifndef INIHELPER_H_
+#define INIHELPER_H_
+
+#include "../iniparser/iniparser.h"
+
+#include <stdbool.h>
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urlencode(const char *inbuf);
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urldecode(const char *inbuf);
+
+/**
+ * Create a key in the format "sectionName:keyName"
+ * Returned buffer must be free()d
+ */
+char *inihelper_createDictKey(const char *sectionName, const char *keyName);
+
+/**
+ * Returns an internal buffer, don't modify or free
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Returned buffer must be free()d
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
+#endif /* INIHELPER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,87 @@
+#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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,25 @@
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+#include<stdint.h>
+#include <stdio.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
+
+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);
+
+#endif /* LOGGING_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,49 @@
+#include "util.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.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;
+ int requiredSize = vsnprintf(NULL, 0, fmt, args)+1; // Figure out how much memory we need,
+ if(requiredSize>=0) {
+ char *tmpbuf = malloc(requiredSize); // allocate it
+ if(tmpbuf) {
+ if(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 = malloc(size);
+ if(result) {
+ memcpy(result, buf, size);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h Mon Jun 11 00:06:22 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef FLIB_UTIL_H_
+#define FLIB_UTIL_H_
+
+#include <stddef.h>
+#include <stdarg.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 va_args.
+ */
+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);
+
+#endif