Cleanup, refactoring and generally more development in the frontlib
authorMedo <smaxein@googlemail.com>
Tue, 12 Jun 2012 11:25:05 +0200
changeset 7224 5143861c83bd
parent 7221 8d04e85ca204
child 7227 1c859f572d72
Cleanup, refactoring and generally more development in the frontlib
project_files/frontlib/frontlib.c
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/model/cfg.c
project_files/frontlib/model/cfg.h
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/model/weapon.h
project_files/frontlib/socket.c
project_files/frontlib/socket.h
project_files/frontlib/test.c
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/frontlib.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/frontlib.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,21 +1,14 @@
 #include "frontlib.h"
 #include "util/logging.h"
-#include "model/map.h"
-#include "ipc/mapconn.h"
-#include "ipc/gameconn.h"
-
 #include <SDL.h>
 #include <SDL_net.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <assert.h>
 
 static int flib_initflags;
 
 int flib_init(int flags) {
 	flib_initflags = flags;
 
+	flib_log_d("Initializing frontlib");
 	if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
 		if(SDL_Init(0)==-1) {
 		    flib_log_e("Error in SDL_Init: %s", SDL_GetError());
@@ -35,81 +28,9 @@
 }
 
 void flib_quit() {
+	flib_log_d("Shutting down frontlib");
 	SDLNet_Quit();
 	if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) {
 		SDL_Quit();
 	}
 }
-
-static void onDisconnect(void *context, int reason) {
-	flib_log_i("Connection closed. Reason: %i", reason);
-	flib_gameconn **connptr = context;
-	flib_gameconn_destroy(*connptr);
-	*connptr = NULL;
-}
-
-static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
-	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
-	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
-	fwrite(record, 1, size, file);
-	fclose(file);
-}
-
-int main(int argc, char *argv[]) {
-	flib_init(0);
-
-	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.script = NULL;
-	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_gameconn *gameconn = flib_gameconn_create("Medo42", metaconf, &setup, false);
-	assert(gameconn);
-
-	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
-	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
-
-	while(gameconn) {
-		flib_gameconn_tick(gameconn);
-	}
-	flib_log_i("Shutting down...");
-	flib_quit();
-	return 0;
-}
--- a/project_files/frontlib/ipc/demo.c	Mon Jun 11 00:06:22 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-#include "demo.h"
-#include "../util/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;
-	}
-}
--- a/project_files/frontlib/ipc/demo.h	Mon Jun 11 00:06:22 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 "../util/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/ipc/gameconn.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.c	Tue Jun 12 11:25:05 2012 +0200
@@ -2,9 +2,11 @@
 #include "ipcconn.h"
 #include "ipcprotocol.h"
 #include "../util/logging.h"
+#include "../util/util.h"
 #include "../hwconsts.h"
 #include <stdbool.h>
 #include <stdlib.h>
+#include <string.h>
 
 typedef enum {
 	AWAIT_CONNECTION,
@@ -13,8 +15,10 @@
 } gameconn_state;
 
 struct _flib_gameconn {
-	flib_ipcconn connection;
-	flib_vector configBuffer;
+	flib_ipcconn *connection;
+	flib_vector *configBuffer;
+	flib_vector *demoBuffer;
+	char *playerName;
 
 	gameconn_state state;
 	bool netgame;
@@ -69,7 +73,7 @@
 	return false;
 }
 
-static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+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;
@@ -89,7 +93,7 @@
 	}
 	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_addteam(configBuffer, setup->teams[i], perHogAmmo, sharedAmmo);
 		}
 	}
 	error |= flib_ipc_append_message(configBuffer, "!");
@@ -98,11 +102,15 @@
 
 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));
+	flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
 	if(tempConn) {
-		tempConn->connection = flib_ipcconn_create(record, playerName);
+		tempConn->connection = flib_ipcconn_create();
 		tempConn->configBuffer = flib_vector_create();
-		if(tempConn->connection && tempConn->configBuffer) {
+		tempConn->playerName = flib_strdupnull(playerName);
+		if(tempConn->connection && tempConn->configBuffer && tempConn->playerName) {
+			if(record) {
+				tempConn->demoBuffer = flib_vector_create();
+			}
 			tempConn->state = AWAIT_CONNECTION;
 			tempConn->netgame = netGame;
 			clearCallbacks(tempConn);
@@ -164,8 +172,10 @@
 			clearCallbacks(conn);
 			conn->destroyRequested = true;
 		} else {
-			flib_ipcconn_destroy(&conn->connection);
-			flib_vector_destroy(&conn->configBuffer);
+			flib_ipcconn_destroy(conn->connection);
+			flib_vector_destroy(conn->configBuffer);
+			flib_vector_destroy(conn->demoBuffer);
+			free(conn->playerName);
 			free(conn);
 		}
 	}
@@ -180,6 +190,93 @@
 	}
 }
 
+static void demo_append(flib_gameconn *conn, const void *data, size_t len) {
+	if(conn->demoBuffer) {
+		if(flib_vector_append(conn->demoBuffer, data, len) < len) {
+			flib_log_e("Error recording demo: Out of memory.");
+			flib_vector_destroy(conn->demoBuffer);
+			conn->demoBuffer = NULL;
+		}
+	}
+}
+
+static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) {
+	size_t msglen = strlen(message);
+
+	// If the message starts with /me, it will be displayed differently.
+	bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4);
+	const char *template = meMessage ? "s\x02* %s %s  " : "s\x01%s: %s  ";
+	int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message);
+	if(size>0) {
+		buffer[0] = size>255 ? 255 : size;
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static void demo_append_chatmessage(flib_gameconn *conn, const char *message) {
+	// Chat messages are reformatted to make them look as if they were received, not sent.
+	uint8_t converted[257];
+	if(!format_chatmessage(converted, conn->playerName, message)) {
+		demo_append(conn, converted, converted[0]+1);
+	}
+}
+
+static void demo_replace_gamemode(flib_buffer buf, char gamemode) {
+	size_t msgStart = 0;
+	uint8_t *data = (uint8_t*)buf.data;
+	while(msgStart+2 < buf.size) {
+		if(!memcmp(data+msgStart, "\x02T", 2)) {
+			data[msgStart+2] = gamemode;
+		}
+		msgStart += (uint8_t)data[msgStart]+1;
+	}
+}
+
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len) {
+	int result = -1;
+	if(!conn || (!data && len>0)) {
+		flib_log_e("null parameter in flib_gameconn_send_enginemsg");
+	} else if(!flib_ipcconn_send_raw(conn->connection, data, len)) {
+		demo_append(conn, data, len);
+		result = 0;
+	}
+	return result;
+}
+
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) {
+	int result = -1;
+	if(!conn || !msg) {
+		flib_log_e("null parameter in flib_gameconn_send_textmsg");
+	} else {
+		uint8_t converted[257];
+		int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg);
+		if(size>0) {
+			converted[0] = size>255 ? 255 : size;
+			if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
+				demo_append(conn, converted, converted[0]+1);
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) {
+	int result = -1;
+	uint8_t converted[257];
+	if(!conn || !playername || !msg) {
+		flib_log_e("null parameter in flib_gameconn_send_chatmsg");
+	} else if(format_chatmessage(converted, playername, msg)) {
+		flib_log_e("Error formatting message in flib_gameconn_send_chatmsg");
+	} else if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
+		demo_append(conn, converted, converted[0]+1);
+		result = 0;
+	}
+	return result;
+}
+
 void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
 	if(!conn) {
 		flib_log_e("null parameter in flib_gameconn_onConnect");
@@ -246,6 +343,7 @@
 					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
 					return;
 				} else {
+					demo_append(conn, configBuffer.data, configBuffer.size);
 					conn->state = CONNECTED;
 					conn->onConnectCb(conn->onConnectCtx);
 					if(conn->destroyRequested) {
@@ -272,30 +370,31 @@
 				continue;
 			}
 			switch(msgbuffer[1]) {
-			case '?':
+			case '?':	// Ping
 				// The pong is already part of the config message
 				break;
-			case 'C':
+			case 'C':	// Config query
 				// And we already send the config message on connecting.
 				break;
-			case 'E':
+			case 'E':	// Error message
 				if(len>=3) {
 					msgbuffer[len-2] = 0;
 					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
 				}
 				break;
-			case 'i':
+			case 'i':	// Statistics
 				// TODO stats
 				break;
-			case 'Q':
-			case 'H':
-			case 'q':
+			case 'Q':	// Game interrupted
+			case 'H':	// Game halted
+			case 'q':	// game finished
 				{
 					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
 					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
-					flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
-					if(record.size) {
-						conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
+					if(conn->demoBuffer) {
+						flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer);
+						demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D');
+						conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame);
 						if(conn->destroyRequested) {
 							return;
 						}
@@ -304,19 +403,23 @@
 					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
 					return;
 				}
-			case 's':
+			case 's':	// Chat message
 				if(len>=3) {
 					msgbuffer[len-2] = 0;
+					demo_append_chatmessage(conn, (char*)msgbuffer+2);
+
 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
 				}
 				break;
-			case 'b':
+			case 'b':	// Teamchat message
 				if(len>=3) {
 					msgbuffer[len-2] = 0;
 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
 				}
 				break;
-			default:
+			default:	// Engine message
+				demo_append(conn, msgbuffer, len);
+
 				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
 				break;
 			}
--- a/project_files/frontlib/ipc/gameconn.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/gameconn.h	Tue Jun 12 11:25:05 2012 +0200
@@ -33,12 +33,9 @@
  */
 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);
-*/
+int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len);
+int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg);
+int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg);
 
 /**
  * handleConnect(void *context)
--- a/project_files/frontlib/ipc/ipcconn.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,6 +1,6 @@
 #include "ipcconn.h"
-#include "demo.h"
 #include "../util/logging.h"
+#include "../util/util.h"
 #include "../socket.h"
 
 #include <string.h>
@@ -17,25 +17,21 @@
  */
 typedef struct _flib_ipcconn {
 	uint8_t readBuffer[8192];
-	char playerName[256];
-
 	int readBufferSize;
 
-	flib_acceptor acceptor;
+	flib_acceptor *acceptor;
 	uint16_t port;
 
-	flib_tcpsocket sock;
-	flib_vector demoBuffer;
+	flib_tcpsocket *sock;
 } _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);
+flib_ipcconn *flib_ipcconn_create() {
+	flib_ipcconn *result = flib_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);
+		flib_acceptor_close(acceptor);
 		return NULL;
 	}
 
@@ -44,44 +40,29 @@
 	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) {
+uint16_t flib_ipcconn_port(flib_ipcconn *ipc) {
 	if(!ipc) {
-		flib_log_e("Call to flib_ipcconn_port with ipc==null");
+		flib_log_e("null parameter in flib_ipcconn_port");
 		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);
+void flib_ipcconn_destroy(flib_ipcconn *ipc) {
+	if(ipc) {
+		flib_acceptor_close(ipc->acceptor);
+		flib_socket_close(ipc->sock);
 		free(ipc);
-		*ipcptr = NULL;
 	}
 }
 
-IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
+IpcConnState flib_ipcconn_state(flib_ipcconn *ipc) {
 	if(!ipc) {
-		flib_log_e("Call to flib_ipcconn_state with ipc==null");
+		flib_log_e("null parameter in flib_ipcconn_state");
 		return IPC_NOT_CONNECTED;
 	} else if(ipc->sock) {
 		return IPC_CONNECTED;
@@ -92,24 +73,25 @@
 	}
 }
 
-static bool isMessageReady(flib_ipcconn ipc) {
+static bool isMessageReady(flib_ipcconn *ipc) {
 	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
 }
 
-static void receiveToBuffer(flib_ipcconn ipc) {
+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);
+			flib_socket_close(ipc->sock);
+			ipc->sock = NULL;
 		}
 	}
 }
 
-int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
+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");
+		flib_log_e("null parameter in flib_ipcconn_recv_message");
 		return -1;
 	}
 
@@ -118,12 +100,6 @@
 	}
 
 	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);
@@ -138,9 +114,9 @@
 	}
 }
 
-int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data) {
+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");
+		flib_log_e("null parameter in flib_ipcconn_recv_map");
 		return -1;
 	}
 
@@ -156,7 +132,7 @@
 }
 
 static void logSentMsg(const uint8_t *data, size_t len) {
-	if(flib_log_getLevel() > FLIB_LOGLEVEL_DEBUG) {
+	if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) {
 		size_t msgStart = 0;
 		while(msgStart < len) {
 			uint8_t msglen = data[msgStart];
@@ -171,9 +147,9 @@
 	}
 }
 
-int flib_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len) {
+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");
+		flib_log_e("null parameter in flib_ipcconn_send_raw");
 		return -1;
 	}
 	if(!ipc->sock) {
@@ -183,23 +159,21 @@
 
 	if(flib_socket_send(ipc->sock, data, len) == len) {
 		logSentMsg(data, 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);
+		flib_socket_close(ipc->sock);
+		ipc->sock = NULL;
 		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");
+int flib_ipcconn_send_message(flib_ipcconn *ipc, void *data, size_t len) {
+	if(!ipc || (!data && len>0)) {
+		flib_log_e("null parameter in flib_ipcconn_send_message");
+		return -1;
+	} else if(len>255) {
+		flib_log_e("Overlong message (%zu bytes) in flib_ipcconn_send_message", len);
 		return -1;
 	}
 
@@ -209,29 +183,18 @@
 	return flib_ipcconn_send_raw(ipc, sendbuf, len+1);
 }
 
-int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
+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) {
+void flib_ipcconn_accept(flib_ipcconn *ipc) {
 	if(!ipc) {
-		flib_log_e("Call to flib_ipcconn_accept with ipc==null");
+		flib_log_e("null parameter in flib_ipcconn_accept");
 	} else if(!ipc->sock && ipc->acceptor) {
 		ipc->sock = flib_socket_accept(ipc->acceptor, true);
 		if(ipc->sock) {
-			flib_acceptor_close(&ipc->acceptor);
+			flib_acceptor_close(ipc->acceptor);
+			ipc->acceptor = NULL;
 		}
 	}
 }
-
-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/ipc/ipcconn.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.h	Tue Jun 12 11:25:05 2012 +0200
@@ -15,11 +15,9 @@
 typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState;
 
 struct _flib_ipcconn;
-typedef struct _flib_ipcconn *flib_ipcconn;
+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.
  *
@@ -32,19 +30,19 @@
  * 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);
+flib_ipcconn *flib_ipcconn_create();
 
-uint16_t flib_ipcconn_port(flib_ipcconn ipc);
+uint16_t flib_ipcconn_port(flib_ipcconn *ipc);
 
 /**
- * Free resources, close sockets, and set the pointer to NULL.
+ * Free resources and close sockets.
  */
-void flib_ipcconn_destroy(flib_ipcconn *ipcptr);
+void flib_ipcconn_destroy(flib_ipcconn *ipc);
 
 /**
  * Determine the current connection state
  */
-IpcConnState flib_ipcconn_state(flib_ipcconn ipc);
+IpcConnState flib_ipcconn_state(flib_ipcconn *ipc);
 
 /**
  * Receive a single message (up to 256 bytes) and copy it into the data buffer.
@@ -58,7 +56,7 @@
  * 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);
+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
@@ -66,9 +64,9 @@
  * 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_recv_map(flib_ipcconn *ipc, void *data);
 
-int flib_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len);
+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
@@ -77,32 +75,17 @@
  * 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);
+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);
+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);
+void flib_ipcconn_accept(flib_ipcconn *ipc);
 
 #endif /* IPCCONN_H_ */
 
--- a/project_files/frontlib/ipc/ipcprotocol.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Tue Jun 12 11:25:05 2012 +0200
@@ -7,7 +7,7 @@
 #include <string.h>
 #include <inttypes.h>
 
-int flib_ipc_append_message(flib_vector vec, const char *fmt, ...) {
+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");
@@ -38,9 +38,9 @@
 	return result;
 }
 
-int flib_ipc_append_mapconf(flib_vector vec, flib_map *map, bool mappreview) {
+int flib_ipc_append_mapconf(flib_vector *vec, flib_map *map, bool mappreview) {
 	int result = -1;
-	flib_vector tempvector = flib_vector_create();
+	flib_vector *tempvector = flib_vector_create();
 	if(!vec || !map) {
 		flib_log_e("null parameter in flib_ipc_append_mapconf");
 	} else if(tempvector) {
@@ -83,11 +83,11 @@
 			}
 		}
 	}
-	flib_vector_destroy(&tempvector);
+	flib_vector_destroy(tempvector);
 	return result;
 }
 
-int flib_ipc_append_seed(flib_vector vec, const char *seed) {
+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;
@@ -96,9 +96,9 @@
 	}
 }
 
-int flib_ipc_append_gamescheme(flib_vector vec, flib_cfg *scheme, flib_cfg_meta *meta) {
+int flib_ipc_append_gamescheme(flib_vector *vec, flib_cfg *scheme, flib_cfg_meta *meta) {
 	int result = -1;
-	flib_vector tempvector = flib_vector_create();
+	flib_vector *tempvector = flib_vector_create();
 	if(!vec || !scheme || !meta) {
 		flib_log_e("null parameter in flib_ipc_append_gamescheme");
 	} else if(tempvector) {
@@ -129,14 +129,14 @@
 			}
 		}
 	}
-	flib_vector_destroy(&tempvector);
+	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 flib_ipc_append_addteam(flib_vector *vec, flib_team *team, bool perHogAmmo, bool sharedAmmo) {
 	int result = -1;
-	flib_vector tempvector = flib_vector_create();
+	flib_vector *tempvector = flib_vector_create();
 	if(!vec || !team || !team->weaponset) {
 		flib_log_e("invalid parameter in flib_ipc_append_addteam");
 	} else if(tempvector) {
@@ -161,7 +161,9 @@
 		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->bindingCount; i++) {
+			error |= flib_ipc_append_message(tempvector, "ebind %s %s", team->bindings[i].binding, team->bindings[i].action);
+		}
 
 		for(int i=0; i<team->hogsInGame; i++) {
 			error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
@@ -176,6 +178,6 @@
 			}
 		}
 	}
-	flib_vector_destroy(&tempvector);
+	flib_vector_destroy(tempvector);
 	return result;
 }
--- a/project_files/frontlib/ipc/ipcprotocol.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Tue Jun 12 11:25:05 2012 +0200
@@ -15,7 +15,7 @@
  * 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, ...);
+int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...);
 
 /**
  * Append IPC messages to the buffer that configure the engine for
@@ -27,7 +27,7 @@
  * 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);
+int flib_ipc_append_mapconf(flib_vector *vec, flib_map *map, bool mappreview);
 
 /**
  * Append a seed message to the buffer.
@@ -35,7 +35,7 @@
  * 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);
+int flib_ipc_append_seed(flib_vector *vec, const char *seed);
 
 /**
  * Append the game scheme to the buffer.
@@ -43,8 +43,8 @@
  * 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_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);
+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	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Tue Jun 12 11:25:05 2012 +0200
@@ -4,6 +4,7 @@
 
 #include "../util/logging.h"
 #include "../util/buffer.h"
+#include "../util/util.h"
 
 #include <stdlib.h>
 
@@ -15,8 +16,8 @@
 
 struct _flib_mapconn {
 	uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
-	flib_ipcconn connection;
-	flib_vector configBuffer;
+	flib_ipcconn *connection;
+	flib_vector *configBuffer;
 
 	mapconn_state progress;
 
@@ -38,9 +39,9 @@
 	conn->onFailureCb = &noop_handleFailure;
 }
 
-static flib_vector createConfigBuffer(char *seed, flib_map *mapdesc) {
-	flib_vector result = NULL;
-	flib_vector tempbuffer = flib_vector_create();
+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);
@@ -51,15 +52,15 @@
 			tempbuffer = NULL;
 		}
 	}
-	flib_vector_destroy(&tempbuffer);
+	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));
+	flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn));
 	if(tempConn) {
-		tempConn->connection = flib_ipcconn_create(false, "Player");
+		tempConn->connection = flib_ipcconn_create();
 		tempConn->configBuffer = createConfigBuffer(seed, mapdesc);
 		if(tempConn->connection && tempConn->configBuffer) {
 			tempConn->progress = AWAIT_CONNECTION;
@@ -83,8 +84,8 @@
 			clearCallbacks(conn);
 			conn->destroyRequested = true;
 		} else {
-			flib_ipcconn_destroy(&conn->connection);
-			flib_vector_destroy(&conn->configBuffer);
+			flib_ipcconn_destroy(conn->connection);
+			flib_vector_destroy(conn->configBuffer);
 			free(conn);
 		}
 	}
--- a/project_files/frontlib/model/cfg.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/cfg.c	Tue Jun 12 11:25:05 2012 +0200
@@ -8,7 +8,78 @@
 
 #include <stdio.h>
 
-static void freeCfgMeta(flib_cfg_meta *cfg) {
+static flib_cfg_meta *flib_cfg_meta_from_ini_handleError(flib_cfg_meta *result, dictionary *settingfile, dictionary *modfile) {
+	flib_cfg_meta_destroy(result);
+	iniparser_freedict(settingfile);
+	iniparser_freedict(modfile);
+	return NULL;
+}
+
+flib_cfg_meta *flib_cfg_meta_from_ini(const char *settingpath, const char *modpath) {
+	if(!settingpath || !modpath) {
+		flib_log_e("null parameter in flib_cfg_meta_from_ini");
+		return NULL;
+	}
+	flib_cfg_meta *result = flib_calloc(1, sizeof(flib_cfg_meta));
+	dictionary *settingfile = iniparser_load(settingpath);
+	dictionary *modfile = iniparser_load(modpath);
+
+	if(!result || !settingfile || !modfile) {
+		return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+	}
+
+	result->settingCount = iniparser_getnsec(settingfile);
+	result->modCount = iniparser_getnsec(modfile);
+	result->settings = flib_calloc(result->settingCount, sizeof(flib_cfg_setting_meta));
+	result->mods = flib_calloc(result->modCount, sizeof(flib_cfg_mod_meta));
+
+	if(!result->settings || !result->mods) {
+		return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+	}
+
+	for(int i=0; i<result->settingCount; i++) {
+		char *sectionName = iniparser_getsecname(settingfile, i);
+		if(!sectionName) {
+			return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+		}
+
+		bool error = false;
+		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");
+		result->settings[i].checkOverMax = inihelper_getbool(settingfile, &error, sectionName, "checkOverMax");
+		result->settings[i].times1000 = inihelper_getbool(settingfile, &error, sectionName, "times1000");
+		result->settings[i].min = inihelper_getint(settingfile, &error, sectionName, "min");
+		result->settings[i].max = inihelper_getint(settingfile, &error, sectionName, "max");
+		result->settings[i].def = inihelper_getint(settingfile, &error, sectionName, "default");
+		if(error) {
+			flib_log_e("Missing or malformed ini parameter in file %s, section %s", settingpath, sectionName);
+			return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+		}
+	}
+
+	for(int i=0; i<result->modCount; i++) {
+		char *sectionName = iniparser_getsecname(modfile, i);
+		if(!sectionName) {
+			return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+		}
+
+		bool error = false;
+		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);
+			return flib_cfg_meta_from_ini_handleError(result, settingfile, modfile);
+		}
+	}
+
+	iniparser_freedict(settingfile);
+	iniparser_freedict(modfile);
+	return result;
+}
+
+void flib_cfg_meta_destroy(flib_cfg_meta *cfg) {
 	if(cfg) {
 		if(cfg->settings) {
 			for(int i=0; i<cfg->settingCount; i++) {
@@ -29,90 +100,18 @@
 	}
 }
 
-flib_cfg_meta *flib_cfg_meta_from_ini(const char *settingpath, const char *modpath) {
-	if(!settingpath || !modpath) {
-		return NULL;
-	}
-	flib_cfg_meta *result = calloc(1, sizeof(flib_cfg_meta));
-	dictionary *settingfile = iniparser_load(settingpath);
-	dictionary *modfile = iniparser_load(modpath);
-
-	if(!result || !settingfile || !modfile) {
-		goto handleError;
-	}
-
-	result->settingCount = iniparser_getnsec(settingfile);
-	result->modCount = iniparser_getnsec(modfile);
-	result->settings = calloc(result->settingCount, sizeof(flib_cfg_setting_meta));
-	result->mods = calloc(result->modCount, sizeof(flib_cfg_mod_meta));
-
-	if(!result->settings || !result->mods) {
-		goto handleError;
-	}
-
-	for(int i=0; i<result->settingCount; i++) {
-		char *sectionName = iniparser_getsecname(settingfile, i);
-		if(!sectionName) {
-			goto handleError;
-		}
-
-		bool error = false;
-		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");
-		result->settings[i].checkOverMax = inihelper_getbool(settingfile, &error, sectionName, "checkOverMax");
-		result->settings[i].times1000 = inihelper_getbool(settingfile, &error, sectionName, "times1000");
-		result->settings[i].min = inihelper_getint(settingfile, &error, sectionName, "min");
-		result->settings[i].max = inihelper_getint(settingfile, &error, sectionName, "max");
-		result->settings[i].def = inihelper_getint(settingfile, &error, sectionName, "default");
-		if(error) {
-			flib_log_e("Missing or malformed ini parameter in file %s, section %s", settingpath, sectionName);
-			goto handleError;
-		}
-	}
-
-	for(int i=0; i<result->modCount; i++) {
-		char *sectionName = iniparser_getsecname(modfile, i);
-		if(!sectionName) {
-			goto handleError;
-		}
-
-		bool error = false;
-		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);
-			goto handleError;
-		}
-	}
-
-	iniparser_freedict(settingfile);
-	iniparser_freedict(modfile);
-	return result;
-
-	handleError:
-	freeCfgMeta(result);
-	iniparser_freedict(settingfile);
-	iniparser_freedict(modfile);
-	return NULL;
-}
-
-void flib_cfg_meta_destroy(flib_cfg_meta *metainfo) {
-	freeCfgMeta(metainfo);
-}
-
 flib_cfg *flib_cfg_create(const flib_cfg_meta *meta, const char *schemeName) {
-	flib_cfg *result = calloc(1, sizeof(flib_cfg));
+	flib_cfg *result = flib_calloc(1, sizeof(flib_cfg));
 	if(!meta || !result || !schemeName) {
+		flib_log_e("null parameter in flib_cfg_create");
 		return NULL;
 	}
 
 	result->modCount = meta->modCount;
 	result->settingCount = meta->settingCount;
 	result->schemeName = flib_strdupnull(schemeName);
-	result->mods = calloc(meta->modCount, sizeof(*result->mods));
-	result->settings = calloc(meta->settingCount, sizeof(*result->settings));
+	result->mods = flib_calloc(meta->modCount, sizeof(*result->mods));
+	result->settings = flib_calloc(meta->settingCount, sizeof(*result->settings));
 
 	if(!result->mods || !result->settings || !result->schemeName) {
 		flib_cfg_destroy(result);
@@ -133,6 +132,7 @@
 
 flib_cfg *flib_cfg_from_ini(const flib_cfg_meta *meta, const char *filename) {
 	if(!meta || !filename) {
+		flib_log_e("null parameter in flib_cfg_from_ini");
 		return NULL;
 	}
 	dictionary *settingfile = iniparser_load(filename);
@@ -170,8 +170,13 @@
 
 int flib_cfg_to_ini(const flib_cfg_meta *meta, const char *filename, const flib_cfg *config) {
 	int result = -1;
-	if(meta && filename && config && config->modCount==meta->modCount && config->settingCount==meta->settingCount) {
-		dictionary *dict = dictionary_new(0);
+	if(!meta || !filename || !config || config->modCount!=meta->modCount || config->settingCount!=meta->settingCount) {
+		flib_log_e("Invalid parameter in flib_cfg_to_ini");
+	} else {
+		dictionary *dict = iniparser_load(filename);
+		if(!dict) {
+			dict = dictionary_new(0);
+		}
 		if(dict) {
 			bool error = false;
 			// Add the sections
--- a/project_files/frontlib/model/cfg.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/cfg.h	Tue Jun 12 11:25:05 2012 +0200
@@ -44,11 +44,32 @@
     bool *mods;
 } flib_cfg;
 
+/**
+ * Read the meta-configuration from the relevant .ini files (e.g. which settings exist,
+ * what are their defaults etc.)
+ *
+ * Returns the meta-configuration or NULL. Destroy the meta-configuration with
+ * flib_cfg_meta_destroy.
+ */
 flib_cfg_meta *flib_cfg_meta_from_ini(const char *settingpath, const char *modpath);
 void flib_cfg_meta_destroy(flib_cfg_meta *metainfo);
 
+/**
+ * Create a new configuration with default settings.
+ * Returns NULL on error.
+ */
 flib_cfg *flib_cfg_create(const flib_cfg_meta *meta, const char *schemeName);
+
+/**
+ * Load a configuration from the ini file.
+ * Returns NULL on error.
+ */
 flib_cfg *flib_cfg_from_ini(const flib_cfg_meta *meta, const char *filename);
+
+/**
+ * Store the configuration to an ini file.
+ * Returns NULL on error.
+ */
 int flib_cfg_to_ini(const flib_cfg_meta *meta, const char *filename, const flib_cfg *config);
 void flib_cfg_destroy(flib_cfg* cfg);
 
--- a/project_files/frontlib/model/gamesetup.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/gamesetup.h	Tue Jun 12 11:25:05 2012 +0200
@@ -18,7 +18,7 @@
     char *script;					// optional
     flib_cfg *gamescheme;			// optional
     flib_map *map;					// optional
-	flib_team *teams;				// optional
+	flib_team **teams;				// optional
 	int teamcount;
 } flib_gamesetup;
 
--- a/project_files/frontlib/model/map.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/map.c	Tue Jun 12 11:25:05 2012 +0200
@@ -11,7 +11,7 @@
 	if(!theme) {
 		flib_log_e("null parameter in flib_map_create_regular");
 	} else {
-		flib_map *newmap = calloc(1, sizeof(flib_map));
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
 		if(newmap) {
 			newmap->mapgen = MAPGEN_REGULAR;
 			newmap->templateFilter = templateFilter;
@@ -31,7 +31,7 @@
 	if(!theme) {
 		flib_log_e("null parameter in flib_map_create_maze");
 	} else {
-		flib_map *newmap = calloc(1, sizeof(flib_map));
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
 		if(newmap) {
 			newmap->mapgen = MAPGEN_MAZE;
 			newmap->mazeSize = mazeSize;
@@ -51,7 +51,7 @@
 	if(!name) {
 		flib_log_e("null parameter in flib_map_create_named");
 	} else {
-		flib_map *newmap = calloc(1, sizeof(flib_map));
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
 		if(newmap) {
 			newmap->mapgen = MAPGEN_NAMED;
 			newmap->name = flib_strdupnull(name);
@@ -70,7 +70,7 @@
 	if(!theme || !drawData) {
 		flib_log_e("null parameter in flib_map_create_named");
 	} else {
-		flib_map *newmap = calloc(1, sizeof(flib_map));
+		flib_map *newmap = flib_calloc(1, sizeof(flib_map));
 		if(newmap) {
 			newmap->mapgen = MAPGEN_DRAWN;
 			newmap->drawData = flib_bufdupnull(drawData, drawDataSize);
--- a/project_files/frontlib/model/team.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/team.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,1 +1,109 @@
 #include "team.h"
+
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
+
+static flib_team *from_ini_handleError(flib_team *result, dictionary *settingfile, char **bindingKeys) {
+	if(settingfile) {
+		iniparser_freedict(settingfile);
+	}
+	flib_team_destroy(result);
+	free(bindingKeys);
+	return NULL;
+}
+
+flib_team *flib_team_from_ini(const char *filename) {
+	flib_team *result = flib_calloc(1, sizeof(flib_team));
+	dictionary *settingfile = NULL;
+	char **bindingKeys = NULL;
+
+	if(!filename) {
+		flib_log_e("null parameter in flib_team_from_ini");
+		return from_ini_handleError(result, settingfile, bindingKeys);
+	}
+
+	if(!result) {
+		return from_ini_handleError(result, settingfile, bindingKeys);
+	}
+
+	settingfile = iniparser_load(filename);
+	if(!settingfile) {
+		flib_log_e("Error loading team file %s", filename);
+		return from_ini_handleError(result, settingfile, bindingKeys);
+	}
+
+	bool error = false;
+	result->name = inihelper_getstringdup(settingfile, &error, "team", "name");
+	result->grave = inihelper_getstringdup(settingfile, &error, "team", "grave");
+	result->fort = inihelper_getstringdup(settingfile, &error, "team", "fort");
+	result->voicepack = inihelper_getstringdup(settingfile, &error, "team", "voicepack");
+	result->flag = inihelper_getstringdup(settingfile, &error, "team", "flag");
+	result->rounds = inihelper_getint(settingfile, &error, "team", "rounds");
+	result->wins = inihelper_getint(settingfile, &error, "team", "wins");
+	result->campaignProgress = inihelper_getint(settingfile, &error, "team", "campaignprogress");
+	int difficulty = inihelper_getint(settingfile, &error, "team", "difficulty");
+
+	char sectionName[10];
+	strcpy(sectionName, "hedgehog0");
+	for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+		sectionName[8] = '0'+i;
+		result->hogs[i].name = inihelper_getstringdup(settingfile, &error, sectionName, "name");
+		result->hogs[i].hat = inihelper_getstringdup(settingfile, &error, sectionName, "hat");
+		result->hogs[i].rounds = inihelper_getint(settingfile, &error, sectionName, "rounds");
+		result->hogs[i].kills = inihelper_getint(settingfile, &error, sectionName, "kills");
+		result->hogs[i].deaths = inihelper_getint(settingfile, &error, sectionName, "deaths");
+		result->hogs[i].suicides = inihelper_getint(settingfile, &error, sectionName, "suicides");
+		result->hogs[i].difficulty = difficulty;
+		result->hogs[i].initialHealth = TEAM_DEFAULT_HEALTH;
+	}
+
+	result->bindingCount = iniparser_getsecnkeys(settingfile, "binds");
+	result->bindings = flib_calloc(result->bindingCount, sizeof(flib_binding));
+	bindingKeys = iniparser_getseckeys(settingfile, "binds");
+	if(!result->bindings || !bindingKeys) {
+		return from_ini_handleError(result, settingfile, bindingKeys);
+	}
+
+	for(int i=0; i<result->bindingCount; i++) {
+		result->bindings[i].binding = flib_strdupnull(iniparser_getstring(settingfile, bindingKeys[i], NULL));
+		// The key names all start with "binds:", so we skip that.
+		result->bindings[i].action = inihelper_urldecode(bindingKeys[i]+strlen("binds:"));
+		if(!result->bindings[i].action || !result->bindings[i].binding) {
+			error = true;
+		}
+	}
+
+	if(error) {
+		flib_log_e("Error reading team file %s", filename);
+		return from_ini_handleError(result, settingfile, bindingKeys);
+	}
+
+	iniparser_freedict(settingfile);
+	free(bindingKeys);
+	return result;
+}
+
+void flib_team_destroy(flib_team *team) {
+	if(team) {
+		for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) {
+			free(team->hogs[i].name);
+			free(team->hogs[i].hat);
+		}
+		free(team->name);
+		free(team->grave);
+		free(team->fort);
+		free(team->voicepack);
+		free(team->flag);
+		if(team->bindings) {
+			for(int i=0; i<team->bindingCount; i++) {
+				free(team->bindings[i].action);
+				free(team->bindings[i].binding);
+			}
+		}
+		free(team->bindings);
+		free(team->hash);
+		flib_weaponset_destroy(team->weaponset);
+		free(team);
+	}
+}
--- a/project_files/frontlib/model/team.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/team.h	Tue Jun 12 11:25:05 2012 +0200
@@ -12,6 +12,13 @@
 #define TEAM_DEFAULT_DIFFICULTY 0
 #define TEAM_DEFAULT_HEALTH 100
 
+// TODO default bindings?
+
+typedef struct {
+	char *action;
+	char *binding;
+} flib_binding;
+
 typedef struct {
 	char *name;
 	char *hat;
@@ -19,12 +26,13 @@
 	// Statistics. They are irrelevant for the engine or server,
 	// but provided for ini reading/writing by the frontend.
 	int rounds;
+	int kills;
 	int deaths;
-	int kills;
 	int suicides;
 
-	// These settings are sometimes used on a per-team basis.
 	int difficulty;
+
+	// Transient setting used in game setup
 	int initialHealth;
 } flib_hog;
 
@@ -36,16 +44,43 @@
 	char *voicepack;
 	char *flag;
 
-	// TODO binds
+	flib_binding *bindings;
+	int bindingCount;
+
+	// Statistics. They are irrelevant for the engine or server,
+	// but provided for ini reading/writing by the frontend.
+	int rounds;
+	int wins;
+	int campaignProgress;
 
 	// Transient settings used in game setup
 	uint32_t color;
 	int hogsInGame;
 	bool remoteDriven;
-	char *hash;
+	char *hash; // TODO calculate
 
-	// This setting is sometimes used on a per-game basis.
 	flib_weaponset *weaponset;
 } flib_team;
 
+/**
+ * Returns a new team, or NULL on error. name must not be NULL.
+ *
+ * The new team is pre-filled with default settings (see hwconsts.h)
+ */
+flib_team *flib_team_create(const char *name);
+
+/**
+ * Loads a team, returns NULL on error.
+ */
+flib_team *flib_team_from_ini(const char *filename);
+
+/**
+ * Write the team to an ini file. Attempts to retain extra ini settings
+ * that were already present. Note that not all fields of a team struct
+ * are stored in the ini, some are only used intermittently to store
+ * information about a team in the context of a game.
+ */
+int flib_team_to_ini(const char *filename, const flib_team *team);
+void flib_team_destroy(flib_team *team);
+
 #endif /* TEAM_H_ */
--- a/project_files/frontlib/model/weapon.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/weapon.c	Tue Jun 12 11:25:05 2012 +0200
@@ -38,7 +38,7 @@
 	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));
+		flib_weaponset *newSet = flib_calloc(1, sizeof(flib_weaponset));
 		char *nameCopy = flib_strdupnull(name);
 		if(newSet && nameCopy) {
 			newSet->name = nameCopy;
@@ -101,7 +101,10 @@
 	if(!filename || !set) {
 		flib_log_e("null parameter in flib_weaponset_to_ini");
 	} else {
-		dictionary *dict = dictionary_new(0);
+		dictionary *dict = iniparser_load(filename);
+		if(!dict) {
+			dict = dictionary_new(0);
+		}
 		if(dict) {
 			bool error = false;
 			// Add the sections
--- a/project_files/frontlib/model/weapon.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/model/weapon.h	Tue Jun 12 11:25:05 2012 +0200
@@ -7,7 +7,7 @@
  * 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)
+ * For the other setting, 9 is invalid.
  */
 typedef struct {
 	char loadout[WEAPONS_COUNT+1];
@@ -25,7 +25,16 @@
  * settings (see hwconsts.h)
  */
 flib_weaponset *flib_weaponset_create(const char *name);
+
+/**
+ * Loads a weapon set, returns NULL on error.
+ */
 flib_weaponset *flib_weaponset_from_ini(const char *filename);
+
+/**
+ * Write the weapon set to an ini file. Attempts to
+ * retain extra ini settings that were already present.
+ */
 int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set);
 void flib_weaponset_destroy(flib_weaponset *set);
 
--- a/project_files/frontlib/socket.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/socket.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,5 +1,6 @@
 #include "socket.h"
 #include "util/logging.h"
+#include "util/util.h"
 #include <stdlib.h>
 #include <SDL_net.h>
 #include <time.h>
@@ -23,10 +24,9 @@
 	return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
 }
 
-static flib_tcpsocket flib_socket_create(TCPsocket sdlsock) {
-	flib_tcpsocket result = malloc(sizeof(_flib_tcpsocket));
+static flib_tcpsocket *flib_socket_create(TCPsocket sdlsock) {
+	flib_tcpsocket *result = flib_calloc(1, sizeof(_flib_tcpsocket));
 	if(!result) {
-		flib_log_e("Can't allocate socket: Out of memory!");
 		return NULL;
 	}
 	result->sock = sdlsock;
@@ -43,10 +43,9 @@
 	return result;
 }
 
-flib_acceptor flib_acceptor_create(uint16_t port) {
-	flib_acceptor result = malloc(sizeof(_flib_acceptor));
+flib_acceptor *flib_acceptor_create(uint16_t port) {
+	flib_acceptor *result = flib_calloc(1, sizeof(_flib_acceptor));
 	if(!result) {
-		flib_log_e("Can't allocate acceptor: Out of memory!");
 		return NULL;
 	}
 
@@ -86,7 +85,7 @@
 	}
 }
 
-uint16_t flib_acceptor_listenport(flib_acceptor acceptor) {
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) {
 	if(!acceptor) {
 		flib_log_e("Call to flib_acceptor_listenport with acceptor==null");
 		return 0;
@@ -94,22 +93,19 @@
 	return acceptor->port;
 }
 
-void flib_acceptor_close(flib_acceptor *acceptorptr) {
-	if(!acceptorptr) {
-		flib_log_e("Call to flib_acceptor_close with acceptorptr==null");
-	} else if(*acceptorptr) {
-		SDLNet_TCP_Close((*acceptorptr)->sock);
-		free(*acceptorptr);
-		*acceptorptr = NULL;
+void flib_acceptor_close(flib_acceptor *acceptor) {
+	if(acceptor) {
+		SDLNet_TCP_Close(acceptor->sock);
+		free(acceptor);
 	}
 }
 
-flib_tcpsocket flib_socket_accept(flib_acceptor acceptor, bool localOnly) {
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) {
 	if(!acceptor) {
 		flib_log_e("Call to flib_socket_accept with acceptor==null");
 		return NULL;
 	}
-	flib_tcpsocket result = NULL;
+	flib_tcpsocket *result = NULL;
 	TCPsocket sock = NULL;
 	while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
 		if(localOnly && !connection_is_local(sock)) {
@@ -125,20 +121,16 @@
 	return result;
 }
 
-void flib_socket_close(flib_tcpsocket *sockptr) {
-	if(!sockptr) {
-		flib_log_e("Call to flib_socket_close with sockptr==null");
-	} else if(*sockptr) {
-		flib_tcpsocket sock = *sockptr;
+void flib_socket_close(flib_tcpsocket *sock) {
+	if(sock) {
 		SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
 		SDLNet_TCP_Close(sock->sock);
 		SDLNet_FreeSocketSet(sock->sockset);
 		free(sock);
-		*sockptr = NULL;
 	}
 }
 
-int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen) {
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen) {
 	if(!sock || (maxlen>0 && !data)) {
 		flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null");
 		return -1;
@@ -155,7 +147,7 @@
 	}
 }
 
-int flib_socket_send(flib_tcpsocket sock, const 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	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/socket.h	Tue Jun 12 11:25:05 2012 +0200
@@ -16,10 +16,10 @@
 #include <stdint.h>
 
 struct _flib_tcpsocket;
-typedef struct _flib_tcpsocket *flib_tcpsocket;
+typedef struct _flib_tcpsocket flib_tcpsocket;
 
 struct _flib_acceptor;
-typedef struct _flib_acceptor *flib_acceptor;
+typedef struct _flib_acceptor flib_acceptor;
 
 /**
  * Create a new acceptor which will listen for incoming TCP connections
@@ -28,28 +28,28 @@
  *
  * Can return NULL on error.
  */
-flib_acceptor flib_acceptor_create(uint16_t port);
+flib_acceptor *flib_acceptor_create(uint16_t port);
 
 /**
  * Return the port on which the acceptor is listening.
  */
-uint16_t flib_acceptor_listenport(flib_acceptor acceptor);
+uint16_t flib_acceptor_listenport(flib_acceptor *acceptor);
 
 /**
- * Close the acceptor, free its memory and set it to NULL.
+ * Close the acceptor and free its memory.
  * If the acceptor is already NULL, nothing happens.
  */
-void flib_acceptor_close(flib_acceptor *acceptorptr);
+void flib_acceptor_close(flib_acceptor *acceptor);
 
 /**
  * Try to accept a connection from an acceptor (listening socket).
  * if localOnly is true, this will only accept connections which came from 127.0.0.1
  * Returns NULL if nothing can be accepted.
  */
-flib_tcpsocket flib_socket_accept(flib_acceptor acceptor, bool localOnly);
+flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly);
 
 /**
- * Close the socket, free its memory and set it to NULL.
+ * Close the socket and free its memory.
  * If the socket is already NULL, nothing happens.
  */
 void flib_socket_close(flib_tcpsocket *socket);
@@ -60,8 +60,8 @@
  * Returns the ammount of data received, 0 if there was nothing to receive,
  * or a negative number if the connection was closed or an error occurred.
  */
-int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen);
+int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen);
 
-int flib_socket_send(flib_tcpsocket sock, const 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/test.c	Tue Jun 12 11:25:05 2012 +0200
@@ -0,0 +1,178 @@
+#include "frontlib.h"
+#include "util/logging.h"
+#include "model/map.h"
+#include "ipc/mapconn.h"
+#include "ipc/gameconn.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+
+// Callback function that will be called when the map is rendered
+static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {
+	printf("Drawing map for %i brave little hogs...", numHedgehogs);
+
+	// Draw the map as ASCII art
+	for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
+		for(int x=0; x<MAPIMAGE_WIDTH; x++) {
+			int pixelnum = x + y*MAPIMAGE_WIDTH;
+			bool pixel = bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)));
+			printf(pixel ? "#" : " ");
+		}
+		printf("\n");
+	}
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn **connptr = context;
+	flib_gameconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+	fwrite(record, 1, size, file);
+	fclose(file);
+}
+
+// Callback function that will be called on error
+static void handleMapFailure(void *context, const char *errormessage) {
+	flib_log_e("Map rendering failed: %s", errormessage);
+
+	// Destroy the connection object (this will end the "tick" loop below)
+	flib_mapconn **connptr = context;
+	flib_mapconn_destroy(*connptr);
+	*connptr = NULL;
+}
+
+static void startEngineMap(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s %i landpreview", enginePath, configPath, port);
+	system(commandbuffer);
+}
+
+static void startEngineGame(int port) {
+	char commandbuffer[255];
+	const char *enginePath = "C:\\Programmieren\\Hedgewars\\bin";
+	const char *configPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars";
+	const char *dataPath = "C:\\Programmieren\\Hedgewars\\share\\hedgewars\\Data";
+	snprintf(commandbuffer, 255, "start %s\\hwengine.exe %s 1024 768 32 %i 0 0 0 10 10 %s 0 0 TWVkbzQy 0 0 en.txt", enginePath, configPath, port, dataPath);
+	flib_log_d("Starting engine with CMD: %s", commandbuffer);
+	system(commandbuffer);
+}
+
+void testMapPreview() {
+	// Create a map description and check that there was no error
+	flib_map *map = flib_map_create_maze("Jungle", MAZE_SIZE_SMALL_TUNNELS);
+	assert(map);
+
+	// Create a new connection to the engine and check that there was no error
+	flib_mapconn *mapConnection = flib_mapconn_create("This is the seed value", map);
+	assert(mapConnection);
+
+	// We don't need the map description anymore
+	flib_map_destroy(map);
+	map = NULL;
+
+	// Register the callback functions
+	flib_mapconn_onFailure(mapConnection, &handleMapFailure, &mapConnection);
+	flib_mapconn_onSuccess(mapConnection, &handleMapSuccess, &mapConnection);
+
+	// Start the engine process and tell it which port the frontlib is listening on
+	startEngineMap(flib_mapconn_getport(mapConnection));
+
+	// Usually, flib_mapconn_tick will be called in an event loop that runs several
+	// times per second. It handles I/O operations and progress, and calls
+	// callbacks when something interesting happens.
+	while(mapConnection) {
+		flib_mapconn_tick(mapConnection);
+	}
+}
+
+void testGame() {
+	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("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.script = NULL;
+	setup.teamcount = 2;
+	setup.teams = calloc(2, sizeof(flib_team*));
+	setup.teams[0] = calloc(1, sizeof(flib_team));
+	setup.teams[0]->color = 0xffff0000;
+	setup.teams[0]->flag = "australia";
+	setup.teams[0]->fort = "Plane";
+	setup.teams[0]->grave = "Bone";
+	setup.teams[0]->hogsInGame = 2;
+	setup.teams[0]->name = "Team Awesome";
+	setup.teams[0]->voicepack = "British";
+	setup.teams[0]->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] = flib_team_from_ini("Cave Dwellers.hwt");
+	setup.teams[1]->color = 0xff0000ff;
+	setup.teams[1]->hogsInGame = 8;
+	setup.teams[1]->weaponset = flib_weaponset_create("Defaultweaps");
+
+	flib_gameconn *gameconn = flib_gameconn_create("Medo42", metaconf, &setup, false);
+	assert(gameconn);
+
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+void testDemo() {
+	FILE *demofile = fopen("testdemo.42.hwd", "rb");
+	assert(demofile);
+	flib_vector *vec = flib_vector_create();
+	uint8_t demobuf[512];
+	int len;
+	while((len=fread(demobuf, 1, 512, demofile))>0) {
+		flib_vector_append(vec, demobuf, len);
+	}
+	fclose(demofile);
+	flib_constbuffer constbuf = flib_vector_as_constbuffer(vec);
+	flib_gameconn *gameconn = flib_gameconn_create_playdemo(constbuf.data, constbuf.size);
+	flib_vector_destroy(vec);
+	assert(gameconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
+	startEngineGame(flib_gameconn_getport(gameconn));
+
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
+	}
+}
+
+int main(int argc, char *argv[]) {
+	flib_init(0);
+	flib_log_setLevel(FLIB_LOGLEVEL_ALL);
+
+	//testMapPreview();
+	testDemo();
+
+	flib_quit();
+	return 0;
+}
--- a/project_files/frontlib/util/buffer.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/buffer.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,40 +1,43 @@
 #include "buffer.h"
 #include "logging.h"
+#include "util.h"
 
 #include <stdlib.h>
 #include <limits.h>
 #include <string.h>
 
+#define MIN_VECTOR_CAPACITY 16
+
 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;
+flib_vector *flib_vector_create() {
+	flib_vector *result = NULL;
+	flib_vector *tmpVector = flib_calloc(1, sizeof(_flib_vector));
+	if(tmpVector) {
+		tmpVector->data = flib_malloc(MIN_VECTOR_CAPACITY);
+		if(tmpVector->data) {
+			tmpVector->size = 0;
+			tmpVector->capacity = MIN_VECTOR_CAPACITY;
+			result = tmpVector;
+			tmpVector = NULL;
+		}
 	}
-	result->data = malloc(16);
-	if(result->data == NULL) {
-		free(result);
-		return NULL;
-	}
-	result->size = 0;
-	result->capacity = 16;
+	flib_vector_destroy(tmpVector);
 	return result;
 }
 
 void flib_vector_destroy(flib_vector *vec) {
-	if(vec && *vec) {
-		free((*vec)->data);
-		free(*vec);
-		*vec = NULL;
+	if(vec) {
+		free(vec->data);
+		free(vec);
 	}
 }
 
-static void try_realloc(flib_vector vec, size_t newCapacity) {
+static void try_realloc(flib_vector *vec, size_t newCapacity) {
 	void *newData = realloc(vec->data, newCapacity);
 	if(newData) {
 		vec->data = newData;
@@ -42,11 +45,16 @@
 	}
 }
 
-static size_t getFreeCapacity(flib_vector vec) {
+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) {
+int flib_vector_append(flib_vector *vec, const void *data, size_t len) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_append");
+		return 0;
+	}
+
 	if(getFreeCapacity(vec) < len) {
 		// Resize exponentially for constant amortized time,
 		// But at least by as much as we need of course,
@@ -63,7 +71,7 @@
 		}
 
 		// Check if we were able to resize.
-		// If not, try to allocate at least what we need...
+		// If not, try to allocate at least what we need.
 		if(getFreeCapacity(vec) < len) {
 			try_realloc(vec, vec->capacity+minExtraCapacity);
 
@@ -74,17 +82,47 @@
 		}
 	}
 
-	memmove(vec->data + vec->size, data, len);
+	memmove(((uint8_t*)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_buffer flib_vector_as_buffer(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_as_buffer");
+		flib_buffer result = {NULL, 0};
+		return result;
+	} else {
+		flib_buffer result = {vec->data, vec->size};
+		return result;
+	}
 }
 
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
-	flib_constbuffer result = {vec->data, vec->size};
-	return result;
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_as_constbuffer");
+		flib_constbuffer result = {NULL, 0};
+		return result;
+	} else {
+		flib_constbuffer result = {vec->data, vec->size};
+		return result;
+	}
 }
+
+void *flib_vector_data(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_data");
+		return NULL;
+	} else {
+		return vec->data;
+	}
+}
+
+size_t flib_vector_size(flib_vector *vec) {
+	if(!vec) {
+		flib_log_e("null parameter in flib_vector_size");
+		return 0;
+	} else {
+		return vec->size;
+	}
+}
--- a/project_files/frontlib/util/buffer.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/buffer.h	Tue Jun 12 11:25:05 2012 +0200
@@ -24,35 +24,50 @@
 } flib_constbuffer;
 
 /**
- * Simple variable-capacity data structure (opaque type).
+ * Simple variable-capacity data structure that can be efficiently appended to.
  */
 struct _flib_vector;
-typedef struct _flib_vector *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();
+flib_vector *flib_vector_create();
 
 /**
- * Free the memory of this vector and set it to NULL.
+ * Free the memory of this vector
  */
 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.
+ * The vector remains unchanged if appending fails.
  */
-int flib_vector_append(flib_vector vec, const void *data, size_t len);
+int flib_vector_append(flib_vector *vec, const void *data, size_t len);
+
+/**
+ * Return a pointer to the current data buffer of the vector. This pointer can
+ * become invalid if the vector size or capacity is changed.
+ */
+void *flib_vector_data(flib_vector *vec);
 
 /**
- * Return a buffer or constbuffer pointing to the current contents of the vector.
+ * Return the current size of the vector.
+ */
+size_t flib_vector_size(flib_vector *vec);
+
+/**
+ * Return a buffer pointing to the current contents of the vector.
  * These will become invalid if the vector size or capacity is changed.
  */
-flib_buffer flib_vector_as_buffer(flib_vector vec);
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
+flib_buffer flib_vector_as_buffer(flib_vector *vec);
 
+/**
+ * Return a constbuffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_constbuffer flib_vector_as_constbuffer(flib_vector *vec);
 
 #endif
--- a/project_files/frontlib/util/inihelper.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/inihelper.c	Tue Jun 12 11:25:05 2012 +0200
@@ -22,7 +22,7 @@
 		return NULL;
 	}
 
-	char *outbuf = malloc(insize*3+1);
+	char *outbuf = flib_malloc(insize*3+1);
 	if(!outbuf) {
 		return NULL;
 	}
@@ -41,11 +41,12 @@
         }
     }
     outbuf[outpos] = 0;
-    return outbuf;
+    char *shrunk = realloc(outbuf, outpos+1);
+    return shrunk ? shrunk : outbuf;
 }
 
 char *inihelper_urldecode(const char *inbuf) {
-	char *outbuf = malloc(strlen(inbuf)+1);
+	char *outbuf = flib_malloc(strlen(inbuf)+1);
 	if(!outbuf) {
 		return NULL;
 	}
@@ -61,11 +62,13 @@
         }
     }
     outbuf[outpos] = 0;
-    return outbuf;
+    char *shrunk = realloc(outbuf, outpos+1);
+    return shrunk ? shrunk : outbuf;
 }
 
 char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
 	if(!sectionName || !keyName) {
+		flib_log_e("null parameter in inihelper_createDictKey");
 		return NULL;
 	}
 	return flib_asprintf("%s:%s", sectionName, keyName);
@@ -73,6 +76,7 @@
 
 char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
 	if(!inifile || !sectionName || !keyName) {
+		flib_log_e("null parameter in inihelper_getstring");
 		*error = true;
 		return NULL;
 	}
@@ -84,7 +88,7 @@
 	char *result = iniparser_getstring(inifile, extendedkey, NULL);
 	free(extendedkey);
 	if(!result) {
-		flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
+		flib_log_d("Missing ini setting: %s/%s", sectionName, keyName);
 		*error = true;
 	}
 	return result;
@@ -102,10 +106,12 @@
 		errno = 0;
 		long val = strtol(value, NULL, 10);
 		if(errno!=0) {
+			flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", sectionName, keyName, value);
 			*error = true;
 			return 0;
 		}
 		if(val<INT_MIN || val>INT_MAX) {
+			flib_log_w("ini setting %s/%s = \"%s\" is too large or too small.", sectionName, keyName, value);
 			*error = true;
 			return 0;
 		}
@@ -121,6 +127,7 @@
 		bool trueval = strchr("1tTyY", value[0]);
 		bool falseval = strchr("0fFnN", value[0]);
 		if(!trueval && !falseval) {
+			flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", sectionName, keyName, value);
 			*error = true;
 			return false;
 		} else {
@@ -132,13 +139,13 @@
 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");
+		flib_log_e("null parameter in inihelper_setstr");
 	} else {
 		char *extendedkey = inihelper_createDictKey(sectionName, keyName);
 		if(extendedkey) {
 			result = iniparser_set(dict, extendedkey, value);
-			free(extendedkey);
 		}
+		free(extendedkey);
 	}
 	return result;
 }
@@ -146,7 +153,7 @@
 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");
+		flib_log_e("null parameter in inihelper_setint");
 	} else {
 		char *strvalue = flib_asprintf("%i", value);
 		if(strvalue) {
@@ -160,7 +167,7 @@
 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");
+		flib_log_e("null parameter in inihelper_setbool");
 	} else {
 		result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
 	}
--- a/project_files/frontlib/util/inihelper.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/inihelper.h	Tue Jun 12 11:25:05 2012 +0200
@@ -1,6 +1,9 @@
 /**
- * Some helper functions for working with the iniparser functions - in particular,
- * for interoperability with the ini format used by the QtSettings class.
+ * Convenience interface for ini reading/writing.
+ *
+ * We currently use iniparser in the background, but using its interface directly is a bit verbose.
+ * This module is supposed to 1. make ini reading and writing a bit more convenient, and 2. hide
+ * the iniparser dependency so it can at need be easily replaced.
  */
 
 #ifndef INIHELPER_H_
@@ -10,6 +13,9 @@
 
 #include <stdbool.h>
 
+struct _flib_ini;
+typedef struct _flib_inihelper flib_ini;
+
 /**
  * Returned buffer must be free()d
  */
@@ -48,7 +54,20 @@
  */
 bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
 
+/**
+ * Returns a nonzero value on error.
+ */
 int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
+
+/**
+ * Returns a nonzero value on error.
+ */
 int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
+
+/**
+ * Set an ini setting to "true" or "false".
+ * Returns a nonzero value on error.
+ */
 int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
+
 #endif /* INIHELPER_H_ */
--- a/project_files/frontlib/util/logging.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/logging.c	Tue Jun 12 11:25:05 2012 +0200
@@ -85,3 +85,7 @@
 void flib_log_setFile(FILE *file) {
 	flib_logfile = file;
 }
+
+bool flib_log_isActive(int level) {
+	return level >= flib_log_getLevel();
+}
--- a/project_files/frontlib/util/logging.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/logging.h	Tue Jun 12 11:25:05 2012 +0200
@@ -1,8 +1,9 @@
 #ifndef LOGGING_H_
 #define LOGGING_H_
 
-#include<stdint.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #define FLIB_LOGLEVEL_ALL -100
 #define FLIB_LOGLEVEL_DEBUG -1
@@ -11,6 +12,9 @@
 #define FLIB_LOGLEVEL_ERROR 2
 #define FLIB_LOGLEVEL_NONE 100
 
+/**
+ * Returns a pointer to a static buffer, don't free or store.
+ */
 char* flib_format_ip(uint32_t numip);
 
 void flib_log_e(const char *fmt, ...);
@@ -21,5 +25,6 @@
 int flib_log_getLevel();
 void flib_log_setLevel(int level);
 void flib_log_setFile(FILE *logfile);
+bool flib_log_isActive(int level);
 
 #endif /* LOGGING_H_ */
--- a/project_files/frontlib/util/util.c	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/util.c	Tue Jun 12 11:25:05 2012 +0200
@@ -1,4 +1,5 @@
 #include "util.h"
+#include "logging.h"
 
 #include <stddef.h>
 #include <stdarg.h>
@@ -16,16 +17,20 @@
 
 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.
+	if(!fmt) {
+		flib_log_e("null parameter in flib_vasprintf");
+	} else {
+		int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;					// Figure out how much memory we need,
+		if(requiredSize<0) {
+			flib_log_e("Error formatting string with template \"%s\" in flib_vasprintf", fmt);
+		} else {
+			char *tmpbuf = flib_malloc(requiredSize);						// allocate it
+			if(tmpbuf && vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) {	// and then do the actual formatting.
 				result = tmpbuf;
 				tmpbuf = NULL;
 			}
+			free(tmpbuf);
 		}
-		free(tmpbuf);
 	}
 	return result;
 }
@@ -41,9 +46,25 @@
 	if(!buf || size==0) {
 		return NULL;
 	}
-	void *result = malloc(size);
+	void *result = flib_malloc(size);
 	if(result) {
 		memcpy(result, buf, size);
 	}
 	return result;
 }
+
+void *flib_malloc(size_t size) {
+	void *result = malloc(size);
+	if(!result) {
+		flib_log_e("Out of memory trying to malloc %zu bytes.", size);
+	}
+	return result;
+}
+
+void *flib_calloc(size_t count, size_t elementsize) {
+	void *result = calloc(count, elementsize);
+	if(!result) {
+		flib_log_e("Out of memory trying to calloc %zu objects of %zu bytes each.", count, elementsize);
+	}
+	return result;
+}
--- a/project_files/frontlib/util/util.h	Mon Jun 11 00:06:22 2012 +0200
+++ b/project_files/frontlib/util/util.h	Tue Jun 12 11:25:05 2012 +0200
@@ -13,7 +13,7 @@
 char *flib_asprintf(const char *fmt, ...);
 
 /**
- * Exactly as flib_asprintf, but accepts va_args.
+ * Exactly as flib_asprintf, but accepts a va_list.
  */
 char *flib_vasprintf(const char *fmt, va_list args);
 
@@ -33,4 +33,16 @@
  */
 void *flib_bufdupnull(const void *buf, size_t size);
 
+/**
+ * Simple malloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like malloc.
+ */
+void *flib_malloc(size_t size);
+
+/**
+ * Simple calloc wrapper that automatically logs an error if no memory
+ * is available. Otherwise behaves exactly like calloc.
+ */
+void *flib_calloc(size_t count, size_t elementsize);
+
 #endif