Implemented game launching API for the frontlib.
authorMedo <smaxein@googlemail.com>
Sat, 09 Jun 2012 03:28:38 +0200
changeset 7179 f84805e6df03
parent 7177 bf6cf4dd847a
child 7182 076aba32abd3
Implemented game launching API for the frontlib. It is still buggy though, and not all game settings can be conveniently created/modified yet.
project_files/frontlib/buffer.c
project_files/frontlib/buffer.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/gameconn.c
project_files/frontlib/ipc/gameconn.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/logging.c
project_files/frontlib/logging.h
project_files/frontlib/model/cfg.c
project_files/frontlib/model/gamesetup.c
project_files/frontlib/model/gamesetup.h
project_files/frontlib/model/map.c
project_files/frontlib/model/team.c
project_files/frontlib/model/team.h
project_files/frontlib/model/weapon.c
project_files/frontlib/socket.c
project_files/frontlib/util.c
project_files/frontlib/util.h
project_files/frontlib/util/buffer.c
project_files/frontlib/util/buffer.h
project_files/frontlib/util/inihelper.c
project_files/frontlib/util/inihelper.h
project_files/frontlib/util/logging.c
project_files/frontlib/util/logging.h
project_files/frontlib/util/util.c
project_files/frontlib/util/util.h
--- a/project_files/frontlib/buffer.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#include "buffer.h"
-#include "logging.h"
-
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-typedef struct _flib_vector {
-	void *data;
-	size_t size;
-	size_t capacity;
-} _flib_vector;
-
-flib_vector flib_vector_create() {
-	flib_vector result = malloc(sizeof(_flib_vector));
-	if(result == NULL) {
-		return NULL;
-	}
-	result->data = malloc(16);
-	if(result->data == NULL) {
-		free(result);
-		return NULL;
-	}
-	result->size = 0;
-	result->capacity = 16;
-	return result;
-}
-
-void flib_vector_destroy(flib_vector *vec) {
-	if(vec && *vec) {
-		free((*vec)->data);
-		free(*vec);
-		*vec = NULL;
-	}
-}
-
-static void try_realloc(flib_vector vec, size_t newCapacity) {
-	void *newData = realloc(vec->data, newCapacity);
-	if(newData) {
-		vec->data = newData;
-		vec->capacity = newCapacity;
-	}
-}
-
-static size_t getFreeCapacity(flib_vector vec) {
-	return vec->capacity - vec->size;
-}
-
-int flib_vector_append(flib_vector vec, const void *data, size_t len) {
-	if(getFreeCapacity(vec) < len) {
-		// Resize exponentially for constant amortized time,
-		// But at least by as much as we need of course,
-		// and be extra careful with integer overflows...
-		size_t extraCapacity = (vec->capacity)/2;
-
-		size_t minExtraCapacity = len - getFreeCapacity(vec);
-		if(extraCapacity < minExtraCapacity) {
-			extraCapacity = minExtraCapacity;
-		}
-
-		if(extraCapacity <= SIZE_MAX - vec->capacity) {
-			try_realloc(vec, vec->capacity+extraCapacity);
-		}
-
-		// Check if we were able to resize.
-		// If not, try to allocate at least what we need...
-		if(getFreeCapacity(vec) < len) {
-			try_realloc(vec, vec->capacity+minExtraCapacity);
-
-			// Still not working? Then we fail.
-			if(getFreeCapacity(vec) < len) {
-				return 0;
-			}
-		}
-	}
-
-	memmove(vec->data + vec->size, data, len);
-	vec->size += len;
-	return len;
-}
-
-flib_buffer flib_vector_as_buffer(flib_vector vec) {
-	flib_buffer result = {vec->data, vec->size};
-	return result;
-}
-
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
-	flib_constbuffer result = {vec->data, vec->size};
-	return result;
-}
--- a/project_files/frontlib/buffer.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#ifndef BUFFER_H_
-#define BUFFER_H_
-
-#include <stdint.h>
-#include <stddef.h>
-
-/**
- * A simple struct to hold both the pointer to an array and its size,
- * for e.g. conveniently returning it from a function.
- *
- * Convention: Size is zero iff data is a NULL pointer.
- */
-typedef struct {
-	void *data;
-	size_t size;
-} flib_buffer;
-
-/**
- * Just like flib_buffer, but the contents are not supposed to be modified.
- */
-typedef struct {
-	const void *data;
-	size_t size;
-} flib_constbuffer;
-
-/**
- * Simple variable-capacity data structure (opaque type).
- */
-struct _flib_vector;
-typedef struct _flib_vector *flib_vector;
-
-/**
- * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
- * May return NULL if memory runs out.
- */
-flib_vector flib_vector_create();
-
-/**
- * Free the memory of this vector and set it to NULL.
- */
-void flib_vector_destroy(flib_vector *vec);
-
-/**
- * Append the provided data to the end of the vector, enlarging it as required.
- * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
- * The vector remains unchanged if an out of memory situation occurs.
- */
-int flib_vector_append(flib_vector vec, const void *data, size_t len);
-
-/**
- * Return a buffer or constbuffer pointing to the current contents of the vector.
- * These will become invalid if the vector size or capacity is changed.
- */
-flib_buffer flib_vector_as_buffer(flib_vector vec);
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
-
-
-#endif
--- a/project_files/frontlib/frontlib.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/frontlib.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,8 @@
 #include "frontlib.h"
-#include "logging.h"
+#include "util/logging.h"
 #include "model/map.h"
 #include "ipc/mapconn.h"
-#include "ipc.h"
+#include "ipc/gameconn.h"
 
 #include <SDL.h>
 #include <SDL_net.h>
@@ -41,97 +41,72 @@
 	}
 }
 
-static void onConfigQuery(void *context) {
-	flib_log_i("Sending config...");
-	flib_ipc ipc = (flib_ipc)context;
-	flib_ipc_send_messagestr(ipc, "TL");
-	flib_ipc_send_messagestr(ipc, "eseed loremipsum");
-	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) {
-	flib_log_i("Connection closed.");
-	flib_ipc_destroy((flib_ipc*)context);
-}
-
-static void onGameEnd(void *context, int gameEndType) {
-	switch(gameEndType) {
-	case GAME_END_FINISHED:
-		flib_log_i("Game finished.");
-		flib_constbuffer demobuf = flib_ipc_getdemo(context);
-		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);
-		file = NULL;
-		break;
-	case GAME_END_HALTED:
-		flib_log_i("Game halted.");
-		break;
-	case GAME_END_INTERRUPTED:
-		flib_log_i("Game iterrupted.");
-		break;
-	}
-}
-
-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);
+static void onDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn **connptr = context;
+	flib_gameconn_destroy(*connptr);
 	*connptr = NULL;
 }
 
-static void handleMapFailure(void *context, const char *errormessage) {
-	flib_log_e("Map rendering failed: %s", errormessage);
-
-	flib_mapconn **connptr = context;
-	flib_mapconn_destroy(*connptr);
-	*connptr = NULL;
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+	fwrite(record, 1, size, file);
+	fclose(file);
 }
 
 int main(int argc, char *argv[]) {
-/*	flib_init(0);
-
-	flib_cfg_meta *meta = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
-	flib_cfg *cfg = flib_cfg_create(meta, "DefaultScheme");
-	flib_cfg_to_ini(meta, "defaulttest.ini", cfg);
-
-	flib_cfg_meta_destroy(meta);
-
-	flib_quit();
-	return 0;*/
+	flib_init(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);
+	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
+	assert(metaconf);
+	flib_gamesetup setup;
+	setup.gamescheme = flib_cfg_from_ini(metaconf, "scheme_shoppa.ini");
+	setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+	setup.seed = "apsfooasdgnds";
+	setup.teamcount = 2;
+	setup.teams = calloc(2, sizeof(flib_team));
+	setup.teams[0].color = 0xffff0000;
+	setup.teams[0].flag = "australia";
+	setup.teams[0].fort = "Plane";
+	setup.teams[0].grave = "Bone";
+	setup.teams[0].hogsInGame = 2;
+	setup.teams[0].name = "Team Awesome";
+	setup.teams[0].voicepack = "British";
+	setup.teams[0].weaponset = flib_weaponset_create("Defaultweaps");
+	setup.teams[0].hogs[0].difficulty = 2;
+	setup.teams[0].hogs[0].hat = "NoHat";
+	setup.teams[0].hogs[0].initialHealth = 100;
+	setup.teams[0].hogs[0].name = "Harry 120";
+	setup.teams[0].hogs[1].difficulty = 2;
+	setup.teams[0].hogs[1].hat = "chef";
+	setup.teams[0].hogs[1].initialHealth = 100;
+	setup.teams[0].hogs[1].name = "Chefkoch";
+	setup.teams[1].color = 0xff0000ff;
+	setup.teams[1].flag = "germany";
+	setup.teams[1].fort = "Cake";
+	setup.teams[1].grave = "Cherry";
+	setup.teams[1].hogsInGame = 2;
+	setup.teams[1].name = "The Krauts";
+	setup.teams[1].voicepack = "Pirate";
+	setup.teams[1].weaponset = flib_weaponset_create("Defaultweaps");
+	setup.teams[1].hogs[0].difficulty = 0;
+	setup.teams[1].hogs[0].hat = "quotecap";
+	setup.teams[1].hogs[0].initialHealth = 100;
+	setup.teams[1].hogs[0].name = "Quote";
+	setup.teams[1].hogs[1].difficulty = 0;
+	setup.teams[1].hogs[1].hat = "chef";
+	setup.teams[1].hogs[1].initialHealth = 100;
+	setup.teams[1].hogs[1].name = "Chefkoch2";
 
-	flib_map_destroy(mapconf);
-	mapconf = NULL;
+	flib_gameconn *gameconn = flib_gameconn_create("Medo42", metaconf, &setup, false);
+	assert(gameconn);
 
-	flib_mapconn_onFailure(mapconn, &handleMapFailure, &mapconn);
-	flib_mapconn_onSuccess(mapconn, &handleMapSuccess, &mapconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
 
-	while(mapconn) {
-		flib_mapconn_tick(mapconn);
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
 	}
 	flib_log_i("Shutting down...");
 	flib_quit();
--- a/project_files/frontlib/hwconsts.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/hwconsts.h	Sat Jun 09 03:28:38 2012 +0200
@@ -9,6 +9,8 @@
 #define HEDGEHOGS_PER_TEAM 8
 #define NETGAME_DEFAULT_PORT 46631
 
+#define GAMEMOD_PERHOGAMMO_MASKBIT 22
+#define GAMEMOD_SHAREDAMMO_MASKBIT 16
 
 #define WEAPONS_COUNT 55
 #define AMMOLINE_DEFAULT_QT     "9391929422199121032235111001201000000211110101011111011"
--- a/project_files/frontlib/ini/inihelper.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-#include "inihelper.h"
-#include "../logging.h"
-#include "../util.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdarg.h>
-
-static bool keychar_needs_urlencoding(char c) {
-	return !isalnum(c);
-}
-
-char *inihelper_urlencode(const char *inbuf) {
-	if(!inbuf) {
-		return NULL;
-	}
-	size_t insize = strlen(inbuf);
-	if(insize > SIZE_MAX/4) {
-		return NULL;
-	}
-
-	char *outbuf = malloc(insize*3+1);
-	if(!outbuf) {
-		return NULL;
-	}
-
-    size_t inpos = 0, outpos = 0;
-    while(inbuf[inpos]) {
-        if(!keychar_needs_urlencoding(inbuf[inpos])) {
-        	outbuf[outpos++] = inbuf[inpos++];
-        } else {
-            if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
-            	free(outbuf);
-            	return NULL;
-            }
-            inpos++;
-            outpos += 3;
-        }
-    }
-    outbuf[outpos] = 0;
-    return outbuf;
-}
-
-char *inihelper_urldecode(const char *inbuf) {
-	char *outbuf = malloc(strlen(inbuf)+1);
-	if(!outbuf) {
-		return NULL;
-	}
-
-    size_t inpos = 0, outpos = 0;
-    while(inbuf[inpos]) {
-        if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
-            char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
-            outbuf[outpos++] = strtol(temp, NULL, 16);
-            inpos += 3;
-        } else {
-        	outbuf[outpos++] = inbuf[inpos++];
-        }
-    }
-    outbuf[outpos] = 0;
-    return outbuf;
-}
-
-char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
-	if(!sectionName || !keyName) {
-		return NULL;
-	}
-	return flib_asprintf("%s:%s", sectionName, keyName);
-}
-
-char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	if(!inifile || !sectionName || !keyName) {
-		*error = true;
-		return NULL;
-	}
-	char *extendedkey = inihelper_createDictKey(sectionName, keyName);
-	if(!extendedkey) {
-		*error = true;
-		return NULL;
-	}
-	char *result = iniparser_getstring(inifile, extendedkey, NULL);
-	free(extendedkey);
-	if(!result) {
-		flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
-		*error = true;
-	}
-	return result;
-}
-
-char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
-}
-
-int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
-	if(!value) {
-		return 0;
-	} else {
-		errno = 0;
-		long val = strtol(value, NULL, 10);
-		if(errno!=0) {
-			*error = true;
-			return 0;
-		}
-		if(val<INT_MIN || val>INT_MAX) {
-			*error = true;
-			return 0;
-		}
-		return (int)val;
-	}
-}
-
-bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
-	if(!value) {
-		return false;
-	} else {
-		bool trueval = strchr("1tTyY", value[0]);
-		bool falseval = strchr("0fFnN", value[0]);
-		if(!trueval && !falseval) {
-			*error = true;
-			return false;
-		} else {
-			return trueval;
-		}
-	}
-}
-
-int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName || !value) {
-		flib_log_e("inihelper_setstr called with bad parameters");
-	} else {
-		char *extendedkey = inihelper_createDictKey(sectionName, keyName);
-		if(extendedkey) {
-			result = iniparser_set(dict, extendedkey, value);
-			free(extendedkey);
-		}
-	}
-	return result;
-}
-
-int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName) {
-		flib_log_e("inihelper_setint called with bad parameters");
-	} else {
-		char *strvalue = flib_asprintf("%i", value);
-		if(strvalue) {
-			result = inihelper_setstr(dict, sectionName, keyName, strvalue);
-			free(strvalue);
-		}
-	}
-	return result;
-}
-
-int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName) {
-		flib_log_e("inihelper_setint called with bad parameters");
-	} else {
-		result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
-	}
-	return result;
-}
--- a/project_files/frontlib/ini/inihelper.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Some helper functions for working with the iniparser functions - in particular,
- * for interoperability with the ini format used by the QtSettings class.
- */
-
-#ifndef INIHELPER_H_
-#define INIHELPER_H_
-
-#include "../iniparser/iniparser.h"
-#include <stdbool.h>
-
-/**
- * Returned buffer must be free()d
- */
-char *inihelper_urlencode(const char *inbuf);
-
-/**
- * Returned buffer must be free()d
- */
-char *inihelper_urldecode(const char *inbuf);
-
-/**
- * Create a key in the format "sectionName:keyName"
- * Returned buffer must be free()d
- */
-char *inihelper_createDictKey(const char *sectionName, const char *keyName);
-
-/**
- * Returns an internal buffer, don't modify or free
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Returned buffer must be free()d
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
-int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
-int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
-#endif /* INIHELPER_H_ */
--- a/project_files/frontlib/ipc.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +0,0 @@
-#include "ipc.h"
-#include "ipc/ipcconn.h"
-#include "logging.h"
-
-#include <stdbool.h>
-#include <stdlib.h>
-
-typedef struct _flib_ipc {
-	flib_ipcconn connection;
-	IpcConnState oldConnState;
-
-	void (*onConnectCb)(void*);
-	void *onConnectCtx;
-
-	void (*onDisconnectCb)(void*);
-	void *onDisconnectCtx;
-
-	void (*onConfigQueryCb)(void*);
-	void *onConfigQueryCtx;
-
-	void (*onEngineErrorCb)(void*, const uint8_t*);
-	void *onEngineErrorCtx;
-
-	void (*onGameEndCb)(void*, int);
-	void *onGameEndCtx;
-
-	void (*onChatCb)(void*, const uint8_t*, int);
-	void *onChatCtx;
-
-	void (*onEngineMessageCb)(void*, const uint8_t*, int);
-	void *onEngineMessageCtx;
-
-	bool running;
-	bool destroyRequested;
-} _flib_ipc;
-
-static void emptyCallback(void* ptr) {}
-static void emptyCallback_int(void* ptr, int i) {}
-static void emptyCallback_str(void* ptr, const uint8_t* str) {}
-static void emptyCallback_str_int(void* ptr, const uint8_t* str, int i) {}
-
-static void clearCallbacks(flib_ipc ipc) {
-	ipc->onConnectCb = &emptyCallback;
-	ipc->onDisconnectCb = &emptyCallback;
-	ipc->onConfigQueryCb = &emptyCallback;
-	ipc->onEngineErrorCb = &emptyCallback_str;
-	ipc->onGameEndCb = &emptyCallback_int;
-	ipc->onChatCb = &emptyCallback_str_int;
-	ipc->onEngineMessageCb = &emptyCallback_str_int;
-}
-
-flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName) {
-	flib_ipc result = malloc(sizeof(_flib_ipc));
-	flib_ipcconn connection = flib_ipcconn_create(recordDemo, localPlayerName);
-
-	if(!result || !connection) {
-		free(result);
-		flib_ipcconn_destroy(&connection);
-		return NULL;
-	}
-
-	result->connection = connection;
-	result->oldConnState = IPC_LISTENING;
-	result->running = false;
-	result->destroyRequested = false;
-
-	clearCallbacks(result);
-	return result;
-}
-
-void flib_ipc_destroy(flib_ipc *ipcptr) {
-	if(!ipcptr || !*ipcptr) {
-		return;
-	}
-	flib_ipc ipc = *ipcptr;
-	if(ipc->running) {
-		// The function was called from a callback of this ipc connection,
-		// 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(ipc);
-		ipc->destroyRequested = true;
-	} else {
-		flib_ipcconn_destroy(&ipc->connection);
-		free(ipc);
-	}
-	*ipcptr = NULL;
-}
-
-void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onConnect with ipc==null");
-		return;
-	}
-	ipc->onConnectCb = callback ? callback : &emptyCallback;
-	ipc->onConnectCtx = context;
-}
-
-void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onDisconnect with ipc==null");
-		return;
-	}
-	ipc->onDisconnectCb = callback ? callback : &emptyCallback;
-	ipc->onDisconnectCtx = context;
-}
-
-void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onConfigQuery with ipc==null");
-		return;
-	}
-	ipc->onConfigQueryCb = callback ? callback : &emptyCallback;
-	ipc->onConfigQueryCtx = context;
-}
-
-void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onEngineError with ipc==null");
-		return;
-	}
-	ipc->onEngineErrorCb = callback ? callback : &emptyCallback_str;
-	ipc->onEngineErrorCtx = context;
-}
-
-void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onGameEnd with ipc==null");
-		return;
-	}
-	ipc->onGameEndCb = callback ? callback : &emptyCallback_int;
-	ipc->onGameEndCtx = context;
-}
-
-void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onChat with ipc==null");
-		return;
-	}
-	ipc->onChatCb = callback ? callback : &emptyCallback_str_int;
-	ipc->onChatCtx = context;
-}
-
-void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onEngineMessage with ipc==null");
-		return;
-	}
-	ipc->onEngineMessageCb = callback ? callback : &emptyCallback_str_int;
-	ipc->onEngineMessageCtx = context;
-}
-
-static void flib_ipc_wrappedtick(flib_ipc ipc) {
-	if(ipc->oldConnState == IPC_NOT_CONNECTED) {
-		return;
-	}
-
-	if(ipc->oldConnState == IPC_LISTENING) {
-		flib_ipcconn_accept(ipc->connection);
-		if(flib_ipcconn_state(ipc->connection) == IPC_CONNECTED) {
-			ipc->oldConnState = IPC_CONNECTED;
-			ipc->onConnectCb(ipc->onConnectCtx);
-		}
-	}
-
-	if(ipc->oldConnState == IPC_CONNECTED) {
-		uint8_t msgbuffer[257];
-		int len;
-		while(!ipc->destroyRequested && (len = flib_ipcconn_recv_message(ipc->connection, msgbuffer))>=0) {
-			if(len<2) {
-				flib_log_w("Received short message from IPC (<2 bytes)");
-				continue;
-			}
-			msgbuffer[len] = 0;
-			flib_log_i("[IPC in] %s", msgbuffer+1);
-			switch(msgbuffer[1]) {
-			case '?':
-				flib_ipcconn_send_messagestr(ipc->connection, "!");
-				break;
-			case 'C':
-				ipc->onConfigQueryCb(ipc->onConfigQueryCtx);
-				break;
-			case 'E':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onEngineErrorCb(ipc->onEngineErrorCtx, msgbuffer+2);
-				}
-				break;
-			case 'i':
-				// TODO
-				break;
-			case 'Q':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_INTERRUPTED);
-				break;
-			case 'q':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_FINISHED);
-				break;
-			case 'H':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_HALTED);
-				break;
-			case 's':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 0);
-				}
-				break;
-			case 'b':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 1);
-				}
-				break;
-			default:
-				ipc->onEngineMessageCb(ipc->onEngineMessageCtx, msgbuffer, len);
-				break;
-			}
-		}
-	}
-
-	if(flib_ipcconn_state(ipc->connection) == IPC_NOT_CONNECTED) {
-		ipc->oldConnState = IPC_NOT_CONNECTED;
-		ipc->onDisconnectCb(ipc->onDisconnectCtx);
-	}
-}
-
-void flib_ipc_tick(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_tick with ipc==null");
-		return;
-	}
-	if(ipc->running) {
-		flib_log_w("Call to flib_ipc_tick from a callback");
-		return;
-	}
-
-	ipc->running = true;
-	flib_ipc_wrappedtick(ipc);
-	ipc->running = false;
-
-	if(ipc->destroyRequested) {
-		flib_ipc_destroy(&ipc);
-	}
-}
-
-int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_raw with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_raw(ipc->connection, data, len);
-}
-
-int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_message with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_message(ipc->connection, data, len);
-}
-
-int flib_ipc_send_messagestr(flib_ipc ipc, char *data) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_messagestr(ipc->connection, data);
-}
-
-uint16_t flib_ipc_port(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		return 0;
-	}
-	return flib_ipcconn_port(ipc->connection);
-}
-
-flib_constbuffer flib_ipc_getdemo(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		flib_constbuffer result = {NULL, 0};
-		return result;
-	}
-	return flib_ipcconn_getrecord(ipc->connection, false);
-}
--- a/project_files/frontlib/ipc.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#ifndef IPC_H_
-#define IPC_H_
-
-#include "buffer.h"
-#include "model/weapon.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-struct _flib_ipc;
-typedef struct _flib_ipc *flib_ipc;
-
-typedef enum {
-	GAME_END_FINISHED,
-	GAME_END_INTERRUPTED,
-	GAME_END_HALTED
-} flib_GameEndType;
-
-flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName);
-void flib_ipc_destroy(flib_ipc *ipcptr);
-
-void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context);
-void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context);
-void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context);
-void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context);
-
-int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len);
-int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len);
-int flib_ipc_send_messagestr(flib_ipc ipc, char *data);
-
-// Configuration
-int flib_ipc_send_seed(flib_ipc ipc, const char *seed);
-int flib_ipc_send_script(flib_ipc ipc, const char *scriptpath);
-int flib_ipc_send_map_regular(flib_ipc ipc, const char *theme, int templateFilter);
-int flib_ipc_send_map_maze(flib_ipc ipc, const char *theme, int mazeType);
-int flib_ipc_send_map_drawn(flib_ipc ipc, const char *theme, void *drawnMapData, size_t drawnMapDataLen);
-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_weaponset(flib_ipc ipc, flib_weaponset *set);
-
-int flib_ipc_send_conf_end(flib_ipc ipc);
-
-
-uint16_t flib_ipc_port(flib_ipc ipc);
-flib_constbuffer flib_ipc_getdemo(flib_ipc ipc);
-
-void flib_ipc_tick(flib_ipc ipc);
-
-#endif /* IPC_H_ */
--- a/project_files/frontlib/ipc/demo.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/demo.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,5 +1,5 @@
 #include "demo.h"
-#include "../logging.h"
+#include "../util/logging.h"
 
 #include <stdbool.h>
 #include <stdio.h>
--- a/project_files/frontlib/ipc/demo.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/demo.h	Sat Jun 09 03:28:38 2012 +0200
@@ -5,7 +5,7 @@
 #ifndef DEMO_H_
 #define DEMO_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 
 /**
  * Record a message sent from the engine to the frontend.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,348 @@
+#include "gameconn.h"
+#include "ipcconn.h"
+#include "ipcprotocol.h"
+#include "../util/logging.h"
+#include "../hwconsts.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef enum {
+	AWAIT_CONNECTION,
+	CONNECTED,
+	FINISHED
+} gameconn_state;
+
+struct _flib_gameconn {
+	flib_ipcconn connection;
+	flib_vector configBuffer;
+
+	gameconn_state state;
+	bool netgame;
+
+	void (*onConnectCb)(void* context);
+	void *onConnectCtx;
+
+	void (*onDisconnectCb)(void* context, int reason);
+	void *onDisconnectCtx;
+
+	void (*onErrorMessageCb)(void* context, const char *msg);
+	void *onErrorMessageCtx;
+
+	void (*onChatCb)(void* context, const char *msg, bool teamchat);
+	void *onChatCtx;
+
+	void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
+	void *onGameRecordedCtx;
+
+	void (*onNetMessageCb)(void *context, const uint8_t *em, int size);
+	void *onNetMessageCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onConnect(void* context) {}
+static void defaultCallback_onDisconnect(void* context, int reason) {}
+static void defaultCallback_onErrorMessage(void* context, const char *msg) {
+	flib_log_w("Error from engine (no callback set): %s", msg);
+}
+static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
+static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
+static void defaultCallback_onNetMessage(void *context, const uint8_t *em, int size) {}
+
+static void clearCallbacks(flib_gameconn *conn) {
+	conn->onConnectCb = &defaultCallback_onConnect;
+	conn->onDisconnectCb = &defaultCallback_onDisconnect;
+	conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
+	conn->onChatCb = &defaultCallback_onChat;
+	conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
+	conn->onNetMessageCb = &defaultCallback_onNetMessage;
+}
+
+static bool getGameMod(flib_cfg_meta *meta, flib_cfg *conf, int maskbit) {
+	for(int i=0; i<meta->modCount; i++) {
+		if(meta->mods[i].bitmaskIndex == maskbit) {
+			return conf->mods[i];
+		}
+	}
+	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
+	return false;
+}
+
+static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+	bool error = false;
+	bool perHogAmmo = false;
+	bool sharedAmmo = false;
+
+	error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
+	error |= flib_ipc_append_seed(configBuffer, setup->seed);
+	if(setup->map) {
+		error |= flib_ipc_append_mapconf(configBuffer, setup->map, false);
+	}
+	if(setup->script) {
+		error |= flib_ipc_append_message(configBuffer, "escript %s", setup->script);
+	}
+	if(setup->gamescheme) {
+		error |= flib_ipc_append_gamescheme(configBuffer, setup->gamescheme, metaconf);
+		perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
+		sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
+	}
+	if(setup->teams) {
+		for(int i=0; i<setup->teamcount; i++) {
+			error |= flib_ipc_append_addteam(configBuffer, &setup->teams[i], perHogAmmo, sharedAmmo);
+		}
+	}
+	error |= flib_ipc_append_message(configBuffer, "!");
+	return error ? -1 : 0;
+}
+
+static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = calloc(1, sizeof(flib_gameconn));
+	if(tempConn) {
+		tempConn->connection = flib_ipcconn_create(record, playerName);
+		tempConn->configBuffer = flib_vector_create();
+		if(tempConn->connection && tempConn->configBuffer) {
+			tempConn->state = AWAIT_CONNECTION;
+			tempConn->netgame = netGame;
+			clearCallbacks(tempConn);
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
+	if(tempConn) {
+		if(fillConfigBuffer(tempConn->configBuffer, playerName, metaconf, setup, netgame) == 0) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, demo, size) == size) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, save, size) == size) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+void flib_gameconn_destroy(flib_gameconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_ipcconn_destroy(&conn->connection);
+			flib_vector_destroy(&conn->configBuffer);
+			free(conn);
+		}
+	}
+}
+
+int flib_gameconn_getport(flib_gameconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_getport");
+		return 0;
+	} else {
+		return flib_ipcconn_port(conn->connection);
+	}
+}
+
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onConnect");
+	} else {
+		conn->onConnectCb = callback ? callback : &defaultCallback_onConnect;
+		conn->onConnectCtx = context;
+	}
+}
+
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onDisconnect");
+	} else {
+		conn->onDisconnectCb = callback ? callback : &defaultCallback_onDisconnect;
+		conn->onDisconnectCtx = context;
+	}
+}
+
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onErrorMessage");
+	} else {
+		conn->onErrorMessageCb = callback ? callback : &defaultCallback_onErrorMessage;
+		conn->onErrorMessageCtx = context;
+	}
+}
+
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onChat");
+	} else {
+		conn->onChatCb = callback ? callback : &defaultCallback_onChat;
+		conn->onChatCtx = context;
+	}
+}
+
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onGameRecorded");
+	} else {
+		conn->onGameRecordedCb = callback ? callback : &defaultCallback_onGameRecorded;
+		conn->onGameRecordedCtx = context;
+	}
+}
+
+void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onNetMessage");
+	} else {
+		conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage;
+		conn->onNetMessageCtx = context;
+	}
+}
+
+static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
+	if(conn->state == AWAIT_CONNECTION) {
+		flib_ipcconn_accept(conn->connection);
+		switch(flib_ipcconn_state(conn->connection)) {
+		case IPC_CONNECTED:
+			{
+				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+					return;
+				} else {
+					conn->state = CONNECTED;
+					conn->onConnectCb(conn->onConnectCtx);
+					if(conn->destroyRequested) {
+						return;
+					}
+				}
+			}
+			break;
+		case IPC_NOT_CONNECTED:
+			conn->state = FINISHED;
+			conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+			return;
+		default:
+			break;
+		}
+	}
+
+	if(conn->state == CONNECTED) {
+		uint8_t msgbuffer[257];
+		int len;
+		while(!conn->destroyRequested && (len = flib_ipcconn_recv_message(conn->connection, msgbuffer))>=0) {
+			if(len<2) {
+				flib_log_w("Received short message from IPC (<2 bytes)");
+				continue;
+			}
+			switch(msgbuffer[1]) {
+			case '?':
+				// The pong is already part of the config message
+				break;
+			case 'C':
+				// And we already send the config message on connecting.
+				break;
+			case 'E':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
+				}
+				break;
+			case 'i':
+				// TODO stats
+				break;
+			case 'Q':
+			case 'H':
+			case 'q':
+				{
+					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
+					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
+					flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
+					if(record.size) {
+						conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
+						if(conn->destroyRequested) {
+							return;
+						}
+					}
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
+					return;
+				}
+			case 's':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
+				}
+				break;
+			case 'b':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
+				}
+				break;
+			default:
+				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
+				break;
+			}
+		}
+	}
+
+	if(flib_ipcconn_state(conn->connection) == IPC_NOT_CONNECTED) {
+		conn->state = FINISHED;
+		conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+	}
+}
+
+void flib_gameconn_tick(flib_gameconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_gameconn_tick from a callback");
+	} else if(conn->state == FINISHED) {
+		flib_log_w("Call to flib_gameconn_tick, but we are already done.");
+	} else {
+		conn->running = true;
+		flib_gameconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_gameconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,81 @@
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../util/buffer.h"
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define GAME_END_FINISHED 0
+#define GAME_END_INTERRUPTED 1
+#define GAME_END_HALTED 2
+#define GAME_END_ERROR 3
+
+struct _flib_gameconn;
+typedef struct _flib_gameconn flib_gameconn;
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame);
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size);
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size);
+void flib_gameconn_destroy(flib_gameconn *conn);
+
+/**
+ * Returns the port on which the gameconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_gameconn_getport(flib_gameconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_gameconn_tick(flib_gameconn *conn);
+
+// TODO: Not needed yet, only for netgames
+/*
+flib_gameconn_send_enginemsg(flib_gameconn conn, uint8_t *data, int len);
+flib_gameconn_send_textmsg(flib_gameconn conn, int msgtype, const char *msg);
+flib_gameconn_send_chatmsg(flib_gameconn conn, const char *playername, const char *msg);
+*/
+
+/**
+ * handleConnect(void *context)
+ */
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context);
+
+/**
+ * handleDisconnect(void *context, int reason)
+ */
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context);
+
+/**
+ * Receives error messages sent by the engine
+ * handleErrorMessage(void* context, const char *msg)
+ */
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context);
+
+/**
+ * handleChat(void* context, const char *msg, bool teamchat)
+ */
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context);
+
+/**
+ * Called when the game ends
+ * handleGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame)
+ */
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context);
+
+/**
+ * Called when the game ends
+ * TODO handleStats(???)
+ */
+
+/**
+ * ...needs to be passed on to the server in a net game
+ * handleEngineMessage(void *context, const uint8_t *em, int size)
+ */
+void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
+
+#endif
--- a/project_files/frontlib/ipc/ipcconn.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,7 +1,7 @@
 #include "ipcconn.h"
-#include "../logging.h"
+#include "demo.h"
+#include "../util/logging.h"
 #include "../socket.h"
-#include "demo.h"
 
 #include <string.h>
 #include <stdbool.h>
--- a/project_files/frontlib/ipc/ipcconn.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -5,7 +5,7 @@
 #ifndef IPCCONN_H_
 #define IPCCONN_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 
 #include <stddef.h>
 #include <stdbool.h>
@@ -18,6 +18,8 @@
 typedef struct _flib_ipcconn *flib_ipcconn;
 
 /**
+ * TODO move demo recording up by one layer?
+ *
  * Start an engine connection by listening on a random port. The selected port can
  * be queried with flib_ipcconn_port and has to be passed to the engine.
  *
--- a/project_files/frontlib/ipc/ipcprotocol.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,10 +1,11 @@
 #include "ipcprotocol.h"
-#include "../util.h"
-#include "../logging.h"
+#include "../util/util.h"
+#include "../util/logging.h"
 
 #include <stdio.h>
 #include <stdbool.h>
 #include <string.h>
+#include <inttypes.h>
 
 int flib_ipc_append_message(flib_vector vec, const char *fmt, ...) {
 	int result = -1;
@@ -94,3 +95,87 @@
 		return flib_ipc_append_message(vec, "eseed %s", seed);
 	}
 }
+
+int flib_ipc_append_gamescheme(flib_vector vec, flib_cfg *scheme, flib_cfg_meta *meta) {
+	int result = -1;
+	flib_vector tempvector = flib_vector_create();
+	if(!vec || !scheme || !meta) {
+		flib_log_e("null parameter in flib_ipc_append_gamescheme");
+	} else if(tempvector) {
+		bool error = false;
+		uint32_t gamemods = 0;
+		for(int i=0; i<meta->modCount; i++) {
+			if(scheme->mods[i]) {
+				gamemods |= (1<<meta->mods[i].bitmaskIndex);
+			}
+		}
+		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, gamemods);
+		for(int i=0; i<meta->settingCount; i++) {
+			int value = scheme->settings[i];
+			if(meta->settings[i].checkOverMax) {
+				value = value>meta->settings[i].max ? meta->settings[i].max : value;
+			}
+			if(meta->settings[i].times1000) {
+				value *= 1000;
+			}
+			error |= flib_ipc_append_message(tempvector, "%s %i", meta->settings[i].engineCommand, value);
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(&tempvector);
+	return result;
+}
+
+// FIXME shared ammo will break per-team ammo
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo) {
+	int result = -1;
+	flib_vector tempvector = flib_vector_create();
+	if(!vec || !team || !team->weaponset) {
+		flib_log_e("invalid parameter in flib_ipc_append_addteam");
+	} else if(tempvector) {
+		bool error = false;
+		error |= flib_ipc_append_message(tempvector, "eammloadt %s", team->weaponset->loadout);
+		error |= flib_ipc_append_message(tempvector, "eammprob %s", team->weaponset->crateprob);
+		error |= flib_ipc_append_message(tempvector, "eammdelay %s", team->weaponset->delay);
+		error |= flib_ipc_append_message(tempvector, "eammreinf %s", team->weaponset->crateammo);
+		if(!perHogAmmo) {
+			error |= flib_ipc_append_message(tempvector, "eammstore");
+		}
+
+		char *hash = team->hash ? team->hash : "00000000000000000000000000000000";
+		error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, team->color, team->name);
+
+		if(team->remoteDriven) {
+			error |= flib_ipc_append_message(tempvector, "erdriven");
+		}
+
+		error |= flib_ipc_append_message(tempvector, "egrave %s", team->grave);
+		error |= flib_ipc_append_message(tempvector, "efort %s", team->fort);
+		error |= flib_ipc_append_message(tempvector, "evoicepack %s", team->voicepack);
+		error |= flib_ipc_append_message(tempvector, "eflag %s", team->flag);
+
+		// TODO bindings
+
+		for(int i=0; i<team->hogsInGame; i++) {
+			error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
+			error |= flib_ipc_append_message(tempvector, "ehat %s", team->hogs[i].hat);
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(&tempvector);
+	return result;
+}
--- a/project_files/frontlib/ipc/ipcprotocol.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,10 @@
 #ifndef IPCPROTOCOL_H_
 #define IPCPROTOCOL_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 #include "../model/map.h"
+#include "../model/team.h"
+#include "../model/cfg.h"
 
 #include <stdbool.h>
 
@@ -35,4 +37,14 @@
  */
 int flib_ipc_append_seed(flib_vector vec, const char *seed);
 
+/**
+ * Append the game scheme to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_gamescheme(flib_vector vec, flib_cfg *seed, flib_cfg_meta *meta);
+
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo);
+
 #endif /* IPCPROTOCOL_H_ */
--- a/project_files/frontlib/ipc/mapconn.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -2,8 +2,8 @@
 #include "ipcconn.h"
 #include "ipcprotocol.h"
 
-#include "../logging.h"
-#include "../buffer.h"
+#include "../util/logging.h"
+#include "../util/buffer.h"
 
 #include <stdlib.h>
 
@@ -11,14 +11,14 @@
 	AWAIT_CONNECTION,
 	AWAIT_REPLY,
 	FINISHED
-} mapconn_progress;
+} mapconn_state;
 
 struct _flib_mapconn {
 	uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
 	flib_ipcconn connection;
 	flib_vector configBuffer;
 
-	mapconn_progress progress;
+	mapconn_state progress;
 
 	void (*onSuccessCb)(void*, const uint8_t*, int);
 	void *onSuccessCtx;
--- a/project_files/frontlib/ipc/mapconn.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -2,6 +2,7 @@
 #define IPC_MAPCONN_H_
 
 #include "../model/map.h"
+
 #include <stdint.h>
 
 #define MAPIMAGE_WIDTH 256
--- a/project_files/frontlib/logging.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#include "logging.h"
-
-#include <time.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-char* flib_format_ip(uint32_t numip) {
-	static char ip[16];
-	snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
-	return ip;
-}
-
-static void log_time(FILE *file) {
-    time_t timer;
-    char buffer[25];
-    struct tm* tm_info;
-
-    time(&timer);
-    tm_info = localtime(&timer);
-
-    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
-    fprintf(file, "%s", buffer);
-}
-
-static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) {
-	log_time(file);
-	fprintf(file, " [%s]", prefix);
-	vfprintf(file, fmt, args);
-	fprintf(file, "\n");
-	fflush(file);
-}
-
-void flib_log_e(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stderr, "E", fmt, argp);
-	va_end(argp);
-}
-
-void flib_log_w(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stdout, "W", fmt, argp);
-	va_end(argp);
-}
-
-void flib_log_i(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stdout, "I", fmt, argp);
-	va_end(argp);
-}
--- a/project_files/frontlib/logging.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-/*
- *
- */
-
-#ifndef LOGGING_H_
-#define LOGGING_H_
-
-#include<stdint.h>
-
-char* flib_format_ip(uint32_t numip);
-
-void flib_log_e(const char *fmt, ...);
-void flib_log_w(const char *fmt, ...);
-void flib_log_i(const char *fmt, ...);
-
-#endif /* LOGGING_H_ */
--- a/project_files/frontlib/model/cfg.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/cfg.c	Sat Jun 09 03:28:38 2012 +0200
@@ -2,9 +2,9 @@
 
 #include "../iniparser/iniparser.h"
 #include "../iniparser/dictionary.h"
-#include "../ini/inihelper.h"
-#include "../logging.h"
-#include "../util.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
 
 #include <stdio.h>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,2 @@
+#include "gamesetup.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,25 @@
+/**
+ * A complete game configuration that contains all settings for a
+ * local or networked game.
+ *
+ * It should be noted that the meta-configuration is not included.
+ */
+
+#ifndef MODEL_GAMESETUP_H_
+#define MODEL_GAMESETUP_H_
+
+#include "cfg.h"
+#include "weapon.h"
+#include "map.h"
+#include "team.h"
+
+typedef struct {
+    char *seed;						// required
+    char *script;					// optional
+    flib_cfg *gamescheme;			// optional
+    flib_map *map;					// optional
+	flib_team *teams;				// optional
+	int teamcount;
+} flib_gamesetup;
+
+#endif
--- a/project_files/frontlib/model/map.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/map.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,8 @@
 #include "map.h"
 
-#include "../ini/inihelper.h"
-#include "../util.h"
-#include "../logging.h"
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
 
 #include <stdlib.h>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,1 @@
+#include "team.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,51 @@
+#ifndef TEAM_H_
+#define TEAM_H_
+
+#include "weapon.h"
+#include "../hwconsts.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TEAM_DEFAULT_HOGNAME "Hog"
+#define TEAM_DEFAULT_HAT "NoHat"
+#define TEAM_DEFAULT_DIFFICULTY 0
+#define TEAM_DEFAULT_HEALTH 100
+
+typedef struct {
+	char *name;
+	char *hat;
+
+	// Statistics. They are irrelevant for the engine or server,
+	// but provided for ini reading/writing by the frontend.
+	int rounds;
+	int deaths;
+	int kills;
+	int suicides;
+
+	// These settings are sometimes used on a per-team basis.
+	int difficulty;
+	int initialHealth;
+} flib_hog;
+
+typedef struct {
+	flib_hog hogs[HEDGEHOGS_PER_TEAM];
+	char *name;
+	char *grave;
+	char *fort;
+	char *voicepack;
+	char *flag;
+
+	// TODO binds
+
+	// Transient settings used in game setup
+	uint32_t color;
+	int hogsInGame;
+	bool remoteDriven;
+	char *hash;
+
+	// This setting is sometimes used on a per-game basis.
+	flib_weaponset *weaponset;
+} flib_team;
+
+#endif /* TEAM_H_ */
--- a/project_files/frontlib/model/weapon.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/weapon.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,9 +1,9 @@
 #include "weapon.h"
 
-#include "../ini/inihelper.h"
 #include "../iniparser/iniparser.h"
-#include "../logging.h"
-#include "../util.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
 
 #include <stdlib.h>
 #include <ctype.h>
--- a/project_files/frontlib/socket.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/socket.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,5 +1,5 @@
 #include "socket.h"
-#include "logging.h"
+#include "util/logging.h"
 #include <stdlib.h>
 #include <SDL_net.h>
 #include <time.h>
--- a/project_files/frontlib/util.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#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;
-}
--- a/project_files/frontlib/util.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,90 @@
+#include "buffer.h"
+#include "logging.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+typedef struct _flib_vector {
+	void *data;
+	size_t size;
+	size_t capacity;
+} _flib_vector;
+
+flib_vector flib_vector_create() {
+	flib_vector result = malloc(sizeof(_flib_vector));
+	if(result == NULL) {
+		return NULL;
+	}
+	result->data = malloc(16);
+	if(result->data == NULL) {
+		free(result);
+		return NULL;
+	}
+	result->size = 0;
+	result->capacity = 16;
+	return result;
+}
+
+void flib_vector_destroy(flib_vector *vec) {
+	if(vec && *vec) {
+		free((*vec)->data);
+		free(*vec);
+		*vec = NULL;
+	}
+}
+
+static void try_realloc(flib_vector vec, size_t newCapacity) {
+	void *newData = realloc(vec->data, newCapacity);
+	if(newData) {
+		vec->data = newData;
+		vec->capacity = newCapacity;
+	}
+}
+
+static size_t getFreeCapacity(flib_vector vec) {
+	return vec->capacity - vec->size;
+}
+
+int flib_vector_append(flib_vector vec, const void *data, size_t len) {
+	if(getFreeCapacity(vec) < len) {
+		// Resize exponentially for constant amortized time,
+		// But at least by as much as we need of course,
+		// and be extra careful with integer overflows...
+		size_t extraCapacity = (vec->capacity)/2;
+
+		size_t minExtraCapacity = len - getFreeCapacity(vec);
+		if(extraCapacity < minExtraCapacity) {
+			extraCapacity = minExtraCapacity;
+		}
+
+		if(extraCapacity <= SIZE_MAX - vec->capacity) {
+			try_realloc(vec, vec->capacity+extraCapacity);
+		}
+
+		// Check if we were able to resize.
+		// If not, try to allocate at least what we need...
+		if(getFreeCapacity(vec) < len) {
+			try_realloc(vec, vec->capacity+minExtraCapacity);
+
+			// Still not working? Then we fail.
+			if(getFreeCapacity(vec) < len) {
+				return 0;
+			}
+		}
+	}
+
+	memmove(vec->data + vec->size, data, len);
+	vec->size += len;
+	return len;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector vec) {
+	flib_buffer result = {vec->data, vec->size};
+	return result;
+}
+
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
+	flib_constbuffer result = {vec->data, vec->size};
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,58 @@
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * A simple struct to hold both the pointer to an array and its size,
+ * for e.g. conveniently returning it from a function.
+ *
+ * Convention: Size is zero iff data is a NULL pointer.
+ */
+typedef struct {
+	void *data;
+	size_t size;
+} flib_buffer;
+
+/**
+ * Just like flib_buffer, but the contents are not supposed to be modified.
+ */
+typedef struct {
+	const void *data;
+	size_t size;
+} flib_constbuffer;
+
+/**
+ * Simple variable-capacity data structure (opaque type).
+ */
+struct _flib_vector;
+typedef struct _flib_vector *flib_vector;
+
+/**
+ * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
+ * May return NULL if memory runs out.
+ */
+flib_vector flib_vector_create();
+
+/**
+ * Free the memory of this vector and set it to NULL.
+ */
+void flib_vector_destroy(flib_vector *vec);
+
+/**
+ * Append the provided data to the end of the vector, enlarging it as required.
+ * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
+ * The vector remains unchanged if an out of memory situation occurs.
+ */
+int flib_vector_append(flib_vector vec, const void *data, size_t len);
+
+/**
+ * Return a buffer or constbuffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_buffer flib_vector_as_buffer(flib_vector vec);
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,168 @@
+#include "inihelper.h"
+#include "logging.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdarg.h>
+
+static bool keychar_needs_urlencoding(char c) {
+	return !isalnum(c);
+}
+
+char *inihelper_urlencode(const char *inbuf) {
+	if(!inbuf) {
+		return NULL;
+	}
+	size_t insize = strlen(inbuf);
+	if(insize > SIZE_MAX/4) {
+		return NULL;
+	}
+
+	char *outbuf = malloc(insize*3+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(!keychar_needs_urlencoding(inbuf[inpos])) {
+        	outbuf[outpos++] = inbuf[inpos++];
+        } else {
+            if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+            	free(outbuf);
+            	return NULL;
+            }
+            inpos++;
+            outpos += 3;
+        }
+    }
+    outbuf[outpos] = 0;
+    return outbuf;
+}
+
+char *inihelper_urldecode(const char *inbuf) {
+	char *outbuf = malloc(strlen(inbuf)+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
+            char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
+            outbuf[outpos++] = strtol(temp, NULL, 16);
+            inpos += 3;
+        } else {
+        	outbuf[outpos++] = inbuf[inpos++];
+        }
+    }
+    outbuf[outpos] = 0;
+    return outbuf;
+}
+
+char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
+	if(!sectionName || !keyName) {
+		return NULL;
+	}
+	return flib_asprintf("%s:%s", sectionName, keyName);
+}
+
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	if(!inifile || !sectionName || !keyName) {
+		*error = true;
+		return NULL;
+	}
+	char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+	if(!extendedkey) {
+		*error = true;
+		return NULL;
+	}
+	char *result = iniparser_getstring(inifile, extendedkey, NULL);
+	free(extendedkey);
+	if(!result) {
+		flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
+		*error = true;
+	}
+	return result;
+}
+
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
+}
+
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+	if(!value) {
+		return 0;
+	} else {
+		errno = 0;
+		long val = strtol(value, NULL, 10);
+		if(errno!=0) {
+			*error = true;
+			return 0;
+		}
+		if(val<INT_MIN || val>INT_MAX) {
+			*error = true;
+			return 0;
+		}
+		return (int)val;
+	}
+}
+
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+	if(!value) {
+		return false;
+	} else {
+		bool trueval = strchr("1tTyY", value[0]);
+		bool falseval = strchr("0fFnN", value[0]);
+		if(!trueval && !falseval) {
+			*error = true;
+			return false;
+		} else {
+			return trueval;
+		}
+	}
+}
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName || !value) {
+		flib_log_e("inihelper_setstr called with bad parameters");
+	} else {
+		char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+		if(extendedkey) {
+			result = iniparser_set(dict, extendedkey, value);
+			free(extendedkey);
+		}
+	}
+	return result;
+}
+
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName) {
+		flib_log_e("inihelper_setint called with bad parameters");
+	} else {
+		char *strvalue = flib_asprintf("%i", value);
+		if(strvalue) {
+			result = inihelper_setstr(dict, sectionName, keyName, strvalue);
+			free(strvalue);
+		}
+	}
+	return result;
+}
+
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName) {
+		flib_log_e("inihelper_setint called with bad parameters");
+	} else {
+		result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,54 @@
+/**
+ * Some helper functions for working with the iniparser functions - in particular,
+ * for interoperability with the ini format used by the QtSettings class.
+ */
+
+#ifndef INIHELPER_H_
+#define INIHELPER_H_
+
+#include "../iniparser/iniparser.h"
+
+#include <stdbool.h>
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urlencode(const char *inbuf);
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urldecode(const char *inbuf);
+
+/**
+ * Create a key in the format "sectionName:keyName"
+ * Returned buffer must be free()d
+ */
+char *inihelper_createDictKey(const char *sectionName, const char *keyName);
+
+/**
+ * Returns an internal buffer, don't modify or free
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Returned buffer must be free()d
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
+#endif /* INIHELPER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,53 @@
+#include "logging.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+char* flib_format_ip(uint32_t numip) {
+	static char ip[16];
+	snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
+	return ip;
+}
+
+static void log_time(FILE *file) {
+    time_t timer;
+    char buffer[25];
+    struct tm* tm_info;
+
+    time(&timer);
+    tm_info = localtime(&timer);
+
+    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
+    fprintf(file, "%s", buffer);
+}
+
+static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) {
+	log_time(file);
+	fprintf(file, " [%s]", prefix);
+	vfprintf(file, fmt, args);
+	fprintf(file, "\n");
+	fflush(file);
+}
+
+void flib_log_e(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stderr, "E", fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_w(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stdout, "W", fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_i(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stdout, "I", fmt, argp);
+	va_end(argp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,16 @@
+/*
+ *
+ */
+
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+#include<stdint.h>
+
+char* flib_format_ip(uint32_t numip);
+
+void flib_log_e(const char *fmt, ...);
+void flib_log_w(const char *fmt, ...);
+void flib_log_i(const char *fmt, ...);
+
+#endif /* LOGGING_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,49 @@
+#include "util.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+char *flib_asprintf(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	char *result = flib_vasprintf(fmt, argp);
+	va_end(argp);
+	return result;
+}
+
+char *flib_vasprintf(const char *fmt, va_list args) {
+	char *result = NULL;
+	int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;				// Figure out how much memory we need,
+	if(requiredSize>=0) {
+		char *tmpbuf = malloc(requiredSize);						// allocate it
+		if(tmpbuf) {
+			if(vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) {		// and then do the actual formatting.
+				result = tmpbuf;
+				tmpbuf = NULL;
+			}
+		}
+		free(tmpbuf);
+	}
+	return result;
+}
+
+char *flib_strdupnull(const char *str) {
+	if(!str) {
+		return NULL;
+	}
+	return flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+	if(!buf || size==0) {
+		return NULL;
+	}
+	void *result = malloc(size);
+	if(result) {
+		memcpy(result, buf, size);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h	Sat Jun 09 03:28:38 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