--- a/project_files/frontlib/demo.c Thu Jun 07 02:45:18 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-#include "demo.h"
-#include "logging.h"
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-
-static int demo_record(flib_vector demoBuffer, const void *data, size_t len) {
- if(flib_vector_append(demoBuffer, data, len) < len) {
- flib_log_e("Error recording demo: Out of memory.");
- return -1;
- } else {
- return 0;
- }
-}
-
-int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName) {
- if(!demoBuffer || !message || !playerName) {
- flib_log_e("Call to flib_demo_record_from_engine with demoBuffer==null or message==null or playerName==null");
- return -1;
- }
-
- if(strchr("?CEiQqHb", message[1])) {
- return 0; // Those message types are not recorded in a demo.
- }
-
- if(message[1] == 's') {
- if(message[0] >= 3) {
- // Chat messages are reformatted to make them look as if they were received, not sent.
- // Get the actual chat message as C string
- char chatMsg[256];
- memcpy(chatMsg, message+2, message[0]-3);
- chatMsg[message[0]-3] = 0;
-
- // If the message starts with /me, it will be displayed differently.
- char converted[257];
- bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4);
- const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s ";
- int size = snprintf(converted+1, 256, template, playerName, chatMsg);
- converted[0] = size>255 ? 255 : size;
- return demo_record(demoBuffer, converted, converted[0]+1);
- } else {
- return 0; // Malformed chat message is no reason to abort...
- }
- } else {
- return demo_record(demoBuffer, message, message[0]+1);
- }
-}
-
-int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len) {
- if(!demoBuffer || (len>0 && !message)) {
- flib_log_e("Call to flib_demo_record_to_engine with demoBuffer==null or message==null");
- return -1;
- }
- return demo_record(demoBuffer, message, len);
-}
-
-void flib_demo_replace_gamemode(flib_buffer buf, char gamemode) {
- size_t msgStart = 0;
- char *data = (char*)buf.data;
- while(msgStart+2 < buf.size) {
- if(!memcmp(data+msgStart, "\x02T", 2)) {
- data[msgStart+2] = gamemode;
- }
- msgStart += (uint8_t)data[msgStart]+1;
- }
-}
--- a/project_files/frontlib/demo.h Thu Jun 07 02:45:18 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/**
- * Demo recording functions. Only used by the ipc game code.
- */
-
-#ifndef DEMO_H_
-#define DEMO_H_
-
-#include "buffer.h"
-
-/**
- * Record a message sent from the engine to the frontend.
- * Returns 0 for OK, a negative value on error.
- * Don't pass NULL.
- */
-int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName);
-
-/**
- * Record a message sent from the frontend to the engine.
- * Returns 0 for OK, a negative value on error.
- * Don't pass NULL.
- */
-int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len);
-
-/**
- * Replace game mode messages ("TL", "TD", "TS", "TN") in the recording to mirror
- * the intended use. Pass 'S' for savegames, 'D' for demos.
- */
-void flib_demo_replace_gamemode(flib_buffer buf, char gamemode);
-
-#endif /* DEMO_H_ */
--- a/project_files/frontlib/frontlib.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/frontlib.c Fri Jun 08 19:52:24 2012 +0200
@@ -1,8 +1,8 @@
#include "frontlib.h"
#include "logging.h"
-#include "socket.h"
+#include "model/map.h"
+#include "ipc/mapconn.h"
#include "ipc.h"
-#include "model/cfg.h"
#include <SDL.h>
#include <SDL_net.h>
@@ -46,7 +46,10 @@
flib_ipc ipc = (flib_ipc)context;
flib_ipc_send_messagestr(ipc, "TL");
flib_ipc_send_messagestr(ipc, "eseed loremipsum");
- flib_ipc_send_messagestr(ipc, "escript Missions/Training/Basic_Training_-_Bazooka.lua");
+ flib_ipc_send_messagestr(ipc, "e$mapgen 0");
+ flib_ipc_send_messagestr(ipc, "e$template_filter 0");
+ flib_ipc_send_messagestr(ipc, "etheme Jungle");
+ flib_ipc_send_messagestr(ipc, "eaddteam 11111111111111111111111111111111 255 Medo42");
}
static void onDisconnect(void *context) {
@@ -59,7 +62,7 @@
case GAME_END_FINISHED:
flib_log_i("Game finished.");
flib_constbuffer demobuf = flib_ipc_getdemo(context);
- flib_log_i("Writing demo (%u bytes)...", demobuf.size);
+ flib_log_i("Writing demo (%u bytes)...", (unsigned)demobuf.size);
FILE *file = fopen("testdemo.dem", "wb");
fwrite(demobuf.data, 1, demobuf.size, file);
fclose(file);
@@ -74,8 +77,36 @@
}
}
+static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {
+ printf("Drawing map for %i brave little hogs...", numHedgehogs);
+ int pixelnum = 0;
+ for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
+ for(int x=0; x<MAPIMAGE_WIDTH; x++) {
+ if(bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)))) {
+ printf("#");
+ } else {
+ printf(" ");
+ }
+ pixelnum++;
+ }
+ printf("\n");
+ }
+
+ flib_mapconn **connptr = context;
+ flib_mapconn_destroy(*connptr);
+ *connptr = NULL;
+}
+
+static void handleMapFailure(void *context, const char *errormessage) {
+ flib_log_e("Map rendering failed: %s", errormessage);
+
+ flib_mapconn **connptr = context;
+ flib_mapconn_destroy(*connptr);
+ *connptr = NULL;
+}
+
int main(int argc, char *argv[]) {
- flib_init(0);
+/* flib_init(0);
flib_cfg_meta *meta = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
flib_cfg *cfg = flib_cfg_create(meta, "DefaultScheme");
@@ -84,18 +115,25 @@
flib_cfg_meta_destroy(meta);
flib_quit();
- return 0;
-/*
- flib_ipc ipc = flib_ipc_create(true, "Medo42");
- assert(ipc);
- flib_ipc_onConfigQuery(ipc, &onConfigQuery, ipc);
- flib_ipc_onDisconnect(ipc, &onDisconnect, &ipc);
- flib_ipc_onGameEnd(ipc, &onGameEnd, ipc);
+ return 0;*/
+
+ flib_init(0);
+ flib_map *mapconf = flib_map_create_regular("Jungle", TEMPLATEFILTER_CAVERN);
+ assert(mapconf);
+
+ flib_mapconn *mapconn = flib_mapconn_create("foobart", mapconf);
+ assert(mapconn);
- while(ipc) {
- flib_ipc_tick(ipc);
+ flib_map_destroy(mapconf);
+ mapconf = NULL;
+
+ flib_mapconn_onFailure(mapconn, &handleMapFailure, &mapconn);
+ flib_mapconn_onSuccess(mapconn, &handleMapSuccess, &mapconn);
+
+ while(mapconn) {
+ flib_mapconn_tick(mapconn);
}
flib_log_i("Shutting down...");
flib_quit();
- return 0;*/
+ return 0;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/hwconsts.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,19 @@
+/**
+ * This file contains important constants which might need to be changed to adapt to
+ * changes in the engine or protocols.
+ */
+
+#ifndef HWCONSTS_H_
+#define HWCONSTS_H_
+
+#define HEDGEHOGS_PER_TEAM 8
+#define NETGAME_DEFAULT_PORT 46631
+
+
+#define WEAPONS_COUNT 55
+#define AMMOLINE_DEFAULT_QT "9391929422199121032235111001201000000211110101011111011"
+#define AMMOLINE_DEFAULT_PROB "0405040541600655546554464776576666666155510101115411011"
+#define AMMOLINE_DEFAULT_DELAY "0000000000000205500000040007004000000000220000000600000"
+#define AMMOLINE_DEFAULT_CRATE "1311110312111111123114111111111111111211111101111111010"
+
+#endif
--- a/project_files/frontlib/ini/inihelper.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/ini/inihelper.c Fri Jun 08 19:52:24 2012 +0200
@@ -1,5 +1,6 @@
#include "inihelper.h"
#include "../logging.h"
+#include "../util.h"
#include <string.h>
#include <stdlib.h>
@@ -8,25 +9,6 @@
#include <errno.h>
#include <stdarg.h>
-static char *flib_asprintf(const char *fmt, ...) {
- va_list argp;
- va_start(argp, fmt);
- size_t requiredSize = vsnprintf(NULL, 0, fmt, argp)+1;
- char *buf = malloc(requiredSize);
- if(buf) {
- vsnprintf(buf, requiredSize, fmt, argp);
- }
- va_end(argp);
- return buf;
-}
-
-char *inihelper_strdupnull(const char *str) {
- if(!str) {
- return NULL;
- }
- return flib_asprintf("%s", str);
-}
-
static bool keychar_needs_urlencoding(char c) {
return !isalnum(c);
}
@@ -50,7 +32,10 @@
if(!keychar_needs_urlencoding(inbuf[inpos])) {
outbuf[outpos++] = inbuf[inpos++];
} else {
- sprintf(outbuf+outpos, "%%%02X", inbuf[inpos]);
+ if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+ free(outbuf);
+ return NULL;
+ }
inpos++;
outpos += 3;
}
@@ -106,7 +91,7 @@
}
char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
- return inihelper_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
+ return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
}
int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
--- a/project_files/frontlib/ini/inihelper.h Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/ini/inihelper.h Fri Jun 08 19:52:24 2012 +0200
@@ -12,11 +12,6 @@
/**
* Returned buffer must be free()d
*/
-char *inihelper_strdupnull(const char *str);
-
-/**
- * Returned buffer must be free()d
- */
char *inihelper_urlencode(const char *inbuf);
/**
--- a/project_files/frontlib/ipc.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/ipc.c Fri Jun 08 19:52:24 2012 +0200
@@ -1,5 +1,5 @@
#include "ipc.h"
-#include "ipcconn.h"
+#include "ipc/ipcconn.h"
#include "logging.h"
#include <stdbool.h>
--- a/project_files/frontlib/ipc.h Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/ipc.h Fri Jun 08 19:52:24 2012 +0200
@@ -2,6 +2,7 @@
#define IPC_H_
#include "buffer.h"
+#include "model/weapon.h"
#include <stddef.h>
#include <stdint.h>
@@ -40,10 +41,7 @@
int flib_ipc_send_map_named(flib_ipc ipc, const char *mappath);
int flib_ipc_send_gamemods(flib_ipc ipc, uint32_t modflags);
int flib_ipc_send_gamesetting(flib_ipc ipc, const char *settingname, int modflags);
-int flib_ipc_send_weapon_loadout(flib_ipc ipc, const char *weapsettings);
-int flib_ipc_send_weapon_delay(flib_ipc ipc, const char *weapsettings);
-int flib_ipc_send_weapon_cratechance(flib_ipc ipc, const char *weapsettings);
-int flib_ipc_send_weapon_crateammo(flib_ipc ipc, const char *weapsettings);
+int flib_ipc_send_weaponset(flib_ipc ipc, flib_weaponset *set);
int flib_ipc_send_conf_end(flib_ipc ipc);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/demo.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,71 @@
+#include "demo.h"
+#include "../logging.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static int demo_record(flib_vector demoBuffer, const void *data, size_t len) {
+ if(flib_vector_append(demoBuffer, data, len) < len) {
+ flib_log_e("Error recording demo: Out of memory.");
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName) {
+ if(!demoBuffer || !message || !playerName) {
+ flib_log_e("Call to flib_demo_record_from_engine with demoBuffer==null or message==null or playerName==null");
+ return -1;
+ }
+
+ if(strchr("?CEiQqHb", message[1])) {
+ return 0; // Those message types are not recorded in a demo.
+ }
+
+ if(message[1] == 's') {
+ if(message[0] >= 3) {
+ // Chat messages are reformatted to make them look as if they were received, not sent.
+ // Get the actual chat message as C string
+ char chatMsg[256];
+ memcpy(chatMsg, message+2, message[0]-3);
+ chatMsg[message[0]-3] = 0;
+
+ // If the message starts with /me, it will be displayed differently.
+ char converted[257];
+ bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4);
+ const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s ";
+ int size = snprintf(converted+1, 256, template, playerName, chatMsg);
+ if(size>0) {
+ converted[0] = size>255 ? 255 : size;
+ return demo_record(demoBuffer, converted, converted[0]+1);
+ } else {
+ return 0;
+ }
+ } else {
+ return 0; // Malformed chat message is no reason to abort...
+ }
+ } else {
+ return demo_record(demoBuffer, message, message[0]+1);
+ }
+}
+
+int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len) {
+ if(!demoBuffer || (len>0 && !message)) {
+ flib_log_e("Call to flib_demo_record_to_engine with demoBuffer==null or message==null");
+ return -1;
+ }
+ return demo_record(demoBuffer, message, len);
+}
+
+void flib_demo_replace_gamemode(flib_buffer buf, char gamemode) {
+ size_t msgStart = 0;
+ char *data = (char*)buf.data;
+ while(msgStart+2 < buf.size) {
+ if(!memcmp(data+msgStart, "\x02T", 2)) {
+ data[msgStart+2] = gamemode;
+ }
+ msgStart += (uint8_t)data[msgStart]+1;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/demo.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,30 @@
+/**
+ * Demo recording functions. Only used by the ipc game code.
+ */
+
+#ifndef DEMO_H_
+#define DEMO_H_
+
+#include "../buffer.h"
+
+/**
+ * Record a message sent from the engine to the frontend.
+ * Returns 0 for OK, a negative value on error.
+ * Don't pass NULL.
+ */
+int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName);
+
+/**
+ * Record a message sent from the frontend to the engine.
+ * Returns 0 for OK, a negative value on error.
+ * Don't pass NULL.
+ */
+int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len);
+
+/**
+ * Replace game mode messages ("TL", "TD", "TS", "TN") in the recording to mirror
+ * the intended use. Pass 'S' for savegames, 'D' for demos.
+ */
+void flib_demo_replace_gamemode(flib_buffer buf, char gamemode);
+
+#endif /* DEMO_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcconn.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,221 @@
+#include "ipcconn.h"
+#include "../logging.h"
+#include "../socket.h"
+#include "demo.h"
+
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * The receive buffer has to be able to hold any message that might be received. Normally
+ * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
+ * bitmap, 1 for the number of hogs which fit on the map).
+ *
+ * We don't need to worry about wasting a few kb though, and I like powers of two...
+ */
+typedef struct _flib_ipcconn {
+ uint8_t readBuffer[8192];
+ char playerName[256];
+
+ int readBufferSize;
+
+ flib_acceptor acceptor;
+ uint16_t port;
+
+ flib_tcpsocket sock;
+ flib_vector demoBuffer;
+} _flib_ipcconn;
+
+flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) {
+ flib_ipcconn result = malloc(sizeof(_flib_ipcconn));
+ flib_acceptor acceptor = flib_acceptor_create(0);
+
+ if(!result || !acceptor) {
+ flib_log_e("Can't create ipcconn.");
+ free(result);
+ flib_acceptor_close(&acceptor);
+ return NULL;
+ }
+
+ result->acceptor = acceptor;
+ result->sock = NULL;
+ result->readBufferSize = 0;
+ result->port = flib_acceptor_listenport(acceptor);
+
+ if(localPlayerName) {
+ strncpy(result->playerName, localPlayerName, 255);
+ } else {
+ strncpy(result->playerName, "Player", 255);
+ }
+
+ if(recordDemo) {
+ result->demoBuffer = flib_vector_create();
+ }
+
+ flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port);
+ return result;
+}
+
+uint16_t flib_ipcconn_port(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_port with ipc==null");
+ return 0;
+ }
+ return ipc->port;
+}
+
+void flib_ipcconn_destroy(flib_ipcconn *ipcptr) {
+ if(!ipcptr) {
+ flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null");
+ } else if(*ipcptr) {
+ flib_ipcconn ipc = *ipcptr;
+ flib_acceptor_close(&ipc->acceptor);
+ flib_socket_close(&ipc->sock);
+ flib_vector_destroy(&ipc->demoBuffer);
+ free(ipc);
+ *ipcptr = NULL;
+ }
+}
+
+IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_state with ipc==null");
+ return IPC_NOT_CONNECTED;
+ } else if(ipc->sock) {
+ return IPC_CONNECTED;
+ } else if(ipc->acceptor) {
+ return IPC_LISTENING;
+ } else {
+ return IPC_NOT_CONNECTED;
+ }
+}
+
+static bool isMessageReady(flib_ipcconn ipc) {
+ return ipc->readBufferSize >= ipc->readBuffer[0]+1;
+}
+
+static void receiveToBuffer(flib_ipcconn ipc) {
+ if(ipc->sock) {
+ int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
+ if(size>=0) {
+ ipc->readBufferSize += size;
+ } else {
+ flib_socket_close(&ipc->sock);
+ }
+ }
+}
+
+int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null");
+ return -1;
+ }
+
+ if(!isMessageReady(ipc)) {
+ receiveToBuffer(ipc);
+ }
+
+ if(isMessageReady(ipc)) {
+ if(ipc->demoBuffer) {
+ if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) {
+ flib_log_w("Stopping demo recording due to an error.");
+ flib_vector_destroy(&ipc->demoBuffer);
+ }
+ }
+ int msgsize = ipc->readBuffer[0]+1;
+ memcpy(data, ipc->readBuffer, msgsize);
+ memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
+ ipc->readBufferSize -= msgsize;
+ return msgsize;
+ } else if(!ipc->sock && ipc->readBufferSize>0) {
+ flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1);
+ ipc->readBufferSize = 0;
+ return -1;
+ } else {
+ return -1;
+ }
+}
+
+int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data) {
+ if(!ipc || !data) {
+ flib_log_e("Call to flib_ipcconn_recv_map with ipc==null or data==null");
+ return -1;
+ }
+
+ receiveToBuffer(ipc);
+
+ if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) {
+ memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES);
+ memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES);
+ return IPCCONN_MAPMSG_BYTES;
+ } else {
+ return -1;
+ }
+}
+
+int flib_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len) {
+ if(!ipc || (!data && len>0)) {
+ flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null");
+ return -1;
+ }
+ if(!ipc->sock) {
+ flib_log_w("flib_ipcconn_send_raw: Not connected.");
+ return -1;
+ }
+
+ if(flib_socket_send(ipc->sock, data, len) == len) {
+ if(ipc->demoBuffer) {
+ if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) {
+ flib_log_w("Stopping demo recording due to an error.");
+ flib_vector_destroy(&ipc->demoBuffer);
+ }
+ }
+ return 0;
+ } else {
+ flib_log_w("Failed or incomplete ICP write: engine connection lost.");
+ flib_socket_close(&ipc->sock);
+ return -1;
+ }
+}
+
+int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) {
+ if(!ipc || (!data && len>0) || len>255) {
+ flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255");
+ return -1;
+ }
+
+ uint8_t sendbuf[256];
+ sendbuf[0] = len;
+ memcpy(sendbuf+1, data, len);
+
+ return flib_ipcconn_send_raw(ipc, sendbuf, len+1);
+}
+
+int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
+ return flib_ipcconn_send_message(ipc, data, strlen(data));
+}
+
+void flib_ipcconn_accept(flib_ipcconn ipc) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_accept with ipc==null");
+ } else if(!ipc->sock && ipc->acceptor) {
+ ipc->sock = flib_socket_accept(ipc->acceptor, true);
+ if(ipc->sock) {
+ flib_acceptor_close(&ipc->acceptor);
+ }
+ }
+}
+
+flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) {
+ if(!ipc) {
+ flib_log_e("Call to flib_ipcconn_getrecord with ipc==null");
+ }
+ if(!ipc || !ipc->demoBuffer) {
+ flib_constbuffer result = {NULL, 0};
+ return result;
+ }
+ flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D');
+ return flib_vector_as_constbuffer(ipc->demoBuffer);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcconn.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,106 @@
+/*
+ * Low-level protocol support for the IPC connection to the engine.
+ */
+
+#ifndef IPCCONN_H_
+#define IPCCONN_H_
+
+#include "../buffer.h"
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define IPCCONN_MAPMSG_BYTES 4097
+
+typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState;
+
+struct _flib_ipcconn;
+typedef struct _flib_ipcconn *flib_ipcconn;
+
+/**
+ * Start an engine connection by listening on a random port. The selected port can
+ * be queried with flib_ipcconn_port and has to be passed to the engine.
+ *
+ * The parameter "recordDemo" can be used to control whether demo recording should
+ * be enabled for this connection. The localPlayerName is needed for demo
+ * recording purposes.
+ *
+ * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy.
+ *
+ * We stop accepting new connections once a connection has been established, so you
+ * need to create a new ipcconn in order to start a new connection.
+ */
+flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName);
+
+uint16_t flib_ipcconn_port(flib_ipcconn ipc);
+
+/**
+ * Free resources, close sockets, and set the pointer to NULL.
+ */
+void flib_ipcconn_destroy(flib_ipcconn *ipcptr);
+
+/**
+ * Determine the current connection state
+ */
+IpcConnState flib_ipcconn_state(flib_ipcconn ipc);
+
+/**
+ * Receive a single message (up to 256 bytes) and copy it into the data buffer.
+ * Returns the length of the received message, a negative value if no message could
+ * be read.
+ *
+ * The first byte of a message is its content length, which is one less than the returned
+ * value.
+ *
+ * Note: When a connection is closed, you probably want to call this function until
+ * no further message is returned, to ensure you see all messages that were sent
+ * before the connection closed.
+ */
+int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data);
+
+/**
+ * Try to receive 4097 bytes. This is the size of the reply the engine sends
+ * when successfully queried for map data. The first 4096 bytes are a bit-packed
+ * twocolor image of the map (256x128), the last byte is the number of hogs that
+ * fit on the map.
+ */
+int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data);
+
+int flib_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len);
+
+/**
+ * Write a single message (up to 255 bytes) to the engine. This call blocks until the
+ * message is completely written or the connection is closed or an error occurs.
+ *
+ * Calling this function in a state other than IPC_CONNECTED will fail immediately.
+ * Returns a negative value on failure.
+ */
+int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len);
+
+/**
+ * Convenience function for sending a 0-delimited string.
+ */
+int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data);
+
+/**
+ * Call regularly to allow background work to proceed
+ */
+void flib_ipcconn_accept(flib_ipcconn ipc);
+
+/**
+ * Get a record of the connection. This should be called after
+ * the connection is closed and all messages have been received.
+ *
+ * If demo recording was not enabled, or if the recording failed for some reason,
+ * the buffer will be empty.
+ *
+ * If save=true is passed, the result will be a savegame, otherwise it will be a
+ * demo.
+ *
+ * The buffer is only valid until flib_ipcconn_getsave is called again or the ipcconn
+ * is destroyed.
+ */
+flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save);
+
+#endif /* IPCCONN_H_ */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,96 @@
+#include "ipcprotocol.h"
+#include "../util.h"
+#include "../logging.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+int flib_ipc_append_message(flib_vector vec, const char *fmt, ...) {
+ int result = -1;
+ if(!vec || !fmt) {
+ flib_log_e("null parameter in flib_ipc_appendmessage");
+ } else {
+ // 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf
+ char msgbuffer[257];
+
+ // Format the message, leaving one byte at the start for the length
+ va_list argp;
+ va_start(argp, fmt);
+ int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp);
+ va_end(argp);
+
+ if(msgSize > 255) {
+ flib_log_e("Message too long (%u bytes) in flib_ipc_appendmessage", (unsigned)msgSize);
+ } else if(msgSize<0) {
+ flib_log_e("printf error in flib_ipc_appendmessage");
+ } else {
+ // Add the length prefix
+ ((uint8_t*)msgbuffer)[0] = msgSize;
+
+ // Append it to the vector
+ if(flib_vector_append(vec, msgbuffer, msgSize+1) == msgSize+1) {
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
+int flib_ipc_append_mapconf(flib_vector vec, flib_map *map, bool mappreview) {
+ int result = -1;
+ flib_vector tempvector = flib_vector_create();
+ if(!vec || !map) {
+ flib_log_e("null parameter in flib_ipc_append_mapconf");
+ } else if(tempvector) {
+ bool error = false;
+
+ if(map->mapgen == MAPGEN_NAMED) {
+ error |= flib_ipc_append_message(tempvector, "emap %s", map->name);
+ }
+ if(map->theme && !mappreview) {
+ error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme);
+ }
+ error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
+ error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen);
+
+ if(map->mapgen == MAPGEN_MAZE) {
+ error |= flib_ipc_append_message(tempvector, "e$maze_size %i", map->mazeSize);
+ }
+ if(map->mapgen == MAPGEN_DRAWN) {
+ /*
+ * We have to split the drawn map data into several edraw messages here because
+ * it can be longer than the maximum message size.
+ */
+ const char *edraw = "edraw ";
+ int edrawlen = strlen(edraw);
+ for(int offset=0; offset<map->drawDataSize; offset+=200) {
+ int bytesRemaining = map->drawDataSize-offset;
+ int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200;
+ uint8_t messagesize = edrawlen + fragmentsize;
+ error |= (flib_vector_append(tempvector, &messagesize, 1) != 1);
+ error |= (flib_vector_append(tempvector, edraw, edrawlen) != edrawlen);
+ error |= (flib_vector_append(tempvector, map->drawData+offset, fragmentsize) != fragmentsize);
+ }
+ }
+
+ if(!error) {
+ // Message created, now we can copy everything.
+ flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+ if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+ result = 0;
+ }
+ }
+ }
+ flib_vector_destroy(&tempvector);
+ return result;
+}
+
+int flib_ipc_append_seed(flib_vector vec, const char *seed) {
+ if(!vec || !seed) {
+ flib_log_e("null parameter in flib_ipc_append_seed");
+ return -1;
+ } else {
+ return flib_ipc_append_message(vec, "eseed %s", seed);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/ipcprotocol.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,38 @@
+#ifndef IPCPROTOCOL_H_
+#define IPCPROTOCOL_H_
+
+#include "../buffer.h"
+#include "../model/map.h"
+
+#include <stdbool.h>
+
+/**
+ * Create a message in the IPC protocol format and add it to
+ * the vector. Use a format string and extra parameters as with printf.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_message(flib_vector vec, const char *fmt, ...);
+
+/**
+ * Append IPC messages to the buffer that configure the engine for
+ * this map.
+ *
+ * Unfortunately the engine needs a slightly different configuration
+ * for generating a map preview.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_mapconf(flib_vector vec, flib_map *map, bool mappreview);
+
+/**
+ * Append a seed message to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_seed(flib_vector vec, const char *seed);
+
+#endif /* IPCPROTOCOL_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,174 @@
+#include "mapconn.h"
+#include "ipcconn.h"
+#include "ipcprotocol.h"
+
+#include "../logging.h"
+#include "../buffer.h"
+
+#include <stdlib.h>
+
+typedef enum {
+ AWAIT_CONNECTION,
+ AWAIT_REPLY,
+ FINISHED
+} mapconn_progress;
+
+struct _flib_mapconn {
+ uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
+ flib_ipcconn connection;
+ flib_vector configBuffer;
+
+ mapconn_progress progress;
+
+ void (*onSuccessCb)(void*, const uint8_t*, int);
+ void *onSuccessCtx;
+
+ void (*onFailureCb)(void*, const char*);
+ void *onFailureCtx;
+
+ bool running;
+ bool destroyRequested;
+};
+
+static void noop_handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {}
+static void noop_handleFailure(void *context, const char *errormessage) {}
+
+static void clearCallbacks(flib_mapconn *conn) {
+ conn->onSuccessCb = &noop_handleSuccess;
+ conn->onFailureCb = &noop_handleFailure;
+}
+
+static flib_vector createConfigBuffer(char *seed, flib_map *mapdesc) {
+ flib_vector result = NULL;
+ flib_vector tempbuffer = flib_vector_create();
+ if(tempbuffer) {
+ bool error = false;
+ error |= flib_ipc_append_seed(tempbuffer, seed);
+ error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true);
+ error |= flib_ipc_append_message(tempbuffer, "!");
+ if(!error) {
+ result = tempbuffer;
+ tempbuffer = NULL;
+ }
+ }
+ flib_vector_destroy(&tempbuffer);
+ return result;
+}
+
+flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc) {
+ flib_mapconn *result = NULL;
+ flib_mapconn *tempConn = calloc(1, sizeof(flib_mapconn));
+ if(tempConn) {
+ tempConn->connection = flib_ipcconn_create(false, "Player");
+ tempConn->configBuffer = createConfigBuffer(seed, mapdesc);
+ if(tempConn->connection && tempConn->configBuffer) {
+ tempConn->progress = AWAIT_CONNECTION;
+ clearCallbacks(tempConn);
+ result = tempConn;
+ tempConn = NULL;
+ }
+ }
+ flib_mapconn_destroy(tempConn);
+ return result;
+}
+
+void flib_mapconn_destroy(flib_mapconn *conn) {
+ if(conn) {
+ if(conn->running) {
+ /*
+ * The function was called from a callback, so the tick function is still running
+ * and we delay the actual destruction. We ensure no further callbacks will be
+ * sent to prevent surprises.
+ */
+ clearCallbacks(conn);
+ conn->destroyRequested = true;
+ } else {
+ flib_ipcconn_destroy(&conn->connection);
+ flib_vector_destroy(&conn->configBuffer);
+ free(conn);
+ }
+ }
+}
+
+int flib_mapconn_getport(flib_mapconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_getport");
+ return 0;
+ } else {
+ return flib_ipcconn_port(conn->connection);
+ }
+}
+
+void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_onSuccess");
+ } else {
+ conn->onSuccessCb = callback ? callback : &noop_handleSuccess;
+ conn->onSuccessCtx = context;
+ }
+}
+
+void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_onError");
+ } else {
+ conn->onFailureCb = callback ? callback : &noop_handleFailure;
+ conn->onFailureCtx = context;
+ }
+}
+
+static void flib_mapconn_wrappedtick(flib_mapconn *conn) {
+ if(conn->progress == AWAIT_CONNECTION) {
+ flib_ipcconn_accept(conn->connection);
+ switch(flib_ipcconn_state(conn->connection)) {
+ case IPC_CONNECTED:
+ {
+ flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+ if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine.");
+ return;
+ } else {
+ conn->progress = AWAIT_REPLY;
+ }
+ }
+ break;
+ case IPC_NOT_CONNECTED:
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onFailureCtx, "Engine connection closed unexpectedly.");
+ return;
+ default:
+ break;
+ }
+ }
+
+ if(conn->progress == AWAIT_REPLY) {
+ if(flib_ipcconn_recv_map(conn->connection, conn->mapBuffer) >= 0) {
+ conn->progress = FINISHED;
+ conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCCONN_MAPMSG_BYTES-1]);
+ return;
+ } else if(flib_ipcconn_state(conn->connection) != IPC_CONNECTED) {
+ conn->progress = FINISHED;
+ conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly.");
+ return;
+ }
+ }
+}
+
+void flib_mapconn_tick(flib_mapconn *conn) {
+ if(!conn) {
+ flib_log_e("null parameter in flib_mapconn_tick");
+ } else if(conn->running) {
+ flib_log_w("Call to flib_mapconn_tick from a callback");
+ } else if(conn->progress == FINISHED) {
+ flib_log_w("Call to flib_mapconn_tick, but we are already done. Best destroy your flib_mapconn object in the callbacks.");
+ } else {
+ conn->running = true;
+ flib_mapconn_wrappedtick(conn);
+ conn->running = false;
+
+ if(conn->destroyRequested) {
+ flib_mapconn_destroy(conn);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/mapconn.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,77 @@
+#ifndef IPC_MAPCONN_H_
+#define IPC_MAPCONN_H_
+
+#include "../model/map.h"
+#include <stdint.h>
+
+#define MAPIMAGE_WIDTH 256
+#define MAPIMAGE_HEIGHT 128
+#define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT)
+
+struct _flib_mapconn;
+typedef struct _flib_mapconn flib_mapconn;
+
+/**
+ * Start a new map rendering connection (mapconn). This means a listening socket
+ * will be started on a random unused port, waiting for a connection from the
+ * engine process. Once this connection is established, the required information
+ * will be sent to the engine, and the reply is read.
+ *
+ * No NULL parameters allowed, returns NULL on failure.
+ * Use flib_mapconn_destroy to free the returned object.
+ */
+flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc);
+
+/**
+ * Destroy the mapconn object. Passing NULL is allowed and does nothing.
+ * flib_mapconn_destroy may be called from inside a callback function.
+ */
+void flib_mapconn_destroy(flib_mapconn *conn);
+
+/**
+ * Returns the port on which the mapconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_mapconn_getport(flib_mapconn *conn);
+
+/**
+ * Set a callback which will receive the rendered map if the rendering succeeds.
+ * You can pass callback=NULL to unset a callback.
+ *
+ * Expected callback signature:
+ * void handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs)
+ *
+ * The context passed to the callback is the same pointer you provided when
+ * registering the callback. bitmap is a pointer to a buffer of size MAPIMAGE_BYTES
+ * containing a bit-packed image of size MAPIMAGE_WIDTH * MAPIMAGE_HEIGHT.
+ * numHedgehogs is the number of hogs that fit on this map.
+ *
+ * The bitmap pointer passed to the callback belongs to the caller,
+ * so it should not be stored elsewhere. Note that it remains valid
+ * inside the callback method even if flib_mapconn_destroy is called.
+ */
+void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context);
+
+/**
+ * Set a callback which will receive an error message if rendering fails.
+ * You can pass callback=NULL to unset a callback.
+ *
+ * Expected callback signature:
+ * void handleFailure(void *context, const char *errormessage)
+ *
+ * The context passed to the callback is the same pointer you provided when
+ * registering the callback.
+ *
+ * The error message passed to the callback belongs to the caller,
+ * so it should not be stored elsewhere. Note that it remains valid
+ * inside the callback method even if flib_mapconn_destroy is called.
+ */
+void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_mapconn_tick(flib_mapconn *conn);
+
+#endif
--- a/project_files/frontlib/ipcconn.c Thu Jun 07 02:45:18 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-#include "ipcconn.h"
-#include "logging.h"
-#include "socket.h"
-#include "demo.h"
-
-#include <string.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-/*
- * The receive buffer has to be able to hold any message that might be received. Normally
- * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
- * bitmap, 1 for the number of hogs which fit on the map).
- *
- * We don't need to worry about wasting a few kb though, and I like powers of two...
- */
-typedef struct _flib_ipcconn {
- uint8_t readBuffer[8192];
- char playerName[256];
-
- int readBufferSize;
-
- flib_acceptor acceptor;
- uint16_t port;
-
- flib_tcpsocket sock;
- flib_vector demoBuffer;
-} _flib_ipcconn;
-
-flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) {
- flib_ipcconn result = malloc(sizeof(_flib_ipcconn));
- flib_acceptor acceptor = flib_acceptor_create(0);
-
- if(!result || !acceptor) {
- flib_log_e("Can't create ipcconn.");
- free(result);
- flib_acceptor_close(&acceptor);
- return NULL;
- }
-
- result->acceptor = acceptor;
- result->sock = NULL;
- result->readBufferSize = 0;
- result->port = flib_acceptor_listenport(acceptor);
-
- if(localPlayerName) {
- strncpy(result->playerName, localPlayerName, 255);
- } else {
- strncpy(result->playerName, "Player", 255);
- }
-
- if(recordDemo) {
- result->demoBuffer = flib_vector_create();
- }
-
- flib_log_i("Started listening for IPC connections on port %u", result->port);
- return result;
-}
-
-uint16_t flib_ipcconn_port(flib_ipcconn ipc) {
- if(!ipc) {
- flib_log_e("Call to flib_ipcconn_port with ipc==null");
- return 0;
- }
- return ipc->port;
-}
-
-void flib_ipcconn_destroy(flib_ipcconn *ipcptr) {
- if(!ipcptr) {
- flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null");
- } else if(*ipcptr) {
- flib_ipcconn ipc = *ipcptr;
- flib_acceptor_close(&ipc->acceptor);
- flib_socket_close(&ipc->sock);
- flib_vector_destroy(&ipc->demoBuffer);
- free(ipc);
- *ipcptr = NULL;
- }
-}
-
-IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
- if(!ipc) {
- flib_log_e("Call to flib_ipcconn_state with ipc==null");
- return IPC_NOT_CONNECTED;
- } else if(ipc->sock) {
- return IPC_CONNECTED;
- } else if(ipc->acceptor) {
- return IPC_LISTENING;
- } else {
- return IPC_NOT_CONNECTED;
- }
-}
-
-static bool isMessageReady(flib_ipcconn ipc) {
- return ipc->readBufferSize >= ipc->readBuffer[0]+1;
-}
-
-static void receiveToBuffer(flib_ipcconn ipc) {
- if(ipc->sock) {
- int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
- if(size>=0) {
- ipc->readBufferSize += size;
- } else {
- flib_socket_close(&ipc->sock);
- }
- }
-}
-
-int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
- if(!ipc || !data) {
- flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null");
- return -1;
- }
-
- if(!isMessageReady(ipc)) {
- receiveToBuffer(ipc);
- }
-
- if(isMessageReady(ipc)) {
- if(ipc->demoBuffer) {
- if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) {
- flib_log_w("Stopping demo recording due to an error.");
- flib_vector_destroy(&ipc->demoBuffer);
- }
- }
- int msgsize = ipc->readBuffer[0]+1;
- memcpy(data, ipc->readBuffer, msgsize);
- memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
- ipc->readBufferSize -= msgsize;
- return msgsize;
- } else if(!ipc->sock && ipc->readBufferSize>0) {
- flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize, ipc->readBuffer[0]+1);
- ipc->readBufferSize = 0;
- return -1;
- } else {
- return -1;
- }
-}
-
-int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data) {
- if(!ipc || !data) {
- flib_log_e("Call to flib_ipcconn_recv_map with ipc==null or data==null");
- return -1;
- }
-
- receiveToBuffer(ipc);
-
- if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) {
- memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES);
- memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES);
- return IPCCONN_MAPMSG_BYTES;
- } else {
- return -1;
- }
-}
-
-int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) {
- if(!ipc || (!data && len>0)) {
- flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null");
- return -1;
- }
- if(!ipc->sock) {
- flib_log_w("flib_ipcconn_send_raw: Not connected.");
- return -1;
- }
-
- if(flib_socket_send(ipc->sock, data, len) == len) {
- if(ipc->demoBuffer) {
- if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) {
- flib_log_w("Stopping demo recording due to an error.");
- flib_vector_destroy(&ipc->demoBuffer);
- }
- }
- return 0;
- } else {
- flib_log_w("Failed or incomplete ICP write: engine connection lost.");
- flib_socket_close(&ipc->sock);
- return -1;
- }
-}
-
-int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) {
- if(!ipc || (!data && len>0) || len>255) {
- flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255");
- return -1;
- }
-
- uint8_t sendbuf[256];
- sendbuf[0] = len;
- memcpy(sendbuf+1, data, len);
-
- return flib_ipcconn_send_raw(ipc, sendbuf, len+1);
-}
-
-int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
- return flib_ipcconn_send_message(ipc, data, strlen(data));
-}
-
-void flib_ipcconn_accept(flib_ipcconn ipc) {
- if(!ipc) {
- flib_log_e("Call to flib_ipcconn_accept with ipc==null");
- } else if(!ipc->sock && ipc->acceptor) {
- ipc->sock = flib_socket_accept(ipc->acceptor, true);
- if(ipc->sock) {
- flib_acceptor_close(&ipc->acceptor);
- }
- }
-}
-
-flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) {
- if(!ipc) {
- flib_log_e("Call to flib_ipcconn_getrecord with ipc==null");
- }
- if(!ipc || !ipc->demoBuffer) {
- flib_constbuffer result = {NULL, 0};
- return result;
- }
- flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D');
- return flib_vector_as_constbuffer(ipc->demoBuffer);
-}
--- a/project_files/frontlib/ipcconn.h Thu Jun 07 02:45:18 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * Low-level protocol support for the IPC connection to the engine.
- */
-
-#ifndef IPCCONN_H_
-#define IPCCONN_H_
-
-#include "buffer.h"
-
-#include <stddef.h>
-#include <stdbool.h>
-
-#define IPCCONN_MAPMSG_BYTES 4097
-
-typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState;
-
-struct _flib_ipcconn;
-typedef struct _flib_ipcconn *flib_ipcconn;
-
-/**
- * Start an engine connection by listening on a random port. The selected port can
- * be queried with flib_ipcconn_port and has to be passed to the engine.
- *
- * The parameter "recordDemo" can be used to control whether demo recording should
- * be enabled for this connection. The localPlayerName is needed for demo
- * recording purposes.
- *
- * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy.
- *
- * We stop accepting new connections once a connection has been established, so you
- * need to create a new ipcconn in order to start a new connection.
- */
-flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName);
-
-uint16_t flib_ipcconn_port(flib_ipcconn ipc);
-
-/**
- * Free resources, close sockets, and set the pointer to NULL.
- */
-void flib_ipcconn_destroy(flib_ipcconn *ipcptr);
-
-/**
- * Determine the current connection state
- */
-IpcConnState flib_ipcconn_state(flib_ipcconn ipc);
-
-/**
- * Receive a single message (up to 256 bytes) and copy it into the data buffer.
- * Returns the length of the received message, a negative value if no message could
- * be read.
- *
- * The first byte of a message is its content length, which is one less than the returned
- * value.
- *
- * Note: When a connection is closed, you probably want to call this function until
- * no further message is returned, to ensure you see all messages that were sent
- * before the connection closed.
- */
-int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data);
-
-/**
- * Try to receive 4097 bytes. This is the size of the reply the engine sends
- * when successfully queried for map data. The first 4096 bytes are a bit-packed
- * twocolor image of the map (256x128), the last byte is the number of hogs that
- * fit on the map.
- */
-int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data);
-
-int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len);
-
-/**
- * Write a single message (up to 255 bytes) to the engine. This call blocks until the
- * message is completely written or the connection is closed or an error occurs.
- *
- * Calling this function in a state other than IPC_CONNECTED will fail immediately.
- * Returns a negative value on failure.
- */
-int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len);
-
-/**
- * Convenience function for sending a 0-delimited string.
- */
-int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data);
-
-/**
- * Call regularly to allow background work to proceed
- */
-void flib_ipcconn_accept(flib_ipcconn ipc);
-
-/**
- * Get a record of the connection. This should be called after
- * the connection is closed and all messages have been received.
- *
- * If demo recording was not enabled, or if the recording failed for some reason,
- * the buffer will be empty.
- *
- * If save=true is passed, the result will be a savegame, otherwise it will be a
- * demo.
- *
- * The buffer is only valid until flib_ipcconn_getsave is called again or the ipcconn
- * is destroyed.
- */
-flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save);
-
-#endif /* IPCCONN_H_ */
-
--- a/project_files/frontlib/logging.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/logging.c Fri Jun 08 19:52:24 2012 +0200
@@ -7,7 +7,7 @@
char* flib_format_ip(uint32_t numip) {
static char ip[16];
- snprintf(ip, 16, "%u.%u.%u.%u", numip>>24, (numip>>16)&0xff, (numip>>8)&0xff, numip&0xff);
+ snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
return ip;
}
--- a/project_files/frontlib/model/cfg.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/model/cfg.c Fri Jun 08 19:52:24 2012 +0200
@@ -4,6 +4,7 @@
#include "../iniparser/dictionary.h"
#include "../ini/inihelper.h"
#include "../logging.h"
+#include "../util.h"
#include <stdio.h>
@@ -56,7 +57,7 @@
}
bool error = false;
- result->settings[i].iniName = inihelper_strdupnull(sectionName);
+ result->settings[i].iniName = flib_strdupnull(sectionName);
result->settings[i].title = inihelper_getstringdup(settingfile, &error, sectionName, "title");
result->settings[i].engineCommand = inihelper_getstringdup(settingfile, &error, sectionName, "command");
result->settings[i].image = inihelper_getstringdup(settingfile, &error, sectionName, "image");
@@ -78,7 +79,7 @@
}
bool error = false;
- result->mods[i].iniName = inihelper_strdupnull(sectionName);
+ result->mods[i].iniName = flib_strdupnull(sectionName);
result->mods[i].bitmaskIndex = inihelper_getint(modfile, &error, sectionName, "bitmaskIndex");
if(error) {
flib_log_e("Missing or malformed ini parameter in file %s, section %s", modpath, sectionName);
@@ -109,7 +110,7 @@
result->modCount = meta->modCount;
result->settingCount = meta->settingCount;
- result->schemeName = inihelper_strdupnull(schemeName);
+ result->schemeName = flib_strdupnull(schemeName);
result->mods = calloc(meta->modCount, sizeof(*result->mods));
result->settings = calloc(meta->settingCount, sizeof(*result->settings));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,95 @@
+#include "map.h"
+
+#include "../ini/inihelper.h"
+#include "../util.h"
+#include "../logging.h"
+
+#include <stdlib.h>
+
+flib_map *flib_map_create_regular(const char *theme, int templateFilter) {
+ flib_map *result = NULL;
+ if(!theme) {
+ flib_log_e("null parameter in flib_map_create_regular");
+ } else {
+ flib_map *newmap = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ newmap->mapgen = MAPGEN_REGULAR;
+ newmap->templateFilter = templateFilter;
+ newmap->theme = flib_strdupnull(theme);
+ if(newmap->theme) {
+ result = newmap;
+ newmap = NULL;
+ }
+ }
+ flib_map_destroy(newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_maze(const char *theme, int mazeSize) {
+ flib_map *result = NULL;
+ if(!theme) {
+ flib_log_e("null parameter in flib_map_create_maze");
+ } else {
+ flib_map *newmap = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ newmap->mapgen = MAPGEN_MAZE;
+ newmap->mazeSize = mazeSize;
+ newmap->theme = flib_strdupnull(theme);
+ if(newmap->theme) {
+ result = newmap;
+ newmap = NULL;
+ }
+ }
+ flib_map_destroy(newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_named(const char *name) {
+ flib_map *result = NULL;
+ if(!name) {
+ flib_log_e("null parameter in flib_map_create_named");
+ } else {
+ flib_map *newmap = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ newmap->mapgen = MAPGEN_NAMED;
+ newmap->name = flib_strdupnull(name);
+ if(newmap->name) {
+ result = newmap;
+ newmap = NULL;
+ }
+ }
+ flib_map_destroy(newmap);
+ }
+ return result;
+}
+
+flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize) {
+ flib_map *result = NULL;
+ if(!theme || !drawData) {
+ flib_log_e("null parameter in flib_map_create_named");
+ } else {
+ flib_map *newmap = calloc(1, sizeof(flib_map));
+ if(newmap) {
+ newmap->mapgen = MAPGEN_DRAWN;
+ newmap->drawData = flib_bufdupnull(drawData, drawDataSize);
+ newmap->drawDataSize = drawDataSize;
+ if(newmap->drawData) {
+ result = newmap;
+ newmap = NULL;
+ }
+ }
+ flib_map_destroy(newmap);
+ }
+ return result;
+}
+
+void flib_map_destroy(flib_map *map) {
+ if(map) {
+ free(map->drawData);
+ free(map->name);
+ free(map->theme);
+ free(map);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/map.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,87 @@
+/**
+ * Data structure for defining a map. Note that most maps also depend on the
+ * random seed passed to the engine, if you store that in addition to the
+ * flib_map structure you have the whole recipe to exactly recreate a particular
+ * map. For named maps, you also need the corresponding files.
+ */
+
+#ifndef MODEL_MAP_H_
+#define MODEL_MAP_H_
+
+#include <stdint.h>
+
+#define MAPGEN_REGULAR 0
+#define MAPGEN_MAZE 1
+#define MAPGEN_DRAWN 2
+#define MAPGEN_NAMED 3
+
+#define TEMPLATEFILTER_ALL 0
+#define TEMPLATEFILTER_SMALL 1
+#define TEMPLATEFILTER_MEDIUM 2
+#define TEMPLATEFILTER_LARGE 3
+#define TEMPLATEFILTER_CAVERN 4
+#define TEMPLATEFILTER_WACKY 5
+
+#define MAZE_SIZE_SMALL_TUNNELS 0
+#define MAZE_SIZE_MEDIUM_TUNNELS 1
+#define MAZE_SIZE_LARGE_TUNNELS 2
+#define MAZE_SIZE_SMALL_ISLANDS 3
+#define MAZE_SIZE_MEDIUM_ISLANDS 4
+#define MAZE_SIZE_LARGE_ISLANDS 5
+
+typedef struct {
+ int mapgen; // Always one of the MAPGEN_ constants
+ char *theme; // Used for all except MAPGEN_NAMED
+ char *name; // Used for MAPGEN_NAMED
+ uint8_t *drawData; // Used for MAPGEN_DRAWN
+ int drawDataSize; // Used for MAPGEN_DRAWN
+ int templateFilter; // Used for MAPGEN_REGULAR
+ int mazeSize; // Used for MAPGEN_MAZE
+} flib_map;
+
+/**
+ * Create a generated map. theme should be the name of a
+ * directory in "Themes" and templateFilter should be one of the
+ * TEMPLATEFILTER_* constants, but this is not checked before
+ * passing it to the engine.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_regular(const char *theme, int templateFilter);
+
+/**
+ * Create a generated maze-type map. theme should be the name of a
+ * directory in "Themes" and mazeSize should be one of the
+ * MAZE_SIZE_* constants, but this is not checked before
+ * passing it to the engine.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_maze(const char *theme, int mazeSize);
+
+/**
+ * Create a map from the Maps-Directory. name should be the name of a
+ * directory in "Maps", but this is not checked before
+ * passing it to the engine. If this is a mission, the corresponding
+ * script is used automatically.
+ *
+ * Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_named(const char *name);
+
+/**
+ * Create a hand-drawn map. Use flib_map_destroy to free the returned object.
+ * No NULL parameters allowed, returns NULL on failure.
+ */
+flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize);
+
+/**
+ * Free the memory taken up by the map. Passing NULL is allowed and does nothing.
+ */
+void flib_map_destroy(flib_map *map);
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,128 @@
+#include "weapon.h"
+
+#include "../ini/inihelper.h"
+#include "../iniparser/iniparser.h"
+#include "../logging.h"
+#include "../util.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+int set_field(char field[WEAPONS_COUNT+1], const char *line, bool no9) {
+ // Validate the new string
+ for(int i=0; i<WEAPONS_COUNT && line[i]; i++) {
+ if(line[i] < '0' || line[i] > '9' || (no9 && line[i] == '9')) {
+ flib_log_e("Invalid character in weapon config string \"%.*s\", position %i", WEAPONS_COUNT, line, i);
+ return -1;
+ }
+ }
+
+ bool lineEnded = false;
+ for(int i=0; i<WEAPONS_COUNT; i++) {
+ if(!lineEnded && !line[i]) {
+ flib_log_w("Incomplete weapon config line \"%s\", filling with zeroes.", line);
+ lineEnded = true;
+ }
+ if(lineEnded) {
+ field[i] = '0';
+ } else {
+ field[i] = line[i];
+ }
+ }
+ field[WEAPONS_COUNT] = 0;
+ return 0;
+}
+
+static flib_weaponset *flib_weaponset_create_str(const char *name, const char *loadoutStr, const char *crateProbStr, const char *crateAmmoStr, const char *delayStr) {
+ flib_weaponset *result = NULL;
+ if(!name || !loadoutStr || !crateProbStr || !crateAmmoStr || !delayStr) {
+ flib_log_e("null parameter in flib_weaponset_create_str");
+ } else {
+ flib_weaponset *newSet = calloc(1, sizeof(flib_weaponset));
+ char *nameCopy = flib_strdupnull(name);
+ if(newSet && nameCopy) {
+ newSet->name = nameCopy;
+ nameCopy = NULL;
+ bool error = false;
+ error |= set_field(newSet->loadout, loadoutStr, false);
+ error |= set_field(newSet->crateprob, crateProbStr, false);
+ error |= set_field(newSet->crateammo, crateAmmoStr, false);
+ error |= set_field(newSet->delay, delayStr, false);
+ if(!error) {
+ result = newSet;
+ newSet = NULL;
+ }
+ }
+ free(nameCopy);
+ flib_weaponset_destroy(newSet);
+ }
+ return result;
+}
+
+void flib_weaponset_destroy(flib_weaponset *cfg) {
+ if(cfg) {
+ free(cfg->name);
+ free(cfg);
+ }
+}
+
+flib_weaponset *flib_weaponset_create(const char *name) {
+ return flib_weaponset_create_str(name, AMMOLINE_DEFAULT_QT, AMMOLINE_DEFAULT_PROB, AMMOLINE_DEFAULT_CRATE, AMMOLINE_DEFAULT_DELAY);
+}
+
+flib_weaponset *flib_weaponset_from_ini(const char *filename) {
+ flib_weaponset *result = NULL;
+ if(!filename) {
+ flib_log_e("null parameter in flib_weaponset_from_ini");
+ } else {
+ dictionary *settingfile = iniparser_load(filename);
+ if(!settingfile) {
+ flib_log_e("Error loading weapon scheme file %s", filename);
+ } else {
+ bool error = false;
+ char *name = inihelper_getstring(settingfile, &error, "weaponset", "name");
+ char *loadout = inihelper_getstring(settingfile, &error, "weaponset", "loadout");
+ char *crateprob = inihelper_getstring(settingfile, &error, "weaponset", "crateprob");
+ char *crateammo = inihelper_getstring(settingfile, &error, "weaponset", "crateammo");
+ char *delay = inihelper_getstring(settingfile, &error, "weaponset", "delay");
+ if(error) {
+ flib_log_e("Missing key in weapon scheme file %s", filename);
+ } else {
+ result = flib_weaponset_create_str(name, loadout, crateprob, crateammo, delay);
+ }
+ }
+ iniparser_freedict(settingfile);
+ }
+ return result;
+}
+
+int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set) {
+ int result = -1;
+ if(!filename || !set) {
+ flib_log_e("null parameter in flib_weaponset_to_ini");
+ } else {
+ dictionary *dict = dictionary_new(0);
+ if(dict) {
+ bool error = false;
+ // Add the sections
+ error |= iniparser_set(dict, "weaponset", NULL);
+
+ // Add the values
+ error |= inihelper_setstr(dict, "weaponset", "name", set->name);
+ error |= inihelper_setstr(dict, "weaponset", "loadout", set->loadout);
+ error |= inihelper_setstr(dict, "weaponset", "crateprob", set->crateprob);
+ error |= inihelper_setstr(dict, "weaponset", "crateammo", set->crateammo);
+ error |= inihelper_setstr(dict, "weaponset", "delay", set->delay);
+ if(!error) {
+ FILE *inifile = fopen(filename, "wb");
+ if(inifile) {
+ iniparser_dump_ini(dict, inifile);
+ fclose(inifile);
+ result = 0;
+ }
+ }
+ dictionary_del(dict);
+ }
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/weapon.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,32 @@
+#ifndef MODEL_WEAPON_H_
+#define MODEL_WEAPON_H_
+
+#include "../hwconsts.h"
+
+/**
+ * These values are all in the range 0..9
+ *
+ * For loadout, 9 means inifinite ammo.
+ * For the other setting, 9 might actually be invalid, it's not possible to set more than 8 in the QtFrontend. (TODO)
+ */
+typedef struct {
+ char loadout[WEAPONS_COUNT+1];
+ char crateprob[WEAPONS_COUNT+1];
+ char crateammo[WEAPONS_COUNT+1];
+ char delay[WEAPONS_COUNT+1];
+ char *name;
+} flib_weaponset;
+
+/**
+ * Returns a new weapon set, or NULL on error.
+ * name must not be NULL.
+ *
+ * The new weapon set is pre-filled with default
+ * settings (see hwconsts.h)
+ */
+flib_weaponset *flib_weaponset_create(const char *name);
+flib_weaponset *flib_weaponset_from_ini(const char *filename);
+int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set);
+void flib_weaponset_destroy(flib_weaponset *set);
+
+#endif
--- a/project_files/frontlib/socket.c Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/socket.c Fri Jun 08 19:52:24 2012 +0200
@@ -60,7 +60,7 @@
if(result->sock) {
return result;
} else {
- flib_log_e("Unable to listen on port %u: %s", port, SDLNet_GetError());
+ flib_log_e("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
free(result);
return NULL;
}
@@ -77,7 +77,7 @@
if(result->sock) {
return result;
} else {
- flib_log_w("Unable to listen on port %u: %s", result->port, SDLNet_GetError());
+ flib_log_w("Unable to listen on port %u: %s", (unsigned)result->port, SDLNet_GetError());
}
}
flib_log_e("Unable to listen on a random unused port.");
@@ -155,7 +155,7 @@
}
}
-int flib_socket_send(flib_tcpsocket sock, void *data, int len) {
+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;
--- a/project_files/frontlib/socket.h Thu Jun 07 02:45:18 2012 +0200
+++ b/project_files/frontlib/socket.h Fri Jun 08 19:52:24 2012 +0200
@@ -62,6 +62,6 @@
*/
int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen);
-int flib_socket_send(flib_tcpsocket sock, void *data, int len);
+int flib_socket_send(flib_tcpsocket sock, const void *data, int len);
#endif /* SOCKET_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util.c Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,49 @@
+#include "util.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+char *flib_asprintf(const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ char *result = flib_vasprintf(fmt, argp);
+ va_end(argp);
+ return result;
+}
+
+char *flib_vasprintf(const char *fmt, va_list args) {
+ char *result = NULL;
+ int requiredSize = vsnprintf(NULL, 0, fmt, args)+1; // Figure out how much memory we need,
+ if(requiredSize>=0) {
+ char *tmpbuf = malloc(requiredSize); // allocate it
+ if(tmpbuf) {
+ if(vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) { // and then do the actual formatting.
+ result = tmpbuf;
+ tmpbuf = NULL;
+ }
+ }
+ free(tmpbuf);
+ }
+ return result;
+}
+
+char *flib_strdupnull(const char *str) {
+ if(!str) {
+ return NULL;
+ }
+ return flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+ if(!buf || size==0) {
+ return NULL;
+ }
+ void *result = malloc(size);
+ if(result) {
+ memcpy(result, buf, size);
+ }
+ return result;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util.h Fri Jun 08 19:52:24 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef FLIB_UTIL_H_
+#define FLIB_UTIL_H_
+
+#include <stddef.h>
+#include <stdarg.h>
+
+/**
+ * Prints a format string to a newly allocated buffer of the required size.
+ * Parameters are like those for printf. Returns NULL on error.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_asprintf(const char *fmt, ...);
+
+/**
+ * Exactly as flib_asprintf, but accepts va_args.
+ */
+char *flib_vasprintf(const char *fmt, va_list args);
+
+/**
+ * Return a duplicate of the provided string, or NULL if an error
+ * occurs or if str is already NULL.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_strdupnull(const char *str);
+
+/**
+ * Return a duplicate of the provided buffer, or NULL if an error
+ * occurs or if buf is already NULL or if size is 0.
+ *
+ * Returned buffer must be free()d
+ */
+void *flib_bufdupnull(const void *buf, size_t size);
+
+#endif