# HG changeset patch # User Medo # Date 1340813057 -7200 # Node ID d1db8aaa8edca727342337083268bb7877a922be # Parent 15f722e0b96fdb0d510c9368c24e708942b541c0# Parent 7f8d62b869bdbf36e3a6a8b854821245d81615ba Merge diff -r 7f8d62b869bd -r d1db8aaa8edc .hgignore --- 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 diff -r 7f8d62b869bd -r d1db8aaa8edc QTfrontend/CMakeLists.txt --- 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 ) diff -r 7f8d62b869bd -r d1db8aaa8edc QTfrontend/game.cpp --- 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); } } } diff -r 7f8d62b869bd -r d1db8aaa8edc QTfrontend/net/newnetclient.h --- 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 - 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 - 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); diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/Licenses/Android Support library/NOTICE.txt --- /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 + + diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/Licenses/ini4j/LICENSE.txt --- /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. diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/Licenses/ini4j/NOTICE.txt --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/libs/android-support-v13.jar Binary file project_files/Android-build/SDL-android-project/libs/android-support-v13.jar has changed diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/libs/ini4j-0.5.2.jar Binary file project_files/Android-build/SDL-android-project/libs/ini4j-0.5.2.jar has changed diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/basicflags.xml --- 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 @@ - - - - - false - - - false - - - e$damagepct - - - 100 - - - Damage - - - 300 - - - 10 - - - <string>Damage Modifier</string> - - - - - true - - - true - - - e$turntime - - - 45 - - - Time - - - 100 - - - 1 - - - <string>Turn Time</string> - - - - - false - - - inithealth - - - 200 - - - Health - - - 200 - - - 50 - - - <string>Initial Health</string> - - - - - true - - - false - - - e$sd_turns - - - 15 - - - SuddenDeath - - - 50 - - - 0 - - - <string>Sudden Death Timeout</string> - - - - - false - - - false - - - e$casefreq - - - 5 - - - Box - - - 9 - - - 0 - - - <string>Crate Drop Turns</string> - - - - - false - - - true - - - e$minestime - - - 3 - - - Time - - - 5 - - - -1 - - - <string>Mines Time</string> - - - - - false - - - false - - - e$minesnum - - - 4 - - - Mine - - - 80 - - - 0 - - - <string>Mines Number</string> - - - - - false - - - false - - - e$minedudpct - - - 0 - - - Dud - - - 100 - - - 0 - - - <string>Dud Mines Probability (%)</string> - - - - - false - - - false - - - e$explosives - - - 2 - - - Damage - - - 40 - - - 0 - - - <string>Explosives</string> - - - - - false - - - false - - - e$healthprob - - - 35 - - - Health - - - 100 - - - 0 - - - <string>Health Kit Probability (%)</string> - - - - - false - - - false - - - e$hcaseamount - - - 25 - - - Health - - - 200 - - - 0 - - - <string>Health Amount in Kit</string> - - - - - false - - - false - - - e$waterrise - - - 47 - - - SuddenDeath - - - 100 - - - 0 - - - <string>Water Rise Amount</string> - - - - - false - - - false - - - e$healthdec - - - 5 - - - SuddenDeath - - - 100 - - - 0 - - - <string>Health Decrease</string> - - - - - false - - - false - - - e$ropepct - - - 100 - - - Rope - - - 999 - - - 25 - - - <string>Rope Length (%)</string> - - - - - false - - - false - - - e$getawaytime - - - 100 - - - Time - - - 999 - - - 0 - - - <string>Get Away Time (%)</string> - - - diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/basicsettings.ini --- /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 (%) + diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/gamemods.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_barrelmayhem.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_barrelmayhem.xml --- 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 @@ - - - Barrel Mayhem - - 100 - 30 - 100 - 15 - 0 - 0 - 0 - 0 - 80 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_cleanslate.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_cleanslate.xml --- 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 @@ - - - Clean Slate - - 100 - 45 - 100 - 15 - 5 - 3 - 4 - 0 - 2 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_default_scheme.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_default_scheme.xml --- 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 @@ - - - Default - - 100 - 45 - 100 - 15 - 5 - 3 - 4 - 0 - 2 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_fortmode.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_fortmode.xml --- 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 @@ - - - Fort Mode - - 100 - 45 - 100 - 15 - 5 - 3 - 0 - 0 - 0 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_kingmode.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_kingmode.xml --- 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 @@ - - - King Mode - - 100 - 45 - 100 - 15 - 5 - 3 - 4 - 0 - 2 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_minefield.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_minefield.xml --- 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 @@ - - - Minefield - - 100 - 30 - 50 - 15 - 0 - 0 - 80 - 0 - 0 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_promode.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_promode.xml --- 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 @@ - - - Pro Mode - - 100 - 15 - 100 - 15 - 0 - 3 - 0 - 0 - 2 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_shoppa.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_shoppa.xml --- 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 @@ - - - Shoppa - - 100 - 30 - 100 - 50 - 1 - 3 - 0 - 0 - 0 - 0 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_thinkingwithportals.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_thinkingwithportals.xml --- 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 @@ - - - Thinking with Portals - - 100 - 45 - 100 - 15 - 2 - 3 - 5 - 0 - 5 - 25 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_timeless.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_timeless.xml --- 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 @@ - - - Timeless - - 100 - 9999 - 100 - 15 - 5 - 3 - 5 - 10 - 2 - 35 - 30 - 0 - 0 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_tunnelhogs.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/raw/scheme_tunnelhogs.xml --- 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 @@ - - - Tunnelhogs - - 100 - 30 - 100 - 15 - 5 - 3 - 10 - 10 - 10 - 35 - 25 - 47 - 5 - 100 - 100 - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml --- 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 @@ - @raw/basicflags + @raw/basicsettings + @raw/gamemods @raw/scheme_default_scheme @raw/scheme_barrelmayhem @raw/scheme_cleanslate diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java --- 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 - * + * Copyright (c) 2012 Simeon Maxein + * * 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{ - public static final String DIRECTORY_SCHEME = "schemes"; + private static final Map basicSettingsMeta = new TreeMap(); + private static final Map gameModsMeta = new TreeMap(); - private String name; - //private ArrayList basic; - private Integer gamemod; - private ArrayList basic;; - private static ArrayList> basicflags = new ArrayList>();//TODO why is it static? - public int health; - - public Scheme(String _name, ArrayList _basic, int _gamemod){ + private final String name; + private final int gamemod; + private final Map basic = new TreeMap(); + + public Scheme(String _name, Map _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 basicflag = basicflags.get(pos); + for(Map.Entry 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 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 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 schemes = new ArrayList(); - - 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 basic = new ArrayList(); - Integer gamemod = 0; - int health = 0; - int mask = 0x000000004; + List schemes = new ArrayList(); - 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 basicSettings = new TreeMap(); + for(Entry 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 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();//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 - * - * - * false - * - * - * - * 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 parseFlag(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{ - LinkedHashMap hash = new LinkedHashMap(); - - 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);// - while(eventType == XmlPullParser.START_TAG){ - lcKey = xmlPuller.getName();//checkOverMax - if(getEventType(xmlPuller) == XmlPullParser.START_TAG){// - lcType = xmlPuller.getName().toLowerCase(); - if(getEventType(xmlPuller) == XmlPullParser.TEXT){ - value = xmlPuller.getText(); - if(getEventType(xmlPuller) == XmlPullParser.END_TAG && // - getEventType(xmlPuller) == XmlPullParser.END_TAG){// - 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); - } - }// / - }//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 sectionEntry : ini.entrySet()) { + basicSettingsMeta.put(sectionEntry.getKey(), new BasicSettingMeta(sectionEntry.getValue())); } - eventType = getEventType(xmlPuller);// + + ini = new Ini(gameModsFile); + for(Entry 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 CREATOR = new Parcelable.Creator() { @@ -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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java --- 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; diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java --- 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; } diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java --- 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; diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadPackage.java --- 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; diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java --- 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()); } } diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java --- 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; diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java --- 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); } } - } diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java --- 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); diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java --- 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) } diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java --- 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(); diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/base64/base64.c --- /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 . + * + * 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 + +/* Get UCHAR_MAX. */ +#include + +/* 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/base64/base64.h --- /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 + +/* Get bool. */ +# include + +/* 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 */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/cmdlineClient.c --- /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 +#include +#include +#include +#include +#include + +#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>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; iroomCount; 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; iteamlist->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 ."); + printf(" You can also /quit or let me /describe . Once in a room, you can /add 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/frontlib.c --- /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 +#include + +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(); + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/frontlib.h --- /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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/hwconsts.c --- /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; diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/hwconsts.h --- /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 + * Copyright (c) 2012 Simeon Maxein + * + * 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 +#include + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/LICENSE --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/VERSION --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/dictionary.c --- /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 +#include +#include +#include + +/** 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>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 (sizesize = 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; in != 0) { + printf("error deleting values\n"); + } + printf("deallocating...\n"); + dictionary_del(d); + return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/dictionary.h --- /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 +#include +#include +#include + +/*--------------------------------------------------------------------------- + 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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/iniparser.c --- /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 +#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 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 ; isize ; 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 ; isize ; 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 ; isize ; 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 ; isize ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; isize ; 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 ; jsize ; 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 ; jsize ; 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 */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/iniparser/iniparser.h --- /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 +#include +#include + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include */ + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/gameconn.c --- /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 +#include +#include + +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); + } + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/gameconn.h --- /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 +#include +#include + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/ipcbase.c --- /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 +#include +#include +#include + +/* + * 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; + } + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/ipcbase.h --- /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 +#include +#include + +#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_ */ + diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/ipcprotocol.c --- /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 +#include +#include +#include +#include + +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; offsetdrawDataSize; 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; imeta->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; isettingCount; 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; ibindingCount; i++) { + error |= flib_ipc_append_message(tempvector, "ebind %s %s", team->bindings[i].binding, team->bindings[i].action); + } + + for(int i=0; ihogsInGame; 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; imeta->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 && iteamlist->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(clancolorIndex) { + 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/ipcprotocol.h --- /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 + +/** + * 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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/mapconn.c --- /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 + +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); + } + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/ipc/mapconn.h --- /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 + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/md5/md5.c --- /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 + . 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 + 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 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 + +#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)); +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/md5/md5.h --- /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 + . 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 . + 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 */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/cfg.c --- /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 +#include +#include +#include + +static void flib_cfg_meta_destroy(flib_cfg_meta *cfg) { + if(cfg) { + if(cfg->settings) { + for(int i=0; isettingCount; i++) { + free(cfg->settings[i].name); + free(cfg->settings[i].engineCommand); + } + free(cfg->settings); + } + if(cfg->mods) { + for(int i=0; imodCount; 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->settingCountsettingCount) <= 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->modCountmodCount) <= 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; isettingCount; 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); + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/cfg.h --- /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 + +// 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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/gamesetup.c --- /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 + +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); + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/gamesetup.h --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/map.c --- /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 + +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); + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/map.h --- /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 +#include + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/mapcfg.c --- /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 +#include +#include +#include + +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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/mapcfg.h --- /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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/roomlist.c --- /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 +#include + +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; iroomCount; 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; iroomCount; 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; iroomCount; i++) { + flib_roomlist_room_destroy(list->rooms[i]); + } + free(list->rooms); + list->rooms = NULL; + list->roomCount = 0; + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/roomlist.h --- /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 + +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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/schemelist.c --- /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 +#include +#include +#include + +static void flib_schemelist_destroy(flib_schemelist *list) { + if(list) { + for(int i=0; ischemeCount; 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; isettingCount && !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; imodCount && !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; imeta; + 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; imodCount && !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; isettingCount && !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; ischemeCount && !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; ischemeCount; 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/schemelist.h --- /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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/team.c --- /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 +#include + +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; ihogs[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; ibindingCount; 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; ihogs[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; ibindingCount; 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; ihogs[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; ibindingCount; 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; ihogs[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; ihogs[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; ihogs[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; ibindingCount; 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/team.h --- /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 +#include + +#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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/teamlist.c --- /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 +#include + +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; iteamCount; 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; iteamCount; 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; iteamCount; i++) { + flib_team_release(list->teams[i]); + } + free(list->teams); + list->teams = NULL; + list->teamCount = 0; + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/teamlist.h --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/weapon.c --- /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 +#include +#include + +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= 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; iname = 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; iweaponsetCount; 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='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; iweaponsetCount && !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); + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/model/weapon.h --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netbase.c --- /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 +#include +#include +#include + +#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; ipartCount; 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; ipartCount; 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; ipartCount; 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netbase.h --- /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 +#include +#include + +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_ */ + diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netconn.c --- /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 + * Copyright (c) 2004-2012 Andrey Korotaev + * Copyright (c) 2012 Simeon Maxein + * + * 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 +#include +#include +#include + +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; iteamlist.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])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; ipartCount; 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+2partCount; 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 && colorteamlist, 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); + } + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netconn.h --- /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 +#include +#include + +#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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netconn_callbacks.c --- /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 +#include +#include + +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(digits0) { + 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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netconn_internal.h --- /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 +#include +#include + +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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netconn_send.c --- /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 +#include +#include + +// 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;inetBase, "%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; ihogs[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; ihogs[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; imeta->modCount; i++) { + error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false"); + } + for(int i=0; imeta->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"); +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netprotocol.c --- /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 + +#include +#include +#include +#include + +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; ihogs[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; imodCount; i++) { + result->mods[i] = !strcmp(parts[i+1], "true"); + } + for(int i=0; isettingCount; 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/net/netprotocol.h --- /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 + +/** + * 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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/resources/metasettings.ini --- /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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/socket.c --- /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 +#include +#include + +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); +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/socket.h --- /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 +#include + +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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/test.c.nocompile --- /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 +#include +#include +#include + +// 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>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; iroomCount; 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/buffer.c --- /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 +#include +#include + +#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; + } +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/buffer.h --- /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 +#include + +/** + * 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 diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/inihelper.c --- /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 +#include +#include +#include +#include +#include + +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 || valINT_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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/inihelper.h --- /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 + +#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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/list.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 +#include +#include +#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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/logging.c --- /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 +#include +#include +#include + +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(); +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/logging.h --- /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 +#include +#include + +#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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/refcounter.c --- /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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/refcounter.h --- /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 + +/** + * 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_ */ diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/util.c --- /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 +#include +#include +#include +#include +#include + +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; i0) { + 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; +} diff -r 7f8d62b869bd -r d1db8aaa8edc project_files/frontlib/util/util.h --- /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 +#include +#include + +/** + * 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 diff -r 7f8d62b869bd -r d1db8aaa8edc share/hedgewars/Data/Maps/Sticks/map.cfg diff -r 7f8d62b869bd -r d1db8aaa8edc share/hedgewars/Data/Maps/Sticks/preview@2x.png diff -r 7f8d62b869bd -r d1db8aaa8edc share/hedgewars/Data/Music/Nature.ogg