--- a/.hgignore Wed Jun 27 09:16:34 2012 -0400
+++ b/.hgignore Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/QTfrontend/CMakeLists.txt Wed Jun 27 18:04:17 2012 +0200
@@ -130,7 +130,6 @@
achievements.h
binds.h
ui_hwform.h
- KB.h
hwconsts.h
sdlkeys.h
)
--- a/QTfrontend/game.cpp Wed Jun 27 09:16:34 2012 -0400
+++ b/QTfrontend/game.cpp Wed Jun 27 18:04:17 2012 +0200
@@ -28,7 +28,6 @@
#include "gameuiconfig.h"
#include "gamecfgwidget.h"
#include "teamselect.h"
-#include "KB.h"
#include "proto.h"
#include "ThemeModel.h"
@@ -225,20 +224,6 @@
emit ErrorMessage(QString("Last two engine messages:\n") + QString().append(msg.mid(2)).left(size - 4));
return;
}
- case 'K':
- {
- ulong kb = msg.mid(2).toULong();
- if (kb==1)
- {
- qWarning("%s", KBMessages[kb - 1].toLocal8Bit().constData());
- return;
- }
- if (kb && kb <= KBmsgsCount)
- {
- emit ErrorMessage(KBMessages[kb - 1]);
- }
- return;
- }
case 'i':
{
emit GameStats(msg.at(2), QString::fromUtf8(msg.mid(3)));
@@ -264,7 +249,6 @@
int size = msg.size();
QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4));
emit SendChat(msgbody);
- // FIXME: /me command doesn't work here
QByteArray buf;
HWProto::addStringToBuffer(buf, "s" + HWProto::formatChatMsg(config->netNick(), msgbody) + "\x20\x20");
demo.append(buf);
@@ -283,8 +267,7 @@
{
emit SendNet(msg);
}
- if (msg.at(1) != 's')
- demo.append(msg);
+ demo.append(msg);
}
}
}
--- a/QTfrontend/net/newnetclient.h Wed Jun 27 09:16:34 2012 -0400
+++ b/QTfrontend/net/newnetclient.h Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Wed Jun 27 18:04:17 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 Wed Jun 27 09:16:34 2012 -0400
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java Wed Jun 27 18:04:17 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/base64/base64.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,423 @@
+/* base64.c -- Encode binary data using printable characters.
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Simon Josefsson. Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 3548 <http://www.ietf.org/rfc/rfc3548.txt>.
+ *
+ * Be careful with error checking. Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ * FAIL: input was not valid base64
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ * FAIL: input too long
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+ return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+ If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+ possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+ terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen)
+{
+ static const char b64str[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ while (inlen && outlen)
+ {
+ *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ = b64str[((to_uchar (in[0]) << 4)
+ + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+ & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ =
+ (inlen
+ ? b64str[((to_uchar (in[1]) << 2)
+ + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+ & 0x3f]
+ : '=');
+ if (!--outlen)
+ break;
+ *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+ if (!--outlen)
+ break;
+ if (inlen)
+ inlen--;
+ if (inlen)
+ in += 3;
+ }
+
+ if (outlen)
+ *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+ from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+ the length of the encoded data, excluding the terminating zero. On
+ return, the OUT variable will hold a pointer to newly allocated
+ memory that must be deallocated by the caller. If output string
+ length would overflow, 0 is returned and OUT is set to NULL. If
+ memory allocation failed, OUT is set to NULL, and the return value
+ indicates length of the requested memory block, i.e.,
+ BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+ size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+ /* Check for overflow in outlen computation.
+ *
+ * If there is no overflow, outlen >= inlen.
+ *
+ * If the operation (inlen + 2) overflows then it yields at most +1, so
+ * outlen is 0.
+ *
+ * If the multiplication overflows, we lose at least half of the
+ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+ * (inlen > 4).
+ */
+ if (inlen > outlen)
+ {
+ *out = NULL;
+ return 0;
+ }
+
+ *out = malloc (outlen);
+ if (!*out)
+ return outlen;
+
+ base64_encode (in, inlen, *out, outlen);
+
+ return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+ (think EBCDIC). However, it does assume that the characters in the
+ Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX
+ 1003.1-2001 require that char and unsigned char are 8-bit
+ quantities, though, taking care of that problem. But this may be a
+ potential problem on non-POSIX C99 platforms.
+
+ IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+ as the formal parameter rather than "x". */
+#define B64(_) \
+ ((_) == 'A' ? 0 \
+ : (_) == 'B' ? 1 \
+ : (_) == 'C' ? 2 \
+ : (_) == 'D' ? 3 \
+ : (_) == 'E' ? 4 \
+ : (_) == 'F' ? 5 \
+ : (_) == 'G' ? 6 \
+ : (_) == 'H' ? 7 \
+ : (_) == 'I' ? 8 \
+ : (_) == 'J' ? 9 \
+ : (_) == 'K' ? 10 \
+ : (_) == 'L' ? 11 \
+ : (_) == 'M' ? 12 \
+ : (_) == 'N' ? 13 \
+ : (_) == 'O' ? 14 \
+ : (_) == 'P' ? 15 \
+ : (_) == 'Q' ? 16 \
+ : (_) == 'R' ? 17 \
+ : (_) == 'S' ? 18 \
+ : (_) == 'T' ? 19 \
+ : (_) == 'U' ? 20 \
+ : (_) == 'V' ? 21 \
+ : (_) == 'W' ? 22 \
+ : (_) == 'X' ? 23 \
+ : (_) == 'Y' ? 24 \
+ : (_) == 'Z' ? 25 \
+ : (_) == 'a' ? 26 \
+ : (_) == 'b' ? 27 \
+ : (_) == 'c' ? 28 \
+ : (_) == 'd' ? 29 \
+ : (_) == 'e' ? 30 \
+ : (_) == 'f' ? 31 \
+ : (_) == 'g' ? 32 \
+ : (_) == 'h' ? 33 \
+ : (_) == 'i' ? 34 \
+ : (_) == 'j' ? 35 \
+ : (_) == 'k' ? 36 \
+ : (_) == 'l' ? 37 \
+ : (_) == 'm' ? 38 \
+ : (_) == 'n' ? 39 \
+ : (_) == 'o' ? 40 \
+ : (_) == 'p' ? 41 \
+ : (_) == 'q' ? 42 \
+ : (_) == 'r' ? 43 \
+ : (_) == 's' ? 44 \
+ : (_) == 't' ? 45 \
+ : (_) == 'u' ? 46 \
+ : (_) == 'v' ? 47 \
+ : (_) == 'w' ? 48 \
+ : (_) == 'x' ? 49 \
+ : (_) == 'y' ? 50 \
+ : (_) == 'z' ? 51 \
+ : (_) == '0' ? 52 \
+ : (_) == '1' ? 53 \
+ : (_) == '2' ? 54 \
+ : (_) == '3' ? 55 \
+ : (_) == '4' ? 56 \
+ : (_) == '5' ? 57 \
+ : (_) == '6' ? 58 \
+ : (_) == '7' ? 59 \
+ : (_) == '8' ? 60 \
+ : (_) == '9' ? 61 \
+ : (_) == '+' ? 62 \
+ : (_) == '/' ? 63 \
+ : -1)
+
+static const signed char b64[0x100] = {
+ B64 (0), B64 (1), B64 (2), B64 (3),
+ B64 (4), B64 (5), B64 (6), B64 (7),
+ B64 (8), B64 (9), B64 (10), B64 (11),
+ B64 (12), B64 (13), B64 (14), B64 (15),
+ B64 (16), B64 (17), B64 (18), B64 (19),
+ B64 (20), B64 (21), B64 (22), B64 (23),
+ B64 (24), B64 (25), B64 (26), B64 (27),
+ B64 (28), B64 (29), B64 (30), B64 (31),
+ B64 (32), B64 (33), B64 (34), B64 (35),
+ B64 (36), B64 (37), B64 (38), B64 (39),
+ B64 (40), B64 (41), B64 (42), B64 (43),
+ B64 (44), B64 (45), B64 (46), B64 (47),
+ B64 (48), B64 (49), B64 (50), B64 (51),
+ B64 (52), B64 (53), B64 (54), B64 (55),
+ B64 (56), B64 (57), B64 (58), B64 (59),
+ B64 (60), B64 (61), B64 (62), B64 (63),
+ B64 (64), B64 (65), B64 (66), B64 (67),
+ B64 (68), B64 (69), B64 (70), B64 (71),
+ B64 (72), B64 (73), B64 (74), B64 (75),
+ B64 (76), B64 (77), B64 (78), B64 (79),
+ B64 (80), B64 (81), B64 (82), B64 (83),
+ B64 (84), B64 (85), B64 (86), B64 (87),
+ B64 (88), B64 (89), B64 (90), B64 (91),
+ B64 (92), B64 (93), B64 (94), B64 (95),
+ B64 (96), B64 (97), B64 (98), B64 (99),
+ B64 (100), B64 (101), B64 (102), B64 (103),
+ B64 (104), B64 (105), B64 (106), B64 (107),
+ B64 (108), B64 (109), B64 (110), B64 (111),
+ B64 (112), B64 (113), B64 (114), B64 (115),
+ B64 (116), B64 (117), B64 (118), B64 (119),
+ B64 (120), B64 (121), B64 (122), B64 (123),
+ B64 (124), B64 (125), B64 (126), B64 (127),
+ B64 (128), B64 (129), B64 (130), B64 (131),
+ B64 (132), B64 (133), B64 (134), B64 (135),
+ B64 (136), B64 (137), B64 (138), B64 (139),
+ B64 (140), B64 (141), B64 (142), B64 (143),
+ B64 (144), B64 (145), B64 (146), B64 (147),
+ B64 (148), B64 (149), B64 (150), B64 (151),
+ B64 (152), B64 (153), B64 (154), B64 (155),
+ B64 (156), B64 (157), B64 (158), B64 (159),
+ B64 (160), B64 (161), B64 (162), B64 (163),
+ B64 (164), B64 (165), B64 (166), B64 (167),
+ B64 (168), B64 (169), B64 (170), B64 (171),
+ B64 (172), B64 (173), B64 (174), B64 (175),
+ B64 (176), B64 (177), B64 (178), B64 (179),
+ B64 (180), B64 (181), B64 (182), B64 (183),
+ B64 (184), B64 (185), B64 (186), B64 (187),
+ B64 (188), B64 (189), B64 (190), B64 (191),
+ B64 (192), B64 (193), B64 (194), B64 (195),
+ B64 (196), B64 (197), B64 (198), B64 (199),
+ B64 (200), B64 (201), B64 (202), B64 (203),
+ B64 (204), B64 (205), B64 (206), B64 (207),
+ B64 (208), B64 (209), B64 (210), B64 (211),
+ B64 (212), B64 (213), B64 (214), B64 (215),
+ B64 (216), B64 (217), B64 (218), B64 (219),
+ B64 (220), B64 (221), B64 (222), B64 (223),
+ B64 (224), B64 (225), B64 (226), B64 (227),
+ B64 (228), B64 (229), B64 (230), B64 (231),
+ B64 (232), B64 (233), B64 (234), B64 (235),
+ B64 (236), B64 (237), B64 (238), B64 (239),
+ B64 (240), B64 (241), B64 (242), B64 (243),
+ B64 (244), B64 (245), B64 (246), B64 (247),
+ B64 (248), B64 (249), B64 (250), B64 (251),
+ B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+ false otherwise. Note that '=' is padding and not considered to be
+ part of the alphabet. */
+bool
+isbase64 (char ch)
+{
+ return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Decode base64 encoded input array IN of length INLEN to output
+ array OUT that can hold *OUTLEN bytes. Return true if decoding was
+ successful, i.e. if the input was valid base64 data, false
+ otherwise. If *OUTLEN is too small, as many bytes as possible will
+ be written to OUT. On return, *OUTLEN holds the length of decoded
+ bytes in OUT. Note that as soon as any non-alphabet characters are
+ encountered, decoding is stopped and false is returned. This means
+ that, when applicable, you must remove any line terminators that is
+ part of the data stream before calling this function. */
+bool
+base64_decode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen)
+{
+ size_t outleft = *outlen;
+
+ while (inlen >= 2)
+ {
+ if (!isbase64 (in[0]) || !isbase64 (in[1]))
+ break;
+
+ if (outleft)
+ {
+ *out++ = ((b64[to_uchar (in[0])] << 2)
+ | (b64[to_uchar (in[1])] >> 4));
+ outleft--;
+ }
+
+ if (inlen == 2)
+ break;
+
+ if (in[2] == '=')
+ {
+ if (inlen != 4)
+ break;
+
+ if (in[3] != '=')
+ break;
+
+ }
+ else
+ {
+ if (!isbase64 (in[2]))
+ break;
+
+ if (outleft)
+ {
+ *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+ | (b64[to_uchar (in[2])] >> 2));
+ outleft--;
+ }
+
+ if (inlen == 3)
+ break;
+
+ if (in[3] == '=')
+ {
+ if (inlen != 4)
+ break;
+ }
+ else
+ {
+ if (!isbase64 (in[3]))
+ break;
+
+ if (outleft)
+ {
+ *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+ | b64[to_uchar (in[3])]);
+ outleft--;
+ }
+ }
+ }
+
+ in += 4;
+ inlen -= 4;
+ }
+
+ *outlen -= outleft;
+
+ if (inlen != 0)
+ return false;
+
+ return true;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+ data stored in IN of size INLEN to the *OUT buffer. On return, the
+ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
+ if the caller is not interested in the decoded length. *OUT may be
+ NULL to indicate an out of memory error, in which case *OUTLEN
+ contains the size of the memory block needed. The function returns
+ true on successful decoding and memory allocation errors. (Use the
+ *OUT and *OUTLEN parameters to differentiate between successful
+ decoding and memory error.) The function returns false if the
+ input was invalid, in which case *OUT is NULL and *OUTLEN is
+ undefined. */
+bool
+base64_decode_alloc (const char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ /* This may allocate a few bytes too much, depending on input,
+ but it's not worth the extra CPU time to compute the exact amount.
+ The exact amount is 3 * inlen / 4, minus 1 if the input ends
+ with "=" and minus another 1 if the input ends with "==".
+ Dividing before multiplying avoids the possibility of overflow. */
+ size_t needlen = 3 * (inlen / 4) + 2;
+
+ *out = malloc (needlen);
+ if (!*out)
+ return true;
+
+ if (!base64_decode (in, inlen, *out, &needlen))
+ {
+ free (*out);
+ *out = NULL;
+ return false;
+ }
+
+ if (outlen)
+ *outlen = needlen;
+
+ return true;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/base64/base64.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,45 @@
+/* base64.h -- Encode binary data using printable characters.
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+ Written by Simon Josefsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+ integer >= n/k, i.e., the ceiling of n/k. */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+extern bool isbase64 (char ch);
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern bool base64_decode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc (const char *in, size_t inlen,
+ char **out, size_t *outlen);
+
+#endif /* BASE64_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/cmdlineClient.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,474 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "util/buffer.h"
+#include "util/util.h"
+#include "util/list.h"
+#include "model/map.h"
+#include "model/weapon.h"
+#include "model/schemelist.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+#include "net/netconn.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <conio.h>
+#include <windows.h>
+
+#define ENGINE_DIR ".\\"
+#define CONFIG_DIR "..\\share\\hedgewars"
+#define DATA_DIR CONFIG_DIR"\\Data"
+
+static flib_netconn *netconn;
+static flib_gameconn *gameconn;
+static flib_mapconn *mapconn;
+static char nickname[128];
+static flib_cfg_meta *metacfg;
+static bool netConnected = false;
+
+// Callback function that will be called when the map is rendered
+static void handleMapGenerated(void *context, const uint8_t *bitmap, int numHedgehogs) {
+ printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+ // Draw the map as ASCII art
+ for(int y=0; y<MAPIMAGE_HEIGHT; y+=8) {
+ for(int x=0; x<MAPIMAGE_WIDTH; x+=6) {
+ int pixelnum = x + y*MAPIMAGE_WIDTH;
+ bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+ printf(pixel ? "#" : " ");
+ }
+ printf("\n");
+ }
+
+ // Destroy the connection object (this will end the "tick" loop below)
+ flib_mapconn_destroy(mapconn);
+ mapconn = NULL;
+}
+
+static void onGameDisconnect(void *context, int reason) {
+ flib_log_i("Connection closed. Reason: %i", reason);
+ flib_gameconn_destroy(gameconn);
+ gameconn = NULL;
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+ flib_log_e("Map rendering failed: %s", errormessage);
+
+ // Destroy the connection object (this will end the "tick" loop below)
+ flib_mapconn_destroy(mapconn);
+ mapconn = NULL;
+}
+
+static void startEngineMap(int port) {
+ char cmdbuffer[255];
+ char argbuffer[255];
+ snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+ snprintf(argbuffer, 255, "%s %i landpreview", CONFIG_DIR, port);
+ ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+static void startEngineGame(int port) {
+ char cmdbuffer[255];
+ char argbuffer[255];
+ snprintf(cmdbuffer, 255, "%shwengine.exe", ENGINE_DIR);
+ snprintf(argbuffer, 255, "%s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", CONFIG_DIR, port, DATA_DIR);
+ ShellExecute(NULL, NULL, cmdbuffer, argbuffer, NULL, SW_HIDE);
+}
+
+void testMapPreview() {
+ // Create a map description and check that there was no error
+ flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS);
+ assert(map);
+
+ // Create a new connection to the engine and check that there was no error
+ flib_mapconn *mapConnection = flib_mapconn_create(map);
+ assert(mapConnection);
+
+ // We don't need the map description anymore
+ flib_map_release(map);
+ map = NULL;
+
+ // Register the callback functions
+ flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+ flib_mapconn_onSuccess(mapConnection, &handleMapGenerated, &mapConnection);
+
+ // Start the engine process and tell it which port the frontlib is listening on
+ startEngineMap(flib_mapconn_getport(mapConnection));
+
+ // Usually, flib_mapconn_tick will be called in an event loop that runs several
+ // times per second. It handles I/O operations and progress, and calls
+ // callbacks when something interesting happens.
+ while(mapConnection) {
+ flib_mapconn_tick(mapConnection);
+ }
+}
+
+void handleNetDisconnect(void *context, int reason, const char *message) {
+ printf("Disconnected: %s", message);
+ flib_netconn_destroy(netconn);
+ netconn = NULL;
+}
+
+void printRoomList() {
+ const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+ if(roomlist) {
+ for(int i=0; i<roomlist->roomCount; i++) {
+ if(i>0) {
+ printf(", ");
+ }
+ flib_room *room = roomlist->rooms[i];
+ printf("%s", room->name);
+ }
+ puts("\n");
+ } else {
+ puts("Sorry, due to an error the room list is not available.");
+ }
+}
+
+void printTeamList() {
+ flib_gamesetup *setup = flib_netconn_create_gameSetup(netconn);
+ if(setup) {
+ puts("The following teams are in this room:");
+ for(int i=0; i<setup->teamlist->teamCount; i++) {
+ if(i>0) {
+ printf(", ");
+ }
+ printf("%s", setup->teamlist->teams[i]->name);
+ }
+ puts("\n");
+ } else {
+ puts("Sorry, due to an error the team list is not available.");
+ }
+ flib_gamesetup_destroy(setup);
+}
+
+void handleNetConnected(void *context) {
+ printf("You enter a strange house inhabited by dozens of hedgehogs. There are many rooms in here:\n");
+ printRoomList();
+ printf("\n\nNow, you can chat by just entering text, or join a room with /join <roomname>.");
+ printf(" You can also /quit or let me /describe <roomname>. Once in a room, you can /add <teamname> and set yourself /ready. You can also /list the available rooms (in the lobby) or the teams (in a room).\n");
+ netConnected = true;
+}
+
+void handleChat(void *context, const char *nick, const char *msg) {
+ printf("%s: %s\n", nick, msg);
+}
+
+void handleEnterRoom(void *context, bool isChief) {
+ puts("You have entered the room.");
+}
+
+void handleRoomJoin(void *context, const char *nick) {
+ if(strcmp(nick, nickname)) {
+ printf("%s is here.\n", nick);
+ }
+}
+
+void handleRoomLeave(void *context, const char *nick, const char *partmsg) {
+ if(strcmp(nick, nickname)) {
+ printf("%s leaves.\n", nick);
+ }
+}
+
+void handleReady(void *context, const char *nick, bool ready) {
+ if(strcmp(nick, nickname)) {
+ if(ready) {
+ printf("%s is ready to go.\n", nick);
+ } else {
+ printf("%s is not ready.\n", nick);
+ }
+ } else {
+ if(ready) {
+ printf("You are ready to go.\n");
+ } else {
+ printf("You are not ready.\n");
+ }
+ }
+}
+
+void handleEmFromNet(void *context, const uint8_t *em, size_t size) {
+ if(gameconn) {
+ flib_gameconn_send_enginemsg(gameconn, (const uint8_t*)em, size);
+ }
+}
+
+void handleEmFromEngine(void *context, const uint8_t *em, size_t size) {
+ if(netconn) {
+ flib_netconn_send_engineMessage(netconn, em, size);
+ }
+}
+
+void handleChatFromGame(void *context, const char *message, bool teamchat) {
+ if(netconn) {
+ if(teamchat) {
+ flib_netconn_send_teamchat(netconn, message);
+ } else {
+ flib_netconn_send_chat(netconn, message);
+ }
+ }
+}
+
+void handleRunGame(void *context) {
+ flib_gamesetup *gamesetup = flib_netconn_create_gameSetup(netconn);
+ if(gamesetup) {
+ gameconn = flib_gameconn_create(nickname, gamesetup, true);
+ flib_gameconn_onEngineMessage(gameconn, handleEmFromEngine, NULL);
+ flib_gameconn_onDisconnect(gameconn, onGameDisconnect, NULL);
+ flib_gameconn_onChat(gameconn, handleChatFromGame, NULL);
+ startEngineGame(flib_gameconn_getport(gameconn));
+ }
+ flib_gamesetup_destroy(gamesetup);
+}
+
+void handleNickTaken(void *context, const char *nick) {
+ printf("The nickname %s is already in use, please choose a different one:\n", nick);
+ flib_gets(nickname, sizeof(nickname));
+ flib_netconn_send_nick(netconn, nickname);
+}
+
+void handlePwRequest(void *context, const char *nick) {
+ printf("A password is required to log in as %s, please enter (warning: shown in cleartext):\n", nick);
+ char password[256];
+ flib_gets(password, sizeof(password));
+ flib_netconn_send_password(netconn, password);
+}
+
+void handleMessage(void *context, int type, const char *msg) {
+ printf("*** %s\n", msg);
+}
+
+void handleTeamAccepted(void *context, const char *teamname) {
+ printf("The team %s has been accepted.\n", teamname);
+}
+
+void handleMapChanged(void *context, const flib_map *map, int changetype) {
+ if(map->mapgen != MAPGEN_NAMED && changetype != NETCONN_MAPCHANGE_THEME) {
+ if(mapconn) {
+ flib_mapconn_destroy(mapconn);
+ mapconn = NULL;
+ }
+ mapconn = flib_mapconn_create(map);
+ if(mapconn) {
+ flib_mapconn_onSuccess(mapconn, handleMapGenerated, NULL);
+ flib_mapconn_onFailure(mapconn, handleMapFailure, NULL);
+ startEngineMap(flib_mapconn_getport(mapconn));
+ }
+ }
+}
+
+void handleLeaveRoom(void *context, int reason, const char *msg) {
+ if(reason == NETCONN_ROOMLEAVE_ABANDONED) {
+ printf("The chief has abandoned the room.");
+ } else if(reason == NETCONN_ROOMLEAVE_KICKED) {
+ printf("You have been kicked from the room.");
+ }
+ if(msg) {
+ printf(" (%s)", msg);
+ }
+ puts(" You are back in the lobby.");
+}
+
+void handleSchemeChanged(void *context, flib_cfg *scheme) {
+ printf("Game scheme: %s.\n", scheme->name);
+}
+
+void handleWeaponsetChanged(void *context, flib_weaponset *weaponset) {
+ printf("Weaponset: %s.\n", weaponset->name);
+}
+
+void handleHogcountChanged(void *context, const char *team, int count) {
+ printf("Team %s will send %i hogs into the fight.\n", team, count);
+}
+
+void handleRoomAdd(void *context, const flib_room *room) {
+ printf("%s created a new room called %s.\n", room->owner, room->name);
+}
+
+void handleRoomDelete(void *context, const char *roomName) {
+ printf("The room %s has collapsed.\n", roomName);
+}
+
+void handleScriptChanged(void *context, const char *script) {
+ printf("Game Type: %s\n", script);
+}
+
+void handleTeamAdd(void *context, flib_team *team) {
+ printf("%s puts the team %s to the planning board.\n", team->ownerName, team->name);
+}
+
+void handleTeamDelete(void *context, const char *teamName) {
+ printf("The team %s decided not to fight this battle after all.\n", teamName);
+}
+
+void handleTeamColorChanged(void *context, const char *name, int colorIndex) {
+ static const char* colorNames[] = {"red", "blue", "teal", "purple", "pink", "green", "orange", "brown", "yellow"};
+ const char *colorName = "strange";
+ if(colorIndex>=0 && colorIndex < 9) {
+ colorName = colorNames[colorIndex];
+ }
+ printf("The team %s will wear %s uniforms today.\n", name, colorName);
+}
+
+void tick() {
+ if(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+ if(netconn) {
+ flib_netconn_tick(netconn);
+ }
+ if(mapconn) {
+ flib_mapconn_tick(mapconn);
+ }
+}
+
+static HANDLE hStdin;
+
+static int init() {
+ hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ if(hStdin == INVALID_HANDLE_VALUE) {
+ flib_log_e("Unable to get stdin handle");
+ return 1;
+ }
+ if(!flib_init(0)) {
+ flib_log_setLevel(FLIB_LOGLEVEL_WARNING);
+ freopen( "CON", "w", stdout );
+ freopen( "CON", "w", stderr );
+ metacfg = flib_cfg_meta_from_ini("metasettings.ini");
+ if(!metacfg) {
+ flib_quit();
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int main(int argc, char *argv[]) {
+ if(init()) {
+ return -1;
+ }
+
+ puts("Please enter a nickname:");
+ flib_gets(nickname, sizeof(nickname));
+
+ netconn = flib_netconn_create(nickname, metacfg, DATA_DIR"\\", "140.247.62.101", 46631);
+ if(!netconn) {
+ flib_quit();
+ return -1;
+ }
+
+ flib_netconn_onConnected(netconn, handleNetConnected, NULL);
+ flib_netconn_onDisconnected(netconn, handleNetDisconnect, NULL);
+ flib_netconn_onChat(netconn, handleChat, NULL);
+ flib_netconn_onEnterRoom(netconn, handleEnterRoom, NULL);
+ flib_netconn_onRunGame(netconn, handleRunGame, NULL);
+ flib_netconn_onEngineMessage(netconn, handleEmFromNet, NULL);
+ flib_netconn_onRoomJoin(netconn, handleRoomJoin, NULL);
+ flib_netconn_onRoomLeave(netconn, handleRoomLeave, NULL);
+ flib_netconn_onReadyState(netconn, handleReady, NULL);
+ flib_netconn_onNickTaken(netconn, handleNickTaken, NULL);
+ flib_netconn_onPasswordRequest(netconn, handlePwRequest, NULL);
+ flib_netconn_onMessage(netconn, handleMessage, NULL);
+ flib_netconn_onTeamAccepted(netconn, handleTeamAccepted, NULL);
+ flib_netconn_onMapChanged(netconn, handleMapChanged, NULL);
+ flib_netconn_onLeaveRoom(netconn, handleLeaveRoom, NULL);
+ flib_netconn_onCfgScheme(netconn, handleSchemeChanged, NULL);
+ flib_netconn_onWeaponsetChanged(netconn, handleWeaponsetChanged, NULL);
+ flib_netconn_onHogCountChanged(netconn, handleHogcountChanged, NULL);
+ flib_netconn_onRoomAdd(netconn, handleRoomAdd, NULL);
+ flib_netconn_onRoomDelete(netconn, handleRoomDelete, NULL);
+ flib_netconn_onScriptChanged(netconn, handleScriptChanged, NULL);
+ flib_netconn_onTeamAdd(netconn, handleTeamAdd, NULL);
+ flib_netconn_onTeamDelete(netconn, handleTeamDelete, NULL);
+ flib_netconn_onTeamColorChanged(netconn, handleTeamColorChanged, NULL);
+
+ INPUT_RECORD inputRecord;
+ DWORD eventCount = 0;
+
+ while(netconn || gameconn) {
+ tick();
+ if(netconn && netConnected) {
+ while(PeekConsoleInput(hStdin, &inputRecord, 1, &eventCount) && eventCount>0) {
+ if(inputRecord.EventType != KEY_EVENT) {
+ ReadConsoleInput(hStdin, &inputRecord, 1, &eventCount);
+ } else {
+ printf("%s: ", nickname);
+ char input[256];
+ if(!flib_gets(input, sizeof(input))) {
+ if(!memcmp("/quit", input, strlen("/quit"))) {
+ flib_netconn_send_quit(netconn, "Player quit.");
+ } else if(!memcmp("/describe ", input, strlen("/describe "))) {
+ const char *roomname = input+strlen("/describe ");
+ const flib_roomlist *roomlist = flib_netconn_get_roomlist(netconn);
+ flib_room *room = flib_roomlist_find(roomlist, roomname);
+ if(!room) {
+ puts("Unknown room.");
+ } else {
+ char *text = flib_asprintf(
+ "%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
+ room->name,
+ room->owner,
+ room->playerCount,
+ room->teamCount,
+ room->inProgress ? "fighting" : "preparing to fight",
+ room->map[0]=='+' ? "" : "the map ",
+ !strcmp("+rnd+", room->map) ? "a random map" :
+ !strcmp("+maze+", room->map) ? "a random maze" :
+ !strcmp("+drawn+", room->map) ? "a hand-drawn map" :
+ room->map,
+ room->scheme,
+ room->weapons);
+ if(text) {
+ puts(text);
+ }
+ free(text);
+ }
+ } else if(!memcmp("/join ", input, strlen("/join "))) {
+ const char *roomname = input+strlen("/join ");
+ flib_netconn_send_joinRoom(netconn, roomname);
+ } else if(!memcmp("/ready", input, strlen("/ready"))) {
+ flib_netconn_send_toggleReady(netconn);
+ } else if(!memcmp("/loglevel ", input, strlen("/loglevel "))) {
+ int loglevel = atoi(input+strlen("/loglevel "));
+ flib_log_setLevel(loglevel);
+ } else if(!memcmp("/list", input, strlen("/list"))) {
+ if(flib_netconn_is_in_room_context(netconn)) {
+ printTeamList();
+ } else {
+ puts("From this big and expansive lobby, hallways branch off to these rooms:");
+ printRoomList();
+ }
+ } else if(!memcmp("/addteam ", input, strlen("/addteam "))) {
+ const char *teamname = input+strlen("/addteam ");
+ if(!flib_contains_dir_separator(teamname)) {
+ char *teamfilename = flib_asprintf("%s.hwt", teamname);
+ if(teamfilename) {
+ flib_team *team = flib_team_from_ini(teamfilename);
+ if(team) {
+ flib_netconn_send_addTeam(netconn, team);
+ } else {
+ printf("Teamfile %s not found.\n", teamfilename);
+ }
+ flib_team_release(team);
+ }
+ free(teamfilename);
+ }
+ } else if(strlen(input)>0) {
+ flib_netconn_send_chat(netconn, input);
+ }
+ }
+ }
+ }
+ }
+ fflush(stdout);
+ Sleep(10);
+ }
+
+
+ flib_cfg_meta_release(metacfg);
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,36 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include <SDL.h>
+#include <SDL_net.h>
+
+static int flib_initflags;
+
+int flib_init(int flags) {
+ flib_initflags = flags;
+
+ flib_log_d("Initializing frontlib");
+ if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+ if(SDL_Init(0)==-1) {
+ flib_log_e("Error in SDL_Init: %s", SDL_GetError());
+ return -1;
+ }
+ }
+
+ if(SDLNet_Init()==-1) {
+ flib_log_e("Error in SDLNet_Init: %s", SDLNet_GetError());
+ if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+ SDL_Quit();
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+void flib_quit() {
+ flib_log_d("Shutting down frontlib");
+ SDLNet_Quit();
+ if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
+ SDL_Quit();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/frontlib.h Wed Jun 27 18:04:17 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.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,4 @@
+#include "hwconsts.h"
+
+const uint32_t flib_teamcolor_defaults[] = HW_TEAMCOLOR_ARRAY;
+const size_t flib_teamcolor_defaults_len = sizeof(flib_teamcolor_defaults)/sizeof(uint32_t)-1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/hwconsts.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,58 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/**
+ * This file contains important constants which might need to be changed to adapt to
+ * changes in the engine or protocols.
+ */
+
+#ifndef HWCONSTS_H_
+#define HWCONSTS_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#define HEDGEHOGS_PER_TEAM 8
+#define NETGAME_DEFAULT_PORT 46631
+#define PROTOCOL_VERSION 42
+#define MIN_SERVER_VERSION 1
+
+// Used for sending scripts to the engine
+#define MULTIPLAYER_SCRIPT_PATH "Scripts/Multiplayer/"
+
+#define WEAPONS_COUNT 55
+
+/* A merge of mikade/bugq colours w/ a bit of channel feedback */
+#define HW_TEAMCOLOR_ARRAY { UINT32_C(0xffff0204), /* red */ \
+ UINT32_C(0xff4980c1), /* blue */ \
+ UINT32_C(0xff1de6ba), /* teal */ \
+ UINT32_C(0xffb541ef), /* purple */ \
+ UINT32_C(0xffe55bb0), /* pink */ \
+ UINT32_C(0xff20bf00), /* green */ \
+ UINT32_C(0xfffe8b0e), /* orange */ \
+ UINT32_C(0xff5f3605), /* brown */ \
+ UINT32_C(0xffffff01), /* yellow */ \
+ /* add new colors here */ \
+ 0 } /* Keep this 0 at the end or the length will be calculated wrong */
+
+// TODO allow setting alternative color lists?
+extern const size_t flib_teamcolor_defaults_len;
+extern const uint32_t flib_teamcolor_defaults[];
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/iniparser/LICENSE Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 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 Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,307 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.h
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,431 @@
+#include "gameconn.h"
+#include "ipcbase.h"
+#include "ipcprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../hwconsts.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum {
+ AWAIT_CONNECTION,
+ CONNECTED,
+ FINISHED
+} gameconn_state;
+
+struct _flib_gameconn {
+ flib_ipcbase *ipcBase;
+ flib_vector *configBuffer;
+ flib_vector *demoBuffer;
+ char *playerName;
+
+ gameconn_state state;
+ bool netgame;
+
+ void (*onConnectCb)(void* context);
+ void *onConnectCtx;
+
+ void (*onDisconnectCb)(void* context, int reason);
+ void *onDisconnectCtx;
+
+ void (*onErrorMessageCb)(void* context, const char *msg);
+ void *onErrorMessageCtx;
+
+ void (*onChatCb)(void* context, const char *msg, bool teamchat);
+ void *onChatCtx;
+
+ void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
+ void *onGameRecordedCtx;
+
+ void (*onEngineMessageCb)(void *context, const uint8_t *em, size_t size);
+ void *onEngineMessageCtx;
+
+ bool running;
+ bool destroyRequested;
+};
+
+static void defaultCallback_onConnect(void* context) {}
+static void defaultCallback_onDisconnect(void* context, int reason) {}
+static void defaultCallback_onErrorMessage(void* context, const char *msg) {
+ flib_log_w("Error from engine (no callback set): %s", msg);
+}
+static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
+static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
+static void defaultCallback_onEngineMessage(void *context, const uint8_t *em, size_t size) {}
+
+static void clearCallbacks(flib_gameconn *conn) {
+ conn->onConnectCb = &defaultCallback_onConnect;
+ conn->onDisconnectCb = &defaultCallback_onDisconnect;
+ conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
+ conn->onChatCb = &defaultCallback_onChat;
+ conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
+ conn->onEngineMessageCb = &defaultCallback_onEngineMessage;
+}
+
+static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
+ flib_gameconn *result = NULL;
+ if(!log_badparams_if(!playerName)) {
+ flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
+ if(tempConn) {
+ tempConn->ipcBase = flib_ipcbase_create();
+ tempConn->configBuffer = flib_vector_create();
+ tempConn->playerName = flib_strdupnull(playerName);
+ if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) {
+ if(record) {
+ tempConn->demoBuffer = flib_vector_create();
+ }
+ tempConn->state = AWAIT_CONNECTION;
+ tempConn->netgame = netGame;
+ clearCallbacks(tempConn);
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_gameconn_destroy(tempConn);
+ }
+ return result;
+}
+
+flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) {
+ flib_gameconn *result = NULL;
+ flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
+ if(tempConn) {
+ if(flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) {
+ flib_log_e("Error generating full game configuration for the engine.");
+ } else {
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_gameconn_destroy(tempConn);
+ return result;
+}
+
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *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)) {
+ 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)) {
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_gameconn_destroy(tempConn);
+ return result;
+}
+
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) {
+ flib_gameconn *result = NULL;
+ flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+ if(tempConn) {
+ if(!flib_ipc_append_message(tempConn->configBuffer, "TL")
+ && !flib_ipc_append_seed(tempConn->configBuffer, seed)
+ && !flib_ipc_append_script(tempConn->configBuffer, script)
+ && !flib_ipc_append_message(tempConn->configBuffer, "!")) {
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_gameconn_destroy(tempConn);
+ return result;
+}
+
+void flib_gameconn_destroy(flib_gameconn *conn) {
+ if(conn) {
+ if(conn->running) {
+ /*
+ * The function was called from a callback, so the tick function is still running
+ * and we delay the actual destruction. We ensure no further callbacks will be
+ * sent to prevent surprises.
+ */
+ clearCallbacks(conn);
+ conn->destroyRequested = true;
+ } else {
+ flib_ipcbase_destroy(conn->ipcBase);
+ flib_vector_destroy(conn->configBuffer);
+ flib_vector_destroy(conn->demoBuffer);
+ free(conn->playerName);
+ free(conn);
+ }
+ }
+}
+
+int flib_gameconn_getport(flib_gameconn *conn) {
+ if(!log_badparams_if(!conn)) {
+ return flib_ipcbase_port(conn->ipcBase);
+ }
+ return 0;
+}
+
+static void demo_append(flib_gameconn *conn, const void *data, size_t len) {
+ if(conn->demoBuffer) {
+ if(flib_vector_append(conn->demoBuffer, data, len)) {
+ flib_log_e("Error recording demo: Out of memory.");
+ flib_vector_destroy(conn->demoBuffer);
+ conn->demoBuffer = NULL;
+ }
+ }
+}
+
+static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) {
+ size_t msglen = strlen(message);
+
+ // If the message starts with /me, it will be displayed differently.
+ bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4);
+ const char *template = meMessage ? "s\x02* %s %s " : "s\x01%s: %s ";
+ int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message);
+ if(size>0) {
+ buffer[0] = size>255 ? 255 : size;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void demo_append_chatmessage(flib_gameconn *conn, const char *message) {
+ // Chat messages are reformatted to make them look as if they were received, not sent.
+ uint8_t converted[257];
+ if(!format_chatmessage(converted, conn->playerName, message)) {
+ demo_append(conn, converted, converted[0]+1);
+ }
+}
+
+static void demo_replace_gamemode(flib_buffer buf, char gamemode) {
+ size_t msgStart = 0;
+ uint8_t *data = (uint8_t*)buf.data;
+ while(msgStart+2 < buf.size) {
+ if(!memcmp(data+msgStart, "\x02T", 2)) {
+ data[msgStart+2] = gamemode;
+ }
+ msgStart += (uint8_t)data[msgStart]+1;
+ }
+}
+
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len) {
+ int result = -1;
+ if(!log_badparams_if(!conn || (!data && len>0))
+ && !flib_ipcbase_send_raw(conn->ipcBase, data, len)) {
+ demo_append(conn, data, len);
+ result = 0;
+ }
+ return result;
+}
+
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) {
+ int result = -1;
+ if(!conn || !msg) {
+ flib_log_e("null parameter in flib_gameconn_send_textmsg");
+ } else {
+ uint8_t converted[257];
+ int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg);
+ if(size>0) {
+ converted[0] = size>255 ? 255 : size;
+ if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
+ demo_append(conn, converted, converted[0]+1);
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) {
+ int result = -1;
+ uint8_t converted[257];
+ if(!conn || !playername || !msg) {
+ flib_log_e("null parameter in flib_gameconn_send_chatmsg");
+ } else if(format_chatmessage(converted, playername, msg)) {
+ flib_log_e("Error formatting message in flib_gameconn_send_chatmsg");
+ } else if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) {
+ demo_append(conn, converted, converted[0]+1);
+ result = 0;
+ }
+ return result;
+}
+
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onConnect");
+ } else {
+ conn->onConnectCb = callback ? callback : &defaultCallback_onConnect;
+ conn->onConnectCtx = context;
+ }
+}
+
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onDisconnect");
+ } else {
+ conn->onDisconnectCb = callback ? callback : &defaultCallback_onDisconnect;
+ conn->onDisconnectCtx = context;
+ }
+}
+
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onErrorMessage");
+ } else {
+ conn->onErrorMessageCb = callback ? callback : &defaultCallback_onErrorMessage;
+ conn->onErrorMessageCtx = context;
+ }
+}
+
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onChat");
+ } else {
+ conn->onChatCb = callback ? callback : &defaultCallback_onChat;
+ conn->onChatCtx = context;
+ }
+}
+
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onGameRecorded");
+ } else {
+ conn->onGameRecordedCb = callback ? callback : &defaultCallback_onGameRecorded;
+ conn->onGameRecordedCtx = context;
+ }
+}
+
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, size_t size), void* context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_onEngineMessage");
+ } else {
+ conn->onEngineMessageCb = callback ? callback : &defaultCallback_onEngineMessage;
+ conn->onEngineMessageCtx = context;
+ }
+}
+
+static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
+ if(conn->state == AWAIT_CONNECTION) {
+ flib_ipcbase_accept(conn->ipcBase);
+ switch(flib_ipcbase_state(conn->ipcBase)) {
+ case IPC_CONNECTED:
+ {
+ flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+ if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+ return;
+ } else {
+ demo_append(conn, configBuffer.data, configBuffer.size);
+ conn->state = CONNECTED;
+ conn->onConnectCb(conn->onConnectCtx);
+ if(conn->destroyRequested) {
+ return;
+ }
+ }
+ }
+ break;
+ case IPC_NOT_CONNECTED:
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if(conn->state == CONNECTED) {
+ uint8_t msgbuffer[257];
+ int len;
+ while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) {
+ if(len<2) {
+ flib_log_w("Received short message from IPC (<2 bytes)");
+ continue;
+ }
+ switch(msgbuffer[1]) {
+ case '?': // Ping
+ // The pong is already part of the config message
+ break;
+ case 'C': // Config query
+ // And we already send the config message on connecting.
+ break;
+ case 'E': // Error message
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
+ }
+ break;
+ case 'i': // Statistics
+ // TODO stats
+ break;
+ case 'Q': // Game interrupted
+ case 'H': // Game halted
+ case 'q': // game finished
+ {
+ int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
+ bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
+ if(conn->demoBuffer) {
+ flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer);
+ demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D');
+ conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame);
+ if(conn->destroyRequested) {
+ return;
+ }
+ }
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, reason);
+ return;
+ }
+ case 's': // Chat message
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ demo_append_chatmessage(conn, (char*)msgbuffer+2);
+
+ conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
+ }
+ break;
+ case 'b': // Teamchat message
+ if(len>=3) {
+ msgbuffer[len-2] = 0;
+ conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
+ }
+ break;
+ default: // Engine message
+ demo_append(conn, msgbuffer, len);
+
+ conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len);
+ break;
+ }
+ }
+ }
+
+ if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) {
+ conn->state = FINISHED;
+ conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+ }
+}
+
+void flib_gameconn_tick(flib_gameconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_gameconn_tick");
+ } else if(conn->running) {
+ flib_log_w("Call to flib_gameconn_tick from a callback");
+ } else if(conn->state == FINISHED) {
+ flib_log_w("Call to flib_gameconn_tick, but we are already done.");
+ } else {
+ conn->running = true;
+ flib_gameconn_wrappedtick(conn);
+ conn->running = false;
+
+ if(conn->destroyRequested) {
+ flib_gameconn_destroy(conn);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,81 @@
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define GAME_END_FINISHED 0
+#define GAME_END_INTERRUPTED 1
+#define GAME_END_HALTED 2
+#define GAME_END_ERROR 3
+
+struct _flib_gameconn;
+typedef struct _flib_gameconn flib_gameconn;
+
+flib_gameconn *flib_gameconn_create(const char *playerName, const 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);
+flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script);
+
+void flib_gameconn_destroy(flib_gameconn *conn);
+
+/**
+ * Returns the port on which the gameconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_gameconn_getport(flib_gameconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_gameconn_tick(flib_gameconn *conn);
+
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len);
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg);
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
+
+/**
+ * handleConnect(void *context)
+ */
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context);
+
+/**
+ * handleDisconnect(void *context, int reason)
+ */
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context);
+
+/**
+ * Receives error messages sent by the engine
+ * handleErrorMessage(void* context, const char *msg)
+ */
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context);
+
+/**
+ * handleChat(void* context, const char *msg, bool teamchat)
+ */
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context);
+
+/**
+ * Called when the game ends
+ * handleGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame)
+ */
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context);
+
+/**
+ * Called when the game ends
+ * TODO handleStats(???)
+ */
+
+/**
+ * ...needs to be passed on to the server in a net game
+ * handleEngineMessage(void *context, const uint8_t *em, size_t size)
+ */
+void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, size_t size), void* context);
+
+// TODO efinish
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,196 @@
+#include "ipcbase.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * The receive buffer has to be able to hold any message that might be received. Normally
+ * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
+ * bitmap, 1 for the number of hogs which fit on the map).
+ *
+ * We don't need to worry about wasting a few kb though, and I like powers of two...
+ */
+struct _flib_ipcbase {
+ uint8_t readBuffer[8192];
+ int readBufferSize;
+
+ flib_acceptor *acceptor;
+ uint16_t port;
+
+ flib_tcpsocket *sock;
+};
+
+flib_ipcbase *flib_ipcbase_create() {
+ flib_ipcbase *result = flib_calloc(1, sizeof(flib_ipcbase));
+ flib_acceptor *acceptor = flib_acceptor_create(0);
+
+ if(!result || !acceptor) {
+ free(result);
+ flib_acceptor_close(acceptor);
+ return NULL;
+ }
+
+ result->acceptor = acceptor;
+ result->sock = NULL;
+ result->readBufferSize = 0;
+ result->port = flib_acceptor_listenport(acceptor);
+
+ flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
+ return result;
+}
+
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc) {
+ if(!ipc) {
+ flib_log_e("null parameter in flib_ipcbase_port");
+ return 0;
+ }
+ return ipc->port;
+}
+
+void flib_ipcbase_destroy(flib_ipcbase *ipc) {
+ if(ipc) {
+ flib_acceptor_close(ipc->acceptor);
+ flib_socket_close(ipc->sock);
+ free(ipc);
+ }
+}
+
+IpcState flib_ipcbase_state(flib_ipcbase *ipc) {
+ if(!ipc) {
+ flib_log_e("null parameter in flib_ipcbase_state");
+ return IPC_NOT_CONNECTED;
+ } else if(ipc->sock) {
+ return IPC_CONNECTED;
+ } else if(ipc->acceptor) {
+ return IPC_LISTENING;
+ } else {
+ return IPC_NOT_CONNECTED;
+ }
+}
+
+static void receiveToBuffer(flib_ipcbase *ipc) {
+ if(ipc->sock) {
+ int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
+ if(size>=0) {
+ ipc->readBufferSize += size;
+ } else {
+ flib_socket_close(ipc->sock);
+ ipc->sock = NULL;
+ }
+ }
+}
+
+static bool isMessageReady(flib_ipcbase *ipc) {
+ return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("null parameter in flib_ipcbase_recv_message");
+ return -1;
+ }
+
+ if(!isMessageReady(ipc)) {
+ receiveToBuffer(ipc);
+ }
+
+ if(isMessageReady(ipc)) {
+ int msgsize = ipc->readBuffer[0]+1;
+ memcpy(data, ipc->readBuffer, msgsize);
+ memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
+ ipc->readBufferSize -= msgsize;
+ return msgsize;
+ } else if(!ipc->sock && ipc->readBufferSize>0) {
+ flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
+ ipc->readBufferSize = 0;
+ return -1;
+ } else {
+ return -1;
+ }
+}
+
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("null parameter in flib_ipcbase_recv_map");
+ return -1;
+ }
+
+ receiveToBuffer(ipc);
+
+ if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) {
+ memcpy(data, ipc->readBuffer, IPCBASE_MAPMSG_BYTES);
+ memmove(ipc->readBuffer, ipc->readBuffer+IPCBASE_MAPMSG_BYTES, ipc->readBufferSize-IPCBASE_MAPMSG_BYTES);
+ return IPCBASE_MAPMSG_BYTES;
+ } else {
+ return -1;
+ }
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+ if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+ size_t msgStart = 0;
+ while(msgStart < len) {
+ uint8_t msglen = data[msgStart];
+ if(msgStart+msglen < len) {
+ flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1);
+ } else {
+ uint8_t msglen2 = len-msgStart-1;
+ flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1);
+ }
+ msgStart += (uint8_t)data[msgStart]+1;
+ }
+ }
+}
+
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) {
+ if(!ipc || (!data && len>0)) {
+ flib_log_e("null parameter in flib_ipcbase_send_raw");
+ return -1;
+ }
+ if(!ipc->sock) {
+ flib_log_w("flib_ipcbase_send_raw: Not connected.");
+ return -1;
+ }
+
+ if(flib_socket_send(ipc->sock, data, len) == len) {
+ logSentMsg(data, len);
+ return 0;
+ } else {
+ flib_log_w("Failed or incomplete ICP write: engine connection lost.");
+ flib_socket_close(ipc->sock);
+ ipc->sock = NULL;
+ return -1;
+ }
+}
+
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len) {
+ if(!ipc || (!data && len>0)) {
+ flib_log_e("null parameter in flib_ipcbase_send_message");
+ return -1;
+ } else if(len>255) {
+ flib_log_e("Overlong message (%zu bytes) in flib_ipcbase_send_message", len);
+ return -1;
+ }
+
+ uint8_t sendbuf[256];
+ sendbuf[0] = len;
+ memcpy(sendbuf+1, data, len);
+ return flib_ipcbase_send_raw(ipc, sendbuf, len+1);
+}
+
+void flib_ipcbase_accept(flib_ipcbase *ipc) {
+ if(!ipc) {
+ flib_log_e("null parameter in flib_ipcbase_accept");
+ } else if(!ipc->sock && ipc->acceptor) {
+ ipc->sock = flib_socket_accept(ipc->acceptor, true);
+ if(ipc->sock) {
+ flib_acceptor_close(ipc->acceptor);
+ ipc->acceptor = NULL;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcbase.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Low-level protocol support for the IPC connection to the engine.
+ */
+
+#ifndef IPCBASE_H_
+#define IPCBASE_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define IPCBASE_MAPMSG_BYTES 4097
+
+typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState;
+
+typedef struct _flib_ipcbase flib_ipcbase;
+
+/**
+ * Start an engine connection by listening on a random port. The selected port can
+ * be queried with flib_ipcbase_port and has to be passed to the engine.
+ *
+ * Returns NULL on error. Destroy the created object with flib_ipcbase_destroy.
+ *
+ * We stop accepting new connections once a connection has been established, so you
+ * need to create a new ipcbase in order to start a new connection.
+ */
+flib_ipcbase *flib_ipcbase_create();
+
+/**
+ * Return the listening port
+ */
+uint16_t flib_ipcbase_port(flib_ipcbase *ipc);
+
+/**
+ * Free resources and close sockets. NULL safe.
+ */
+void flib_ipcbase_destroy(flib_ipcbase *ipc);
+
+/**
+ * Determine the current connection state
+ */
+IpcState flib_ipcbase_state(flib_ipcbase *ipc);
+
+/**
+ * Receive a single message (up to 256 bytes) and copy it into the data buffer.
+ * Returns the length of the received message, a negative value if no message could
+ * be read.
+ *
+ * The first byte of a message is its content length, which is one less than the returned
+ * value.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data);
+
+/**
+ * Try to receive 4097 bytes. This is the size of the reply the engine sends
+ * when successfully queried for map data. The first 4096 bytes are a bit-packed
+ * twocolor image of the map (256x128), the last byte is the number of hogs that
+ * fit on the map.
+ */
+int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data);
+
+/**
+ * Blocking send bytes over the socket. No message framing will be added.
+ * Returns 0 on success.
+ */
+int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len);
+
+/**
+ * Write a single message (up to 255 bytes) to the engine. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Calling this function in a state other than IPC_CONNECTED will fail immediately.
+ * Returns 0 on success.
+ */
+int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len);
+
+/**
+ * Try to accept a connection. Only has an effect in state IPC_LISTENING.
+ */
+void flib_ipcbase_accept(flib_ipcbase *ipc);
+
+#endif /* IPCBASE_H_ */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,287 @@
+#include "ipcprotocol.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...) {
+ int result = -1;
+ if(!log_badparams_if(!vec || !fmt)) {
+ // 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf
+ char msgbuffer[257];
+
+ // Format the message, leaving one byte at the start for the length
+ va_list argp;
+ va_start(argp, fmt);
+ int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp);
+ va_end(argp);
+
+ if(!log_e_if(msgSize > 255, "Message too long (%u bytes)", (unsigned)msgSize)
+ && !log_e_if(msgSize < 0, "printf error")) {
+ // Add the length prefix
+ ((uint8_t*)msgbuffer)[0] = msgSize;
+
+ // Append it to the vector
+ result = flib_vector_append(vec, msgbuffer, msgSize+1);
+ }
+ }
+ return result;
+}
+
+int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview) {
+ int result = -1;
+ flib_vector *tempvector = flib_vector_create();
+ if(!log_badparams_if(!vec || !map)) {
+ bool error = false;
+
+ if(map->mapgen == MAPGEN_NAMED) {
+ error |= log_e_if(!map->name, "Missing map name")
+ || flib_ipc_append_message(tempvector, "emap %s", map->name);
+ }
+ if(!mappreview) {
+ error |= log_e_if(!map->theme, "Missing map theme")
+ || flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+ }
+ error |= flib_ipc_append_seed(tempvector, map->seed);
+ error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
+ error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen);
+
+ if(map->mapgen == MAPGEN_MAZE) {
+ error |= flib_ipc_append_message(tempvector, "e$maze_size %i", map->mazeSize);
+ }
+ if(map->mapgen == MAPGEN_DRAWN) {
+ /*
+ * We have to split the drawn map data into several edraw messages here because
+ * it can be longer than the maximum message size.
+ */
+ const char *edraw = "edraw ";
+ int edrawlen = strlen(edraw);
+ for(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);
+ error |= flib_vector_append(tempvector, edraw, edrawlen);
+ error |= flib_vector_append(tempvector, map->drawData+offset, fragmentsize);
+ }
+ }
+
+ if(!log_e_if(error, "Error generating map config")) {
+ // Message created, now we can copy everything.
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+ if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+ result = 0;
+ }
+ }
+ }
+ flib_vector_destroy(tempvector);
+ return result;
+}
+
+int flib_ipc_append_seed(flib_vector *vec, const char *seed) {
+ if(!log_badparams_if(!vec || !seed)) {
+ return flib_ipc_append_message(vec, "eseed %s", seed);
+ }
+ return -1;
+}
+
+int flib_ipc_append_script(flib_vector *vec, const char *script) {
+ int result = -1;
+ char *copy = flib_strdupnull(script);
+ if(!log_badparams_if(!vec) && copy) {
+ if(!strcmp("Normal", copy)) {
+ // "Normal" means no gametype script
+ result = 0;
+ } else {
+ size_t len = strlen(copy);
+ for(size_t i=0; i<len; i++) {
+ if(copy[i] == ' ') {
+ copy[i] = '_';
+ }
+ }
+
+ result = flib_ipc_append_message(vec, "escript %s%s.lua", MULTIPLAYER_SCRIPT_PATH, copy);
+ }
+ }
+ free(copy);
+ return result;
+}
+
+uint32_t buildModFlags(const flib_cfg *scheme) {
+ uint32_t result = 0;
+ for(int i=0; i<scheme->meta->modCount; i++) {
+ if(scheme->mods[i]) {
+ int bitmaskIndex = scheme->meta->mods[i].bitmaskIndex;
+ result |= (UINT32_C(1) << bitmaskIndex);
+ }
+ }
+ return result;
+}
+
+int flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *scheme) {
+ int result = -1;
+ flib_vector *tempvector = flib_vector_create();
+ if(!log_badparams_if(!vec || !scheme) && tempvector) {
+ const flib_cfg_meta *meta = scheme->meta;
+ bool error = false;
+ error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, buildModFlags(scheme));
+ for(int i=0; i<meta->settingCount; i++) {
+ if(meta->settings[i].engineCommand) {
+ int value = scheme->settings[i];
+ if(meta->settings[i].maxMeansInfinity) {
+ value = value>=meta->settings[i].max ? 9999 : value;
+ }
+ if(meta->settings[i].times1000) {
+ value *= 1000;
+ }
+ error |= flib_ipc_append_message(tempvector, "%s %i", meta->settings[i].engineCommand, value);
+ }
+ }
+
+ if(!error) {
+ // Message created, now we can copy everything.
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+ if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+ result = 0;
+ }
+ }
+ }
+ flib_vector_destroy(tempvector);
+ return result;
+}
+
+static int appendWeaponSet(flib_vector *vec, flib_weaponset *set) {
+ return flib_ipc_append_message(vec, "eammloadt %s", set->loadout)
+ || flib_ipc_append_message(vec, "eammprob %s", set->crateprob)
+ || flib_ipc_append_message(vec, "eammdelay %s", set->delay)
+ || flib_ipc_append_message(vec, "eammreinf %s", set->crateammo);
+}
+
+int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore) {
+ int result = -1;
+ flib_vector *tempvector = flib_vector_create();
+ if(!log_badparams_if(!vec || !team) && tempvector) {
+ bool error = false;
+
+ if(!perHogAmmo && !noAmmoStore) {
+ error = error
+ || appendWeaponSet(tempvector, team->hogs[0].weaponset)
+ || flib_ipc_append_message(tempvector, "eammstore");
+ }
+
+ // TODO
+ char *hash = team->ownerName ? team->ownerName : "00000000000000000000000000000000";
+ if(team->colorIndex<0 || team->colorIndex>=flib_teamcolor_defaults_len) {
+ flib_log_e("Color index out of bounds for team %s: %i", team->name, team->colorIndex);
+ error = true;
+ } else {
+ error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, flib_teamcolor_defaults[team->colorIndex], team->name);
+ }
+
+ if(team->remoteDriven) {
+ error |= flib_ipc_append_message(tempvector, "erdriven");
+ }
+
+ error |= flib_ipc_append_message(tempvector, "egrave %s", team->grave);
+ error |= flib_ipc_append_message(tempvector, "efort %s", team->fort);
+ error |= flib_ipc_append_message(tempvector, "evoicepack %s", team->voicepack);
+ error |= flib_ipc_append_message(tempvector, "eflag %s", team->flag);
+
+ for(int i=0; i<team->bindingCount; i++) {
+ error |= flib_ipc_append_message(tempvector, "ebind %s %s", team->bindings[i].binding, team->bindings[i].action);
+ }
+
+ for(int i=0; i<team->hogsInGame; i++) {
+ if(perHogAmmo && !noAmmoStore) {
+ error |= appendWeaponSet(tempvector, team->hogs[i].weaponset);
+ }
+ error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
+ error |= flib_ipc_append_message(tempvector, "ehat %s", team->hogs[i].hat);
+ }
+
+ if(!error) {
+ // Message created, now we can copy everything.
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+ if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+ result = 0;
+ }
+ }
+ }
+ flib_vector_destroy(tempvector);
+ return result;
+}
+
+static bool getGameMod(const flib_cfg *conf, const char *name) {
+ for(int i=0; i<conf->meta->modCount; i++) {
+ if(!strcmp(conf->meta->mods[i].name, name)) {
+ return conf->mods[i];
+ }
+ }
+ flib_log_e("Unable to find game mod %s", name);
+ return false;
+}
+
+int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame) {
+ int result = -1;
+ flib_vector *tempvector = flib_vector_create();
+ if(!log_badparams_if(!vec || !setup) && tempvector) {
+ bool error = false;
+ bool perHogAmmo = false;
+ bool sharedAmmo = false;
+
+ error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL");
+ if(setup->map) {
+ error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
+ }
+ if(setup->script) {
+ error |= flib_ipc_append_script(tempvector, setup->script);
+ }
+ if(setup->gamescheme) {
+ error |= flib_ipc_append_gamescheme(tempvector, setup->gamescheme);
+ sharedAmmo = getGameMod(setup->gamescheme, "sharedammo");
+ // Shared ammo has priority over per-hog ammo
+ perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, "perhogammo");
+ }
+ if(setup->teamlist->teams && setup->teamlist->teamCount>0) {
+ int *clanColors = flib_calloc(setup->teamlist->teamCount, sizeof(int));
+ if(!clanColors) {
+ error = true;
+ } else {
+ int clanCount = 0;
+ for(int i=0; !error && i<setup->teamlist->teamCount; i++) {
+ flib_team *team = setup->teamlist->teams[i];
+ // Find the clan index of this team (clans are identified by color).
+ bool newClan = false;
+ int clan = 0;
+ while(clan<clanCount && clanColors[clan] != team->colorIndex) {
+ clan++;
+ }
+ if(clan==clanCount) {
+ newClan = true;
+ clanCount++;
+ clanColors[clan] = team->colorIndex;
+ }
+
+ // If shared ammo is active, only add an ammo store for the first team in each clan.
+ bool noAmmoStore = sharedAmmo&&!newClan;
+ error |= flib_ipc_append_addteam(tempvector, setup->teamlist->teams[i], perHogAmmo, noAmmoStore);
+ }
+ }
+ free(clanColors);
+ }
+ error |= flib_ipc_append_message(tempvector, "!");
+
+ if(!error) {
+ // Message created, now we can copy everything.
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+ if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,61 @@
+#ifndef IPCPROTOCOL_H_
+#define IPCPROTOCOL_H_
+
+#include "../util/buffer.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/cfg.h"
+#include "../model/gamesetup.h"
+
+#include <stdbool.h>
+
+/**
+ * Create a message in the IPC protocol format and add it to
+ * the vector. Use a format string and extra parameters as with printf.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...);
+
+/**
+ * Append IPC messages to the buffer that configure the engine for
+ * this map.
+ *
+ * Unfortunately the engine needs a slightly different configuration
+ * for generating a map preview.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview);
+
+/**
+ * Append a seed message to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_seed(flib_vector *vec, const char *seed);
+
+/**
+ * Append a script message to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_script(flib_vector *vec, const char *script);
+
+/**
+ * Append the game scheme to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *cfg);
+
+int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore);
+
+int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame);
+
+#endif /* IPCPROTOCOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,174 @@
+#include "mapconn.h"
+#include "ipcbase.h"
+#include "ipcprotocol.h"
+
+#include "../util/logging.h"
+#include "../util/buffer.h"
+#include "../util/util.h"
+
+#include <stdlib.h>
+
+typedef enum {
+ AWAIT_CONNECTION,
+ AWAIT_REPLY,
+ FINISHED
+} mapconn_state;
+
+struct _flib_mapconn {
+ uint8_t mapBuffer[IPCBASE_MAPMSG_BYTES];
+ flib_ipcbase *ipcBase;
+ flib_vector *configBuffer;
+
+ mapconn_state progress;
+
+ void (*onSuccessCb)(void*, const uint8_t*, int);
+ void *onSuccessCtx;
+
+ void (*onFailureCb)(void*, const char*);
+ void *onFailureCtx;
+
+ bool running;
+ bool destroyRequested;
+};
+
+static void noop_handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {}
+static void noop_handleFailure(void *context, const char *errormessage) {}
+
+static void clearCallbacks(flib_mapconn *conn) {
+ conn->onSuccessCb = &noop_handleSuccess;
+ conn->onFailureCb = &noop_handleFailure;
+}
+
+static flib_vector *createConfigBuffer(const flib_map *mapdesc) {
+ flib_vector *result = NULL;
+ flib_vector *tempbuffer = flib_vector_create();
+ if(tempbuffer) {
+ bool error = false;
+ error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true);
+ error |= flib_ipc_append_message(tempbuffer, "!");
+ if(!error) {
+ result = tempbuffer;
+ tempbuffer = NULL;
+ }
+ }
+ flib_vector_destroy(tempbuffer);
+ return result;
+}
+
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc) {
+ flib_mapconn *result = NULL;
+ flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
+ if(tempConn) {
+ tempConn->ipcBase = flib_ipcbase_create();
+ tempConn->configBuffer = createConfigBuffer(mapdesc);
+ if(tempConn->ipcBase && tempConn->configBuffer) {
+ tempConn->progress = AWAIT_CONNECTION;
+ clearCallbacks(tempConn);
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_mapconn_destroy(tempConn);
+ return result;
+}
+
+void flib_mapconn_destroy(flib_mapconn *conn) {
+ if(conn) {
+ if(conn->running) {
+ /*
+ * The function was called from a callback, so the tick function is still running
+ * and we delay the actual destruction. We ensure no further callbacks will be
+ * sent to prevent surprises.
+ */
+ clearCallbacks(conn);
+ conn->destroyRequested = true;
+ } else {
+ flib_ipcbase_destroy(conn->ipcBase);
+ flib_vector_destroy(conn->configBuffer);
+ free(conn);
+ }
+ }
+}
+
+int flib_mapconn_getport(flib_mapconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_getport");
+ return 0;
+ } else {
+ return flib_ipcbase_port(conn->ipcBase);
+ }
+}
+
+void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_onSuccess");
+ } else {
+ conn->onSuccessCb = callback ? callback : &noop_handleSuccess;
+ conn->onSuccessCtx = context;
+ }
+}
+
+void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_onError");
+ } else {
+ conn->onFailureCb = callback ? callback : &noop_handleFailure;
+ conn->onFailureCtx = context;
+ }
+}
+
+static void flib_mapconn_wrappedtick(flib_mapconn *conn) {
+ if(conn->progress == AWAIT_CONNECTION) {
+ flib_ipcbase_accept(conn->ipcBase);
+ switch(flib_ipcbase_state(conn->ipcBase)) {
+ case IPC_CONNECTED:
+ {
+ flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+ if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) {
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine.");
+ return;
+ } else {
+ conn->progress = AWAIT_REPLY;
+ }
+ }
+ break;
+ case IPC_NOT_CONNECTED:
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onFailureCtx, "Engine connection closed unexpectedly.");
+ return;
+ default:
+ break;
+ }
+ }
+
+ if(conn->progress == AWAIT_REPLY) {
+ if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) {
+ conn->progress = FINISHED;
+ conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]);
+ return;
+ } else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) {
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
+ return;
+ }
+ }
+}
+
+void flib_mapconn_tick(flib_mapconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_tick");
+ } else if(conn->running) {
+ flib_log_w("Call to flib_mapconn_tick from a callback");
+ } else if(conn->progress == FINISHED) {
+ flib_log_w("Call to flib_mapconn_tick, but we are already done. Best destroy your flib_mapconn object in the callbacks.");
+ } else {
+ conn->running = true;
+ flib_mapconn_wrappedtick(conn);
+ conn->running = false;
+
+ if(conn->destroyRequested) {
+ flib_mapconn_destroy(conn);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,81 @@
+#ifndef IPC_MAPCONN_H_
+#define IPC_MAPCONN_H_
+
+#include "../model/map.h"
+
+#include <stdint.h>
+
+#define MAPIMAGE_WIDTH 256
+#define MAPIMAGE_HEIGHT 128
+#define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT)
+
+typedef struct _flib_mapconn flib_mapconn;
+
+/**
+ * Start a new map rendering connection (mapconn). This means a listening socket
+ * will be started on a random unused port, waiting for a connection from the
+ * engine process. Once this connection is established, the required information
+ * will be sent to the engine, and the reply is read.
+ *
+ * The map must be a regular, maze or drawn map - for a preview of a named map,
+ * use the preview images in the map's directory, and for the hog count read the
+ * map information (flib_mapcfg_read).
+ *
+ * No NULL parameters allowed, returns NULL on failure.
+ * Use flib_mapconn_destroy to free the returned object.
+ */
+flib_mapconn *flib_mapconn_create(const flib_map *mapdesc);
+
+/**
+ * Destroy the mapconn object. Passing NULL is allowed and does nothing.
+ * flib_mapconn_destroy may be called from inside a callback function.
+ */
+void flib_mapconn_destroy(flib_mapconn *conn);
+
+/**
+ * Returns the port on which the mapconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_mapconn_getport(flib_mapconn *conn);
+
+/**
+ * Set a callback which will receive the rendered map if the rendering succeeds.
+ * 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/md5/md5.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,381 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/md5/md5.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/cfg.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,196 @@
+#include "cfg.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+static void flib_cfg_meta_destroy(flib_cfg_meta *cfg) {
+ if(cfg) {
+ if(cfg->settings) {
+ for(int i=0; i<cfg->settingCount; i++) {
+ free(cfg->settings[i].name);
+ free(cfg->settings[i].engineCommand);
+ }
+ free(cfg->settings);
+ }
+ if(cfg->mods) {
+ for(int i=0; i<cfg->modCount; i++) {
+ free(cfg->mods[i].name);
+ }
+ free(cfg->mods);
+ }
+ free(cfg);
+ }
+}
+
+static void flib_cfg_destroy(flib_cfg* cfg) {
+ if(cfg) {
+ flib_cfg_meta_release(cfg->meta);
+ free(cfg->mods);
+ free(cfg->settings);
+ free(cfg->name);
+ free(cfg);
+ }
+}
+
+static flib_cfg_meta *flib_cfg_meta_from_ini_handleError(flib_cfg_meta *result, flib_ini *ini) {
+ flib_cfg_meta_destroy(result);
+ flib_ini_destroy(ini);
+ return NULL;
+}
+
+static int readMetaSettingSections(flib_ini *ini, flib_cfg_meta *result, int limit) {
+ while(result->settingCount<limit) {
+ char sectionName[32];
+ if(snprintf(sectionName, sizeof(sectionName), "setting%i", result->settingCount) <= 0) {
+ return -1;
+ }
+ if(!flib_ini_enter_section(ini, sectionName)) {
+ flib_cfg_setting_meta *metasetting = &result->settings[result->settingCount];
+ result->settingCount++;
+
+ bool error = false;
+ error |= flib_ini_get_str(ini, &metasetting->name, "name");
+ error |= flib_ini_get_str_opt(ini, &metasetting->engineCommand, "command", NULL);
+ error |= flib_ini_get_bool(ini, &metasetting->times1000, "times1000");
+ error |= flib_ini_get_bool(ini, &metasetting->maxMeansInfinity, "maxmeansinfinity");
+ error |= flib_ini_get_int(ini, &metasetting->min, "min");
+ error |= flib_ini_get_int(ini, &metasetting->max, "max");
+ error |= flib_ini_get_int(ini, &metasetting->def, "default");
+ if(error) {
+ flib_log_e("Missing or malformed ini parameter in metaconfig, section %s", sectionName);
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int readMetaModSections(flib_ini *ini, flib_cfg_meta *result, int limit) {
+ while(result->modCount<limit) {
+ char sectionName[32];
+ if(snprintf(sectionName, sizeof(sectionName), "mod%i", result->modCount) <= 0) {
+ return -1;
+ }
+ if(!flib_ini_enter_section(ini, sectionName)) {
+ flib_cfg_mod_meta *metamod = &result->mods[result->modCount];
+ result->modCount++;
+
+ bool error = false;
+ error |= flib_ini_get_str(ini, &metamod->name, "name");
+ error |= flib_ini_get_int(ini, &metamod->bitmaskIndex, "bitmaskIndex");
+ if(error) {
+ flib_log_e("Missing or malformed ini parameter in metaconfig, section %s", sectionName);
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *filename) {
+ if(!filename) {
+ flib_log_e("null parameter in flib_cfg_meta_from_ini");
+ return NULL;
+ }
+ flib_cfg_meta *result = flib_cfg_meta_retain(flib_calloc(1, sizeof(flib_cfg_meta)));
+ flib_ini *ini = flib_ini_load(filename);
+
+ if(!result || !ini) {
+ return flib_cfg_meta_from_ini_handleError(result, ini);
+ }
+
+ // We're overallocating here for simplicity
+ int sectionCount = flib_ini_get_sectioncount(ini);
+ result->settingCount = 0;
+ result->modCount = 0;
+ result->settings = flib_calloc(sectionCount, sizeof(flib_cfg_setting_meta));
+ result->mods = flib_calloc(sectionCount, sizeof(flib_cfg_mod_meta));
+
+ if(!result->settings || !result->mods) {
+ return flib_cfg_meta_from_ini_handleError(result, ini);
+ }
+
+ if(readMetaSettingSections(ini, result, sectionCount) || readMetaModSections(ini, result, sectionCount)) {
+ return flib_cfg_meta_from_ini_handleError(result, ini);
+ }
+
+ if(result->settingCount+result->modCount != sectionCount) {
+ flib_log_e("Unknown or non-contiguous sections headers in metaconfig.");
+ return flib_cfg_meta_from_ini_handleError(result, ini);
+ }
+
+ flib_ini_destroy(ini);
+ return result;
+}
+
+flib_cfg_meta *flib_cfg_meta_retain(flib_cfg_meta *metainfo) {
+ if(metainfo) {
+ flib_retain(&metainfo->_referenceCount, "flib_cfg_meta");
+ }
+ return metainfo;
+}
+
+void flib_cfg_meta_release(flib_cfg_meta *cfg) {
+ if(cfg && flib_release(&cfg->_referenceCount, "flib_cfg_meta")) {
+ flib_cfg_meta_destroy(cfg);
+ }
+}
+
+flib_cfg *flib_cfg_create(flib_cfg_meta *meta, const char *schemeName) {
+ flib_cfg *result = flib_cfg_retain(flib_calloc(1, sizeof(flib_cfg)));
+ if(!meta || !result || !schemeName) {
+ flib_log_e("null parameter in flib_cfg_create");
+ return NULL;
+ }
+
+ result->meta = flib_cfg_meta_retain(meta);
+ result->name = flib_strdupnull(schemeName);
+ result->mods = flib_calloc(meta->modCount, sizeof(*result->mods));
+ result->settings = flib_calloc(meta->settingCount, sizeof(*result->settings));
+
+ if(!result->mods || !result->settings || !result->name) {
+ flib_cfg_destroy(result);
+ return NULL;
+ }
+
+ for(int i=0; i<meta->settingCount; i++) {
+ result->settings[i] = meta->settings[i].def;
+ }
+ return result;
+}
+
+flib_cfg *flib_cfg_copy(const flib_cfg *cfg) {
+ flib_cfg *result = NULL;
+ if(cfg) {
+ result = flib_cfg_create(cfg->meta, cfg->name);
+ if(result) {
+ memcpy(result->mods, cfg->mods, cfg->meta->modCount * sizeof(*cfg->mods));
+ memcpy(result->settings, cfg->settings, cfg->meta->settingCount * sizeof(*cfg->settings));
+ }
+ }
+ return result;
+}
+
+flib_cfg *flib_cfg_retain(flib_cfg *cfg) {
+ if(cfg) {
+ flib_retain(&cfg->_referenceCount, "flib_cfg");
+ }
+ return cfg;
+}
+
+void flib_cfg_release(flib_cfg *cfg) {
+ if(cfg && flib_release(&cfg->_referenceCount, "flib_cfg")) {
+ flib_cfg_destroy(cfg);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/cfg.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,89 @@
+/**
+ * Data structures for game scheme information.
+ */
+
+#ifndef CFG_H_
+#define CFG_H_
+
+#include <stdbool.h>
+
+// TODO: cfg/config -> scheme
+
+typedef struct {
+ char *name;
+ char *engineCommand;
+ bool maxMeansInfinity;
+ bool times1000;
+ int min;
+ int max;
+ int def;
+} flib_cfg_setting_meta;
+
+typedef struct {
+ char *name;
+ int bitmaskIndex;
+} flib_cfg_mod_meta;
+
+/**
+ * The order of the meta information in the arrays is the same as the order
+ * of the mod/setting information in the net protocol messages.
+ */
+typedef struct {
+ int _referenceCount;
+ int settingCount;
+ int modCount;
+ flib_cfg_setting_meta *settings;
+ flib_cfg_mod_meta *mods;
+} flib_cfg_meta;
+
+typedef struct {
+ int _referenceCount;
+ flib_cfg_meta *meta;
+
+ char *name;
+ int *settings;
+ bool *mods;
+} flib_cfg;
+
+/**
+ * Read the meta-configuration from a .ini file (e.g. which settings exist,
+ * what are their defaults etc.)
+ *
+ * Returns the meta-configuration or NULL.
+ */
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *filename);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_cfg_meta *flib_cfg_meta_retain(flib_cfg_meta *metainfo);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_cfg_meta_release(flib_cfg_meta *metainfo);
+
+/**
+ * Create a new configuration with everything set to default or false
+ * Returns NULL on error.
+ */
+flib_cfg *flib_cfg_create(flib_cfg_meta *meta, const char *schemeName);
+
+/**
+ * Create a copy of the scheme. Returns NULL on error or if NULL was passed.
+ */
+flib_cfg *flib_cfg_copy(const flib_cfg *cfg);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_cfg *flib_cfg_retain(flib_cfg *cfg);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_cfg_release(flib_cfg* cfg);
+
+#endif /* CFG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,13 @@
+#include "gamesetup.h"
+
+#include <stdlib.h>
+
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup) {
+ if(gamesetup) {
+ free(gamesetup->script);
+ flib_cfg_release(gamesetup->gamescheme);
+ flib_map_release(gamesetup->map);
+ flib_teamlist_destroy(gamesetup->teamlist);
+ free(gamesetup);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,23 @@
+/**
+ * A complete game configuration that contains all settings for a
+ * local or networked game.
+ */
+
+#ifndef MODEL_GAMESETUP_H_
+#define MODEL_GAMESETUP_H_
+
+#include "cfg.h"
+#include "weapon.h"
+#include "map.h"
+#include "teamlist.h"
+
+typedef struct {
+ char *script;
+ flib_cfg *gamescheme;
+ flib_map *map;
+ flib_teamlist *teamlist;
+} flib_gamesetup;
+
+void flib_gamesetup_destroy(flib_gamesetup *gamesetup);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,117 @@
+#include "map.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+#include "../util/refcounter.h"
+
+#include <stdlib.h>
+
+static void flib_map_destroy(flib_map *map) {
+ if(map) {
+ free(map->seed);
+ free(map->drawData);
+ free(map->name);
+ free(map->theme);
+ free(map);
+ }
+}
+
+flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter) {
+ flib_map *result = NULL;
+ if(!seed || !theme) {
+ flib_log_e("null parameter in flib_map_create_regular");
+ } else {
+ flib_map newmap = {0};
+ newmap.mapgen = MAPGEN_REGULAR;
+ newmap.name = "+rnd+";
+ newmap.seed = (char*)seed;
+ newmap.theme = (char*)theme;
+ newmap.templateFilter = templateFilter;
+ result = flib_map_copy(&newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize) {
+ flib_map *result = NULL;
+ if(!seed || !theme) {
+ flib_log_e("null parameter in flib_map_create_maze");
+ } else {
+ flib_map newmap = {0};
+ newmap.mapgen = MAPGEN_MAZE;
+ newmap.name = "+maze+";
+ newmap.seed = (char*)seed;
+ newmap.theme = (char*)theme;
+ newmap.mazeSize = mazeSize;
+ result = flib_map_copy(&newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_named(const char *seed, const char *name) {
+ flib_map *result = NULL;
+ if(!seed || !name) {
+ flib_log_e("null parameter in flib_map_create_named");
+ } else {
+ flib_map newmap = {0};
+ newmap.mapgen = MAPGEN_NAMED;
+ newmap.name = (char*)name;
+ newmap.seed = (char*)seed;
+ result = flib_map_copy(&newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize) {
+ flib_map *result = NULL;
+ if(!seed || !theme || (!drawData && drawDataSize)) {
+ flib_log_e("null parameter in flib_map_create_drawn");
+ } else {
+ flib_map newmap = {0};
+ newmap.mapgen = MAPGEN_DRAWN;
+ newmap.name = "+drawn+";
+ newmap.seed = (char*)seed;
+ newmap.theme = (char*)theme;
+ newmap.drawData = (uint8_t*) drawData;
+ newmap.drawDataSize = drawDataSize;
+ result = flib_map_copy(&newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_copy(const flib_map *map) {
+ flib_map *result = NULL;
+ if(map) {
+ flib_map *newmap = flib_map_retain(flib_calloc(1, sizeof(flib_map)));
+ if(newmap) {
+ newmap->mapgen = map->mapgen;
+ newmap->drawDataSize = map->drawDataSize;
+ newmap->drawData = flib_bufdupnull(map->drawData, map->drawDataSize);
+ newmap->mazeSize = map->mazeSize;
+ newmap->name = flib_strdupnull(map->name);
+ newmap->seed = flib_strdupnull(map->seed);
+ newmap->templateFilter = map->templateFilter;
+ newmap->theme = flib_strdupnull(map->theme);
+ if((newmap->drawData || !map->drawData) && (newmap->name || !map->name) && (newmap->seed || !map->seed) && (newmap->theme || !map->theme)) {
+ result = newmap;
+ newmap = NULL;
+ }
+ }
+ flib_map_release(newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_retain(flib_map *map) {
+ if(map) {
+ flib_retain(&map->_referenceCount, "flib_map");
+ }
+ return map;
+}
+
+void flib_map_release(flib_map *map) {
+ if(map && flib_release(&map->_referenceCount, "flib_map")) {
+ flib_map_destroy(map);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,102 @@
+#ifndef MODEL_MAP_H_
+#define MODEL_MAP_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define MAPGEN_REGULAR 0
+#define MAPGEN_MAZE 1
+#define MAPGEN_DRAWN 2
+#define MAPGEN_NAMED 3
+
+#define TEMPLATEFILTER_ALL 0
+#define TEMPLATEFILTER_SMALL 1
+#define TEMPLATEFILTER_MEDIUM 2
+#define TEMPLATEFILTER_LARGE 3
+#define TEMPLATEFILTER_CAVERN 4
+#define TEMPLATEFILTER_WACKY 5
+
+#define MAZE_SIZE_SMALL_TUNNELS 0
+#define MAZE_SIZE_MEDIUM_TUNNELS 1
+#define MAZE_SIZE_LARGE_TUNNELS 2
+#define MAZE_SIZE_SMALL_ISLANDS 3
+#define MAZE_SIZE_MEDIUM_ISLANDS 4
+#define MAZE_SIZE_LARGE_ISLANDS 5
+
+/**
+ * Data structure for defining a map. This contains the whole recipe to
+ * exactly recreate a particular map. For named maps, you also need the
+ * corresponding files.
+ *
+ * The required fields depend on the map generator, see the comments
+ * at the struct for details.
+ */
+typedef struct {
+ int _referenceCount;
+ int mapgen; // Always one of the MAPGEN_ constants
+ char *name; // The name of the map for MAPGEN_NAMED, otherwise one of "+rnd+", "+maze+" or "+drawn+".
+ char *seed; // Used for all maps
+ char *theme; // Used for all maps
+ 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 *seed, const char *theme, int templateFilter);
+
+/**
+ * Create a generated maze-type map. theme should be the name of a
+ * directory in "Themes" and mazeSize should be one of the
+ * MAZE_SIZE_* constants, but this is not checked before
+ * passing it to the engine.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize);
+
+/**
+ * Create a map from the Maps-Directory. name should be the name of a
+ * directory in "Maps", but this is not checked before
+ * passing it to the engine. If this is a mission, the corresponding
+ * script is used automatically.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_named(const char *seed, const char *name);
+
+/**
+ * Create a hand-drawn map. Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize);
+
+/**
+ * Create a deep copy of the map. Returns NULL on failure or if NULL was passed.
+ */
+flib_map *flib_map_copy(const flib_map *map);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_map *flib_map_retain(flib_map *map);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_map_release(flib_map *map);
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,44 @@
+#include "mapcfg.h"
+
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+void removeNewline(char *str) {
+ for(;*str;str++) {
+ if(*str=='\n' || *str=='\r') {
+ *str = 0;
+ return;
+ }
+ }
+}
+
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out) {
+ int result = -1;
+ if(!log_badparams_if(!dataDirPath || !mapname || !out)
+ && !log_e_if(flib_contains_dir_separator(mapname), "Illegal character in mapname %s", mapname)) {
+ char *path = flib_asprintf("%sMaps/%s/map.cfg", dataDirPath, mapname);
+ if(path) {
+ FILE *file = fopen(path, "rb");
+ if(!log_e_if(!file, "Unable to open map config file %s", path)) {
+ if(!log_e_if(!fgets(out->theme, sizeof(out->theme), file), "Error reading theme from %s", path)) {
+ removeNewline(out->theme);
+ char buf[64];
+ if(!log_e_if(!fgets(buf, sizeof(buf), file), "Error reading hoglimit from %s", path)) {
+ removeNewline(buf);
+ errno = 0;
+ out->hogLimit = strtol(buf, NULL, 10);
+ result = !log_e_if(errno, "Invalid hoglimit in %s: %i", path, buf);
+ }
+ }
+ fclose(file);
+ }
+ }
+ free(path);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/mapcfg.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,19 @@
+/*
+ * Data structure and functions for accessing the map.cfg of named maps.
+ */
+
+#ifndef MAPCFG_H_
+#define MAPCFG_H_
+
+typedef struct {
+ char theme[256];
+ int hogLimit;
+} flib_mapcfg;
+
+/**
+ * Read the map configuration for the map with this name.
+ * The dataDirPath must end in a path separator.
+ */
+int flib_mapcfg_read(const char *dataDirPath, const char *mapname, flib_mapcfg *out);
+
+#endif /* MAPCFG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/roomlist.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,146 @@
+#include "roomlist.h"
+
+#include "../util/util.h"
+#include "../util/list.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+flib_roomlist *flib_roomlist_create() {
+ return flib_calloc(1, sizeof(flib_roomlist));
+}
+
+static void flib_roomlist_room_destroy(flib_room *room) {
+ if(room) {
+ free(room->map);
+ free(room->name);
+ free(room->owner);
+ free(room->scheme);
+ free(room->weapons);
+ free(room);
+ }
+}
+
+void flib_roomlist_destroy(flib_roomlist *list) {
+ if(list) {
+ for(int i=0; i<list->roomCount; i++) {
+ flib_roomlist_room_destroy(list->rooms[i]);
+ }
+ free(list->rooms);
+ free(list);
+ }
+}
+
+static flib_room *fillRoomFromParams(char **params) {
+ flib_room *result = NULL;
+ flib_room *tmpRoom = flib_calloc(1, sizeof(flib_room));
+ if(tmpRoom) {
+ tmpRoom->inProgress = !strcmp(params[0], "True");
+ tmpRoom->name = flib_strdupnull(params[1]);
+ tmpRoom->playerCount = atoi(params[2]);
+ tmpRoom->teamCount = atoi(params[3]);
+ tmpRoom->owner = flib_strdupnull(params[4]);
+ tmpRoom->map = flib_strdupnull(params[5]);
+ tmpRoom->scheme = flib_strdupnull(params[6]);
+ tmpRoom->weapons = flib_strdupnull(params[7]);
+ if(tmpRoom->name && tmpRoom->owner && tmpRoom->map && tmpRoom->scheme && tmpRoom->weapons) {
+ result = tmpRoom;
+ tmpRoom = NULL;
+ }
+ }
+ flib_roomlist_room_destroy(tmpRoom);
+ return result;
+}
+
+GENERATE_STATIC_LIST_INSERT(insertRoom, flib_room*)
+GENERATE_STATIC_LIST_DELETE(deleteRoom, flib_room*)
+
+static int findRoom(const flib_roomlist *list, const char *name) {
+ for(int i=0; i<list->roomCount; i++) {
+ if(!strcmp(name, list->rooms[i]->name)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int flib_roomlist_add(flib_roomlist *list, char **params) {
+ int result = -1;
+ if(!list || !params) {
+ flib_log_e("null parameter in flib_roomlist_add");
+ } else {
+ flib_room *tmpRoom = fillRoomFromParams(params);
+ if(tmpRoom) {
+ if(!insertRoom(&list->rooms, &list->roomCount, tmpRoom, 0)) {
+ tmpRoom = NULL;
+ result = 0;
+ }
+ }
+ flib_roomlist_room_destroy(tmpRoom);
+ }
+ return result;
+}
+
+int flib_roomlist_delete(flib_roomlist *list, const char *name) {
+ int result = -1;
+ if(!list || !name) {
+ flib_log_e("null parameter in flib_roomlist_delete");
+ } else {
+ int roomid = findRoom(list, name);
+ if(roomid<0) {
+ flib_log_w("Attempt to delete unknown room %s", name);
+ } else {
+ flib_room *room = list->rooms[roomid];
+ if(!deleteRoom(&list->rooms, &list->roomCount, roomid)) {
+ flib_roomlist_room_destroy(room);
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+int flib_roomlist_update(flib_roomlist *list, const char *name, char **params) {
+ int result = -1;
+ if(!list || !name || !params) {
+ flib_log_e("null parameter in flib_roomlist_update");
+ } else {
+ flib_room *tmpRoom = fillRoomFromParams(params);
+ int roomid = findRoom(list, name);
+ if(tmpRoom && roomid>=0) {
+ flib_roomlist_room_destroy(list->rooms[roomid]);
+ list->rooms[roomid] = tmpRoom;
+ tmpRoom = NULL;
+ result = 0;
+ }
+ flib_roomlist_room_destroy(tmpRoom);
+ }
+ return result;
+}
+
+flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name) {
+ flib_room *result = NULL;
+ if(!list || !name) {
+ flib_log_e("null parameter in flib_roomlist_find");
+ } else {
+ int roomid = findRoom(list, name);
+ if(roomid>=0) {
+ result = list->rooms[roomid];
+ }
+ }
+ return result;
+}
+
+void flib_roomlist_clear(flib_roomlist *list) {
+ if(!list) {
+ flib_log_e("null parameter in flib_roomlist_clear");
+ } else {
+ for(int i=0; i<list->roomCount; i++) {
+ flib_roomlist_room_destroy(list->rooms[i]);
+ }
+ free(list->rooms);
+ list->rooms = NULL;
+ list->roomCount = 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/roomlist.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,61 @@
+/**
+ * Models the list of rooms on a server for netplay.
+ */
+
+#ifndef ROOMLIST_H_
+#define ROOMLIST_H_
+
+#include <stdbool.h>
+
+typedef struct {
+ bool inProgress; // true if the game is running
+ char *name;
+ int playerCount;
+ int teamCount;
+ char *owner;
+ char *map; // This is either a map name, or one of +rnd+, +maze+ or +drawn+.
+ char *scheme;
+ char *weapons;
+} flib_room;
+
+typedef struct {
+ int roomCount;
+ flib_room **rooms;
+} flib_roomlist;
+
+flib_roomlist *flib_roomlist_create();
+
+void flib_roomlist_destroy(flib_roomlist *list);
+
+/**
+ * Insert a new room at the start of the list. The room is defined by the params-array,
+ * which must consist of 8 non-null strings, as sent by the server in netplay.
+ *
+ * Returns 0 on success.
+ */
+int flib_roomlist_add(flib_roomlist *list, char **params);
+
+/**
+ * Update the room with the name [name] with parameters sent by the server.
+ *
+ * Returns 0 on success.
+ */
+int flib_roomlist_update(flib_roomlist *list, const char *name, char **params);
+
+/**
+ * Returns the room with the name [name] from the list if it exists, NULL otherwise
+ */
+flib_room *flib_roomlist_find(const flib_roomlist *list, const char *name);
+
+/**
+ * Removes all rooms from the list
+ */
+void flib_roomlist_clear(flib_roomlist *list);
+
+/**
+ * Delete the room with the name [name] from the room list.
+ * Returns 0 on success.
+ */
+int flib_roomlist_delete(flib_roomlist *list, const char *name);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,220 @@
+#include "schemelist.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+#include "../util/list.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+static void flib_schemelist_destroy(flib_schemelist *list) {
+ if(list) {
+ for(int i=0; i<list->schemeCount; i++) {
+ flib_cfg_release(list->schemes[i]);
+ }
+ free(list->schemes);
+ free(list);
+ }
+}
+
+static char *makePrefixedName(int schemeIndex, const char *settingName) {
+ return flib_asprintf("%i\\%s", schemeIndex, settingName);
+}
+
+static int readSettingsFromIni(flib_ini *ini, flib_cfg *scheme, int index) {
+ flib_cfg_meta *meta = scheme->meta;
+ bool error = false;
+ for(int i=0; i<meta->settingCount && !error; i++) {
+ char *key = makePrefixedName(index, meta->settings[i].name);
+ if(!key) {
+ error = true;
+ } else if(flib_ini_get_int_opt(ini, &scheme->settings[i], key, meta->settings[i].def)) {
+ flib_log_e("Error reading setting %s in schemes file.", key);
+ error = true;
+ }
+ free(key);
+ }
+ return error;
+}
+
+static int readModsFromIni(flib_ini *ini, flib_cfg *scheme, int index) {
+ flib_cfg_meta *meta = scheme->meta;
+ bool error = false;
+ for(int i=0; i<meta->modCount && !error; i++) {
+ char *key = makePrefixedName(index, meta->mods[i].name);
+ if(!key) {
+ error = true;
+ } else if(flib_ini_get_bool_opt(ini, &scheme->mods[i], key, false)) {
+ flib_log_e("Error reading mod %s in schemes file.", key);
+ error = true;
+ }
+ free(key);
+ }
+ return error;
+}
+
+static flib_cfg *readSchemeFromIni(flib_cfg_meta *meta, flib_ini *ini, int index) {
+ flib_cfg *result = NULL;
+ char *schemeNameKey = makePrefixedName(index+1, "name");
+ if(schemeNameKey) {
+ char *schemeName = NULL;
+ if(!flib_ini_get_str_opt(ini, &schemeName, schemeNameKey, "Unnamed")) {
+ flib_cfg *scheme = flib_cfg_create(meta, schemeName);
+ if(scheme) {
+ if(!readSettingsFromIni(ini, scheme, index) && !readModsFromIni(ini, scheme, index)) {
+ result = flib_cfg_retain(scheme);
+ }
+ }
+ flib_cfg_release(scheme);
+ }
+ free(schemeName);
+ }
+ free(schemeNameKey);
+ return result;
+}
+
+static flib_schemelist *fromIniHandleError(flib_schemelist *result, flib_ini *ini) {
+ flib_ini_destroy(ini);
+ flib_schemelist_destroy(result);
+ return NULL;
+}
+
+flib_schemelist *flib_schemelist_from_ini(flib_cfg_meta *meta, const char *filename) {
+ flib_schemelist *list = NULL;
+ if(!meta || !filename) {
+ flib_log_e("null parameter in flib_schemelist_from_ini");
+ return NULL;
+ }
+ flib_ini *ini = flib_ini_load(filename);
+ if(!ini || flib_ini_enter_section(ini, "schemes")) {
+ flib_log_e("Missing file or missing section \"schemes\" in file %s.", filename);
+ return fromIniHandleError(list, ini);
+ }
+
+ list = flib_schemelist_create();
+ if(!list) {
+ return fromIniHandleError(list, ini);
+ }
+
+ int schemeCount = 0;
+ if(flib_ini_get_int(ini, &schemeCount, "size")) {
+ flib_log_e("Missing or malformed scheme count in config file %s.", filename);
+ return fromIniHandleError(list, ini);
+ }
+
+ for(int i=0; i<schemeCount; i++) {
+ flib_cfg *scheme = readSchemeFromIni(meta, ini, i);
+ if(!scheme || flib_schemelist_insert(list, scheme, i)) {
+ flib_cfg_release(scheme);
+ flib_log_e("Error reading scheme %i from config file %s.", i, filename);
+ return fromIniHandleError(list, ini);
+ }
+ flib_cfg_release(scheme);
+ }
+
+
+ flib_ini_destroy(ini);
+ return list;
+}
+
+static int writeSchemeToIni(flib_cfg *scheme, flib_ini *ini, int index) {
+ flib_cfg_meta *meta = scheme->meta;
+ bool error = false;
+
+ char *key = makePrefixedName(index+1, "name");
+ error |= !key || flib_ini_set_str(ini, key, scheme->name);
+ free(key);
+
+ for(int i=0; i<meta->modCount && !error; i++) {
+ char *key = makePrefixedName(index+1, meta->mods[i].name);
+ error |= !key || flib_ini_set_bool(ini, key, scheme->mods[i]);
+ free(key);
+ }
+
+ for(int i=0; i<meta->settingCount && !error; i++) {
+ char *key = makePrefixedName(index+1, meta->settings[i].name);
+ error |= !key || flib_ini_set_int(ini, key, scheme->settings[i]);
+ free(key);
+ }
+ return error;
+}
+
+int flib_schemelist_to_ini(const char *filename, const flib_schemelist *schemes) {
+ int result = -1;
+ if(!filename || !schemes) {
+ flib_log_e("null parameter in flib_schemelist_to_ini");
+ } else {
+ flib_ini *ini = flib_ini_create(NULL);
+ if(ini && !flib_ini_create_section(ini, "schemes")) {
+ bool error = false;
+ error |= flib_ini_set_int(ini, "size", schemes->schemeCount);
+ for(int i=0; i<schemes->schemeCount && !error; i++) {
+ error |= writeSchemeToIni(schemes->schemes[i], ini, i);
+ }
+
+ if(!error) {
+ result = flib_ini_save(ini, filename);
+ }
+ }
+ flib_ini_destroy(ini);
+ }
+ return result;
+}
+
+flib_schemelist *flib_schemelist_create() {
+ return flib_schemelist_retain(flib_calloc(1, sizeof(flib_schemelist)));
+}
+
+flib_schemelist *flib_schemelist_retain(flib_schemelist *list) {
+ if(list) {
+ flib_retain(&list->_referenceCount, "flib_schemelist");
+ }
+ return list;
+}
+
+void flib_schemelist_release(flib_schemelist *list) {
+ if(list && flib_release(&list->_referenceCount, "flib_schemelist")) {
+ flib_schemelist_destroy(list);
+ }
+}
+
+flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name) {
+ if(list && name) {
+ for(int i=0; i<list->schemeCount; i++) {
+ if(!strcmp(name, list->schemes[i]->name)) {
+ return list->schemes[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+GENERATE_STATIC_LIST_INSERT(insertScheme, flib_cfg*)
+GENERATE_STATIC_LIST_DELETE(deleteScheme, flib_cfg*)
+
+int flib_schemelist_insert(flib_schemelist *list, flib_cfg *cfg, int pos) {
+ if(!list) {
+ flib_log_e("Invalid parameter in flib_schemelist_insert");
+ } else if(!insertScheme(&list->schemes, &list->schemeCount, cfg, pos)) {
+ flib_cfg_retain(cfg);
+ return 0;
+ }
+ return -1;
+}
+
+int flib_schemelist_delete(flib_schemelist *list, int pos) {
+ if(!list) {
+ flib_log_e("Invalid parameter in flib_schemelist_delete");
+ } else {
+ flib_cfg *elem = list->schemes[pos];
+ if(!deleteScheme(&list->schemes, &list->schemeCount, pos)) {
+ flib_cfg_release(elem);
+ return 0;
+ }
+ }
+ return -1;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/schemelist.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,67 @@
+/**
+ * Functions for managing a list of schemes.
+ * This is in here because the scheme config file of the QtFrontend (which we are staying compatble with) contains
+ * all the schemes at once, so we need functions to work with a list like that.
+ */
+
+#ifndef SCHEMELIST_H_
+#define SCHEMELIST_H_
+
+#include "cfg.h"
+
+typedef struct {
+ int _referenceCount;
+ int schemeCount;
+ flib_cfg **schemes;
+} flib_schemelist;
+
+/**
+ * Load a list of configurations from the ini file.
+ * Returns NULL on error.
+ */
+flib_schemelist *flib_schemelist_from_ini(flib_cfg_meta *meta, const char *filename);
+
+/**
+ * Store the list of configurations to an ini file.
+ * Returns NULL on error.
+ */
+int flib_schemelist_to_ini(const char *filename, const flib_schemelist *config);
+
+/**
+ * Create an empty scheme list. Returns NULL on error.
+ */
+flib_schemelist *flib_schemelist_create();
+
+/**
+ * Insert a new scheme into the list at position pos, moving all higher schemes to make place.
+ * pos must be at least 0 (insert at the start) and at most list->schemeCount (insert at the end).
+ * The scheme is retained automatically.
+ * Returns 0 on success.
+ */
+int flib_schemelist_insert(flib_schemelist *list, flib_cfg *cfg, int pos);
+
+/**
+ * Delete a cfg from the list at position pos, moving down all higher schemes.
+ * The scheme is released automatically.
+ * Returns 0 on success.
+ */
+int flib_schemelist_delete(flib_schemelist *list, int pos);
+
+/**
+ * Find the scheme with a specific name
+ */
+flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_schemelist *flib_schemelist_retain(flib_schemelist *list);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_schemelist_release(flib_schemelist *list);
+
+
+#endif /* SCHEMELIST_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,305 @@
+#include "team.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+#include "../util/refcounter.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+static flib_team *from_ini_handleError(flib_team *result, flib_ini *settingfile) {
+ flib_ini_destroy(settingfile);
+ flib_team_release(result);
+ return NULL;
+}
+
+flib_team *flib_team_from_ini(const char *filename) {
+ flib_team *result = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
+ flib_ini *ini = NULL;
+
+ if(!filename) {
+ flib_log_e("null parameter in flib_team_from_ini");
+ return from_ini_handleError(result, ini);
+ }
+
+ if(!result) {
+ return from_ini_handleError(result, ini);
+ }
+
+ ini = flib_ini_load(filename);
+ if(!ini) {
+ flib_log_e("Error loading team file %s", filename);
+ return from_ini_handleError(result, ini);
+ }
+
+ if(flib_ini_enter_section(ini, "team")) {
+ flib_log_e("Missing section \"Team\" in team file %s", filename);
+ return from_ini_handleError(result, ini);
+ }
+ bool error = false;
+ error |= flib_ini_get_str(ini, &result->name, "name");
+ error |= flib_ini_get_str(ini, &result->grave, "grave");
+ error |= flib_ini_get_str(ini, &result->fort, "fort");
+ error |= flib_ini_get_str(ini, &result->voicepack, "voicepack");
+ error |= flib_ini_get_str(ini, &result->flag, "flag");
+ error |= flib_ini_get_int(ini, &result->rounds, "rounds");
+ error |= flib_ini_get_int(ini, &result->wins, "wins");
+ error |= flib_ini_get_int(ini, &result->campaignProgress, "campaignprogress");
+
+ int difficulty = 0;
+ error |= flib_ini_get_int(ini, &difficulty, "difficulty");
+
+ if(error) {
+ flib_log_e("Missing or malformed entry in section \"Team\" in file %s", filename);
+ return from_ini_handleError(result, ini);
+ }
+
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ char sectionName[32];
+ if(snprintf(sectionName, sizeof(sectionName), "hedgehog%i", i) <= 0) {
+ return from_ini_handleError(result, ini);
+ }
+ if(flib_ini_enter_section(ini, sectionName)) {
+ flib_log_e("Missing section \"%s\" in team file %s", sectionName, filename);
+ return from_ini_handleError(result, ini);
+ }
+ flib_hog *hog = &result->hogs[i];
+ error |= flib_ini_get_str(ini, &hog->name, "name");
+ error |= flib_ini_get_str(ini, &hog->hat, "hat");
+ error |= flib_ini_get_int(ini, &hog->rounds, "rounds");
+ error |= flib_ini_get_int(ini, &hog->kills, "kills");
+ error |= flib_ini_get_int(ini, &hog->deaths, "deaths");
+ error |= flib_ini_get_int(ini, &hog->suicides, "suicides");
+ result->hogs[i].difficulty = difficulty;
+ result->hogs[i].initialHealth = TEAM_DEFAULT_HEALTH;
+
+ if(error) {
+ flib_log_e("Missing or malformed entry in section \"%s\" in file %s", sectionName, filename);
+ return from_ini_handleError(result, ini);
+ }
+ }
+
+ if(!flib_ini_enter_section(ini, "binds")) {
+ result->bindingCount = flib_ini_get_keycount(ini);
+ if(result->bindingCount<0) {
+ flib_log_e("Error reading bindings from file %s", filename);
+ result->bindingCount = 0;
+ }
+ result->bindings = flib_calloc(result->bindingCount, sizeof(flib_binding));
+ if(!result->bindings) {
+ return from_ini_handleError(result, ini);
+ }
+ for(int i=0; i<result->bindingCount; i++) {
+ char *keyname = flib_ini_get_keyname(ini, i);
+ if(!keyname) {
+ error = true;
+ } else {
+ result->bindings[i].action = flib_urldecode(keyname);
+ error |= !result->bindings[i].action;
+ error |= flib_ini_get_str(ini, &result->bindings[i].binding, keyname);
+ }
+ free(keyname);
+ }
+ }
+
+ if(error) {
+ flib_log_e("Error reading team file %s", filename);
+ return from_ini_handleError(result, ini);
+ }
+
+ flib_ini_destroy(ini);
+ return result;
+}
+
+static int writeTeamSection(const flib_team *team, flib_ini *ini) {
+ if(flib_ini_create_section(ini, "team")) {
+ return -1;
+ }
+ bool error = false;
+ error |= flib_ini_set_str(ini, "name", team->name);
+ error |= flib_ini_set_str(ini, "grave", team->grave);
+ error |= flib_ini_set_str(ini, "fort", team->fort);
+ error |= flib_ini_set_str(ini, "voicepack", team->voicepack);
+ error |= flib_ini_set_str(ini, "flag", team->flag);
+ error |= flib_ini_set_int(ini, "rounds", team->rounds);
+ error |= flib_ini_set_int(ini, "wins", team->wins);
+ error |= flib_ini_set_int(ini, "campaignprogress", team->campaignProgress);
+ error |= flib_ini_set_int(ini, "difficulty", team->hogs[0].difficulty);
+ return error;
+}
+
+static int writeHogSections(const flib_team *team, flib_ini *ini) {
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ const flib_hog *hog = &team->hogs[i];
+ char sectionName[32];
+ if(snprintf(sectionName, sizeof(sectionName), "hedgehog%i", i) <= 0) {
+ return -1;
+ }
+ if(flib_ini_create_section(ini, sectionName)) {
+ return -1;
+ }
+ bool error = false;
+ error |= flib_ini_set_str(ini, "name", hog->name);
+ error |= flib_ini_set_str(ini, "hat", hog->hat);
+ error |= flib_ini_set_int(ini, "rounds", hog->rounds);
+ error |= flib_ini_set_int(ini, "kills", hog->kills);
+ error |= flib_ini_set_int(ini, "deaths", hog->deaths);
+ error |= flib_ini_set_int(ini, "suicides", hog->suicides);
+ if(error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+static int writeBindingSection(const flib_team *team, flib_ini *ini) {
+ if(flib_ini_create_section(ini, "binds")) {
+ return -1;
+ }
+ for(int i=0; i<team->bindingCount; i++) {
+ bool error = false;
+ char *action = flib_urlencode(team->bindings[i].action);
+ if(action) {
+ error |= flib_ini_set_str(ini, action, team->bindings[i].binding);
+ free(action);
+ } else {
+ error = true;
+ }
+ if(error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+int flib_team_to_ini(const char *filename, const flib_team *team) {
+ int result = -1;
+ if(!filename || !team) {
+ flib_log_e("null parameter in flib_team_to_ini");
+ } else {
+ flib_ini *ini = flib_ini_create(filename);
+ bool error = false;
+ error |= writeTeamSection(team, ini);
+ error |= writeHogSections(team, ini);
+ error |= writeBindingSection(team, ini);
+ if(!error) {
+ result = flib_ini_save(ini, filename);
+ }
+ flib_ini_destroy(ini);
+ }
+ return result;
+}
+
+flib_team *flib_team_retain(flib_team *team) {
+ if(team) {
+ flib_retain(&team->_referenceCount, "flib_team");
+ }
+ return team;
+}
+
+void flib_team_release(flib_team *team) {
+ if(team && flib_release(&team->_referenceCount, "flib_team")) {
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ free(team->hogs[i].name);
+ free(team->hogs[i].hat);
+ flib_weaponset_release(team->hogs[i].weaponset);
+ }
+ free(team->name);
+ free(team->grave);
+ free(team->fort);
+ free(team->voicepack);
+ free(team->flag);
+ if(team->bindings) {
+ for(int i=0; i<team->bindingCount; i++) {
+ free(team->bindings[i].action);
+ free(team->bindings[i].binding);
+ }
+ }
+ free(team->bindings);
+ free(team->ownerName);
+ free(team);
+ }
+}
+
+void flib_team_set_weaponset(flib_team *team, flib_weaponset *set) {
+ if(team) {
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ flib_weaponset_release(team->hogs[i].weaponset);
+ team->hogs[i].weaponset = flib_weaponset_retain(set);
+ }
+ }
+}
+
+void flib_team_set_health(flib_team *team, int health) {
+ if(team) {
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ team->hogs[i].initialHealth = health;
+ }
+ }
+}
+
+char *strdupWithError(const char *in, bool *error) {
+ char *out = flib_strdupnull(in);
+ if(in && !out) {
+ *error = true;
+ }
+ return out;
+}
+
+flib_team *flib_team_copy(const flib_team *team) {
+ flib_team *result = NULL;
+ if(team) {
+ flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
+ if(tmpTeam) {
+ bool error = false;
+
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ tmpTeam->hogs[i].name = strdupWithError(team->hogs[i].name, &error);
+ tmpTeam->hogs[i].hat = strdupWithError(team->hogs[i].hat, &error);
+ tmpTeam->hogs[i].rounds = team->hogs[i].rounds;
+ tmpTeam->hogs[i].kills = team->hogs[i].kills;
+ tmpTeam->hogs[i].deaths = team->hogs[i].deaths;
+ tmpTeam->hogs[i].suicides = team->hogs[i].suicides;
+ tmpTeam->hogs[i].difficulty = team->hogs[i].difficulty;
+ tmpTeam->hogs[i].initialHealth = team->hogs[i].initialHealth;
+ tmpTeam->hogs[i].weaponset = flib_weaponset_retain(team->hogs[i].weaponset);
+ }
+
+ tmpTeam->name = strdupWithError(team->name, &error);
+ tmpTeam->grave = strdupWithError(team->grave, &error);
+ tmpTeam->fort = strdupWithError(team->fort, &error);
+ tmpTeam->voicepack = strdupWithError(team->voicepack, &error);
+ tmpTeam->flag = strdupWithError(team->flag, &error);
+ tmpTeam->ownerName = strdupWithError(team->ownerName, &error);
+
+ tmpTeam->bindingCount = team->bindingCount;
+ if(team->bindings) {
+ tmpTeam->bindings = flib_calloc(team->bindingCount, sizeof(flib_binding));
+ if(tmpTeam->bindings) {
+ for(int i=0; i<tmpTeam->bindingCount; i++) {
+ tmpTeam->bindings[i].action = strdupWithError(team->bindings[i].action, &error);
+ tmpTeam->bindings[i].binding = strdupWithError(team->bindings[i].binding, &error);
+ }
+ } else {
+ error = true;
+ }
+ }
+
+ tmpTeam->rounds = team->rounds;
+ tmpTeam->wins = team->wins;
+ tmpTeam->campaignProgress = team->campaignProgress;
+
+ tmpTeam->colorIndex = team->colorIndex;
+ tmpTeam->hogsInGame = team->hogsInGame;
+ tmpTeam->remoteDriven = team->remoteDriven;
+
+ if(!error) {
+ result = tmpTeam;
+ tmpTeam = 0;
+ }
+ }
+ flib_team_release(tmpTeam);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,123 @@
+/**
+ * This file defines a data structure for a hedgewars team.
+ *
+ * Teams are used in several different contexts in Hedgewars, and some of these require
+ * extra information about teams. For example, the weaponset is important
+ * to the engine, but not for ini reading/writing, and with the team statistics it is the
+ * other way around. To keep things simple, the data structure can hold all information
+ * used in any context. On the downside, tat means we can't use static typing to ensure
+ * that team information is "complete" for a particular purpose.
+ */
+#ifndef TEAM_H_
+#define TEAM_H_
+
+
+#include "weapon.h"
+#include "../hwconsts.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TEAM_DEFAULT_HEALTH 100
+
+typedef struct {
+ char *action;
+ char *binding;
+} flib_binding;
+
+typedef struct {
+ char *name;
+ char *hat;
+
+ // Statistics. They are irrelevant for the engine or server,
+ // but provided for ini reading/writing by the frontend.
+ int rounds;
+ int kills;
+ int deaths;
+ int suicides;
+
+ int difficulty;
+
+ // Transient setting used in game setup
+ int initialHealth;
+ flib_weaponset *weaponset;
+} flib_hog;
+
+typedef struct {
+ int _referenceCount;
+ flib_hog hogs[HEDGEHOGS_PER_TEAM];
+ char *name;
+ char *grave;
+ char *fort;
+ char *voicepack;
+ char *flag;
+
+ flib_binding *bindings;
+ int bindingCount;
+
+ // Statistics. They are irrelevant for the engine or server,
+ // but provided for ini reading/writing by the frontend.
+ int rounds;
+ int wins;
+ int campaignProgress;
+
+ // Transient settings used in game setup
+ int colorIndex; // Index into a color table
+ int hogsInGame;
+ bool remoteDriven;
+ char *ownerName;
+} flib_team;
+
+/**
+ * Returns a new team, or NULL on error. name must not be NULL.
+ *
+ * The new team is pre-filled with default settings (see hwconsts.h)
+ */
+flib_team *flib_team_create(const char *name);
+
+/**
+ * Loads a team, returns NULL on error.
+ */
+flib_team *flib_team_from_ini(const char *filename);
+
+/**
+ * Write the team to an ini file. Attempts to retain extra ini settings
+ * that were already present. Note that not all fields of a team struct
+ * are stored in the ini, some are only used intermittently to store
+ * information about a team in the context of a game.
+ *
+ * The flib_team can handle "difficulty" on a per-hog basis, but it
+ * is only written per-team in the team file. The difficulty of the
+ * first hog is used for the entire team when writing.
+ */
+int flib_team_to_ini(const char *filename, const flib_team *team);
+
+/**
+ * Set the same weaponset for every hog in the team
+ */
+void flib_team_set_weaponset(flib_team *team, flib_weaponset *set);
+
+/**
+ * Set the same initial health for every hog.
+ */
+void flib_team_set_health(flib_team *team, int health);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_team *flib_team_retain(flib_team *team);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_team_release(flib_team *team);
+
+/**
+ * Create a deep copy of a team. Returns NULL on failure.
+ * The referenced weaponsets are not copied, so the new
+ * team references the same weaponsets.
+ */
+flib_team *flib_team_copy(const flib_team *team);
+
+#endif /* TEAM_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,87 @@
+#include "teamlist.h"
+
+#include "../util/util.h"
+#include "../util/list.h"
+#include "../util/logging.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+flib_teamlist *flib_teamlist_create() {
+ return flib_calloc(1, sizeof(flib_teamlist));
+}
+
+void flib_teamlist_destroy(flib_teamlist *list) {
+ if(list) {
+ for(int i=0; i<list->teamCount; i++) {
+ flib_team_release(list->teams[i]);
+ }
+ free(list->teams);
+ free(list);
+ }
+}
+
+GENERATE_STATIC_LIST_INSERT(insertTeam, flib_team*)
+GENERATE_STATIC_LIST_DELETE(deleteTeam, flib_team*)
+
+static int findTeam(const flib_teamlist *list, const char *name) {
+ for(int i=0; i<list->teamCount; i++) {
+ if(!strcmp(name, list->teams[i]->name)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos) {
+ if(!list || !team) {
+ flib_log_e("null parameter in flib_teamlist_insert");
+ } else if(!insertTeam(&list->teams, &list->teamCount, team, pos)) {
+ flib_team_retain(team);
+ return 0;
+ }
+ return -1;
+}
+
+int flib_teamlist_delete(flib_teamlist *list, const char *name) {
+ int result = -1;
+ if(!list || !name) {
+ flib_log_e("null parameter in flib_teamlist_delete");
+ } else {
+ int itemid = findTeam(list, name);
+ if(itemid>=0) {
+ flib_team *team = list->teams[itemid];
+ if(!deleteTeam(&list->teams, &list->teamCount, itemid)) {
+ flib_team_release(team);
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name) {
+ flib_team *result = NULL;
+ if(!list || !name) {
+ flib_log_e("null parameter in flib_teamlist_find");
+ } else {
+ int itemid = findTeam(list, name);
+ if(itemid>=0) {
+ result = list->teams[itemid];
+ }
+ }
+ return result;
+}
+
+void flib_teamlist_clear(flib_teamlist *list) {
+ if(!list) {
+ flib_log_e("null parameter in flib_teamlist_clear");
+ } else {
+ for(int i=0; i<list->teamCount; i++) {
+ flib_team_release(list->teams[i]);
+ }
+ free(list->teams);
+ list->teams = NULL;
+ list->teamCount = 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/teamlist.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef TEAMLIST_H_
+#define TEAMLIST_H_
+
+#include "team.h"
+
+typedef struct {
+ int teamCount;
+ flib_team **teams;
+} flib_teamlist;
+
+flib_teamlist *flib_teamlist_create();
+
+void flib_teamlist_destroy(flib_teamlist *list);
+
+/**
+ * Insert a team into the list. Returns 0 on success.
+ */
+int flib_teamlist_insert(flib_teamlist *list, flib_team *team, int pos);
+
+/**
+ * Delete the item with the name [name] from the list.
+ * Returns 0 on success.
+ */
+int flib_teamlist_delete(flib_teamlist *list, const char *name);
+
+/**
+ * Returns the team with the name [name] from the list if it exists, NULL otherwise
+ */
+flib_team *flib_teamlist_find(const flib_teamlist *list, const char *name);
+
+/**
+ * Removes all items from the list and frees "teams".
+ */
+void flib_teamlist_clear(flib_teamlist *list);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,253 @@
+#include "weapon.h"
+
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/refcounter.h"
+#include "../util/list.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+static void flib_weaponset_destroy(flib_weaponset *cfg) {
+ if(cfg) {
+ free(cfg->name);
+ free(cfg);
+ }
+}
+
+static void setField(char field[WEAPONS_COUNT+1], const char *line, int lineLen, bool no9) {
+ if(lineLen>WEAPONS_COUNT) {
+ lineLen = WEAPONS_COUNT;
+ }
+
+ char min = '0';
+ char max = no9 ? '8' : '9';
+ for(int i=0; i<lineLen; i++) {
+ if(line[i] >= min && line[i] <= max) {
+ field[i] = line[i];
+ } else {
+ flib_log_w("Invalid character in weapon config string \"%.*s\", position %i", lineLen, line, i);
+ field[i] = '0';
+ }
+ }
+ for(int i=lineLen; i<WEAPONS_COUNT; i++) {
+ field[i] = '0';
+ }
+ field[WEAPONS_COUNT] = 0;
+}
+
+flib_weaponset *flib_weaponset_create(const char *name) {
+ flib_weaponset *result = NULL;
+ if(!name) {
+ flib_log_e("null parameter in flib_weaponset_create_str");
+ } else {
+ flib_weaponset *newSet = flib_weaponset_retain(flib_calloc(1, sizeof(flib_weaponset)));
+ if(newSet) {
+ newSet->name = flib_strdupnull(name);
+ if(newSet->name) {
+ setField(newSet->loadout, "", 0, false);
+ setField(newSet->crateprob, "", 0, false);
+ setField(newSet->crateammo, "", 0, false);
+ setField(newSet->delay, "", 0, false);
+ result = flib_weaponset_retain(newSet);
+ }
+ }
+ flib_weaponset_release(newSet);
+ }
+ return result;
+}
+
+flib_weaponset *flib_weaponset_retain(flib_weaponset *weaponset) {
+ if(weaponset) {
+ flib_retain(&weaponset->_referenceCount, "flib_weaponset");
+ }
+ return weaponset;
+}
+
+void flib_weaponset_release(flib_weaponset *weaponset) {
+ if(weaponset && flib_release(&weaponset->_referenceCount, "flib_weaponset")) {
+ flib_weaponset_destroy(weaponset);
+ }
+}
+
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset) {
+ if(!weaponset) {
+ return NULL;
+ }
+
+ flib_weaponset *result = flib_weaponset_create(weaponset->name);
+ if(result) {
+ memcpy(result->loadout, weaponset->loadout, WEAPONS_COUNT+1);
+ memcpy(result->crateprob, weaponset->crateprob, WEAPONS_COUNT+1);
+ memcpy(result->delay, weaponset->delay, WEAPONS_COUNT+1);
+ memcpy(result->crateammo, weaponset->crateammo, WEAPONS_COUNT+1);
+ }
+
+ return result;
+}
+
+static void flib_weaponsetlist_destroy(flib_weaponsetlist *list) {
+ if(list) {
+ for(int i=0; i<list->weaponsetCount; i++) {
+ flib_weaponset_release(list->weaponsets[i]);
+ }
+ free(list->weaponsets);
+ free(list);
+ }
+}
+
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring) {
+ flib_weaponset *result = NULL;
+ if(!name || !ammostring) {
+ flib_log_e("null parameter in flib_weaponset_from_ammostring");
+ } else {
+ result = flib_weaponset_create(name);
+ if(result) {
+ int fieldlen = strlen(ammostring)/4;
+ setField(result->loadout, ammostring, fieldlen, false);
+ setField(result->crateprob, ammostring + fieldlen, fieldlen, true);
+ setField(result->delay, ammostring + 2*fieldlen, fieldlen, true);
+ setField(result->crateammo, ammostring + 3*fieldlen, fieldlen, true);
+ }
+ }
+ return result;
+}
+
+static int fillWeaponsetFromIni(flib_weaponsetlist *list, flib_ini *ini, int index) {
+ int result = -1;
+ char *keyname = flib_ini_get_keyname(ini, index);
+ char *decodedKeyname = flib_urldecode(keyname);
+ char *ammostring = NULL;
+ if(decodedKeyname && !flib_ini_get_str(ini, &ammostring, keyname)) {
+ flib_weaponset *set = flib_weaponset_from_ammostring(decodedKeyname, ammostring);
+ if(set) {
+ result = flib_weaponsetlist_insert(list, set, list->weaponsetCount);
+ }
+ flib_weaponset_release(set);
+ }
+ free(ammostring);
+ free(decodedKeyname);
+ free(keyname);
+ return result;
+}
+
+static int fillWeaponsetsFromIni(flib_weaponsetlist *list, flib_ini *ini) {
+ bool error = false;
+ int weaponsets = flib_ini_get_keycount(ini);
+
+ for(int i=0; i<weaponsets && !error; i++) {
+ error |= fillWeaponsetFromIni(list, ini, i);
+ }
+ return error;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_from_ini(const char *filename) {
+ flib_weaponsetlist *result = NULL;
+ if(!filename) {
+ flib_log_e("null parameter in flib_weaponsetlist_from_ini");
+ } else {
+ flib_ini *ini = flib_ini_load(filename);
+ if(!ini) {
+ flib_log_e("Missing file %s.", filename);
+ } else if(flib_ini_enter_section(ini, "General")) {
+ flib_log_e("Missing section \"General\" in file %s.", filename);
+ } else {
+ flib_weaponsetlist *list = flib_weaponsetlist_create();
+ if(list) {
+ if(!fillWeaponsetsFromIni(list, ini)) {
+ result = flib_weaponsetlist_retain(list);
+ }
+ }
+ flib_weaponsetlist_release(list);
+ }
+ flib_ini_destroy(ini);
+ }
+ return result;
+}
+
+static bool needsEscape(char c) {
+ return !((c>='0' && c<='9') || (c>='a' && c <='z'));
+}
+
+
+static int writeWeaponsetToIni(flib_ini *ini, flib_weaponset *set) {
+ int result = -1;
+ char weaponstring[WEAPONS_COUNT*4+1];
+ strcpy(weaponstring, set->loadout);
+ strcat(weaponstring, set->crateprob);
+ strcat(weaponstring, set->delay);
+ strcat(weaponstring, set->crateammo);
+
+ char *escapedname = flib_urlencode_pred(set->name, needsEscape);
+ if(escapedname) {
+ result = flib_ini_set_str(ini, escapedname, weaponstring);
+ }
+ free(escapedname);
+ return result;
+}
+
+int flib_weaponsetlist_to_ini(const char *filename, const flib_weaponsetlist *list) {
+ int result = -1;
+ if(!filename || !list) {
+ flib_log_e("null parameter in flib_weaponsetlist_to_ini");
+ } else {
+ flib_ini *ini = flib_ini_create(NULL);
+ if(ini && !flib_ini_create_section(ini, "General")) {
+ bool error = false;
+ for(int i=0; i<list->weaponsetCount && !error; i++) {
+ error |= writeWeaponsetToIni(ini, list->weaponsets[i]);
+ }
+
+ if(!error) {
+ result = flib_ini_save(ini, filename);
+ }
+ }
+ flib_ini_destroy(ini);
+ }
+ return result;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_create() {
+ return flib_weaponsetlist_retain(flib_calloc(1, sizeof(flib_weaponsetlist)));
+}
+
+GENERATE_STATIC_LIST_INSERT(insertWeaponset, flib_weaponset*)
+GENERATE_STATIC_LIST_DELETE(deleteWeaponset, flib_weaponset*)
+
+int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *set, int pos) {
+ if(!list) {
+ flib_log_e("Invalid parameter in flib_weaponsetlist_insert");
+ } else if(!insertWeaponset(&list->weaponsets, &list->weaponsetCount, set, pos)) {
+ flib_weaponset_retain(set);
+ return 0;
+ }
+ return -1;
+}
+
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos) {
+ if(!list) {
+ flib_log_e("Invalid parameter in flib_weaponsetlist_delete");
+ } else {
+ flib_weaponset *elem = list->weaponsets[pos];
+ if(!deleteWeaponset(&list->weaponsets, &list->weaponsetCount, pos)) {
+ flib_weaponset_release(elem);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+flib_weaponsetlist *flib_weaponsetlist_retain(flib_weaponsetlist *list) {
+ if(list) {
+ flib_retain(&list->_referenceCount, "flib_weaponsetlist");
+ }
+ return list;
+}
+
+void flib_weaponsetlist_release(flib_weaponsetlist *list) {
+ if(list && flib_release(&list->_referenceCount, "flib_weaponsetlist")) {
+ flib_weaponsetlist_destroy(list);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,98 @@
+#ifndef MODEL_WEAPON_H_
+#define MODEL_WEAPON_H_
+
+#include "../hwconsts.h"
+
+/**
+ * These values are all in the range 0..9
+ *
+ * For loadout, 9 means inifinite ammo.
+ * For the other setting, 9 is invalid.
+ */
+typedef struct {
+ int _referenceCount;
+ char loadout[WEAPONS_COUNT+1];
+ char crateprob[WEAPONS_COUNT+1];
+ char crateammo[WEAPONS_COUNT+1];
+ char delay[WEAPONS_COUNT+1];
+ char *name;
+} flib_weaponset;
+
+typedef struct {
+ int _referenceCount;
+ int weaponsetCount;
+ flib_weaponset **weaponsets;
+} flib_weaponsetlist;
+
+/**
+ * Returns a new weapon set, or NULL on error.
+ * name must not be NULL.
+ *
+ * The new weapon set is pre-filled with default
+ * settings (see hwconsts.h)
+ */
+flib_weaponset *flib_weaponset_create(const char *name);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_weaponset *flib_weaponset_retain(flib_weaponset *weaponset);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_weaponset_release(flib_weaponset *weaponset);
+
+flib_weaponset *flib_weaponset_copy(const flib_weaponset *weaponset);
+
+/**
+ * Create a weaponset from an ammostring. This format is used both in the ini files
+ * and in the net protocol.
+ */
+flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring);
+
+/**
+ * Load a list of weaponsets from the ini file.
+ * Returns NULL on error.
+ */
+flib_weaponsetlist *flib_weaponsetlist_from_ini(const char *filename);
+
+/**
+ * Store the list of weaponsets to an ini file.
+ * Returns NULL on error.
+ */
+int flib_weaponsetlist_to_ini(const char *filename, const flib_weaponsetlist *weaponsets);
+
+/**
+ * Create an empty weaponset list. Returns NULL on error.
+ */
+flib_weaponsetlist *flib_weaponsetlist_create();
+
+/**
+ * Insert a new weaponset into the list at position pos, moving all higher weaponsets to make place.
+ * pos must be at least 0 (insert at the start) and at most list->weaponsetCount (insert at the end).
+ * The weaponset is retained automatically.
+ * Returns 0 on success.
+ */
+int flib_weaponsetlist_insert(flib_weaponsetlist *list, flib_weaponset *weaponset, int pos);
+
+/**
+ * Delete a weaponset from the list at position pos, moving down all higher weaponsets.
+ * The weaponset is released automatically.
+ * Returns 0 on success.
+ */
+int flib_weaponsetlist_delete(flib_weaponsetlist *list, int pos);
+
+/**
+ * Increase the reference count of the object. Call this if you store a pointer to it somewhere.
+ * Returns the parameter.
+ */
+flib_weaponsetlist *flib_weaponsetlist_retain(flib_weaponsetlist *list);
+
+/**
+ * Decrease the reference count of the object and free it if this was the last reference.
+ */
+void flib_weaponsetlist_release(flib_weaponsetlist *list);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,247 @@
+#include "netbase.h"
+#include "../util/buffer.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../socket.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NET_READBUFFER_LIMIT (1024*1024)
+
+struct _flib_netbase {
+ flib_vector *readBuffer;
+ flib_tcpsocket *sock;
+};
+
+flib_netbase *flib_netbase_create(const char *server, uint16_t port) {
+ flib_netbase *result = NULL;
+ flib_netbase *newNet = flib_calloc(1, sizeof(flib_netbase));
+
+ if(newNet) {
+ newNet->readBuffer = flib_vector_create();
+ newNet->sock = flib_socket_connect(server, port);
+ if(newNet->readBuffer && newNet->sock) {
+ flib_log_i("Connected to server %s:%u", server, (unsigned)port);
+ result = newNet;
+ newNet = NULL;
+ }
+ }
+ flib_netbase_destroy(newNet);
+
+ return result;
+}
+
+void flib_netbase_destroy(flib_netbase *net) {
+ if(net) {
+ flib_socket_close(net->sock);
+ flib_vector_destroy(net->readBuffer);
+ free(net);
+ }
+}
+
+bool flib_netbase_connected(flib_netbase *net) {
+ if(!net) {
+ flib_log_e("null parameter in flib_netbase_connected");
+ return false;
+ } else if(net->sock) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Parses and returns a message, and removes it from the vector.
+ */
+static flib_netmsg *parseMessage(flib_vector *vec) {
+ const uint8_t *partStart = flib_vector_data(vec);
+ const uint8_t *end = partStart+flib_vector_size(vec);
+ flib_netmsg *result = flib_netmsg_create();
+ if(!result) {
+ return NULL;
+ }
+
+ while(1) {
+ const uint8_t *partEnd = memchr(partStart, '\n', end-partStart);
+ if(!partEnd) {
+ // message incomplete
+ flib_netmsg_destroy(result);
+ return NULL;
+ } else if(partEnd-partStart == 0) {
+ // Zero-length part, message end marker. Remove the message from the vector.
+ uint8_t *vectorStart = flib_vector_data(vec);
+ size_t msgLen = partEnd+1-vectorStart;
+ memmove(vectorStart, partEnd+1, flib_vector_size(vec)-msgLen);
+ flib_vector_resize(vec, flib_vector_size(vec)-msgLen);
+ return result;
+ } else {
+ if(flib_netmsg_append_part(result, partStart, partEnd-partStart)) {
+ flib_netmsg_destroy(result);
+ return NULL;
+ }
+ partStart = partEnd+1; // Skip the '\n'
+ }
+ }
+ return NULL; // Never reached
+}
+
+/**
+ * Receive some bytes and add them to the buffer.
+ * Returns the number of bytes received.
+ * Automatically closes the socket if an error occurs
+ * and sets sock=NULL.
+ */
+static int receiveToBuffer(flib_netbase *net) {
+ uint8_t buffer[256];
+ if(!net->sock) {
+ return 0;
+ } else if(flib_vector_size(net->readBuffer) > NET_READBUFFER_LIMIT) {
+ flib_log_e("Net connection closed: Net message too big");
+ flib_socket_close(net->sock);
+ net->sock = NULL;
+ return 0;
+ } else {
+ int size = flib_socket_nbrecv(net->sock, buffer, sizeof(buffer));
+ if(size>=0 && !flib_vector_append(net->readBuffer, buffer, size)) {
+ return size;
+ } else {
+ flib_socket_close(net->sock);
+ net->sock = NULL;
+ return 0;
+ }
+ }
+}
+
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net) {
+ if(!net) {
+ flib_log_e("null parameter in flib_netbase_recv_message");
+ return NULL;
+ }
+
+ flib_netmsg *msg;
+ while(!(msg=parseMessage(net->readBuffer))
+ && receiveToBuffer(net)) {}
+
+ if(msg) {
+ return msg;
+ } else if(!net->sock && flib_vector_size(net->readBuffer)>0) {
+ // Connection is down and we didn't get a complete message, just flush the rest.
+ flib_vector_resize(net->readBuffer, 0);
+ }
+ return NULL;
+}
+
+static void logSentMsg(const uint8_t *data, size_t len) {
+ if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+ flib_log_d("[NET OUT][%03u]%*.*s",(unsigned)len, (unsigned)len, (unsigned)len, data);
+ }
+}
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len) {
+ if(!net || (!data && len>0)) {
+ flib_log_e("null parameter in flib_netbase_send_raw");
+ return -1;
+ }
+ if(!net->sock) {
+ flib_log_w("flib_netbase_send_raw: Not connected.");
+ return -1;
+ }
+
+ if(flib_socket_send(net->sock, data, len) == len) {
+ logSentMsg(data, len);
+ return 0;
+ } else {
+ flib_log_w("Failed or incomplete write: net connection lost.");
+ flib_socket_close(net->sock);
+ net->sock = NULL;
+ return -1;
+ }
+}
+
+int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg) {
+ if(!net || !msg) {
+ flib_log_e("null parameter in flib_netbase_send_message");
+ return -1;
+ }
+
+ size_t totalSize = 0;
+ for(int i=0; i<msg->partCount; i++) {
+ totalSize += strlen(msg->parts[i]) + 1;
+ }
+ totalSize++; // Last part ends in two '\n' instead of one
+
+ uint8_t *buffer = flib_malloc(totalSize);
+ if(!buffer) {
+ return -1;
+ }
+ size_t pos = 0;
+ for(int i=0; i<msg->partCount; i++) {
+ size_t partsize = strlen(msg->parts[i]);
+ memcpy(buffer+pos, msg->parts[i], partsize);
+ pos += partsize;
+ buffer[pos++] = '\n';
+ }
+ buffer[pos++] = '\n';
+ return flib_netbase_send_raw(net, buffer, pos);
+}
+
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...) {
+ int result = -1;
+ if(!net || !format) {
+ flib_log_e("null parameter in flib_netbase_sendf");
+ } else {
+ va_list argp;
+ va_start(argp, format);
+ char *buffer = flib_vasprintf(format, argp);
+ if(buffer) {
+ result = flib_netbase_send_raw(net, buffer, strlen(buffer));
+ }
+ free(buffer);
+ va_end(argp);
+ }
+ return result;
+}
+
+flib_netmsg *flib_netmsg_create() {
+ flib_netmsg *result = flib_calloc(1, sizeof(flib_netmsg));
+ if(result) {
+ result->partCount = 0;
+ result->parts = NULL;
+ return result;
+ } else {
+ return NULL;
+ }
+}
+
+void flib_netmsg_destroy(flib_netmsg *msg) {
+ if(msg) {
+ for(int i=0; i<msg->partCount; i++) {
+ free(msg->parts[i]);
+ }
+ free(msg->parts);
+ free(msg);
+ }
+}
+
+int flib_netmsg_append_part(flib_netmsg *msg, const void *part, size_t partlen) {
+ int result = -1;
+ if(!msg) {
+ flib_log_e("null parameter in flib_netmsg_append_part");
+ } else {
+ char **newParts = realloc(msg->parts, (msg->partCount+1)*sizeof(*msg->parts));
+ if(newParts) {
+ msg->parts = newParts;
+ msg->parts[msg->partCount] = flib_malloc(partlen+1);
+ if(msg->parts[msg->partCount]) {
+ memcpy(msg->parts[msg->partCount], part, partlen);
+ msg->parts[msg->partCount][partlen] = 0;
+ msg->partCount++;
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netbase.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * Low-level protocol support for the network connection
+ */
+
+#ifndef NETBASE_H_
+#define NETBASE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+struct _flib_netbase;
+typedef struct _flib_netbase flib_netbase;
+
+typedef struct {
+ int partCount;
+ char **parts;
+} flib_netmsg;
+
+/**
+ * Start a connection to the specified Hedgewars server.
+ *
+ * Returns NULL on error. Destroy the created object with flib_netconn_destroy.
+ */
+flib_netbase *flib_netbase_create(const char *server, uint16_t port);
+
+/**
+ * Free resources and close sockets.
+ */
+void flib_netbase_destroy(flib_netbase *net);
+
+/**
+ * Determine the current connection state. Starts out true, and turns to
+ * false when we are disconnected from the server.
+ */
+bool flib_netbase_connected(flib_netbase *net);
+
+/**
+ * Receive a new message and return it as a flib_netmsg. The netmsg has to be
+ * destroyed with flib_netmsg_destroy after use.
+ * Returns NULL if no message is available.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+flib_netmsg *flib_netbase_recv_message(flib_netbase *net);
+
+int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len);
+
+/**
+ * Write a single message to the server. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Returns a negative value on failure.
+ */
+int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg);
+
+/**
+ * Send a message printf-style.
+ *
+ * flib_netbase_sendf(net, "%s\n\n", "TOGGLE_READY");
+ * flib_netbase_sendf(net, "%s\n%s\n%i\n\n", "CFG", "MAPGEN", MAPGEN_MAZE);
+ */
+int flib_netbase_sendf(flib_netbase *net, const char *format, ...);
+
+flib_netmsg *flib_netmsg_create();
+void flib_netmsg_destroy(flib_netmsg *msg);
+int flib_netmsg_append_part(flib_netmsg *msg, const void *param, size_t len);
+
+#endif /* NETBASE_H_ */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,658 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2006-2008 Igor Ulyanov <iulyanov@gmail.com>
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 Simeon Maxein <smaxein@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+// TODO: Check the state transitions. Document with a diagram or something
+
+#include "netconn_internal.h"
+#include "netprotocol.h"
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../model/roomlist.h"
+#include "../md5/md5.h"
+#include "../base64/base64.h"
+#include "../model/mapcfg.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *dataDirPath, const char *host, uint16_t port) {
+ flib_netconn *result = NULL;
+ if(!playerName || !metacfg || !host) {
+ flib_log_e("null parameter in flib_netconn_create");
+ } else {
+ flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn));
+ if(newConn) {
+ newConn->netBase = flib_netbase_create(host, port);
+ newConn->playerName = flib_strdupnull(playerName);
+ newConn->dataDirPath = flib_strdupnull(dataDirPath);
+
+ newConn->netconnState = NETCONN_STATE_CONNECTING;
+ newConn->isAdmin = false;
+ newConn->metaCfg = flib_cfg_meta_retain(metacfg);
+ newConn->roomList.roomCount = 0;
+ newConn->roomList.rooms = NULL;
+
+ newConn->isChief = false;
+ newConn->map = flib_map_create_named("", "NoSuchMap");
+ newConn->pendingTeamlist.teamCount = 0;
+ newConn->pendingTeamlist.teams = NULL;
+ newConn->teamlist.teamCount = 0;
+ newConn->teamlist.teams = NULL;
+ newConn->scheme = NULL;
+ newConn->script = NULL;
+ newConn->weaponset = NULL;
+
+ newConn->running = false;
+ newConn->destroyRequested = false;
+ netconn_clearCallbacks(newConn);
+ if(newConn->netBase && newConn->playerName && newConn->dataDirPath && newConn->map) {
+ result = newConn;
+ newConn = NULL;
+ }
+ }
+ flib_netconn_destroy(newConn);
+ }
+ return result;
+}
+
+void flib_netconn_destroy(flib_netconn *conn) {
+ if(conn) {
+ if(conn->running) {
+ /*
+ * The function was called from a callback, so the tick function is still running
+ * and we delay the actual destruction. We ensure no further callbacks will be
+ * sent to prevent surprises.
+ */
+ netconn_clearCallbacks(conn);
+ conn->destroyRequested = true;
+ } else {
+ flib_netbase_destroy(conn->netBase);
+ free(conn->playerName);
+ free(conn->dataDirPath);
+
+ flib_cfg_meta_release(conn->metaCfg);
+ flib_roomlist_clear(&conn->roomList);
+
+ flib_map_release(conn->map);
+ flib_teamlist_clear(&conn->pendingTeamlist);
+ flib_teamlist_clear(&conn->teamlist);
+ flib_cfg_release(conn->scheme);
+ free(conn->script);
+ flib_weaponset_release(conn->weaponset);
+
+ free(conn);
+ }
+ }
+}
+
+const flib_roomlist *flib_netconn_get_roomlist(flib_netconn *conn) {
+ const flib_roomlist *result = NULL;
+ if(!conn) {
+ flib_log_e("null parameter in flib_netconn_get_roomlist");
+ } else {
+ result = &conn->roomList;
+ }
+ return result;
+}
+
+bool flib_netconn_is_chief(flib_netconn *conn) {
+ bool result = false;
+ if(!conn) {
+ flib_log_e("null parameter in flib_netconn_is_chief");
+ } else if(conn->netconnState == NETCONN_STATE_ROOM || conn->netconnState == NETCONN_STATE_INGAME) {
+ result = conn->isChief;
+ }
+ return result;
+}
+
+void netconn_leaveRoom(flib_netconn *conn) {
+ conn->netconnState = NETCONN_STATE_LOBBY;
+ conn->isChief = false;
+ flib_map_release(conn->map);
+ conn->map = flib_map_create_named("", "NoSuchMap");
+ flib_teamlist_clear(&conn->pendingTeamlist);
+ flib_teamlist_clear(&conn->teamlist);
+ flib_cfg_release(conn->scheme);
+ conn->scheme = NULL;
+ free(conn->script);
+ conn->script = NULL;
+ flib_weaponset_release(conn->weaponset);
+ conn->weaponset = NULL;
+}
+
+bool flib_netconn_is_in_room_context(flib_netconn *conn) {
+ return conn && (conn->netconnState == NETCONN_STATE_ROOM || conn->netconnState == NETCONN_STATE_INGAME);
+}
+
+void netconn_setMap(flib_netconn *conn, const flib_map *map) {
+ flib_map *copy = flib_map_copy(map);
+ if(copy) {
+ flib_map_release(conn->map);
+ conn->map = copy;
+ }
+}
+
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
+ flib_weaponset *copy = flib_weaponset_copy(weaponset);
+ if(copy) {
+ flib_weaponset_release(conn->weaponset);
+ conn->weaponset = copy;
+ }
+}
+
+void netconn_setScript(flib_netconn *conn, const char *script) {
+ char *copy = flib_strdupnull(script);
+ if(copy) {
+ free(conn->script);
+ conn->script = copy;
+ }
+}
+
+void netconn_setScheme(flib_netconn *conn, const flib_cfg *scheme) {
+ flib_cfg *copy = flib_cfg_copy(scheme);
+ if(copy) {
+ flib_cfg_release(conn->scheme);
+ conn->scheme = copy;
+ }
+}
+
+flib_gamesetup *flib_netconn_create_gameSetup(flib_netconn *conn) {
+ flib_gamesetup *result = NULL;
+ if(!conn) {
+ flib_log_e("null parameter in flib_netconn_create_gameSetup");
+ } else {
+ if(conn->teamlist.teamCount==0 || !conn->scheme || !conn->weaponset) {
+ flib_log_e("Incomplete room state to create game setup.");
+ } else {
+ result = flib_calloc(1, sizeof(flib_gamesetup));
+ if(result) {
+ result->gamescheme = flib_cfg_copy(conn->scheme);
+ result->map = flib_map_copy(conn->map);
+ result->script = flib_strdupnull(conn->script);
+ result->teamlist = flib_teamlist_create();
+ for(int i=0; i<conn->teamlist.teamCount; i++) {
+ flib_team *copy = flib_team_copy(conn->teamlist.teams[i]);
+ if(copy) {
+ flib_team_set_weaponset(copy, conn->weaponset);
+ flib_team_set_health(copy, conn->scheme->settings[2]); // TODO by name
+ flib_teamlist_insert(result->teamlist, copy, result->teamlist->teamCount);
+ }
+ flib_team_release(copy);
+ }
+ if(result->map->mapgen == MAPGEN_NAMED && result->map->name) {
+ flib_mapcfg mapcfg;
+ if(!flib_mapcfg_read(conn->dataDirPath, result->map->name, &mapcfg)) {
+ free(result->map->theme);
+ result->map->theme = flib_strdupnull(mapcfg.theme);
+ }
+ }
+ // TODO handle errors
+ }
+ }
+ }
+ return result;
+}
+
+static void flib_netconn_wrappedtick(flib_netconn *conn) {
+ flib_netmsg *netmsg;
+ flib_netbase *net = conn->netBase;
+ bool exit = false;
+
+ while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) {
+ if(netmsg->partCount==0) {
+ flib_log_w("Empty server message");
+ continue;
+ }
+
+ if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
+ char *buf = flib_join(netmsg->parts, netmsg->partCount, "|");
+ if(buf) {
+ flib_log_d("[Net In]%s", buf);
+ }
+ free(buf);
+ }
+
+ const char *cmd = netmsg->parts[0];
+
+ if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) {
+ if(netmsg->partCount<2) {
+ flib_log_w("Net: Malformed NICK message");
+ } else {
+ char *nick = flib_strdupnull(netmsg->parts[1]);
+ if(nick) {
+ free(conn->playerName);
+ conn->playerName = nick;
+ } else {
+ conn->netconnState = NETCONN_STATE_DISCONNECTED;
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory");
+ exit = true;
+ }
+ }
+ } else if (!strcmp(cmd, "PROTO")) {
+ // The server just echoes this back apparently
+ } else if (!strcmp(cmd, "ERROR")) {
+ if (netmsg->partCount >= 2) {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, netmsg->parts[1]);
+ } else {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_ERROR, "Unknown Error");
+ }
+ } else if(!strcmp(cmd, "WARNING")) {
+ if (netmsg->partCount >= 2) {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, netmsg->parts[1]);
+ } else {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_WARNING, "Unknown Warning");
+ }
+ } else if(!strcmp(cmd, "CONNECTED")) {
+ if(netmsg->partCount<3 || atol(netmsg->parts[2])<MIN_SERVER_VERSION) {
+ flib_log_w("Net: Server too old");
+ flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Server too old");
+ conn->netconnState = NETCONN_STATE_DISCONNECTED;
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_SERVER_TOO_OLD, "Server too old");
+ exit = true;
+ } else {
+ flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName);
+ flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION);
+ }
+ } else if(!strcmp(cmd, "PING")) {
+ if (netmsg->partCount > 1) {
+ flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]);
+ } else {
+ flib_netbase_sendf(net, "%s\n\n", "PONG");
+ }
+ } else if(!strcmp(cmd, "ROOMS")) {
+ if(netmsg->partCount % 8 != 1) {
+ flib_log_w("Net: Malformed ROOMS message");
+ } else {
+ flib_roomlist_clear(&conn->roomList);
+ for(int i=1; i<netmsg->partCount; i+=8) {
+ if(flib_roomlist_add(&conn->roomList, netmsg->parts+i)) {
+ flib_log_e("Error adding room to list in ROOMS message");
+ }
+ }
+ if(conn->netconnState == NETCONN_STATE_CONNECTING) {
+ // We delay the "connected" callback until now to ensure the room list is avaliable.
+ conn->onConnectedCb(conn->onConnectedCtx);
+ conn->netconnState = NETCONN_STATE_LOBBY;
+ }
+ }
+ } else if (!strcmp(cmd, "SERVER_MESSAGE")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Empty SERVERMESSAGE message");
+ } else {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_SERVERMESSAGE, netmsg->parts[1]);
+ }
+ } else if (!strcmp(cmd, "CHAT")) {
+ if(netmsg->partCount < 3) {
+ flib_log_w("Net: Empty CHAT message");
+ } else {
+ conn->onChatCb(conn->onChatCtx, netmsg->parts[1], netmsg->parts[2]);
+ }
+ } else if (!strcmp(cmd, "INFO")) {
+ if(netmsg->partCount < 5) {
+ flib_log_w("Net: Malformed INFO message");
+ } else {
+ char *joined = flib_join(netmsg->parts+1, netmsg->partCount-1, "\n");
+ if(joined) {
+ conn->onMessageCb(conn->onMessageCtx, NETCONN_MSG_TYPE_PLAYERINFO, joined);
+ }
+ free(joined);
+ }
+ } else if(!strcmp(cmd, "SERVER_VARS")) {
+ for(int offset=1; offset+2<netmsg->partCount; offset+=2) {
+ conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]);
+ }
+ } else if (!strcmp(cmd, "CLIENT_FLAGS")) {
+ if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) {
+ flib_log_w("Net: Malformed CLIENT_FLAGS message");
+ } else {
+ const char *flags = netmsg->parts[1];
+ bool setFlag = flags[0] == '+';
+
+ for(int i=1; flags[i]; i++) {
+ switch(flags[i]) {
+ case 'r':
+ for(int j = 2; j < netmsg->partCount; ++j) {
+ conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[j], setFlag);
+ }
+ break;
+ default:
+ flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
+ break;
+ }
+ }
+ }
+ } else if (!strcmp(cmd, "ADD_TEAM")) {
+ if(netmsg->partCount != 24 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad ADD_TEAM message");
+ } else {
+ flib_team *team = flib_team_from_netmsg(netmsg->parts+1);
+ flib_team *teamcopy = flib_team_from_netmsg(netmsg->parts+1);
+ if(!team || !teamcopy) {
+ conn->netconnState = NETCONN_STATE_DISCONNECTED;
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error");
+ exit = true;
+ } else {
+ team->remoteDriven = true;
+ teamcopy->remoteDriven = true;
+ flib_teamlist_insert(&conn->teamlist, teamcopy, conn->teamlist.teamCount);
+ conn->onTeamAddCb(conn->onTeamAddCtx, team);
+ }
+ flib_team_release(team);
+ flib_team_release(teamcopy);
+ }
+ } else if (!strcmp(cmd, "REMOVE_TEAM")) {
+ if(netmsg->partCount != 2 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad REMOVETEAM message");
+ } else {
+ flib_teamlist_delete(&conn->teamlist, netmsg->parts[1]);
+ conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]);
+ }
+ } else if(!strcmp(cmd, "ROOMABANDONED")) {
+ netconn_leaveRoom(conn);
+ conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed");
+ } else if(!strcmp(cmd, "KICKED")) {
+ netconn_leaveRoom(conn);
+ conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked");
+ } else if(!strcmp(cmd, "JOINED")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad JOINED message");
+ } else {
+ for(int i = 1; i < netmsg->partCount; ++i)
+ {
+ bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+ if (isMe) {
+ conn->netconnState = NETCONN_STATE_ROOM;
+ conn->onEnterRoomCb(conn->onEnterRoomCtx, conn->isChief);
+ }
+
+ conn->onRoomJoinCb(conn->onRoomJoinCtx, netmsg->parts[i]);
+ }
+ }
+ } else if(!strcmp(cmd, "LOBBY:JOINED")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad JOINED message");
+ } else {
+ for(int i = 1; i < netmsg->partCount; ++i)
+ {
+ bool isMe = !strcmp(conn->playerName, netmsg->parts[i]);
+ if (isMe) {
+ if(flib_netbase_sendf(conn->netBase, "%s\n\n", "LIST")) {
+ // If sending this fails, the protocol breaks (we'd be waiting infinitely for the room list)
+ flib_netbase_sendf(net, "%s\n%s\n\n", "QUIT", "Client error");
+ conn->netconnState = NETCONN_STATE_DISCONNECTED;
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Failed to send a critical message.");
+ exit = true;
+ }
+ }
+ conn->onLobbyJoinCb(conn->onLobbyJoinCtx, netmsg->parts[i]);
+ }
+ }
+ } else if(!strcmp(cmd, "LEFT")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad LEFT message");
+ } else {
+ conn->onRoomLeaveCb(conn->onRoomLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
+ }
+ } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) {
+ const char *subcmd = netmsg->parts[1];
+ if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) {
+ if(flib_roomlist_add(&conn->roomList, netmsg->parts+2)) {
+ flib_log_e("Error adding new room to list");
+ } else {
+ conn->onRoomAddCb(conn->onRoomAddCtx, conn->roomList.rooms[0]);
+ }
+ } else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) {
+ if(flib_roomlist_update(&conn->roomList, netmsg->parts[2], netmsg->parts+3)) {
+ flib_log_e("Error updating room in list");
+ } else {
+ conn->onRoomUpdateCb(conn->onRoomUpdateCtx, netmsg->parts[2], flib_roomlist_find(&conn->roomList, netmsg->parts[2]));
+ }
+ } else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) {
+ if(flib_roomlist_delete(&conn->roomList, netmsg->parts[2])) {
+ flib_log_e("Error deleting room from list");
+ } else {
+ conn->onRoomDeleteCb(conn->onRoomDeleteCtx, netmsg->parts[2]);
+ }
+ } else {
+ flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd);
+ }
+ } else if(!strcmp(cmd, "LOBBY:LEFT")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad LOBBY:LEFT message");
+ } else {
+ conn->onLobbyLeaveCb(conn->onLobbyLeaveCtx, netmsg->parts[1], netmsg->partCount>2 ? netmsg->parts[2] : NULL);
+ }
+ } else if (!strcmp(cmd, "RUN_GAME")) {
+ conn->netconnState = NETCONN_STATE_INGAME;
+ // TODO send along the config
+ conn->onRunGameCb(conn->onRunGameCtx);
+ } else if (!strcmp(cmd, "ASKPASSWORD")) {
+ conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName);
+ } else if (!strcmp(cmd, "NOTICE")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad NOTICE message");
+ } else {
+ errno = 0;
+ long n = strtol(netmsg->parts[1], NULL, 10);
+ if(errno) {
+ flib_log_w("Net: Bad NOTICE message");
+ } else if(n==0) {
+ conn->onNickTakenCb(conn->onNickTakenCtx, conn->playerName);
+ } else {
+ flib_log_w("Net: Unknown NOTICE message: %l", n);
+ }
+ }
+ } else if (!strcmp(cmd, "TEAM_ACCEPTED")) {
+ if (netmsg->partCount != 2 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad TEAM_ACCEPTED message");
+ } else {
+ flib_team *team = flib_teamlist_find(&conn->pendingTeamlist, netmsg->parts[1]);
+ if(team) {
+ flib_teamlist_insert(&conn->teamlist, team, conn->teamlist.teamCount);
+ flib_teamlist_delete(&conn->pendingTeamlist, netmsg->parts[1]);
+ } else {
+ flib_log_e("Team accepted that was not requested: %s", netmsg->parts[1]);
+ }
+ conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[1]);
+ }
+ } else if (!strcmp(cmd, "CFG")) {
+ if(netmsg->partCount < 3 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad CFG message");
+ } else {
+ const char *subcmd = netmsg->parts[1];
+ if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == conn->metaCfg->modCount + conn->metaCfg->settingCount + 3) {
+ flib_cfg *cfg = flib_netmsg_to_cfg(conn->metaCfg, netmsg->parts+2);
+ if(cfg) {
+ netconn_setScheme(conn, cfg);
+ conn->onCfgSchemeCb(conn->onCfgSchemeCtx, cfg);
+ } else {
+ flib_log_e("Error processing CFG SCHEME message");
+ }
+ flib_cfg_release(cfg);
+ } else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) {
+ flib_map *map = flib_netmsg_to_map(netmsg->parts+2);
+ if(map) {
+ netconn_setMap(conn, map);
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL);
+ } else {
+ flib_log_e("Error processing CFG FULLMAPCONFIG message");
+ }
+ flib_map_release(map);
+ } else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) {
+ char *mapname = flib_strdupnull(netmsg->parts[2]);
+ if(mapname) {
+ free(conn->map->name);
+ conn->map->name = mapname;
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP);
+ } else {
+ flib_log_e("Error processing CFG MAP message");
+ }
+ } else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) {
+ char *themename = flib_strdupnull(netmsg->parts[2]);
+ if(themename) {
+ free(conn->map->theme);
+ conn->map->theme = themename;
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME);
+ } else {
+ flib_log_e("Error processing CFG THEME message");
+ }
+ } else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) {
+ char *seed = flib_strdupnull(netmsg->parts[2]);
+ if(seed) {
+ free(conn->map->seed);
+ conn->map->seed = seed;
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED);
+ } else {
+ flib_log_e("Error processing CFG SEED message");
+ }
+ } else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) {
+ conn->map->templateFilter = atoi(netmsg->parts[2]);
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE);
+ } else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) {
+ conn->map->mapgen = atoi(netmsg->parts[2]);
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN);
+ } else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) {
+ conn->map->mazeSize = atoi(netmsg->parts[2]);
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE);
+ } else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) {
+ size_t drawnMapSize = 0;
+ uint8_t *drawnMapData = NULL;
+ if(!flib_netmsg_to_drawnmapdata(netmsg->parts[2], &drawnMapData, &drawnMapSize)) {
+ free(conn->map->drawData);
+ conn->map->drawData = drawnMapData;
+ conn->map->drawDataSize = drawnMapSize;
+ conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP);
+ } else {
+ flib_log_e("Error processing CFG DRAWNMAP message");
+ }
+ } else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) {
+ netconn_setScript(conn, netmsg->parts[2]);
+ conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]);
+ } else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) {
+ flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]);
+ if(weapons) {
+ netconn_setWeaponset(conn, weapons);
+ conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons);
+ } else {
+ flib_log_e("Error processing CFG AMMO message");
+ }
+ flib_weaponset_release(weapons);
+ } else {
+ flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd);
+ }
+ }
+ } else if (!strcmp(cmd, "HH_NUM")) {
+ if (netmsg->partCount != 3 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad HH_NUM message");
+ } else {
+ int hogs = atoi(netmsg->parts[2]);
+ if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) {
+ flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]);
+ } else {
+ flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+ if(team) {
+ team->hogsInGame = hogs;
+ } else {
+ flib_log_e("HH_NUM message for unknown team %s", netmsg->parts[1]);
+ }
+ conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs);
+ }
+ }
+ } else if (!strcmp(cmd, "TEAM_COLOR")) {
+ if (netmsg->partCount != 3 || !flib_netconn_is_in_room_context(conn)) {
+ flib_log_w("Net: Bad TEAM_COLOR message");
+ } else {
+ long color;
+ if(sscanf(netmsg->parts[2], "%lu", &color) && color>=0 && color<flib_teamcolor_defaults_len) {
+ flib_team *team = flib_teamlist_find(&conn->teamlist, netmsg->parts[1]);
+ if(team) {
+ team->colorIndex = color;
+ } else {
+ flib_log_e("TEAM_COLOR message for unknown team %s", netmsg->parts[1]);
+ }
+ conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], color);
+ } else {
+ flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]);
+ }
+ }
+ } else if (!strcmp(cmd, "EM")) {
+ if(netmsg->partCount < 2) {
+ flib_log_w("Net: Bad EM message");
+ } else {
+ for(int i = 1; i < netmsg->partCount; ++i) {
+ char *out = NULL;
+ size_t outlen;
+ bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen);
+ if(ok && outlen) {
+ conn->onEngineMessageCb(conn->onEngineMessageCtx, (uint8_t*)out, outlen);
+ } else {
+ flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]);
+ }
+ free(out);
+ }
+ }
+ } else if (!strcmp(cmd, "BYE")) {
+ if (netmsg->partCount < 2) {
+ flib_log_w("Net: Bad BYE message");
+ } else {
+ conn->netconnState = NETCONN_STATE_DISCONNECTED;
+ if (!strcmp(netmsg->parts[1], "Authentication failed")) {
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_AUTH_FAILED, netmsg->parts[1]);
+ } else {
+ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_NORMAL, netmsg->parts[1]);
+ }
+ exit = true;
+ }
+ } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
+ conn->onAdminAccessCb(conn->onAdminAccessCtx);
+ conn->isAdmin = true;
+ } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
+ if (netmsg->partCount < 2) {
+ flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
+ } else {
+ conn->isChief = strcmp("0", netmsg->parts[1]);
+ conn->onRoomChiefStatusCb(conn->onRoomChiefStatusCtx, conn->isChief);
+ }
+ } else {
+ flib_log_w("Unknown server command: %s", cmd);
+ }
+ flib_netmsg_destroy(netmsg);
+ }
+}
+
+void flib_netconn_tick(flib_netconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_netconn_tick");
+ } else if(conn->running) {
+ flib_log_w("Call to flib_netconn_tick from a callback");
+ } else if(conn->netconnState == NETCONN_STATE_DISCONNECTED) {
+ flib_log_w("Call to flib_netconn_tick, but we are already done.");
+ } else {
+ conn->running = true;
+ flib_netconn_wrappedtick(conn);
+ conn->running = false;
+
+ if(conn->destroyRequested) {
+ flib_netconn_destroy(conn);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,479 @@
+#ifndef NETCONN_H_
+#define NETCONN_H_
+
+#include "../model/gamesetup.h"
+#include "../model/cfg.h"
+#include "../model/roomlist.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define NETCONN_STATE_CONNECTING 0
+#define NETCONN_STATE_LOBBY 1
+#define NETCONN_STATE_ROOM 2
+#define NETCONN_STATE_INGAME 3
+#define NETCONN_STATE_DISCONNECTED 10
+
+#define NETCONN_DISCONNECT_NORMAL 0
+#define NETCONN_DISCONNECT_SERVER_TOO_OLD 1
+#define NETCONN_DISCONNECT_AUTH_FAILED 2
+#define NETCONN_DISCONNECT_INTERNAL_ERROR 100
+
+#define NETCONN_ROOMLEAVE_ABANDONED 0
+#define NETCONN_ROOMLEAVE_KICKED 1
+
+#define NETCONN_MSG_TYPE_PLAYERINFO 0
+#define NETCONN_MSG_TYPE_SERVERMESSAGE 1
+#define NETCONN_MSG_TYPE_WARNING 2
+#define NETCONN_MSG_TYPE_ERROR 3
+
+#define NETCONN_MAPCHANGE_FULL 0
+#define NETCONN_MAPCHANGE_MAP 1
+#define NETCONN_MAPCHANGE_MAPGEN 2
+#define NETCONN_MAPCHANGE_DRAWNMAP 3
+#define NETCONN_MAPCHANGE_MAZE_SIZE 4
+#define NETCONN_MAPCHANGE_TEMPLATE 5
+#define NETCONN_MAPCHANGE_THEME 6
+#define NETCONN_MAPCHANGE_SEED 7
+
+// TODO: Order of functions, and match the order in netconn.c
+typedef struct _flib_netconn flib_netconn;
+
+/**
+ * Create a new netplay connection with these parameters.
+ * The path to the data directory must end with a path delimiter (e.g. C:\Games\Hedgewars\Data\)
+ */
+flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *dataDirPath, const char *host, uint16_t port);
+void flib_netconn_destroy(flib_netconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_netconn_tick(flib_netconn *conn);
+
+
+/**
+ * Return the current roomlist. Don't free or modify.
+ */
+const flib_roomlist *flib_netconn_get_roomlist(flib_netconn *conn);
+
+/**
+ * Are you currently the owner of this room? The return value only makes sense in
+ * NETCONN_STATE_ROOM and NETCONN_STATE_INGAME states.
+ */
+bool flib_netconn_is_chief(flib_netconn *conn);
+
+/**
+ * Are you in the context of a room, i.e. either in room or ingame state?
+ */
+bool flib_netconn_is_in_room_context(flib_netconn *conn);
+
+/**
+ * Generate a game setup from the current room state.
+ * Returns NULL if the room state does not contain enough information
+ * for a complete game setup, or if an error occurs.
+ *
+ * The new gamesetup must be destroyed TODO function for that...
+ */
+flib_gamesetup *flib_netconn_create_gameSetup(flib_netconn *conn);
+
+/**
+ * quitmsg may be null
+ */
+int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg);
+int flib_netconn_send_chat(flib_netconn *conn, const char *chat);
+
+/**
+ * Send a teamchat message, forwarded from the engine. Only makes sense ingame.
+ * The server does not send a reply. TODO figure out details
+ */
+int flib_netconn_send_teamchat(flib_netconn *conn, const char *msg);
+
+/**
+ * Note: Most other functions in this lib accept UTF-8, but the password needs to be
+ * sent as latin1
+ */
+int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd);
+
+/**
+ * Request a different nickname.
+ * This function only makes sense in reaction to an onNickTaken callback, because the netconn automatically
+ * requests the nickname you provide on creation, and once the server accepts the nickname it can no longer
+ * be changed.
+ */
+int flib_netconn_send_nick(flib_netconn *conn, const char *nick);
+
+/**
+ * Join a room as guest (not chief). Only makes sense when in lobby state. If the action succeeds, you will
+ * receive an onEnterRoom callback with chief=false.
+ */
+int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room);
+
+/**
+ * Create and join a new room. Only makes sense when in lobby state. If the action succeeds, you will
+ * receive an onEnterRoom callback with chief=true.
+ */
+int flib_netconn_send_createRoom(flib_netconn *conn, const char *room);
+
+/**
+ * Rename the current room. Only makes sense in room state and if you are chief.
+ * TODO: reply
+ */
+int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName);
+
+/**
+ * Leave the room for the lobby. Only makes sense in room state.
+ * TODO: reply, TODO can you send a message?
+ */
+int flib_netconn_send_leaveRoom(flib_netconn *conn);
+
+/**
+ * Change your "ready" status in the room. Only makes sense when in room state.
+ * TODO: reply
+ */
+int flib_netconn_send_toggleReady(flib_netconn *conn);
+
+/**
+ * Add a team to the current room. The message includes the team color, but not
+ * the number of hogs. Only makes sense when in room state. If the action succeeds, you will
+ * receive an onTeamAccepted callback with the name of the team.
+ */
+int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team);
+
+/**
+ * Remove the team with the name teamname. Only makes sense when in room state.
+ * TODO: reply
+ */
+int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname);
+
+/**
+ * Send an engine message. Only makes sense when ingame.
+ * TODO: reply
+ */
+int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size);
+
+/**
+ * Set the number of hogs for a team. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount);
+
+/**
+ * Set the teamcolor of a team. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex);
+
+/**
+ * Set the weaponset for the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+
+/**
+ * Set the map for the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_map(flib_netconn *conn, const flib_map *map);
+
+/**
+ * Set the mapname. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName);
+
+/**
+ * Set the map generator. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen);
+
+/**
+ * Set the map template for regular maps. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter);
+
+/**
+ * Set the maze template (maze size) for mazes. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize);
+
+/**
+ * Set the seed for the map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed);
+
+/**
+ * Set the theme for the map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme);
+
+/**
+ * Set the draw data for the drawn map. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size);
+
+/**
+ * Set the script (game style). Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_script(flib_netconn *conn, const char *scriptName);
+
+/**
+ * Set the scheme. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme);
+
+/**
+ * Inform the server that the round has ended. TODO: Figure out details
+ */
+int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError);
+
+/**
+ * Ban a player. TODO: Figure out details
+ */
+int flib_netconn_send_ban(flib_netconn *conn, const char *playerName);
+
+/**
+ * Kick a player. TODO: Figure out details
+ */
+int flib_netconn_send_kick(flib_netconn *conn, const char *playerName);
+
+/**
+ * Request information about a player. If the action succeeds, you will
+ * receive an onMessage callback with NETCONN_MSG_TYPE_PLAYERINFO containing
+ * the requested information.
+ */
+int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName);
+
+/**
+ * Follow a player. TODO figure out details
+ */
+int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName);
+
+/**
+ * Signal that you want to start the game. Only makes sense in room state and if you are chief.
+ * TODO figure out details
+ */
+int flib_netconn_send_startGame(flib_netconn *conn);
+
+/**
+ * Allow/forbid players to join the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn);
+
+/**
+ * Allow/forbid adding teams to the room. Only makes sense in room state and if you are chief.
+ * The server does not send a reply.
+ */
+int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn);
+
+/**
+ * Probably does something administrator-y.
+ */
+int flib_netconn_send_clearAccountsCache(flib_netconn *conn);
+
+/**
+ * Sets a server variable to the indicated value. Only makes sense if you are server admin.
+ * Known variables are MOTD_NEW, MOTD_OLD and LATEST_PROTO.
+ * TODO reply?
+ */
+int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value);
+
+/**
+ * Queries all server variables. Only makes sense if you are server admin. (TODO: try)
+ * If the action succeeds, you will receive several onServerVar callbacks with the
+ * current values of all server variables.
+ */
+int flib_netconn_send_getServerVars(flib_netconn *conn);
+
+
+
+
+
+
+
+
+
+
+/**
+ * Callback for several informational messages that should be displayed to the user
+ * (e.g. in the chat window), but do not require a reaction. If a game is running, you might
+ * want to redirect some of these messages to the engine as well so the user will see them.
+ */
+void flib_netconn_onMessage(flib_netconn *conn, void (*callback)(void *context, int msgtype, const char *msg), void* context);
+
+/**
+ * We received a chat message. Where this message belongs depends on the current state (lobby/room/game). In particular,
+ * if a game is running the message should be passed to the engine.
+ */
+void flib_netconn_onChat(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *msg), void* context);
+
+/**
+ * This is called when we receive a CONNECTED message from the server, which should be the first
+ * message arriving from the server.
+ */
+void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context), void* context);
+
+/**
+ * This is *always* the last callback (unless the netconn is destroyed early), and the netconn should be destroyed when it is received.
+ * The reason is one of the NETCONN_DISCONNECT_ constants. Sometime a message is included as well, but that parameter might
+ * also be NULL.
+ */
+void flib_netconn_onDisconnected(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void* context);
+
+/**
+ * Callbacks for room list updates. The room list is managed automatically and can be queried with
+ * flib_netconn_get_roomlist() as soon as the onConnected callback is fired. These callbacks
+ * provide notification about changes.
+ */
+void flib_netconn_onRoomAdd(flib_netconn *conn, void (*callback)(void *context, const flib_room *room), void* context);
+void flib_netconn_onRoomDelete(flib_netconn *conn, void (*callback)(void *context, const char *name), void* context);
+void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_room *room), void* context);
+
+/**
+ * Callbacks for players joining or leaving the lobby. If join is true it's a join, otherwise a leave.
+ * NOTE: partMessage is null if no parting message was given.
+ */
+void flib_netconn_onLobbyJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+void flib_netconn_onLobbyLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
+
+/**
+ * onNickTaken is called on connecting to the server, if it turns out that there is already a player with the same nick.
+ * In order to proceed, a new nickname needs to be sent to the server using flib_netconn_send_nick() (or of course you can
+ * bail out and send a QUIT). If you don't set a callback, the netconn will automatically react by generating a new name.
+ * Once the server accepts a name, you will be informed with an onNickAccept callback.
+ */
+void flib_netconn_onNickTaken(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+
+/**
+ * When connecting with a registered nickname, the server will ask for a password before admitting you in.
+ * This callback is called when that happens. As a reaction, you can send the password using
+ * flib_netconn_send_password or choose a different nick. If you don't register a callback,
+ * the default behavior is to just quit in a way that will cause a disconnect with NETCONN_DISCONNECT_AUTH_FAILED.
+ */
+void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+
+/**
+ * You just left the lobby and entered a room.
+ * If chief is true, you can and should send a full configuration for the room now.
+ * This consists of TODO
+ */
+void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context);
+
+
+/**
+ * The following callbacks are only relevant in room state.
+ */
+
+/**
+ * This callback informs about changes to your room chief status, i.e. whether you are allowed to
+ * modify the current room. Generally when you create a room you start out being room chief, and
+ * when you join an existing room you are not. However, in some situations room ownership can change,
+ * and if that happens this callback is called with the new status.
+ *
+ * Note: This callback does not automatically fire when joining a room. You can always query the
+ * current chief status using flib_netconn_is_chief().
+ */
+void flib_netconn_onRoomChiefStatus(flib_netconn *conn, void (*callback)(void *context, bool chief), void* context);
+
+/**
+ * One of the players in the room (possibly you!) changed their ready state.
+ */
+void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context);
+
+/**
+ * You just left a room and entered the lobby again.
+ * reason is one of the NETCONN_ROOMLEAVE_ constants.
+ * This will not be called when you actively leave a room using PART.
+ */
+void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context);
+
+/**
+ * A new team was added to the room. The person who adds a team does NOT receive this callback (he gets onTeamAccepted instead).
+ * The team does not contain bindings, stats, weaponset, color or the number of hogs.
+ */
+void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context);
+
+/**
+ * A team was removed from the room.
+ */
+void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context);
+
+void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
+void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
+
+/**
+ * The game is starting. Fire up the engine and join in!
+ * TODO: How?
+ */
+void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+/**
+ * When you ask for a team to be added, the server might reject it for several reasons, e.g. because it has the same name
+ * as an existing team, or because the room chief restricted adding new teams. If the team is accepted by the server,
+ * this callback is fired.
+ */
+void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context);
+
+/**
+ * The number of hogs in a team has been changed by the room chief. If you are the chief and change the number of hogs yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context);
+
+/**
+ * The color of a team has been changed by the room chief. If you are the chief and change the color yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int colorIndex), void *context);
+
+void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const uint8_t *message, size_t size), void *context);
+
+void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context);
+
+/**
+ * This is called when the map configuration in a room is changed (or first received). Only non-chiefs receive these messages.
+ * To reduce the number of callback functions, the netconn keeps track of the current map settings and always passes the entire
+ * current map config, but informs the callee about what has changed (see the NETCONN_MAPCHANGE_ constants).
+ * The map parameter passed to the callback is an internally held map config. If you want to keep it around, best make a copy
+ * or it may or may not change while you are not looking.
+ *
+ * Caution: Due to the way the protocol works, the map might not be complete at this point if it is a hand-drawn map, because
+ * the "full" map config does not include the drawn map data.
+ */
+void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context);
+
+/**
+ * The "game style" script has been changed by the room chief. If you are the chief and change the script yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context);
+
+/**
+ * The weaponset has been changed by the room chief. If you are the chief and change the weaponset yourself,
+ * you will not receive this callback!
+ */
+void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context);
+
+/**
+ * This callback is called if the server informs us that we have admin rights.
+ */
+void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context);
+
+/**
+ * When you query the server vars with GET_SERVER_VAR (TODO probably only works as admin), the server
+ * replies with a list of them. This callback is called for each entry in that list.
+ */
+void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_callbacks.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,134 @@
+#include "netconn_internal.h"
+
+#include "../util/logging.h"
+#include "../util/util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) {
+ flib_log_i("Net: [%i] %s", msgtype, msg);
+}
+
+static void defaultCallback_onChat(void *context, const char *nick, const char *msg) {
+ flib_log_i("%s: %s", nick, msg);
+}
+
+// Change the name by suffixing it with a number. If it already ends in a number, increase that number by 1.
+static void defaultCallback_onNickTaken(void *context, const char *requestedNick) {
+ flib_netconn *conn = context;
+ size_t namelen = strlen(requestedNick);
+ int digits = 0;
+ while(digits<namelen && isdigit(requestedNick[namelen-1-digits])) {
+ digits++;
+ }
+ long suffix = 0;
+ if(digits>0) {
+ suffix = atol(requestedNick+namelen-digits)+1;
+ }
+ char *newPlayerName = flib_asprintf("%.*s%li", namelen-digits, requestedNick, suffix);
+ if(newPlayerName) {
+ flib_netconn_send_nick(conn, newPlayerName);
+ } else {
+ flib_netconn_send_quit(conn, "Nick already taken.");
+ }
+ free(newPlayerName);
+}
+
+// Default behavior: Quit
+static void defaultCallback_onPasswordRequest(void *context, const char *requestedNick) {
+ flib_netconn_send_quit((flib_netconn*)context, "Authentication failed");
+}
+
+void netconn_clearCallbacks(flib_netconn *conn) {
+ flib_netconn_onMessage(conn, NULL, NULL);
+ flib_netconn_onConnected(conn, NULL, NULL);
+ flib_netconn_onDisconnected(conn, NULL, NULL);
+ flib_netconn_onRoomAdd(conn, NULL, NULL);
+ flib_netconn_onRoomDelete(conn, NULL, NULL);
+ flib_netconn_onRoomUpdate(conn, NULL, NULL);
+ flib_netconn_onChat(conn, NULL, NULL);
+ flib_netconn_onLobbyJoin(conn, NULL, NULL);
+ flib_netconn_onLobbyLeave(conn, NULL, NULL);
+ flib_netconn_onRoomJoin(conn, NULL, NULL);
+ flib_netconn_onRoomLeave(conn, NULL, NULL);
+ flib_netconn_onNickTaken(conn, NULL, NULL);
+ flib_netconn_onPasswordRequest(conn, NULL, NULL);
+ flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
+ flib_netconn_onReadyState(conn, NULL, NULL);
+ flib_netconn_onEnterRoom(conn, NULL, NULL);
+ flib_netconn_onLeaveRoom(conn, NULL, NULL);
+ flib_netconn_onTeamAdd(conn, NULL, NULL);
+ flib_netconn_onTeamDelete(conn, NULL, NULL);
+ flib_netconn_onRunGame(conn, NULL, NULL);
+ flib_netconn_onTeamAccepted(conn, NULL, NULL);
+ flib_netconn_onHogCountChanged(conn, NULL, NULL);
+ flib_netconn_onTeamColorChanged(conn, NULL, NULL);
+ flib_netconn_onEngineMessage(conn, NULL, NULL);
+ flib_netconn_onCfgScheme(conn, NULL, NULL);
+ flib_netconn_onMapChanged(conn, NULL, NULL);
+ flib_netconn_onScriptChanged(conn, NULL, NULL);
+ flib_netconn_onWeaponsetChanged(conn, NULL, NULL);
+ flib_netconn_onAdminAccess(conn, NULL, NULL);
+ flib_netconn_onServerVar(conn, NULL, NULL);
+}
+
+/**
+ * This macro generates a callback setter function. It uses the name of the callback to
+ * automatically generate the function name and the fields to set, so a consistent naming
+ * convention needs to be enforced (not that that is a bad thing). If null is passed as
+ * callback to the generated function, the defaultCb will be set instead (with conn
+ * as the context).
+ */
+#define GENERATE_CB_SETTER(cbName, cbParameterTypes, defaultCb) \
+ void flib_netconn_##cbName(flib_netconn *conn, void (*callback)cbParameterTypes, void *context) { \
+ if(!conn) { \
+ flib_log_e("null parameter in flib_netconn_%s", #cbName); \
+ } else { \
+ conn->cbName##Cb = callback ? callback : &defaultCb; \
+ conn->cbName##Ctx = callback ? context : conn; \
+ } \
+ }
+
+/**
+ * Generate a callback setter function like GENERATE_CB_SETTER, and automatically generate a
+ * no-op callback function as well that is used as default.
+ */
+#define GENERATE_CB_SETTER_AND_DEFAULT(cbName, cbParameterTypes) \
+ static void _noop_callback_##cbName cbParameterTypes {} \
+ GENERATE_CB_SETTER(cbName, cbParameterTypes, _noop_callback_##cbName)
+
+GENERATE_CB_SETTER(onMessage, (void *context, int msgtype, const char *msg), defaultCallback_onMessage);
+GENERATE_CB_SETTER_AND_DEFAULT(onConnected, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onDisconnected, (void *context, int reason, const char *message));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomAdd, (void *context, const flib_room *room));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomDelete, (void *context, const char *name));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomUpdate, (void *context, const char *oldName, const flib_room *room));
+GENERATE_CB_SETTER(onChat, (void *context, const char *nick, const char *msg), defaultCallback_onChat);
+GENERATE_CB_SETTER_AND_DEFAULT(onLobbyJoin, (void *context, const char *nick));
+GENERATE_CB_SETTER_AND_DEFAULT(onLobbyLeave, (void *context, const char *nick, const char *partMsg));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomJoin, (void *context, const char *nick));
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomLeave, (void *context, const char *nick, const char *partMessage));
+GENERATE_CB_SETTER(onNickTaken, (void *context, const char *nick), defaultCallback_onNickTaken);
+GENERATE_CB_SETTER(onPasswordRequest, (void *context, const char *nick), defaultCallback_onPasswordRequest);
+GENERATE_CB_SETTER_AND_DEFAULT(onRoomChiefStatus, (void *context, bool chief));
+GENERATE_CB_SETTER_AND_DEFAULT(onReadyState, (void *context, const char *nick, bool ready));
+GENERATE_CB_SETTER_AND_DEFAULT(onEnterRoom, (void *context, bool chief));
+GENERATE_CB_SETTER_AND_DEFAULT(onLeaveRoom, (void *context, int reason, const char *message));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamAdd, (void *context, flib_team *team));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamDelete, (void *context, const char *teamname));
+GENERATE_CB_SETTER_AND_DEFAULT(onRunGame, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamAccepted, (void *context, const char *teamName));
+GENERATE_CB_SETTER_AND_DEFAULT(onHogCountChanged, (void *context, const char *teamName, int hogs));
+GENERATE_CB_SETTER_AND_DEFAULT(onTeamColorChanged, (void *context, const char *teamName, int colorIndex));
+GENERATE_CB_SETTER_AND_DEFAULT(onEngineMessage, (void *context, const uint8_t *message, size_t size));
+GENERATE_CB_SETTER_AND_DEFAULT(onCfgScheme, (void *context, flib_cfg *scheme));
+GENERATE_CB_SETTER_AND_DEFAULT(onMapChanged, (void *context, const flib_map *map, int changetype));
+GENERATE_CB_SETTER_AND_DEFAULT(onScriptChanged, (void *context, const char *script));
+GENERATE_CB_SETTER_AND_DEFAULT(onWeaponsetChanged, (void *context, flib_weaponset *weaponset));
+GENERATE_CB_SETTER_AND_DEFAULT(onAdminAccess, (void *context));
+GENERATE_CB_SETTER_AND_DEFAULT(onServerVar, (void *context, const char *name, const char *value));
+
+#undef GENERATE_CB_SETTER_AND_DEFAULT
+#undef GENERATE_CB_SETTER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_internal.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,140 @@
+/**
+ * Common definitions needed by netconn functions, to allow splitting them into several files.
+ */
+
+#ifndef NETCONN_INTERNAL_H_
+#define NETCONN_INTERNAL_H_
+
+#include "netconn.h"
+#include "netbase.h"
+#include "../model/cfg.h"
+#include "../model/roomlist.h"
+#include "../model/map.h"
+#include "../model/team.h"
+#include "../model/weapon.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct _flib_netconn {
+ flib_netbase *netBase;
+ char *playerName;
+ char *dataDirPath;
+
+ int netconnState; // One of the NETCONN_STATE constants
+ bool isAdmin; // Player is server administrator
+
+ flib_cfg_meta *metaCfg;
+ flib_roomlist roomList;
+
+ bool isChief; // Player can modify the current room
+ flib_map *map;
+ flib_teamlist pendingTeamlist;
+ flib_teamlist teamlist;
+ flib_cfg *scheme;
+ char *script;
+ flib_weaponset *weaponset;
+
+ void (*onMessageCb)(void *context, int msgtype, const char *msg);
+ void *onMessageCtx;
+
+ void (*onConnectedCb)(void *context);
+ void *onConnectedCtx;
+
+ void (*onDisconnectedCb)(void *context, int reason, const char *message);
+ void *onDisconnectedCtx;
+
+ void (*onRoomAddCb)(void *context, const flib_room *room);
+ void *onRoomAddCtx;
+
+ void (*onRoomDeleteCb)(void *context, const char *name);
+ void *onRoomDeleteCtx;
+
+ void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_room *room);
+ void *onRoomUpdateCtx;
+
+ void (*onChatCb)(void *context, const char *nick, const char *msg);
+ void *onChatCtx;
+
+ void (*onLobbyJoinCb)(void *context, const char *nick);
+ void *onLobbyJoinCtx;
+
+ void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage);
+ void *onLobbyLeaveCtx;
+
+ void (*onRoomJoinCb)(void *context, const char *nick);
+ void *onRoomJoinCtx;
+
+ void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage);
+ void *onRoomLeaveCtx;
+
+ void (*onNickTakenCb)(void *context, const char *nick);
+ void *onNickTakenCtx;
+
+ void (*onPasswordRequestCb)(void *context, const char *nick);
+ void *onPasswordRequestCtx;
+
+ void (*onRoomChiefStatusCb)(void *context, bool isChief);
+ void *onRoomChiefStatusCtx;
+
+ void (*onReadyStateCb)(void *context, const char *nick, bool ready);
+ void *onReadyStateCtx;
+
+ void (*onEnterRoomCb)(void *context, bool chief);
+ void *onEnterRoomCtx;
+
+ void (*onLeaveRoomCb)(void *context, int reason, const char *message);
+ void *onLeaveRoomCtx;
+
+ void (*onTeamAddCb)(void *context, flib_team *team);
+ void *onTeamAddCtx;
+
+ void (*onTeamDeleteCb)(void *context, const char *teamname);
+ void *onTeamDeleteCtx;
+
+ void (*onRunGameCb)(void *context);
+ void *onRunGameCtx;
+
+ void (*onTeamAcceptedCb)(void *context, const char *teamName);
+ void *onTeamAcceptedCtx;
+
+ void (*onHogCountChangedCb)(void *context, const char *teamName, int hogs);
+ void *onHogCountChangedCtx;
+
+ void (*onTeamColorChangedCb)(void *context, const char *teamName, int colorIndex);
+ void *onTeamColorChangedCtx;
+
+ void (*onEngineMessageCb)(void *context, const uint8_t *message, size_t size);
+ void *onEngineMessageCtx;
+
+ void (*onCfgSchemeCb)(void *context, flib_cfg *scheme);
+ void *onCfgSchemeCtx;
+
+ void (*onMapChangedCb)(void *context, const flib_map *map, int changetype);
+ void *onMapChangedCtx;
+
+ void (*onScriptChangedCb)(void *context, const char *script);
+ void *onScriptChangedCtx;
+
+ void (*onWeaponsetChangedCb)(void *context, flib_weaponset *weaponset);
+ void *onWeaponsetChangedCtx;
+
+ void (*onAdminAccessCb)(void *context);
+ void *onAdminAccessCtx;
+
+ void (*onServerVarCb)(void *context, const char *name, const char *value);
+ void *onServerVarCtx;
+
+ bool running;
+ bool destroyRequested;
+};
+
+void netconn_clearCallbacks(flib_netconn *conn);
+void netconn_leaveRoom(flib_netconn *conn);
+void netconn_setMap(flib_netconn *conn, const flib_map *map);
+void netconn_setWeaponset(flib_netconn *conn, const flib_weaponset *weaponset);
+void netconn_setScript(flib_netconn *conn, const char *script);
+void netconn_setScheme(flib_netconn *conn, const flib_cfg *scheme);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netconn_send.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,454 @@
+#include "netconn_internal.h"
+
+#include "../util/logging.h"
+#include "../util/util.h"
+#include "../util/buffer.h"
+#include "../md5/md5.h"
+#include "../base64/base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+// cmdname is always given as literal from functions in this file, so it is never null.
+static int sendVoid(flib_netconn *conn, const char *cmdname) {
+ if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
+ return -1;
+ }
+ return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname);
+}
+
+// Testing for !*str prevents sending 0-length parameters (they trip up the protocol)
+static int sendStr(flib_netconn *conn, const char *cmdname, const char *str) {
+ if(log_e_if(!conn || !str || !*str, "Invalid parameter sending %s command", cmdname)) {
+ return -1;
+ }
+ return flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", cmdname, str);
+}
+
+static int sendInt(flib_netconn *conn, const char *cmdname, int param) {
+ if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) {
+ return -1;
+ }
+ return flib_netbase_sendf(conn->netBase, "%s\n%i\n\n", cmdname, param);
+}
+
+int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) {
+ return sendStr(conn, "QUIT", (quitmsg && *quitmsg) ? quitmsg : "User quit");
+}
+
+int flib_netconn_send_chat(flib_netconn *conn, const char *chat) {
+ if(chat && *chat) {
+ return sendStr(conn, "CHAT", chat);
+ }
+ return 0;
+}
+
+int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) {
+ if(chat && *chat) {
+ return sendStr(conn, "TEAMCHAT", chat);
+ }
+ return 0;
+}
+
+int flib_netconn_send_nick(flib_netconn *conn, const char *nick) {
+ int result = -1;
+ if(!log_badparams_if(!conn || !nick || !*nick)) {
+ char *tmpName = flib_strdupnull(nick);
+ if(tmpName) {
+ if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) {
+ free(conn->playerName);
+ conn->playerName = tmpName;
+ tmpName = NULL;
+ result = 0;
+ }
+ }
+ free(tmpName);
+ }
+ return result;
+}
+
+int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd) {
+ int result = -1;
+ if(!log_badparams_if(!conn || !latin1Passwd)) {
+ md5_state_t md5state;
+ uint8_t md5bytes[16];
+ char md5hex[33];
+ md5_init(&md5state);
+ md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd));
+ md5_finish(&md5state, md5bytes);
+ for(int i=0;i<sizeof(md5bytes); i++) {
+ // Needs to be lowercase - server checks case sensitive
+ snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]);
+ }
+ result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex);
+ }
+ return result;
+}
+
+int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) {
+ if(!sendStr(conn, "JOIN_ROOM", room)) {
+ conn->isChief = false;
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) {
+ if(!sendStr(conn, "CREATE_ROOM", room)) {
+ conn->isChief = true;
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) {
+ return sendStr(conn, "ROOM_NAME", roomName);
+}
+
+int flib_netconn_send_leaveRoom(flib_netconn *conn) {
+ if(flib_netconn_is_in_room_context(conn) && !sendVoid(conn, "PART")) {
+ netconn_leaveRoom(conn);
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_toggleReady(flib_netconn *conn) {
+ return sendVoid(conn, "TOGGLE_READY");
+}
+
+static void addTeamToPendingList(flib_netconn *conn, const flib_team *team) {
+ flib_team *teamcopy = flib_team_copy(team);
+ if(teamcopy) {
+ teamcopy->remoteDriven = false;
+ free(teamcopy->ownerName);
+ teamcopy->ownerName = flib_strdupnull(conn->playerName);
+ if(teamcopy->ownerName) {
+ flib_teamlist_delete(&conn->pendingTeamlist, team->name);
+ flib_teamlist_insert(&conn->pendingTeamlist, teamcopy, 0);
+ }
+ }
+ flib_team_release(teamcopy);
+}
+
+int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team) {
+ int result = -1;
+ if(!log_badparams_if(!conn || !team)) {
+ bool missingInfo = !team->name || !team->grave || !team->fort || !team->voicepack || !team->flag;
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ missingInfo |= !team->hogs[i].name || !team->hogs[i].hat;
+ }
+ if(!log_e_if(missingInfo, "Incomplete team definition")) {
+ flib_vector *vec = flib_vector_create();
+ if(vec) {
+ bool error = false;
+ error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%i\n%s\n%s\n%s\n%s\n%i\n", team->name, team->colorIndex, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty);
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ error |= flib_vector_appendf(vec, "%s\n%s\n", team->hogs[i].name, team->hogs[i].hat);
+ }
+ error |= flib_vector_appendf(vec, "\n");
+ if(!error && !flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec))) {
+ addTeamToPendingList(conn, team);
+ result = 0;
+ }
+ }
+ flib_vector_destroy(vec);
+ }
+ }
+ return result;
+}
+
+int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) {
+ if(!sendStr(conn, "REMOVE_TEAM", teamname)) {
+ flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+ if(team && !team->remoteDriven) {
+ flib_teamlist_delete(&conn->teamlist, teamname);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size) {
+ int result = -1;
+ if(!log_badparams_if(!conn || (!message && size>0))) {
+ char *base64encout = NULL;
+ base64_encode_alloc((const char*)message, size, &base64encout);
+ if(base64encout) {
+ result = flib_netbase_sendf(conn->netBase, "EM\n%s\n\n", base64encout);
+ }
+ free(base64encout);
+ }
+ return result;
+}
+
+int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount) {
+ if(!log_badparams_if(!conn || !teamname || hogcount<1 || hogcount>HEDGEHOGS_PER_TEAM)
+ && !flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount)) {
+ if(conn->isChief) {
+ flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+ if(team) {
+ team->hogsInGame = hogcount;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex) {
+ if(!log_badparams_if(!conn || !teamname)
+ && !flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%i\n\n", teamname, colorIndex)) {
+ if(conn->isChief) {
+ flib_team *team = flib_teamlist_find(&conn->teamlist, teamname);
+ if(team) {
+ team->colorIndex = colorIndex;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset) {
+ if(!log_badparams_if(!conn || !weaponset)) {
+ char ammostring[WEAPONS_COUNT*4+1];
+ strcpy(ammostring, weaponset->loadout);
+ strcat(ammostring, weaponset->crateprob);
+ strcat(ammostring, weaponset->delay);
+ strcat(ammostring, weaponset->crateammo);
+ if(!flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring)) {
+ if(conn->isChief) {
+ netconn_setWeaponset(conn, weaponset);
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int flib_netconn_send_map(flib_netconn *conn, const flib_map *map) {
+ if(log_badparams_if(!conn || !map)) {
+ return -1;
+ }
+ bool error = false;
+
+ if(map->seed) {
+ error |= flib_netconn_send_mapSeed(conn, map->seed);
+ }
+ error |= flib_netconn_send_mapTemplate(conn, map->templateFilter);
+ if(map->theme) {
+ error |= flib_netconn_send_mapTheme(conn, map->theme);
+ }
+ error |= flib_netconn_send_mapGen(conn, map->mapgen);
+ error |= flib_netconn_send_mapMazeSize(conn, map->mazeSize);
+ if(map->name) {
+ error |= flib_netconn_send_mapName(conn, map->name);
+ }
+ if(map->drawData && map->drawDataSize>0) {
+ error |= flib_netconn_send_mapDrawdata(conn, map->drawData, map->drawDataSize);
+ }
+ return error;
+}
+
+int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) {
+ if(!sendStr(conn, "CFG\nMAP", mapName)) {
+ if(conn->isChief) {
+ char *copy = flib_strdupnull(mapName);
+ if(copy) {
+ free(conn->map->name);
+ conn->map->name = copy;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) {
+ if(!sendInt(conn, "CFG\nMAPGEN", mapGen)) {
+ if(conn->isChief) {
+ conn->map->mapgen = mapGen;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) {
+ if(!sendInt(conn, "CFG\nTEMPLATE", templateFilter)) {
+ if(conn->isChief) {
+ conn->map->templateFilter = templateFilter;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) {
+ if(!sendInt(conn, "CFG\nMAZE_SIZE", mazeSize)) {
+ if(conn->isChief) {
+ conn->map->mazeSize = mazeSize;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) {
+ if(!sendStr(conn, "CFG\nSEED", seed)) {
+ if(conn->isChief) {
+ char *copy = flib_strdupnull(seed);
+ if(copy) {
+ free(conn->map->seed);
+ conn->map->seed = copy;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) {
+ if(!sendStr(conn, "CFG\nTHEME", theme)) {
+ if(conn->isChief) {
+ char *copy = flib_strdupnull(theme);
+ if(copy) {
+ free(conn->map->theme);
+ conn->map->theme = copy;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size) {
+ int result = -1;
+ if(!log_badparams_if(!conn || (!drawData && size>0) || size>SIZE_MAX/2)) {
+ uLongf zippedSize = compressBound(size);
+ uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header
+ if(zipped) {
+ // Create the QCompress size header (uint32 big endian)
+ zipped[0] = (size>>24) & 0xff;
+ zipped[1] = (size>>16) & 0xff;
+ zipped[2] = (size>>8) & 0xff;
+ zipped[3] = (size) & 0xff;
+
+ if(compress(zipped+4, &zippedSize, drawData, size) != Z_OK) {
+ flib_log_e("Error compressing drawn map data.");
+ } else {
+ char *base64encout = NULL;
+ base64_encode_alloc((const char*)zipped, zippedSize+4, &base64encout);
+ if(!base64encout) {
+ flib_log_e("Error base64-encoding drawn map data.");
+ } else {
+ result = flib_netbase_sendf(conn->netBase, "CFG\nDRAWNMAP\n%s\n\n", base64encout);
+ }
+ free(base64encout);
+ }
+ }
+ free(zipped);
+ }
+
+ if(!result && conn->isChief) {
+ uint8_t *copy = flib_bufdupnull(drawData, size);
+ if(copy) {
+ free(conn->map->drawData);
+ conn->map->drawData = copy;
+ conn->map->drawDataSize = size;
+ }
+ }
+ return result;
+}
+
+int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) {
+ if(!sendStr(conn, "CFG\nSCRIPT", scriptName)) {
+ if(conn->isChief) {
+ netconn_setScript(conn, scriptName);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme) {
+ int result = -1;
+ if(!log_badparams_if(!conn || !scheme)) {
+ flib_vector *vec = flib_vector_create();
+ if(vec) {
+ bool error = false;
+ error |= flib_vector_appendf(vec, "CFG\nSCHEME\n%s\n", scheme->name);
+ for(int i=0; i<scheme->meta->modCount; i++) {
+ error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false");
+ }
+ for(int i=0; i<scheme->meta->settingCount; i++) {
+ error |= flib_vector_appendf(vec, "%i\n", scheme->settings[i]);
+ }
+ error |= flib_vector_appendf(vec, "\n");
+ if(!error) {
+ result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec));
+ }
+ }
+ flib_vector_destroy(vec);
+ }
+
+ if(!result && conn->isChief) {
+ netconn_setScheme(conn, scheme);
+ }
+ return result;
+}
+
+int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) {
+ if(!sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0)) {
+ if(conn->netconnState == NETCONN_STATE_INGAME) {
+ conn->netconnState = NETCONN_STATE_ROOM;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) {
+ return sendStr(conn, "BAN", playerName);
+}
+
+int flib_netconn_send_kick(flib_netconn *conn, const char *playerName) {
+ return sendStr(conn, "KICK", playerName);
+}
+
+int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName) {
+ return sendStr(conn, "INFO", playerName);
+}
+
+int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName) {
+ return sendStr(conn, "FOLLOW", playerName);
+}
+
+int flib_netconn_send_startGame(flib_netconn *conn) {
+ return sendVoid(conn, "START_GAME");
+}
+
+int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn) {
+ return sendVoid(conn, "TOGGLE_RESTRICT_JOINS");
+}
+
+int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn) {
+ return sendVoid(conn, "TOGGLE_RESTRICT_TEAMS");
+}
+
+int flib_netconn_send_clearAccountsCache(flib_netconn *conn) {
+ return sendVoid(conn, "CLEAR_ACCOUNTS_CACHE");
+}
+
+int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value) {
+ if(log_badparams_if(!conn || !name || !value)) {
+ return -1;
+ }
+ return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value);
+}
+
+int flib_netconn_send_getServerVars(flib_netconn *conn) {
+ return sendVoid(conn, "GET_SERVER_VAR");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,124 @@
+#include "netprotocol.h"
+
+#include "../util/util.h"
+#include "../util/logging.h"
+
+#include "../base64/base64.h"
+
+#include <zlib.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static int fillTeamFromMsg(flib_team *team, char **parts) {
+ team->name = flib_strdupnull(parts[0]);
+ team->grave = flib_strdupnull(parts[1]);
+ team->fort = flib_strdupnull(parts[2]);
+ team->voicepack = flib_strdupnull(parts[3]);
+ team->flag = flib_strdupnull(parts[4]);
+ team->ownerName = flib_strdupnull(parts[5]);
+ if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag || !team->ownerName) {
+ return -1;
+ }
+
+ errno = 0;
+ long difficulty = strtol(parts[6], NULL, 10);
+ if(errno) {
+ return -1;
+ }
+
+ for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+ flib_hog *hog = &team->hogs[i];
+ hog->difficulty = difficulty;
+ hog->name = flib_strdupnull(parts[7+2*i]);
+ hog->hat = flib_strdupnull(parts[8+2*i]);
+ if(!hog->name || !hog->hat) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+flib_team *flib_team_from_netmsg(char **parts) {
+ flib_team *result = NULL;
+ flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team)));
+ if(tmpTeam) {
+ if(!fillTeamFromMsg(tmpTeam, parts)) {
+ result = tmpTeam;
+ tmpTeam = NULL;
+ } else {
+ flib_log_e("Error parsing team from net.");
+ }
+ }
+ flib_team_release(tmpTeam);
+ return result;
+}
+
+flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts) {
+ flib_cfg *result = flib_cfg_create(meta, parts[0]);
+ if(result) {
+ for(int i=0; i<meta->modCount; i++) {
+ result->mods[i] = !strcmp(parts[i+1], "true");
+ }
+ for(int i=0; i<meta->settingCount; i++) {
+ result->settings[i] = atoi(parts[i+meta->modCount+1]);
+ }
+ }
+ return result;
+}
+
+flib_map *flib_netmsg_to_map(char **parts) {
+ flib_map *result = flib_map_create_named(parts[3], parts[0]);
+ if(result) {
+ result->mapgen = atoi(parts[1]);
+ result->mazeSize = atoi(parts[2]);
+ result->templateFilter = atoi(parts[4]);
+ }
+ return result;
+}
+
+// TODO: Test with empty map
+int flib_netmsg_to_drawnmapdata(char *netmsg, uint8_t** outbuf, size_t *outlen) {
+ int result = -1;
+
+ // First step: base64 decoding
+ char *base64decout = NULL;
+ size_t base64declen;
+ bool ok = base64_decode_alloc(netmsg, strlen(netmsg), &base64decout, &base64declen);
+ if(ok && base64declen>3) {
+ // Second step: unzip with the QCompress header. That header is just a big-endian
+ // uint32 indicating the length of the uncompressed data.
+ uint8_t *ubyteBuf = (uint8_t*)base64decout;
+ uint32_t unzipLen =
+ (((uint32_t)ubyteBuf[0])<<24)
+ + (((uint32_t)ubyteBuf[1])<<16)
+ + (((uint32_t)ubyteBuf[2])<<8)
+ + ubyteBuf[3];
+ if(unzipLen==0) {
+ *outbuf = NULL;
+ *outlen = 0;
+ result = 0;
+ } else {
+ uint8_t *out = flib_malloc(unzipLen);
+ if(out) {
+ uLongf actualUnzipLen = unzipLen;
+ int resultcode = uncompress(out, &actualUnzipLen, (Bytef*)(base64decout+4), base64declen-4);
+ if(resultcode == Z_OK) {
+ *outbuf = out;
+ *outlen = actualUnzipLen;
+ out = NULL;
+ result = 0;
+ } else {
+ flib_log_e("Uncompressing drawn map failed. Code: %i", resultcode);
+ }
+ }
+ free(out);
+ }
+ } else {
+ flib_log_e("base64 decoding of drawn map failed.");
+ }
+ free(base64decout);
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/net/netprotocol.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef NETPROTOCOL_H_
+#define NETPROTOCOL_H_
+
+#include "../model/team.h"
+#include "../model/cfg.h"
+#include "../model/map.h"
+
+#include <stddef.h>
+
+/**
+ * Create a new team from this 23-part net message
+ */
+flib_team *flib_team_from_netmsg(char **parts);
+
+/**
+ * Create a new scheme from this net message, which must have
+ * meta->modCount+meta->settingCount+1 parts.
+ */
+flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts);
+
+/**
+ * Create a new map from this five-part netmsg
+ */
+flib_map *flib_netmsg_to_map(char **parts);
+
+/**
+ * Decode the drawn map data from this netmessage line.
+ *
+ * The data is first base64 decoded and then quncompress()ed.
+ * The return value is a newly allocated byte buffer, the length
+ * is written to the variable pointed to by outlen.
+ * Returns NULL on error.
+ */
+int flib_netmsg_to_drawnmapdata(char *netmsg, uint8_t **outbuf, size_t *outlen);
+
+#endif /* NETPROTOCOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/resources/metasettings.ini Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,234 @@
+[mod0]
+name=fortsmode
+bitmaskindex=12
+
+[mod1]
+name=divteams
+bitmaskindex=4
+
+[mod2]
+name=solidland
+bitmaskindex=2
+
+[mod3]
+name=border
+bitmaskindex=3
+
+[mod4]
+name=lowgrav
+bitmaskindex=5
+
+[mod5]
+name=laser
+bitmaskindex=6
+
+[mod6]
+name=invulnerability
+bitmaskindex=7
+
+[mod7]
+name=resethealth
+bitmaskindex=8
+
+[mod8]
+name=vampiric
+bitmaskindex=9
+
+[mod9]
+name=karma
+bitmaskindex=10
+
+[mod10]
+name=artillery
+bitmaskindex=11
+
+[mod11]
+name=randomorder
+bitmaskindex=13
+
+[mod12]
+name=king
+bitmaskindex=14
+
+[mod13]
+name=placehog
+bitmaskindex=15
+
+[mod14]
+name=sharedammo
+bitmaskindex=16
+
+[mod15]
+name=disablegirders
+bitmaskindex=17
+
+[mod16]
+name=disablelandobjects
+bitmaskindex=18
+
+[mod17]
+name=aisurvival
+bitmaskindex=19
+
+[mod18]
+name=infattack
+bitmaskindex=20
+
+[mod19]
+name=resetweps
+bitmaskindex=21
+
+[mod20]
+name=perhogammo
+bitmaskindex=22
+
+[mod21]
+name=disablewind
+bitmaskindex=23
+
+[mod22]
+name=morewind
+bitmaskindex=24
+
+[mod23]
+name=tagteam
+bitmaskindex=25
+
+[mod24]
+name=bottomborder
+bitmaskindex=26
+
+
+[setting0]
+name=damagefactor
+times1000=false
+command=e$damagepct
+maxmeansinfinity=false
+min=10
+max=300
+default=100
+
+[setting1]
+name=turntime
+times1000=true
+command=e$turntime
+maxmeansinfinity=true
+min=1
+max=9999
+default=45
+
+[setting2]
+name=health
+times1000=false
+maxmeansinfinity=false
+min=50
+max=200
+default=100
+
+[setting3]
+name=suddendeath
+times1000=false
+command=e$sd_turns
+maxmeansinfinity=true
+min=0
+max=50
+default=15
+
+[setting4]
+name=caseprobability
+times1000=false
+command=e$casefreq
+maxmeansinfinity=false
+min=0
+max=9
+default=5
+
+[setting5]
+name=minestime
+times1000=true
+command=e$minestime
+maxmeansinfinity=false
+min=-1
+max=5
+default=3
+
+[setting6]
+name=minesnum
+times1000=false
+command=e$minesnum
+maxmeansinfinity=false
+min=0
+max=80
+default=4
+
+[setting7]
+name=minedudpct
+times1000=false
+command=e$minedudpct
+maxmeansinfinity=false
+min=0
+max=100
+default=0
+
+[setting8]
+name=explosives
+times1000=false
+command=e$explosives
+maxmeansinfinity=false
+min=0
+max=40
+default=2
+
+[setting9]
+name=healthprobability
+times1000=false
+command=e$healthprob
+maxmeansinfinity=false
+min=0
+max=100
+default=35
+
+[setting10]
+name=healthcaseamount
+times1000=false
+command=e$hcaseamount
+maxmeansinfinity=false
+min=0
+max=200
+default=25
+
+[setting11]
+name=waterrise
+times1000=false
+command=e$waterrise
+maxmeansinfinity=false
+min=0
+max=100
+default=47
+
+[setting12]
+name=healthdecrease
+times1000=false
+command=e$healthdec
+maxmeansinfinity=false
+min=0
+max=100
+default=5
+
+[setting13]
+name=ropepct
+times1000=false
+command=e$ropepct
+maxmeansinfinity=false
+min=25
+max=999
+default=100
+
+[setting14]
+name=getawaytime
+times1000=false
+command=e$getawaytime
+maxmeansinfinity=false
+min=0
+max=999
+default=100
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,172 @@
+#include "socket.h"
+#include "util/logging.h"
+#include "util/util.h"
+#include <stdlib.h>
+#include <SDL_net.h>
+#include <time.h>
+
+struct _flib_tcpsocket {
+ TCPsocket sock;
+ SDLNet_SocketSet sockset;
+};
+
+struct _flib_acceptor {
+ TCPsocket sock;
+ uint16_t port;
+};
+
+static uint32_t getPeerIp(TCPsocket sock) {
+ IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
+ return SDLNet_Read32(&addr->host);
+}
+
+static bool connectionIsLocal(TCPsocket sock) {
+ return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
+}
+
+static flib_tcpsocket *createSocket(TCPsocket sdlsock) {
+ flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket));
+ if(result) {
+ result->sock = sdlsock;
+ result->sockset = SDLNet_AllocSocketSet(1);
+
+ if(!result->sockset) {
+ flib_log_e("Can't allocate socket: Out of memory!");
+ SDLNet_FreeSocketSet(result->sockset);
+ free(result);
+ result = NULL;
+ } else {
+ SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
+ }
+ }
+ return result;
+}
+
+TCPsocket listen(uint16_t port) {
+ IPaddress addr;
+ addr.host = INADDR_ANY;
+ SDLNet_Write16(port, &addr.port);
+ TCPsocket sock = SDLNet_TCP_Open(&addr);
+ if(!sock) {
+ flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
+ }
+ return sock;
+}
+
+flib_acceptor *flib_acceptor_create(uint16_t port) {
+ flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor));
+ if(result) {
+ if(port > 0) {
+ result->port = port;
+ result->sock = listen(result->port);
+ } else {
+ /* SDL_net does not seem to have a way to listen on a random unused port
+ and find out which port that is, so let's try to find one ourselves. */
+ srand(time(NULL));
+ for(int i=0; !result->sock && i<1000; i++) {
+ // IANA suggests using ports in the range 49152-65535 for things like this
+ result->port = 49152+(rand()%(65535-49152));
+ result->sock = listen(result->port);
+ }
+ }
+ if(!result->sock) {
+ flib_log_e("Failed to create acceptor.");
+ free(result);
+ result = NULL;
+ }
+ }
+ return result;
+}
+
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) {
+ if(!acceptor) {
+ flib_log_e("Call to flib_acceptor_listenport with acceptor==null");
+ return 0;
+ }
+ return acceptor->port;
+}
+
+void flib_acceptor_close(flib_acceptor *acceptor) {
+ if(acceptor) {
+ SDLNet_TCP_Close(acceptor->sock);
+ free(acceptor);
+ }
+}
+
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) {
+ flib_tcpsocket *result = NULL;
+ if(!acceptor) {
+ flib_log_e("Call to flib_socket_accept with acceptor==null");
+ } else {
+ TCPsocket sock = NULL;
+ while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
+ if(localOnly && !connectionIsLocal(sock)) {
+ flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock)));
+ } else {
+ result = createSocket(sock);
+ }
+ if(!result) {
+ SDLNet_TCP_Close(sock);
+ }
+ }
+ }
+ return result;
+}
+
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) {
+ flib_tcpsocket *result = NULL;
+ if(!host || port==0) {
+ flib_log_e("Invalid parameter in flib_socket_connect");
+ } else {
+ IPaddress ip;
+ if(SDLNet_ResolveHost(&ip,host,port)==-1) {
+ flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+ } else {
+ TCPsocket sock=SDLNet_TCP_Open(&ip);
+ if(!sock) {
+ flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
+ } else {
+ result = createSocket(sock);
+ if(result) {
+ sock = NULL;
+ }
+ }
+ SDLNet_TCP_Close(sock);
+ }
+ }
+ return result;
+}
+
+void flib_socket_close(flib_tcpsocket *sock) {
+ if(sock) {
+ SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
+ SDLNet_TCP_Close(sock->sock);
+ SDLNet_FreeSocketSet(sock->sockset);
+ free(sock);
+ }
+}
+
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen) {
+ if(!sock || (maxlen>0 && !data)) {
+ flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null");
+ return -1;
+ }
+ int readySockets = SDLNet_CheckSockets(sock->sockset, 0);
+ if(readySockets>0) {
+ int size = SDLNet_TCP_Recv(sock->sock, data, maxlen);
+ return size>0 ? size : -1;
+ } else if(readySockets==0) {
+ return 0;
+ } else {
+ flib_log_e("Error in select system call: %s", SDLNet_GetError());
+ return -1;
+ }
+}
+
+int flib_socket_send(flib_tcpsocket *sock, const void *data, int len) {
+ if(!sock || (len>0 && !data)) {
+ flib_log_e("Call to flib_socket_send with sock==null or data==null");
+ return -1;
+ }
+ return SDLNet_TCP_Send(sock->sock, data, len);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/socket.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,72 @@
+/*
+ * Sockets for TCP networking.
+ *
+ * This layer offers some functionality over what SDL_net offers directly: listening
+ * sockets (called acceptors here) can be bound to port 0, which will make them listen
+ * on a random unused port, if one can be found. To support this feature, you can also
+ * query the local port that an acceptor is listening on.
+ *
+ * Further, we support nonblocking reads here.
+ */
+
+#ifndef SOCKET_H_
+#define SOCKET_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct _flib_tcpsocket flib_tcpsocket;
+typedef struct _flib_acceptor flib_acceptor;
+
+/**
+ * Create a new acceptor which will listen for incoming TCP connections
+ * on the given port. If port is 0, this will listen on a random
+ * unused port which can then be queried with flib_acceptor_listenport.
+ *
+ * Returns NULL on error.
+ */
+flib_acceptor *flib_acceptor_create(uint16_t port);
+
+/**
+ * Return the port on which the acceptor is listening.
+ */
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor);
+
+/**
+ * Close the acceptor and free its memory. NULL-safe.
+ */
+void flib_acceptor_close(flib_acceptor *acceptor);
+
+/**
+ * Try to accept a connection from an acceptor (listening socket).
+ * if localOnly is true, this will only accept connections which came from 127.0.0.1
+ * Returns NULL if nothing can be accepted.
+ */
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly);
+
+/**
+ * Try to connect to the server at the given address.
+ */
+flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port);
+
+/**
+ * Close the socket and free its memory. NULL-safe.
+ */
+void flib_socket_close(flib_tcpsocket *socket);
+
+/**
+ * Attempt to receive up to maxlen bytes from the socket, but does not
+ * block if nothing is available.
+ * Returns the ammount of data received, 0 if there was nothing to receive,
+ * or a negative number if the connection was closed or an error occurred.
+ */
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen);
+
+/**
+ * Blocking send all the data in the data buffer. Returns the actual ammount
+ * of data sent, or a negative value on error. If the value returned here
+ * is less than len, either the connection closed or an error occurred.
+ */
+int flib_socket_send(flib_tcpsocket *sock, const void *data, int len);
+
+#endif /* SOCKET_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/test.c.nocompile Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,319 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "util/buffer.h"
+#include "util/util.h"
+#include "util/list.h"
+#include "model/map.h"
+#include "model/weapon.h"
+#include "model/schemelist.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+#include "net/netconn.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+
+// Callback function that will be called when the map is rendered
+static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {
+ printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+ // Draw the map as ASCII art
+ for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
+ for(int x=0; x<MAPIMAGE_WIDTH; x++) {
+ int pixelnum = x + y*MAPIMAGE_WIDTH;
+ bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+ printf(pixel ? "#" : " ");
+ }
+ printf("\n");
+ }
+
+ // Destroy the connection object (this will end the "tick" loop below)
+ flib_mapconn **connptr = context;
+ flib_mapconn_destroy(*connptr);
+ *connptr = NULL;
+}
+
+static void onDisconnect(void *context, int reason) {
+ flib_log_i("Connection closed. Reason: %i", reason);
+ flib_gameconn **connptr = context;
+ flib_gameconn_destroy(*connptr);
+ *connptr = NULL;
+}
+
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+ flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+ FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+ fwrite(record, 1, size, file);
+ fclose(file);
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+ flib_log_e("Map rendering failed: %s", errormessage);
+
+ // Destroy the connection object (this will end the "tick" loop below)
+ flib_mapconn **connptr = context;
+ flib_mapconn_destroy(*connptr);
+ *connptr = NULL;
+}
+
+static void startEngineMap(int port) {
+ char commandbuffer[255];
+ const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+ const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+ snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s %i landpreview", enginePath, configPath, port);
+ system(commandbuffer);
+}
+
+static void startEngineGame(int port) {
+ char commandbuffer[255];
+ const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+ const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+ const char *dataPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars\\Data";
+ snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", enginePath, configPath, port, dataPath);
+ flib_log_d("Starting engine with CMD: %s", commandbuffer);
+ system(commandbuffer);
+}
+
+void testMapPreview() {
+ // Create a map description and check that there was no error
+ flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS);
+ assert(map);
+
+ // Create a new connection to the engine and check that there was no error
+ flib_mapconn *mapConnection = flib_mapconn_create(map);
+ assert(mapConnection);
+
+ // We don't need the map description anymore
+ flib_map_release(map);
+ map = NULL;
+
+ // Register the callback functions
+ flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+ flib_mapconn_onSuccess(mapConnection, &handleMapSuccess, &mapConnection);
+
+ // Start the engine process and tell it which port the frontlib is listening on
+ startEngineMap(flib_mapconn_getport(mapConnection));
+
+ // Usually, flib_mapconn_tick will be called in an event loop that runs several
+ // times per second. It handles I/O operations and progress, and calls
+ // callbacks when something interesting happens.
+ while(mapConnection) {
+ flib_mapconn_tick(mapConnection);
+ }
+}
+
+/*void testGame() {
+ flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("metasettings.ini");
+ assert(metaconf);
+ flib_weaponset *weapons = flib_weaponset_create("Defaultweaps");
+ flib_schemelist *schemelist = flib_schemelist_from_ini(metaconf, "schemes.ini");
+
+ flib_gamesetup setup;
+ setup.gamescheme = flib_schemelist_find(schemelist, "Default");
+ setup.map = flib_map_create_maze("asparagus", "Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+ setup.script = NULL;
+ setup.teamCount = 2;
+ setup.teams = calloc(2, sizeof(flib_team*));
+ setup.teams[0] = calloc(1, sizeof(flib_team));
+ setup.teams[0]->color = 0xffff0000;
+ setup.teams[0]->flag = "australia";
+ setup.teams[0]->fort = "Plane";
+ setup.teams[0]->grave = "Bone";
+ setup.teams[0]->hogsInGame = 2;
+ setup.teams[0]->name = "Team Awesome";
+ setup.teams[0]->voicepack = "British";
+ setup.teams[0]->hogs[0].difficulty = 2;
+ setup.teams[0]->hogs[0].hat = "NoHat";
+ setup.teams[0]->hogs[0].initialHealth = 100;
+ setup.teams[0]->hogs[0].name = "Harry 120";
+ setup.teams[0]->hogs[1].difficulty = 2;
+ setup.teams[0]->hogs[1].hat = "chef";
+ setup.teams[0]->hogs[1].initialHealth = 100;
+ setup.teams[0]->hogs[1].name = "Chefkoch";
+ setup.teams[1] = flib_team_from_ini("Cave Dwellers.hwt");
+ setup.teams[1]->color = 0xFF0000F0;
+ setup.teams[1]->hogsInGame = 8;
+ flib_team_set_weaponset(setup.teams[0], weapons);
+ flib_team_set_weaponset(setup.teams[1], weapons);
+ flib_weaponset_release(weapons);
+
+ flib_gameconn *gameconn = flib_gameconn_create("Medo42", &setup, false);
+ assert(gameconn);
+
+ flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+ //flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+
+ startEngineGame(flib_gameconn_getport(gameconn));
+
+ while(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+}*/
+
+void testDemo() {
+ FILE *demofile = fopen("testdemo.42.hwd", "rb");
+ assert(demofile);
+ flib_vector *vec = flib_vector_create();
+ uint8_t demobuf[512];
+ int len;
+ while((len=fread(demobuf, 1, 512, demofile))>0) {
+ flib_vector_append(vec, demobuf, len);
+ }
+ fclose(demofile);
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+ flib_gameconn *gameconn = flib_gameconn_create_playdemo(constbuf.data, constbuf.size);
+ flib_vector_destroy(vec);
+ assert(gameconn);
+ flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+ flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+ startEngineGame(flib_gameconn_getport(gameconn));
+
+ while(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+}
+
+void testSave() {
+ FILE *demofile = fopen("testsave.42.hws", "rb");
+ assert(demofile);
+ flib_vector *vec = flib_vector_create();
+ uint8_t demobuf[512];
+ int len;
+ while((len=fread(demobuf, 1, 512, demofile))>0) {
+ flib_vector_append(vec, demobuf, len);
+ }
+ fclose(demofile);
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+ flib_gameconn *gameconn = flib_gameconn_create_loadgame("Medo42", constbuf.data, constbuf.size);
+ flib_vector_destroy(vec);
+ assert(gameconn);
+ flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+ flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+ startEngineGame(flib_gameconn_getport(gameconn));
+
+ while(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+}
+
+void handleNetDisconnect(void *context, int reason, const char *message) {
+ flib_log_i("Disconnected: %s", message);
+ flib_netconn_destroy(*(flib_netconn**)context);
+ *(flib_netconn**)context = NULL;
+}
+
+void handleNetConnected(void *context) {
+ const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
+ flib_log_i("List of rooms:");
+ for(int i=0; i<roomlist->roomCount; i++) {
+ flib_roomlist_room *room = roomlist->rooms[i];
+ flib_log_i("%1s %20s %20s %2i %2i %20s %20s %20s", room->inProgress ? "X" : " ", room->name, room->owner, room->playerCount, room->teamCount, room->map, room->scheme, room->weapons);
+ }
+ flib_netconn_send_joinRoom(*(flib_netconn**)context, "frontlib test");
+}
+
+void handleLobbyJoin(void *context, const char *nick) {
+ flib_log_i("%s joined", nick);
+}
+
+void handleChat(void *context, const char *nick, const char *msg) {
+ flib_log_i("%s: %s", nick, msg);
+ if(!memcmp("frontbot ", msg, strlen("frontbot "))) {
+ const char *command = msg+strlen("frontbot ");
+ if(!memcmp("quit", command, strlen("quit"))) {
+ flib_netconn_send_quit(*(flib_netconn**)context, "Yeth Mathter");
+ } else if(!memcmp("describe ", command, strlen("describe "))) {
+ const char *roomname = command+strlen("describe ");
+ const flib_roomlist *roomlist = flib_netconn_get_roomlist(*(flib_netconn**)context);
+ flib_roomlist_room *room = flib_roomlist_find((flib_roomlist*)roomlist, roomname);
+ if(!room) {
+ flib_netconn_send_chat(*(flib_netconn**)context, "Unknown room.");
+ } else {
+ char *text = flib_asprintf(
+ "%s is a room created by %s, where %i players (%i teams) are %s on %s%s, using the %s scheme and %s weaponset.",
+ room->name,
+ room->owner,
+ room->playerCount,
+ room->teamCount,
+ room->inProgress ? "fighting" : "preparing to fight",
+ room->map[0]=='+' ? "" : "the map ",
+ !strcmp("+rnd+", room->map) ? "a random map" :
+ !strcmp("+maze+", room->map) ? "a random maze" :
+ !strcmp("+drawn+", room->map) ? "a hand-drawn map" :
+ room->map,
+ room->scheme,
+ room->weapons);
+ if(text) {
+ flib_netconn_send_chat(*(flib_netconn**)context, text);
+ }
+ free(text);
+ }
+ } else if(!memcmp("join ", command, strlen("join "))) {
+ const char *roomname = command+strlen("join ");
+ flib_netconn_send_joinRoom(*(flib_netconn**)context, roomname);
+ } else if(!memcmp("ready", command, strlen("ready"))) {
+ flib_netconn_send_toggleReady(*(flib_netconn**)context);
+ }
+ }
+}
+
+void handleEnterRoom(void *context, bool isChief) {
+ flib_netconn_send_toggleReady(*(flib_netconn**)context);
+}
+
+flib_gameconn *gameconn = NULL;
+
+void emFromNetHandler(void *context, const char *em, int size) {
+ flib_gameconn_send_enginemsg(gameconn, (const uint8_t*)em, size);
+}
+
+void emFromEngineHandler(void *context, const uint8_t *em, int size) {
+ flib_netconn_send_engineMessage((flib_netconn*)context, em, size);
+}
+
+void handleRunGame(void *context) {
+ flib_gamesetup *gamesetup = flib_netconn_create_gameSetup((flib_netconn*)context);
+ if(gamesetup) {
+ gameconn = flib_gameconn_create("frontbot", gamesetup, true);
+ flib_gameconn_onEngineMessage(gameconn, emFromEngineHandler, context);
+ flib_gameconn_onDisconnect(gameconn, onDisconnect, &gameconn);
+ startEngineGame(flib_gameconn_getport(gameconn));
+ }
+}
+
+int main(int argc, char *argv[]) {
+ flib_init(0);
+ flib_log_setLevel(FLIB_LOGLEVEL_ALL);
+
+ //testMapPreview();
+ //testDemo();
+ //testSave();
+ //testGame();
+
+ flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini");
+ assert(meta);
+ flib_netconn *conn = flib_netconn_create("frontbot", meta, "140.247.62.101", 46631);
+ assert(conn);
+ flib_cfg_meta_release(meta);
+
+ flib_netconn_onConnected(conn, handleNetConnected, &conn);
+ flib_netconn_onDisconnected(conn, handleNetDisconnect, &conn);
+ flib_netconn_onLobbyJoin(conn, handleLobbyJoin, &conn);
+ flib_netconn_onChat(conn, handleChat, &conn);
+ flib_netconn_onEnterRoom(conn, handleEnterRoom, conn);
+ flib_netconn_onRunGame(conn, handleRunGame, conn);
+ flib_netconn_onEngineMessage(conn, emFromNetHandler, NULL);
+
+ while(conn) {
+ flib_netconn_tick(conn);
+ if(gameconn) {
+ flib_gameconn_tick(gameconn);
+ }
+ }
+
+ flib_quit();
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,163 @@
+#include "buffer.h"
+#include "logging.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#define MIN_VECTOR_CAPACITY 16
+
+struct _flib_vector {
+ void *data;
+ size_t size;
+ size_t capacity;
+};
+
+flib_vector *flib_vector_create() {
+ flib_vector *result = NULL;
+ flib_vector *tmpVector = flib_calloc(1, sizeof(flib_vector));
+ if(tmpVector) {
+ tmpVector->data = flib_malloc(MIN_VECTOR_CAPACITY);
+ if(tmpVector->data) {
+ tmpVector->size = 0;
+ tmpVector->capacity = MIN_VECTOR_CAPACITY;
+ result = tmpVector;
+ tmpVector = NULL;
+ }
+ }
+ flib_vector_destroy(tmpVector);
+ return result;
+}
+
+void flib_vector_destroy(flib_vector *vec) {
+ if(vec) {
+ free(vec->data);
+ free(vec);
+ }
+}
+
+static int setCapacity(flib_vector *vec, size_t newCapacity) {
+ if(newCapacity == vec->capacity) {
+ return 0;
+ }
+ void *newData = realloc(vec->data, newCapacity);
+ if(newData) {
+ vec->data = newData;
+ vec->capacity = newCapacity;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int allocateExtraCapacity(flib_vector *vec, size_t extraCapacity) {
+ if(extraCapacity <= SIZE_MAX - vec->capacity) {
+ return setCapacity(vec, vec->capacity + extraCapacity);
+ } else {
+ return -1;
+ }
+}
+
+int flib_vector_resize(flib_vector *vec, size_t newSize) {
+ if(!vec) {
+ flib_log_e("null parameter in flib_vector_resize");
+ return -1;
+ }
+
+ if(vec->capacity < newSize) {
+ // Resize exponentially for constant amortized time,
+ // But at least by as much as we need of course
+ size_t extraCapacity = (vec->capacity)/2;
+ size_t minExtraCapacity = newSize - vec->capacity;
+ if(extraCapacity < minExtraCapacity) {
+ extraCapacity = minExtraCapacity;
+ }
+
+ if(allocateExtraCapacity(vec, extraCapacity)) {
+ allocateExtraCapacity(vec, minExtraCapacity);
+ }
+ } else if(vec->capacity/2 > newSize) {
+ size_t newCapacity = newSize+newSize/4;
+ if(newCapacity < MIN_VECTOR_CAPACITY) {
+ newCapacity = MIN_VECTOR_CAPACITY;
+ }
+ setCapacity(vec, newCapacity);
+ }
+
+ if(vec->capacity >= newSize) {
+ vec->size = newSize;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int flib_vector_append(flib_vector *vec, const void *data, size_t len) {
+ if(!log_badparams_if(!vec || (!data && len>0))
+ && !log_oom_if(len > SIZE_MAX-vec->size)) {
+ size_t oldSize = vec->size;
+ if(!log_oom_if(flib_vector_resize(vec, vec->size+len))) {
+ memmove(((uint8_t*)vec->data) + oldSize, data, len);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int flib_vector_appendf(flib_vector *vec, const char *fmt, ...) {
+ int result = -1;
+ if(!log_badparams_if(!vec || !fmt)) {
+ va_list argp;
+ va_start(argp, fmt);
+ char *formatted = flib_vasprintf(fmt, argp);
+ va_end(argp);
+
+
+ if(formatted) {
+ size_t len = strlen(formatted);
+ result = flib_vector_append(vec, formatted, len);
+ }
+ }
+ return result;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector *vec) {
+ if(!vec) {
+ flib_log_e("null parameter in flib_vector_as_buffer");
+ flib_buffer result = {NULL, 0};
+ return result;
+ } else {
+ flib_buffer result = {vec->data, vec->size};
+ return result;
+ }
+}
+
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec) {
+ if(!vec) {
+ flib_log_e("null parameter in flib_vector_as_constbuffer");
+ flib_constbuffer result = {NULL, 0};
+ return result;
+ } else {
+ flib_constbuffer result = {vec->data, vec->size};
+ return result;
+ }
+}
+
+void *flib_vector_data(flib_vector *vec) {
+ if(!vec) {
+ flib_log_e("null parameter in flib_vector_data");
+ return NULL;
+ } else {
+ return vec->data;
+ }
+}
+
+size_t flib_vector_size(flib_vector *vec) {
+ if(!vec) {
+ flib_log_e("null parameter in flib_vector_size");
+ return 0;
+ } else {
+ return vec->size;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,85 @@
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * A simple struct to hold both the pointer to an array and its size,
+ * for e.g. conveniently returning it from a function.
+ *
+ * Convention: Size is zero iff data is a NULL pointer.
+ */
+typedef struct {
+ void *data;
+ size_t size;
+} flib_buffer;
+
+/**
+ * Just like flib_buffer, but the contents are not supposed to be modified.
+ */
+typedef struct {
+ const void *data;
+ size_t size;
+} flib_constbuffer;
+
+/**
+ * Simple variable-capacity data structure that can be efficiently appended to.
+ */
+typedef struct _flib_vector flib_vector;
+
+/**
+ * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
+ * May return NULL if memory runs out.
+ */
+flib_vector *flib_vector_create();
+
+/**
+ * Free the memory of this vector
+ */
+void flib_vector_destroy(flib_vector *vec);
+
+/**
+ * Resize the vector. This changes the size, and ensures the capacity is large enough to
+ * for the new size. Can also free memory if the new size is smaller. There is no guarantee
+ * about the contents of extra memory.
+ */
+int flib_vector_resize(flib_vector *vec, size_t newSize);
+
+/**
+ * Append the provided data to the end of the vector, enlarging it as required.
+ * The vector remains unchanged if appending fails.
+ * Returns 0 on success.
+ */
+int flib_vector_append(flib_vector *vec, const void *data, size_t len);
+
+/**
+ * Append data from a format string to the buffer (without trailing 0)
+ * Returns 0 on success.
+ */
+int flib_vector_appendf(flib_vector *vec, const char *template, ...);
+
+/**
+ * Return a pointer to the current data buffer of the vector. This pointer can
+ * become invalid if the vector size or capacity is changed.
+ */
+void *flib_vector_data(flib_vector *vec);
+
+/**
+ * Return the current size of the vector.
+ */
+size_t flib_vector_size(flib_vector *vec);
+
+/**
+ * Return a buffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_buffer flib_vector_as_buffer(flib_vector *vec);
+
+/**
+ * Return a constbuffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,320 @@
+#include "inihelper.h"
+#include "../iniparser/dictionary.h"
+#include "../iniparser/iniparser.h"
+
+#include "logging.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdarg.h>
+
+struct _flib_ini {
+ dictionary *inidict;
+ char *currentSection;
+};
+
+static char *createDictKey(const char *sectionName, const char *keyName) {
+ return flib_asprintf("%s:%s", sectionName, keyName);
+}
+
+/**
+ * Turns a string into a lowercase string, in-place.
+ */
+static void strToLower(char *str) {
+ if(str) {
+ while(*str) {
+ *str = tolower(*str);
+ str++;
+ }
+ }
+}
+
+flib_ini *flib_ini_create(const char *filename) {
+ flib_ini *result = NULL;
+ flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
+ if(tmpIni) {
+ if(filename) {
+ tmpIni->inidict = iniparser_load(filename);
+ }
+ if(!tmpIni->inidict) {
+ tmpIni->inidict = dictionary_new(0);
+ }
+ if(tmpIni->inidict) {
+ result = tmpIni;
+ tmpIni = NULL;
+ }
+ }
+ flib_ini_destroy(tmpIni);
+ return result;
+}
+
+flib_ini *flib_ini_load(const char *filename) {
+ flib_ini *result = NULL;
+ if(!filename) {
+ flib_log_e("null parameter in flib_ini_load");
+ } else {
+ flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
+ if(tmpIni) {
+ tmpIni->inidict = iniparser_load(filename);
+ if(tmpIni->inidict) {
+ result = tmpIni;
+ tmpIni = NULL;
+ }
+ }
+ flib_ini_destroy(tmpIni);
+ }
+ return result;
+}
+
+int flib_ini_save(flib_ini *ini, const char *filename) {
+ int result = INI_ERROR_OTHER;
+ if(!ini || !filename) {
+ flib_log_e("null parameter in flib_ini_save");
+ } else {
+ FILE *file = fopen(filename, "wb");
+ if(!file) {
+ flib_log_e("Error opening file \"%s\" for writing.", filename);
+ } else {
+ iniparser_dump_ini(ini->inidict, file);
+ if(fclose(file)) {
+ flib_log_e("Write error on ini file \"%s\"", filename);
+ } else {
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+void flib_ini_destroy(flib_ini *ini) {
+ if(ini) {
+ if(ini->inidict) {
+ iniparser_freedict(ini->inidict);
+ }
+ free(ini->currentSection);
+ free(ini);
+ }
+}
+
+int flib_ini_enter_section(flib_ini *ini, const char *section) {
+ int result = INI_ERROR_OTHER;
+ if(ini) {
+ free(ini->currentSection);
+ ini->currentSection = NULL;
+ }
+ if(!ini || !section) {
+ flib_log_e("null parameter in flib_ini_enter_section");
+ } else {
+ if(!iniparser_find_entry(ini->inidict, section)) {
+ result = INI_ERROR_NOTFOUND;
+ } else {
+ ini->currentSection = flib_strdupnull(section);
+ if(ini->currentSection) {
+ // Usually iniparser ignores case, but some section-handling functions don't,
+ // so we set it to lowercase manually
+ strToLower(ini->currentSection);
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+int flib_ini_create_section(flib_ini *ini, const char *section) {
+ int result = INI_ERROR_OTHER;
+ if(!ini || !section) {
+ flib_log_e("null parameter in flib_ini_create_section");
+ } else {
+ result = flib_ini_enter_section(ini, section);
+ if(result == INI_ERROR_NOTFOUND) {
+ if(iniparser_set(ini->inidict, section, NULL)) {
+ result = INI_ERROR_OTHER;
+ } else {
+ result = flib_ini_enter_section(ini, section);
+ }
+ }
+ }
+ return result;
+}
+
+/**
+ * The result is an internal string of the iniparser, don't free it.
+ */
+static char *findValue(dictionary *dict, const char *section, const char *key) {
+ char *result = NULL;
+ char *dictKey = createDictKey(section, key);
+ if(dictKey) {
+ result = iniparser_getstring(dict, dictKey, NULL);
+ }
+ free(dictKey);
+ return result;
+}
+
+int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key) {
+ char *tmpValue = NULL;
+ int result = flib_ini_get_str_opt(ini, &tmpValue, key, NULL);
+ if(result==0) {
+ if(tmpValue == NULL) {
+ result = INI_ERROR_NOTFOUND;
+ } else {
+ *outVar = tmpValue;
+ tmpValue = NULL;
+ }
+ }
+ free(tmpValue);
+ return result;
+}
+
+int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def) {
+ int result = INI_ERROR_OTHER;
+ if(!ini || !outVar || !key || !ini->currentSection) {
+ flib_log_e("null parameter or no current section in flib_ini_get_str_opt");
+ } else {
+ const char *value = findValue(ini->inidict, ini->currentSection, key);
+ if(!value) {
+ value = def;
+ }
+ char *valueDup = flib_strdupnull(value);
+ if(valueDup || !def) {
+ *outVar = valueDup;
+ result = 0;
+ }
+ }
+ return result;
+}
+
+int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key) {
+ char *tmpValue = NULL;
+ int result = flib_ini_get_str(ini, &tmpValue, key);
+ if(result==0) {
+ errno = 0;
+ long val = strtol(tmpValue, NULL, 10);
+ if(errno!=0 || val<INT_MIN || val>INT_MAX) {
+ flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", ini->currentSection, key, tmpValue);
+ result = INI_ERROR_FORMAT;
+ } else {
+ *outVar = val;
+ }
+ }
+ free(tmpValue);
+ return result;
+}
+
+int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def) {
+ int tmpValue;
+ int result = flib_ini_get_int(ini, &tmpValue, key);
+ if(result == 0) {
+ *outVar = tmpValue;
+ } else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
+ *outVar = def;
+ result = 0;
+ }
+ return result;
+}
+
+int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key) {
+ char *tmpValue = NULL;
+ int result = flib_ini_get_str(ini, &tmpValue, key);
+ if(result==0) {
+ bool trueval = strchr("1tTyY", tmpValue[0]);
+ bool falseval = strchr("0fFnN", tmpValue[0]);
+ if(!trueval && !falseval) {
+ flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", ini->currentSection, key, tmpValue);
+ result = INI_ERROR_FORMAT;
+ } else {
+ *outVar = trueval;
+ }
+ }
+ free(tmpValue);
+ return result;
+}
+
+int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def) {
+ bool tmpValue;
+ int result = flib_ini_get_bool(ini, &tmpValue, key);
+ if(result == 0) {
+ *outVar = tmpValue;
+ } else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
+ *outVar = def;
+ result = 0;
+ }
+ return result;
+}
+
+int flib_ini_set_str(flib_ini *ini, const char *key, const char *value) {
+ int result = INI_ERROR_OTHER;
+ if(!ini || !key || !value || !ini->currentSection) {
+ flib_log_e("null parameter or no current section in flib_ini_set_str");
+ } else {
+ char *dictKey = createDictKey(ini->currentSection, key);
+ if(dictKey) {
+ result = iniparser_set(ini->inidict, dictKey, value);
+ }
+ free(dictKey);
+ }
+ return result;
+}
+
+int flib_ini_set_int(flib_ini *ini, const char *key, int value) {
+ int result = INI_ERROR_OTHER;
+ char *strvalue = flib_asprintf("%i", value);
+ if(strvalue) {
+ result = flib_ini_set_str(ini, key, strvalue);
+ }
+ free(strvalue);
+ return result;
+}
+
+int flib_ini_set_bool(flib_ini *ini, const char *key, bool value) {
+ return flib_ini_set_str(ini, key, value ? "true" : "false");
+}
+
+int flib_ini_get_sectioncount(flib_ini *ini) {
+ int result = INI_ERROR_OTHER;
+ if(!ini) {
+ flib_log_e("null parameter in flib_ini_get_sectioncount");
+ } else {
+ result = iniparser_getnsec(ini->inidict);
+ }
+ return result;
+}
+
+char *flib_ini_get_sectionname(flib_ini *ini, int number) {
+ char *result = NULL;
+ if(!ini || number<0) {
+ flib_log_e("bad parameter in flib_ini_get_sectionname");
+ } else {
+ result = flib_strdupnull(iniparser_getsecname(ini->inidict, number));
+ }
+ return result;
+}
+
+int flib_ini_get_keycount(flib_ini *ini) {
+ int result = INI_ERROR_OTHER;
+ if(!ini || !ini->currentSection) {
+ flib_log_e("null parameter or no current section in flib_ini_get_keycount");
+ } else {
+ result = iniparser_getsecnkeys(ini->inidict, ini->currentSection);
+ }
+ return result;
+}
+
+char *flib_ini_get_keyname(flib_ini *ini, int number) {
+ char *result = NULL;
+ if(!ini || number<0 || !ini->currentSection) {
+ flib_log_e("bad parameter or no current section in flib_ini_get_keyname");
+ } else {
+ int keyCount = iniparser_getsecnkeys(ini->inidict, ini->currentSection);
+ char **keys = iniparser_getseckeys(ini->inidict, ini->currentSection);
+ if(keys && keyCount>number) {
+ // The keys are in the format section:key, so we have to skip the section and colon.
+ result = flib_strdupnull(keys[number]+strlen(ini->currentSection)+1);
+ }
+ free(keys);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,159 @@
+/**
+ * Convenience interface for ini reading/writing.
+ *
+ * We currently use iniparser in the background, but using its interface directly is a bit verbose.
+ * This module is supposed to 1. make ini reading and writing a bit more convenient, and 2. hide
+ * the iniparser dependency so it can at need be easily replaced.
+ */
+
+#ifndef INIHELPER_H_
+#define INIHELPER_H_
+
+#include <stdbool.h>
+
+#define INI_ERROR_NOTFOUND -1
+#define INI_ERROR_FORMAT -2
+#define INI_ERROR_OTHER -100
+
+typedef struct _flib_ini flib_ini;
+
+/**
+ * Create a new ini data structure, pre-filled with the contents of
+ * the file "filename" if it exists. If filename is null, or the file
+ * is not found, an empty ini will be created. However, if an error
+ * occurs while reading the ini file (or any other error), null
+ * is returned.
+ *
+ * This behavior is useful for modifying an existing ini file without
+ * discarding unknown keys.
+ */
+flib_ini *flib_ini_create(const char *filename);
+
+/**
+ * Similar to flib_ini_create, but fails if the file is not found
+ * or if filename is null.
+ */
+flib_ini *flib_ini_load(const char *filename);
+
+/**
+ * Store the ini to the file "filename", overwriting
+ * the previous contents. Returns 0 on success.
+ */
+int flib_ini_save(flib_ini *ini, const char *filename);
+
+void flib_ini_destroy(flib_ini *ini);
+
+/**
+ * Enter the section with the specified name. Returns 0 on
+ * success, INI_ERROR_NOTFOUND if the section does not exist
+ * and a different value if another error occurs.
+ * If an error occurs, there is no current section.
+ *
+ * The section name should only consist of letters and
+ * numbers.
+ */
+int flib_ini_enter_section(flib_ini *ini, const char *section);
+
+/**
+ * Creates and enters the section with the specified name. Simply
+ * enters the section if it exists already. Returns 0 on success
+ * and a different value if another error occurs.
+ * If an error occurs, there is no current section.
+ */
+int flib_ini_create_section(flib_ini *ini, const char *section);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a newly allocated string. Returns 0 on success, INI_ERROR_NOTFOUND
+ * if the key was not found and a different value for other errors,
+ * e.g. if there is no current section.
+ */
+int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a newly allocated string. If the key is not found, the default
+ * value will be used instead. Returns 0 on success.
+ */
+int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as an int. Returns 0 on success, INI_ERROR_NOTFOUND
+ * if the key was not found, INI_ERROR_FORMAT if it was found but
+ * could not be converted to an int, and a different value for other
+ * errors, e.g. if there is no current section.
+ */
+int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as an int. If the key is not found, the default value will be used instead.
+ * Returns 0 on success, INI_ERROR_FORMAT if the value was found but
+ * could not be converted to int, and another value otherwise.
+ */
+int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a bool. Treats everything beginning with "Y", "T" or "1" as true,
+ * everything starting with "N", "F" or "1" as false.
+ *
+ * Returns 0 on success, INI_ERROR_NOTFOUND if the key was not found,
+ * INI_ERROR_FORMAT if the value could not be interpreted as boolean,
+ * and another value otherwise.
+ */
+int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key);
+
+/**
+ * Find a key in the current section and store the value in outVar
+ * as a bool. If the key is not found, the default value will be
+ * used instead. Returns 0 on success, INI_ERROR_FORMAT if the
+ * value could not be interpreted as boolean, and another value otherwise.
+ */
+int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_str(flib_ini *ini, const char *key, const char *value);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_int(flib_ini *ini, const char *key, int value);
+
+/**
+ * In the current section, associate key with value. Returns 0 on success.
+ */
+int flib_ini_set_bool(flib_ini *ini, const char *key, bool value);
+
+/**
+ * Returns the number of sections in the ini file, or a negative value on error.
+ */
+int flib_ini_get_sectioncount(flib_ini *ini);
+
+/**
+ * Returns the name of the section, or NULL on error. The returned string must
+ * be free()d.
+ *
+ * Note: There is no guarantee that the order of the sections
+ * will remain stable if the ini is modified.
+ */
+char *flib_ini_get_sectionname(flib_ini *ini, int number);
+
+/**
+ * Returns the number of keys in the current section, or -1 on error.
+ */
+int flib_ini_get_keycount(flib_ini *ini);
+
+/**
+ * Returns the name of the key in the current section, or NULL on error.
+ * The returned string must be free()d.
+ *
+ * Note: There is no guarantee that the order of the keys in a section
+ * will remain stable if the ini is modified.
+ */
+char *flib_ini_get_keyname(flib_ini *ini, int number);
+
+#endif /* INIHELPER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/list.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,65 @@
+/**
+ * Simple dynamic array manipulation functions.
+ */
+
+#ifndef LIST_H_
+#define LIST_H_
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+#include "logging.h"
+
+/**
+ * Generate a static function that inserts a new value into a heap array of the given type,
+ * using realloc and memmove to increase the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / increment).
+ * The function returns 0 on success and leaves the array unchanged on error.
+ */
+#define GENERATE_STATIC_LIST_INSERT(fname, type) \
+ static int fname(type **listptr, int *listSizePtr, type element, int pos) { \
+ int result = -1; \
+ if(!listptr || !listSizePtr || pos < 0 || pos > *listSizePtr) { \
+ flib_log_e("Invalid parameter in "#fname); \
+ } else { \
+ type *newList = flib_realloc(*listptr, ((*listSizePtr)+1)*sizeof(type)); \
+ if(newList) { \
+ memmove(newList + (pos+1), newList + pos, ((*listSizePtr)-pos)*sizeof(type)); \
+ newList[pos] = element; \
+ (*listSizePtr)++; \
+ *listptr = newList; \
+ result = 0; \
+ } \
+ } \
+ return result; \
+ }
+
+/**
+ * Generate a static function that deletes a value from a heap array of the given type,
+ * using realloc and memmove to decrease the capacity and shift existing values.
+ * The function takes a pointer to the array variable and a pointer to the size variable
+ * because both can be changed by this operation (realloc / decrement).
+ * The function returns 0 on success and leaves the array unchanged on error.
+ */
+#define GENERATE_STATIC_LIST_DELETE(fname, type) \
+ static int fname(type **listPtr, int *listSizePtr, int pos) { \
+ int result = -1; \
+ if(!listPtr || !listSizePtr || pos < 0 || pos >= *listSizePtr) { \
+ flib_log_e("Invalid parameter in "#fname); \
+ } else { \
+ memmove((*listPtr) + pos, (*listPtr) + (pos+1), ((*listSizePtr)-(pos+1))*sizeof(type)); \
+ (*listSizePtr)--; \
+ \
+ size_t newCharSize = (*listSizePtr)*sizeof(type); \
+ type *newList = flib_realloc((*listPtr), newCharSize); \
+ if(newList || newCharSize==0) { \
+ (*listPtr) = newList; \
+ } /* If the realloc fails, just keep using the old buffer...*/ \
+ result = 0; \
+ } \
+ return result; \
+ }
+
+#endif /* LIST_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,94 @@
+#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 const char *getPrefix(int level) {
+ switch(level) {
+ case FLIB_LOGLEVEL_ERROR: return "E";
+ case FLIB_LOGLEVEL_WARNING: return "W";
+ case FLIB_LOGLEVEL_INFO: return "I";
+ case FLIB_LOGLEVEL_DEBUG: return "D";
+ default: return "?";
+ }
+}
+
+static void _flib_vflog(const char *func, int level, const char *fmt, va_list args) {
+ FILE *logfile = flib_log_getfile();
+ if(level >= flib_loglevel) {
+ fprintf(logfile, "%s ", getPrefix(level));
+ log_time(logfile);
+ fprintf(logfile, " [%-30s] ", func);
+ vfprintf(logfile, fmt, args);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+}
+
+void _flib_flog(const char *func, int level, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ _flib_vflog(func, level, fmt, argp);
+ va_end(argp);
+}
+
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...) {
+ if(!cond) {
+ va_list argp;
+ va_start(argp, fmt);
+ _flib_vflog(func, level, fmt, argp);
+ va_end(argp);
+ }
+ return !cond;
+}
+
+bool _flib_assert_params(const char *func, bool cond) {
+ return _flib_fassert(func, FLIB_LOGLEVEL_ERROR, cond, "Invalid parameter to function");
+}
+
+int flib_log_getLevel() {
+ return flib_loglevel;
+}
+
+void flib_log_setLevel(int level) {
+ flib_loglevel = level;
+}
+
+void flib_log_setFile(FILE *file) {
+ flib_logfile = file;
+}
+
+bool flib_log_isActive(int level) {
+ return level >= flib_log_getLevel();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,47 @@
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#define FLIB_LOGLEVEL_ALL -100
+#define FLIB_LOGLEVEL_DEBUG -1
+#define FLIB_LOGLEVEL_INFO 0
+#define FLIB_LOGLEVEL_WARNING 1
+#define FLIB_LOGLEVEL_ERROR 2
+#define FLIB_LOGLEVEL_NONE 100
+
+/**
+ * Returns a pointer to a static buffer, don't free or store.
+ */
+char* flib_format_ip(uint32_t numip);
+
+/**
+ * Evaluates the expression cond. If it is true, a formatted error will be logged.
+ * Returns true if an error is logged, false otherwise (i.e. the boolean value of the argument)
+ */
+#define log_e_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_ERROR, !(bool)(cond), __VA_ARGS__)
+#define log_w_if(cond, ...) _flib_fassert(__func__, FLIB_LOGLEVEL_WARNING, !(bool)(cond), __VA_ARGS__)
+
+/**
+ * Shorthand for some common error types
+ */
+#define log_badparams_if(cond) log_e_if(cond, "Invalid Parameters")
+#define log_oom_if(cond) log_e_if(cond, "Out of Memory")
+
+#define flib_log_e(...) _flib_flog(__func__, FLIB_LOGLEVEL_ERROR, __VA_ARGS__)
+#define flib_log_w(...) _flib_flog(__func__, FLIB_LOGLEVEL_WARNING, __VA_ARGS__)
+#define flib_log_i(...) _flib_flog(__func__, FLIB_LOGLEVEL_INFO, __VA_ARGS__)
+#define flib_log_d(...) _flib_flog(__func__, FLIB_LOGLEVEL_DEBUG, __VA_ARGS__)
+
+bool _flib_assert_params(const char *func, bool cond);
+bool _flib_fassert(const char *func, int level, bool cond, const char *fmt, ...);
+void _flib_flog(const char *func, int level, const char *fmt, ...);
+
+int flib_log_getLevel();
+void flib_log_setLevel(int level);
+void flib_log_setFile(FILE *logfile);
+bool flib_log_isActive(int level);
+
+#endif /* LOGGING_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/refcounter.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,37 @@
+#include "refcounter.h"
+
+#include "logging.h"
+
+void flib_retain(int *referenceCountPtr, const char *objName) {
+ if(!referenceCountPtr || !objName) {
+ flib_log_e("null parameter to flib_retain");
+ } else {
+ if((*referenceCountPtr) >= 0) {
+ (*referenceCountPtr)++;
+ flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr));
+ }
+ if((*referenceCountPtr) < 0) {
+ flib_log_e("Memory leak: Reference count overflow in %s object!", objName);
+ }
+ }
+}
+
+/**
+ * Returns true if the struct should be freed.
+ */
+bool flib_release(int *referenceCountPtr, const char *objName) {
+ bool result = false;
+ if(!referenceCountPtr) {
+ flib_log_e("null parameter to flib_release");
+ } else if((*referenceCountPtr) > 0) {
+ if(--(*referenceCountPtr) == 0) {
+ flib_log_d("releasing and destroying %s", objName);
+ result = true;
+ } else {
+ flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr));
+ }
+ } else if((*referenceCountPtr) == 0) {
+ flib_log_e("Attempt to release a %s with zero references!", objName);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/refcounter.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,30 @@
+/**
+ * Helper functions for reference counted structs.
+ *
+ * We don't have enough of them to justify going crazy with macros, but I still prefer
+ * to have the logic in one place.
+ *
+ * In particular, these functions handle counter overflow in a sensible way
+ * (log and leak).
+ */
+
+#ifndef REFCOUNTER_H_
+#define REFCOUNTER_H_
+
+#include <stdbool.h>
+
+/**
+ * Pass a pointer to the counter variable to be incremented, and the name of the
+ * object for logging purposes. On overflow an error will be logged and the
+ * counter will get "stuck" so neither retain nor release will modify it anymore.
+ */
+void flib_retain(int *referenceCountPtr, const char *objName);
+
+/**
+ * Pass a pointer to the counter variable to be decremented and the name
+ * of the object for logging purposes.
+ * Returns true if the object should be freed.
+ */
+bool flib_release(int *referenceCountPtr, const char *objName);
+
+#endif /* REFCOUNTER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,188 @@
+#include "util.h"
+#include "logging.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+char *flib_asprintf(const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ char *result = flib_vasprintf(fmt, argp);
+ va_end(argp);
+ return result;
+}
+
+char *flib_vasprintf(const char *fmt, va_list args) {
+ char *result = NULL;
+ if(!fmt) {
+ flib_log_e("null parameter in flib_vasprintf");
+ } else {
+ int requiredSize = vsnprintf(NULL, 0, fmt, args)+1; // Figure out how much memory we need,
+ if(requiredSize<0) {
+ flib_log_e("Error formatting string with template \"%s\" in flib_vasprintf", fmt);
+ } else {
+ char *tmpbuf = flib_malloc(requiredSize); // allocate it
+ if(tmpbuf && vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) { // and then do the actual formatting.
+ result = tmpbuf;
+ tmpbuf = NULL;
+ }
+ free(tmpbuf);
+ }
+ }
+ return result;
+}
+
+char *flib_join(char **parts, int partCount, const char *delimiter) {
+ size_t totalSize = 1;
+ size_t delimLen = strlen(delimiter);
+ for(int i=0; i<partCount; i++) {
+ totalSize += strlen(parts[i]) + delimLen;
+ }
+
+ char *result = flib_malloc(totalSize);
+ if(result) {
+ size_t outpos = 0;
+ for(int i=0; i<partCount; i++) {
+ if(i>0) {
+ strcpy(result+outpos, delimiter);
+ outpos += delimLen;
+ }
+ strcpy(result+outpos, parts[i]);
+ outpos += strlen(parts[i]);
+ }
+ }
+ return result;
+}
+
+char *flib_strdupnull(const char *str) {
+ if(!str) {
+ return NULL;
+ }
+ return flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+ if(!buf || size==0) {
+ return NULL;
+ }
+ void *result = flib_malloc(size);
+ if(result) {
+ memcpy(result, buf, size);
+ }
+ return result;
+}
+
+void *flib_malloc(size_t size) {
+ void *result = malloc(size);
+ if(!result && size>0) {
+ flib_log_e("Out of memory trying to malloc %zu bytes.", size);
+ }
+ return result;
+}
+
+void *flib_calloc(size_t count, size_t elementsize) {
+ void *result = calloc(count, elementsize);
+ if(!result && count>0 && elementsize>0) {
+ flib_log_e("Out of memory trying to calloc %zu objects of %zu bytes each.", count, elementsize);
+ }
+ return result;
+}
+
+void *flib_realloc(void *ptr, size_t size) {
+ void *result = realloc(ptr, size);
+ if(!result && size>0) {
+ flib_log_e("Out of memory trying to realloc %zu bytes.", size);
+ }
+ return result;
+}
+
+static bool isAsciiAlnum(char c) {
+ return (c>='0' && c<='9') || (c>='a' && c <='z') || (c>='A' && c <='Z');
+}
+
+char *flib_urlencode(const char *inbuf) {
+ return flib_urlencode_pred(inbuf, isAsciiAlnum);
+}
+
+char *flib_urlencode_pred(const char *inbuf, bool (*needsEscaping)(char c)) {
+ if(!inbuf) {
+ return NULL;
+ }
+ size_t insize = strlen(inbuf);
+ if(insize > SIZE_MAX/4) {
+ flib_log_e("String too long in flib_urlencode: %zu bytes.", insize);
+ return NULL;
+ }
+
+ char *outbuf = flib_malloc(insize*3+1);
+ if(!outbuf) {
+ return NULL;
+ }
+
+ size_t inpos = 0, outpos = 0;
+ while(inbuf[inpos]) {
+ if(!needsEscaping(inbuf[inpos])) {
+ outbuf[outpos++] = inbuf[inpos++];
+ } else {
+ if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+ flib_log_e("printf error in flib_urlencode");
+ free(outbuf);
+ return NULL;
+ }
+ inpos++;
+ outpos += 3;
+ }
+ }
+ outbuf[outpos] = 0;
+ char *shrunk = realloc(outbuf, outpos+1);
+ return shrunk ? shrunk : outbuf;
+}
+
+char *flib_urldecode(const char *inbuf) {
+ char *outbuf = flib_malloc(strlen(inbuf)+1);
+ if(!outbuf) {
+ return NULL;
+ }
+
+ size_t inpos = 0, outpos = 0;
+ while(inbuf[inpos]) {
+ if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
+ char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
+ outbuf[outpos++] = strtol(temp, NULL, 16);
+ inpos += 3;
+ } else {
+ outbuf[outpos++] = inbuf[inpos++];
+ }
+ }
+ outbuf[outpos] = 0;
+ char *shrunk = realloc(outbuf, outpos+1);
+ return shrunk ? shrunk : outbuf;
+}
+
+bool flib_contains_dir_separator(const char *str) {
+ if(!log_badparams_if(!str)) {
+ for(;*str;str++) {
+ if(*str=='\\' || *str=='/') {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+int flib_gets(char *str, size_t strlen) {
+ if(fgets(str, strlen, stdin)) {
+ for(char *s=str; *s; s++) {
+ if(*s=='\r' || *s=='\n') {
+ *s = 0;
+ break;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h Wed Jun 27 18:04:17 2012 +0200
@@ -0,0 +1,98 @@
+#ifndef FLIB_UTIL_H_
+#define FLIB_UTIL_H_
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+/**
+ * Prints a format string to a newly allocated buffer of the required size.
+ * Parameters are like those for printf. Returns NULL on error.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_asprintf(const char *fmt, ...);
+
+/**
+ * Exactly as flib_asprintf, but accepts a va_list.
+ */
+char *flib_vasprintf(const char *fmt, va_list args);
+
+/**
+ * Creates a new string (that must be freed) containing all parts
+ * joined together, with the specified delimiter between each.
+ */
+char *flib_join(char **parts, int partCount, const char *delimiter);
+
+/**
+ * Return a duplicate of the provided string, or NULL if an error
+ * occurs or if str is already NULL.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_strdupnull(const char *str);
+
+/**
+ * Return a duplicate of the provided buffer, or NULL if an error
+ * occurs or if buf is already NULL or if size is 0.
+ *
+ * Returned buffer must be free()d
+ */
+void *flib_bufdupnull(const void *buf, size_t size);
+
+/**
+ * Simple malloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like malloc.
+ */
+void *flib_malloc(size_t size);
+
+/**
+ * Simple calloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like calloc.
+ */
+void *flib_calloc(size_t count, size_t elementsize);
+
+/**
+ * Simple realloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like realloc.
+ */
+void *flib_realloc(void *ptr, size_t size);
+
+/**
+ * Replace all non-alphanumeric and non-ascii bytes with escape
+ * sequences in the form %XX. Does not modify the original string,
+ * but returns a newly allocated one that must be free()d. Returns
+ * null on failure or if null was passed as argument.
+ *
+ * This should work fine with all ASCII-based charsets including UTF-8.
+ */
+char *flib_urlencode(const char *str);
+
+/**
+ * Replace some bytes with escape sequences in the form %XX.
+ * Does not modify the original string, but returns a newly allocated
+ * one that must be free()d.
+ *
+ * All bytes for which the predicate function returns true are escaped.
+ *
+ * Returns null on failure or if null was passed as argument.
+ */
+char *flib_urlencode_pred(const char *str, bool (*needsEscaping)(char c));
+
+/**
+ * Replace escape sequences of the form %XX with their byte values.
+ * Does not modify the original string, but returns a newly allocated
+ * one that must be free()d. Returns null on failure or if null was
+ * passed as argument.
+ */
+char *flib_urldecode(const char *str);
+
+/**
+ * Figure out if the string contains / or \. Useful in routines that
+ * construct filenames.
+ */
+bool flib_contains_dir_separator(const char *str);
+
+int flib_gets(char *str, size_t strlen);
+
+#endif