Implemented public API for letting the engine render maps
authorMedo <smaxein@googlemail.com>
Fri, 08 Jun 2012 19:52:24 +0200
changeset 7177 bf6cf4dd847a
parent 7175 038e3415100a
child 7179 f84805e6df03
Implemented public API for letting the engine render maps
project_files/frontlib/demo.c
project_files/frontlib/demo.h
project_files/frontlib/frontlib.c
project_files/frontlib/hwconsts.h
project_files/frontlib/ini/inihelper.c
project_files/frontlib/ini/inihelper.h
project_files/frontlib/ipc.c
project_files/frontlib/ipc.h
project_files/frontlib/ipc/demo.c
project_files/frontlib/ipc/demo.h
project_files/frontlib/ipc/ipcconn.c
project_files/frontlib/ipc/ipcconn.h
project_files/frontlib/ipc/ipcprotocol.c
project_files/frontlib/ipc/ipcprotocol.h
project_files/frontlib/ipc/mapconn.c
project_files/frontlib/ipc/mapconn.h
project_files/frontlib/ipcconn.c
project_files/frontlib/ipcconn.h
project_files/frontlib/logging.c
project_files/frontlib/model/cfg.c
project_files/frontlib/model/map.c
project_files/frontlib/model/map.h
project_files/frontlib/model/weapon.c
project_files/frontlib/model/weapon.h
project_files/frontlib/socket.c
project_files/frontlib/socket.h
project_files/frontlib/util.c
project_files/frontlib/util.h
--- 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