project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java
changeset 7485 0481bd74267c
parent 7476 2fb781bbdd51
child 7508 763d3961400b
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,4 +1,5 @@
 package org.hedgewars.hedgeroid.frontlib;
+import java.io.UnsupportedEncodingException;
 import java.nio.Buffer;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -6,15 +7,17 @@
 import java.util.Map;
 
 import org.hedgewars.hedgeroid.Datastructures.Hog;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
 import org.hedgewars.hedgeroid.Datastructures.MetaScheme;
 import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Mod;
 import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Setting;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
 import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom;
 import org.hedgewars.hedgeroid.Datastructures.Scheme;
 import org.hedgewars.hedgeroid.Datastructures.Team;
 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
-import org.hedgewars.hedgeroid.EngineProtocol.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
 
 import com.sun.jna.Callback;
 import com.sun.jna.Library;
@@ -23,56 +26,126 @@
 import com.sun.jna.Pointer;
 import com.sun.jna.PointerType;
 import com.sun.jna.Structure;
+import com.sun.jna.ptr.IntByReference;
 
+/**
+ * Here is an introduction to the most important aspects of the JNA code.
+ * 
+ * This interface permits access to the Hedgewars frontend library (frontlib)
+ * from Java. Each function directly contained in the Frontlib interface
+ * represents a mapped C function. The Structure classes (ending in -Struct) are
+ * mappings of C structs, and the PointerType classes (ending in -Ptr) represent
+ * pointers to structs.
+ * 
+ * Quick notes for USING these classes from outside this package:
+ * 
+ * Usage should be fairly straightforward, but there are a few surprising
+ * gotchas. First, when you implement callbacks, YOU are responsible for
+ * ensuring that the callback objects are not garbage-collected while they might
+ * still be called! So make sure you keep them in member variables or similar,
+ * because Java will not know if there are still native references to them.
+ * 
+ * When using Frontlib from outside its package, you only interact with structs
+ * via the PointerType classes. They allow you to get at the data of the struct
+ * with a function called deref(), which creates a plain normal Java object
+ * representing the data (e.g. SchemePtr.deref() will give you a Scheme object).
+ * 
+ * Remember that you usually have to destroy structs that you receive from the
+ * library, because they are owned by the native code, not Java. For example, if
+ * you obtain a {@link MetaschemePtr} metaPtr using flib_metascheme_from_ini,
+ * you have to call flib_metascheme_release(metaPtr) after you are done using
+ * it. The recommended pattern for most cases is to call deref() on the pointer
+ * to get a Java object (that you can keep as long as you like), and then
+ * immediately destroy the struct if it needs destroying. To find out whether
+ * and how the struct needs to be destroyed, see the library's documentation of
+ * the function that you got the struct from.
+ * 
+ * To pass new structs to the library, you can use the static createJavaOwned()
+ * function in each PointerType, which creates a new struct from the Java object
+ * you provide, and returns a pointer to that struct that you can pass to
+ * library functions. This new structure's memory is owned and managed by Java
+ * code, so do not destroy it with frontlib functions!
+ * 
+ * There is a slight mismatch between the data model for the game setup. The
+ * frontlib supports setting initial health and weaponset per-hog, because the
+ * engine allows for that, but currently neither the networking protocol nor the
+ * PC frontend support this feature, so the Android version does not take
+ * advantage of it either and treats both as per-game settings. The initial
+ * health is contained in the game scheme, the weaponset is explicitly part of
+ * the GameConfig. When converting GameConfig to a native flib_gamesetup, both
+ * are automatically copied to all hogs in the game, and for the reverse
+ * conversion the weaponset of the first hog of the first team is used as the
+ * GameConfig weaponset. This means that GameConfig.weaponset will be null if
+ * there are no teams in the game.
+ * 
+ * When starting a network game, you only need to query the GameSetupPtr from
+ * the netconn and use it to create the gameconn - this is preferable to using
+ * your own recreation of the game setup, because that way the same piece of
+ * code is used to determine the game setup on all platforms.
+ * 
+ * The "context" parameter of the callbacks is never needed here because JNA
+ * generates function code for each callback object. Don't worry about it, just
+ * pass null for context and ignore the context parameter in the callbacks.
+ * 
+ * Finally, the library functions are documented in the actual library, not
+ * here, so check the docs there to find out what exactly each function does!
+ * 
+ * Notes about the structure of this class (for the next one who has to touch
+ * this...):
+ * 
+ * Java/C interop is quite fiddly and error-prone, so as long as things work,
+ * try to stick to the established patterns.
+ * 
+ * Structure types should always be hidden from the outside world, because they
+ * can be misused too easily. For example, if you get a Structure from the
+ * library, change a String value in there and pass it back, JNA will re-write
+ * that string using Java-owned memory without freeing the old native-owned
+ * string, which causes a memory leak and possibly a double-free or other Bad
+ * Things (tm). To avoid problems like this, Structure types are only used
+ * internally, to map existing structures to Java types (without modifying them)
+ * or to create brand-new, Java-owned structures. Both operations are exposed to
+ * the outside through the PointerType classes corresponding to the structures
+ * in question.
+ * 
+ * Since all of the struct mapping happens in Java, it is never checked against
+ * the actual struct declarations in the library. That means strange things can
+ * start happening at runtime if the frontlib structs are modified without
+ * changing the mappings here to match. This also applies to the function
+ * signatures: JNA checks whether the functions actually exist when loading the
+ * library, but it has no way of knowing whether the signatures are correct. If
+ * the signatures here deviate from those in the frontlib, you might get stack
+ * corruption.
+ * 
+ * In order to check at least the function signatures, take a look at the file
+ * extra/jnacontrol.c in the frontlib sources. You can validate whether the
+ * function signatures are still correct by copy-pasting them into jnaControl.c
+ * and compiling it against the frontlib headers. The typedefs and #defines in
+ * that file will make the compiler see the Java method signatures as C function
+ * declarations. Since the same functions are already declared in the frontlib
+ * headers, the compiler will give you errors if the signatures don't match.
+ */
 public interface Frontlib extends Library {
 	static final int NATIVE_INT_SIZE = 4;
 	static final int NATIVE_BOOL_SIZE = 1;
 	
-	static final int NETCONN_STATE_CONNECTING = 0;
-	static final int NETCONN_STATE_LOBBY = 1;
-	static final int NETCONN_STATE_ROOM = 2;
-	static final int NETCONN_STATE_INGAME = 3;
-	static final int NETCONN_STATE_DISCONNECTED = 10;
-	
-	static final int NETCONN_DISCONNECT_NORMAL = 0;
-	static final int NETCONN_DISCONNECT_SERVER_TOO_OLD = 1;
-	static final int NETCONN_DISCONNECT_AUTH_FAILED = 2;
-	static final int NETCONN_DISCONNECT_CONNLOST = 3;
-	static final int NETCONN_DISCONNECT_INTERNAL_ERROR = 100;
-	
-	static final int NETCONN_ROOMLEAVE_ABANDONED = 0;
-	static final int NETCONN_ROOMLEAVE_KICKED = 1;
-	
-	static final int NETCONN_MSG_TYPE_PLAYERINFO = 0;
-	static final int NETCONN_MSG_TYPE_SERVERMESSAGE = 1;
-	static final int NETCONN_MSG_TYPE_WARNING = 2;
-	static final int NETCONN_MSG_TYPE_ERROR = 3;
-	
-	static final int NETCONN_MAPCHANGE_FULL = 0;
-	static final int NETCONN_MAPCHANGE_MAP = 1;
-	static final int NETCONN_MAPCHANGE_MAPGEN = 2;
-	static final int NETCONN_MAPCHANGE_DRAWNMAP = 3;
-	static final int NETCONN_MAPCHANGE_MAZE_SIZE = 4;
-	static final int NETCONN_MAPCHANGE_TEMPLATE = 5;
-	static final int NETCONN_MAPCHANGE_THEME = 6;
-	static final int NETCONN_MAPCHANGE_SEED = 7;
-	
-	static final int GAME_END_FINISHED = 0;
-	static final int GAME_END_INTERRUPTED = 1;
-	static final int GAME_END_HALTED = 2;
-	static final int GAME_END_ERROR = 3;
-	
-	static final int HEDGEHOGS_PER_TEAM = 8;
-	
 	public static class NetconnPtr extends PointerType { }
 	public static class MapconnPtr extends PointerType { }
 	public static class GameconnPtr extends PointerType { }
-	public static class MetaschemePtr extends PointerType { }
+	
+	// TODO avoid code duplication in the pointer types
+	public static class MetaschemePtr extends PointerType {
+		public MetaScheme deref() {
+			return deref(getPointer());
+		}
+		
+		public static MetaScheme deref(Pointer p) {
+			MetaschemeStruct struct = new MetaschemeStruct(p);
+			struct.read();
+			return struct.toMetaScheme();
+		}
+	}
 	
 	public static class RoomArrayPtr extends PointerType { 
-		/**
-		 * Returns the (native-owned) rooms in this list
-		 */
 		public RoomlistRoom[] getRooms(int count) {
 			Pointer ptr = getPointer();
 			if(ptr == null) {
@@ -88,65 +161,123 @@
 	}
 	
 	public static class RoomPtr extends PointerType {
-		public RoomPtr() { super(); }
-		public RoomPtr(Pointer ptr) { super(ptr); }
-		
 		public RoomlistRoom deref() {
 			return deref(getPointer());
 		}
 		
 		public static RoomlistRoom deref(Pointer p) {
-			RoomStruct r = new RoomStruct(p);
-			r.read();
-			return new RoomlistRoom(r.name, r.map, r.scheme, r.weapons, r.owner, r.playerCount, r.teamCount, r.inProgress);
+			RoomStruct struct = new RoomStruct(p);
+			struct.read();
+			return struct.toRoomlistRoom();
 		}
 	}
 	
 	public static class TeamPtr extends PointerType {
+		private TeamStruct javaOwnedInstance; 
+		
 		public TeamInGame deref() {
-			return deref(getPointer());
+			TeamStruct struct = new TeamStruct(getPointer());
+			struct.read();
+			return struct.toTeamInGame();
 		}
 		
-		public static TeamInGame deref(Pointer p) {
-			TeamStruct ts = new TeamStruct(p);
-			ts.read();
-			List<Hog> hogs = new ArrayList<Hog>();
-			for(int i=0; i<ts.hogs.length; i++) {
-				HogStruct hog = ts.hogs[i];
-				hogs.add(new Hog(hog.name, hog.hat, hog.difficulty));
-			}
-			Team team = new Team(ts.name, ts.grave, ts.flag, ts.voicepack, ts.fort, hogs);
-			TeamIngameAttributes attrs = new TeamIngameAttributes(ts.ownerName, ts.colorIndex, ts.hogsInGame, ts.remoteDriven);
-			return new TeamInGame(team, attrs);
-		}
-
 		public static TeamPtr createJavaOwned(Team t) {
 			return createJavaOwned(new TeamInGame(t, null));
 		}
 		
 		public static TeamPtr createJavaOwned(TeamInGame ingameTeam) {
-			TeamStruct ts = TeamStruct.from(ingameTeam.team, ingameTeam.ingameAttribs);
-			ts.write();
 			TeamPtr result = new TeamPtr();
-			result.setPointer(ts.getPointer());
+			result.javaOwnedInstance = new TeamStruct();
+			result.javaOwnedInstance.fillFrom(ingameTeam.team, ingameTeam.ingameAttribs);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class WeaponsetPtr extends PointerType {
+		private WeaponsetStruct javaOwnedInstance; 
+		
+		public Weaponset deref() {
+			WeaponsetStruct struct = new WeaponsetStruct(getPointer());
+			struct.read();
+			return struct.toWeaponset();
+		}
+		
+		public static WeaponsetPtr createJavaOwned(Weaponset weaponset) {
+			WeaponsetPtr result = new WeaponsetPtr();
+			result.javaOwnedInstance = new WeaponsetStruct();
+			result.javaOwnedInstance.fillFrom(weaponset);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
 			return result;
 		}
 	}
 	
-	public static class WeaponsetPtr extends PointerType { }
-	public static class MapRecipePtr extends PointerType { }
-	public static class SchemePtr extends PointerType { }
+	public static class WeaponsetListPtr extends PointerType {
+		private WeaponsetListStruct javaOwnedInstance;
+		
+		public List<Weaponset> deref() {
+			WeaponsetListStruct struct = new WeaponsetListStruct(getPointer());
+			struct.read();
+			return struct.toWeaponsetList();
+		}
+		
+		public static WeaponsetListPtr createJavaOwned(List<Weaponset> list) {
+			WeaponsetListPtr result = new WeaponsetListPtr();
+			result.javaOwnedInstance = new WeaponsetListStruct();
+			result.javaOwnedInstance.fillFrom(list);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class MapRecipePtr extends PointerType {
+		private MapRecipeStruct javaOwnedInstance;
+		
+		public MapRecipe deref() {
+			MapRecipeStruct struct = new MapRecipeStruct(getPointer());
+			struct.read();
+			return struct.toMapRecipe();
+		}
+		
+		public static MapRecipePtr createJavaOwned(MapRecipe recipe) {
+			MapRecipePtr result = new MapRecipePtr();
+			result.javaOwnedInstance = new MapRecipeStruct();
+			result.javaOwnedInstance.fillFrom(recipe);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
+	public static class SchemePtr extends PointerType {
+		private SchemeStruct javaOwnedInstance;
+		
+		public Scheme deref() {
+			SchemeStruct struct = new SchemeStruct(getPointer());
+			struct.read();
+			return struct.toScheme();
+		}
+		
+		public static SchemePtr createJavaOwned(Scheme scheme) {
+			SchemePtr result = new SchemePtr();
+			result.javaOwnedInstance = new SchemeStruct();
+			result.javaOwnedInstance.fillFrom(scheme);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
+			return result;
+		}
+	}
+	
 	public static class SchemelistPtr extends PointerType {
 		private SchemelistStruct javaOwnedInstance;
 		
 		public List<Scheme> deref() {
-			return deref(getPointer());
-		}
-		
-		public static List<Scheme> deref(Pointer p) {
-			SchemelistStruct sls = new SchemelistStruct(p);
-			sls.read();
-			return sls.toSchemeList();
+			SchemelistStruct struct = new SchemelistStruct(getPointer());
+			struct.read();
+			return struct.toSchemeList();
 		}
 		
 		public static SchemelistPtr createJavaOwned(List<Scheme> schemes) {
@@ -160,11 +291,20 @@
 	}
 	
 	public static class GameSetupPtr extends PointerType {
+		private GameSetupStruct javaOwnedInstance;
+		
+		public GameConfig deref() {
+			GameSetupStruct struct = new GameSetupStruct(getPointer());
+			struct.read();
+			return struct.toGameConfig();
+		}
+		
 		public static GameSetupPtr createJavaOwned(GameConfig conf) {
-			GameSetupStruct gss = GameSetupStruct.from(conf);
-			gss.write();
 			GameSetupPtr result = new GameSetupPtr();
-			result.setPointer(gss.getPointer());
+			result.javaOwnedInstance = new GameSetupStruct();
+			result.javaOwnedInstance.fillFrom(conf);
+			result.javaOwnedInstance.autoWrite();
+			result.setPointer(result.javaOwnedInstance.getPointer());
 			return result;
 		}
 	}
@@ -177,14 +317,14 @@
 		public HogStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public HogStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
-		public static HogStruct from(Hog hog) {
-			HogStruct hs = new HogStruct();
-			hs.difficulty = hog.level;
-			hs.hat = hog.hat;
-			hs.name = hog.name;
-			// TODO weaponset
-			// TODO initialHealth
-			return hs;
+		public void fillFrom(Hog hog) {
+			difficulty = hog.level;
+			hat = hog.hat;
+			name = hog.name;
+		}
+		
+		public Hog toHog() {
+			return new Hog(name, hat, difficulty);
 		}
 		
 		public String name;
@@ -198,7 +338,7 @@
 		public int difficulty;
 		
 		public int initialHealth;
-		public WeaponsetPtr weaponset;
+		public WeaponsetStruct.ByRef weaponset;
 	}
 	
 	static class TeamStruct extends Structure {
@@ -209,32 +349,55 @@
 		public TeamStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public TeamStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
-		public static TeamStruct from(Team team, TeamIngameAttributes attrs) {
-			TeamStruct ts = new TeamStruct();
+		public void fillFrom(Team team, TeamIngameAttributes attrs) {
 			if(team != null) {
-				ts.name = team.name;
-				ts.grave = team.grave;
-				ts.flag = team.flag;
-				ts.voicepack = team.voice;
-				ts.fort = team.fort;
-				if(team.hogs.size() != HEDGEHOGS_PER_TEAM) {
+				name = team.name;
+				grave = team.grave;
+				flag = team.flag;
+				voicepack = team.voice;
+				fort = team.fort;
+				if(team.hogs.size() != Team.HEDGEHOGS_PER_TEAM) {
 					throw new IllegalArgumentException();
 				}
-				for(int i=0; i<ts.hogs.length; i++) {
-					ts.hogs[i] = HogStruct.from(team.hogs.get(i));
+				for(int i=0; i<hogs.length; i++) {
+					hogs[i] = new HogStruct();
+					hogs[i].fillFrom(team.hogs.get(i));
 				}
 			}
 			
 			if(attrs != null) {
-				ts.hogsInGame = attrs.hogCount;
-				ts.ownerName = attrs.ownerName;
-				ts.colorIndex = attrs.colorIndex;
-				ts.remoteDriven = attrs.remoteDriven;
+				hogsInGame = attrs.hogCount;
+				ownerName = attrs.ownerName;
+				colorIndex = attrs.colorIndex;
+				remoteDriven = attrs.remoteDriven;
 			}
-			return ts;
+		}
+		
+		public void fillFrom(TeamInGame team, WeaponsetStruct.ByRef weaponset, int initialHealth) {
+			fillFrom(team.team, team.ingameAttribs);
+			for(int i=0; i<hogs.length; i++) {
+				hogs[i].initialHealth = initialHealth;
+				hogs[i].weaponset = weaponset;
+			}
 		}
 		
-		public HogStruct[] hogs = new HogStruct[HEDGEHOGS_PER_TEAM];
+		public Team toTeam() {
+			List<Hog> hogList = new ArrayList<Hog>();
+			for(int i=0; i<hogs.length; i++) {
+				hogList.add(hogs[i].toHog());
+			}
+			return new Team(name, grave, flag, voicepack, fort, hogList);
+		}
+		
+		public TeamIngameAttributes toTeamIngameAttributes() {
+			return new TeamIngameAttributes(ownerName, colorIndex, hogsInGame, remoteDriven);
+		}
+		
+		public TeamInGame toTeamInGame() {
+			return new TeamInGame(toTeam(), toTeamIngameAttributes());
+		}
+		
+		public HogStruct[] hogs = new HogStruct[Team.HEDGEHOGS_PER_TEAM];
 		public String name;
 		public String grave;
 		public String fort;
@@ -254,6 +417,106 @@
 		public String ownerName;
 	}
 	
+	static class WeaponsetStruct extends Structure {
+		public static class ByVal extends WeaponsetStruct implements Structure.ByValue {}
+		public static class ByRef extends WeaponsetStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"_referenceCount", "loadout", "crateprob", "crateammo", "delay", "name"};
+		
+		public WeaponsetStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(Weaponset weaponset) {
+			_referenceCount = 0;
+			fillWeaponInfo(loadout, weaponset.loadout);
+			fillWeaponInfo(crateprob, weaponset.crateProb);
+			fillWeaponInfo(crateammo, weaponset.crateAmmo);
+			fillWeaponInfo(delay, weaponset.delay);
+			name = weaponset.name;
+		}
+		
+		private static void fillWeaponInfo(byte[] array, String str) {
+			for(int i=0; i<array.length-1; i++) {
+				array[i] = (byte) (i<str.length() ? str.charAt(i) : '0');
+			}
+			array[array.length-1] = (byte)0;
+		}
+		
+		public Weaponset toWeaponset() {
+			return new Weaponset(name, weaponInfoToString(loadout), weaponInfoToString(crateprob), weaponInfoToString(crateammo), weaponInfoToString(delay));
+		}
+		
+		private static String weaponInfoToString(byte[] array) {
+			try {
+				return new String(array, 0, array.length-1, "ASCII");
+			} catch (UnsupportedEncodingException e) {
+				throw new AssertionError();
+			}
+		}
+		
+		public int _referenceCount;
+		public byte[] loadout = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] crateprob = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] crateammo = new byte[Weaponset.WEAPONS_COUNT+1];
+		public byte[] delay = new byte[Weaponset.WEAPONS_COUNT+1];
+		public String name;
+	}
+	
+	/**
+	 * Represents a flib_weaponset*, for use as part of a flib_weaponset**
+	 */
+	static class WeaponsetPointerByReference extends Structure implements Structure.ByReference {
+		private static String[] FIELD_ORDER = new String[] {"weaponset"};
+		
+		public WeaponsetPointerByReference() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetPointerByReference(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public WeaponsetStruct.ByRef weaponset;
+	}
+	
+	static class WeaponsetListStruct extends Structure {
+		public static class ByVal extends WeaponsetListStruct implements Structure.ByValue {}
+		public static class ByRef extends WeaponsetListStruct implements Structure.ByReference {}
+		private static String[] FIELD_ORDER = new String[] {"weaponsetCount", "weaponsets"};
+		
+		public WeaponsetListStruct() { super(); setFieldOrder(FIELD_ORDER); }
+		public WeaponsetListStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public void fillFrom(List<Weaponset> list) {
+			weaponsetCount = list.size();
+			weaponsets = new WeaponsetPointerByReference();
+			Structure[] structs = weaponsets.toArray(weaponsetCount);
+			
+			for(int i=0; i<weaponsetCount; i++) {
+				WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i];
+				pstruct.weaponset = new WeaponsetStruct.ByRef();
+				pstruct.weaponset.fillFrom(list.get(i));
+			}
+		}
+		
+		/**
+		 * Only use on native-owned structs!
+		 * Calling this method on a Java-owned struct could cause garbage collection of referenced
+		 * structures.
+		 */
+		public List<Weaponset> toWeaponsetList() {
+			if(weaponsetCount<=0) {
+				return new ArrayList<Weaponset>();
+			} else {
+				List<Weaponset> list = new ArrayList<Weaponset>(weaponsetCount);
+				Structure[] structs = weaponsets.toArray(weaponsetCount);
+				
+				for(int i=0; i<weaponsetCount; i++) {
+					WeaponsetPointerByReference pstruct = (WeaponsetPointerByReference)structs[i];
+					list.add(pstruct.weaponset.toWeaponset());
+				}
+				return list;
+			}
+		}
+		
+		public int weaponsetCount;
+		public WeaponsetPointerByReference weaponsets;
+	}
+	
 	static class RoomStruct extends Structure {
 		public static class ByVal extends RoomStruct implements Structure.ByValue {}
 		public static class ByRef extends RoomStruct implements Structure.ByReference {}
@@ -261,6 +524,10 @@
 		
 		public RoomStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public RoomStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+
+		public RoomlistRoom toRoomlistRoom() {
+			return new RoomlistRoom(name, map, scheme, weapons, owner, playerCount, teamCount, inProgress);
+		}
 		
 	    public boolean inProgress;
 	    public String name;
@@ -275,18 +542,44 @@
 	static class MapRecipeStruct extends Structure {
 		public static class ByVal extends MapRecipeStruct implements Structure.ByValue {}
 		public static class ByRef extends MapRecipeStruct implements Structure.ByReference {}
-		private static String[] FIELD_ORDER = new String[] {"_referenceCount", "mapgen", "name", "seed", "theme", "drawData", "drawDataSize", "templateFilter", "mazeSize"};
+		private static String[] FIELD_ORDER = new String[] {"mapgen", "name", "seed", "theme", "drawData", "drawDataSize", "templateFilter", "mazeSize"};
 		
 		public MapRecipeStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public MapRecipeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
-		public int _referenceCount;
+		public void fillFrom(MapRecipe map) {
+			mapgen = map.mapgen;
+			name = map.name;
+			seed = map.seed;
+			theme = map.theme;
+			byte[] buf = map.getDrawData();
+			if(buf != null) {
+				drawData = new Memory(buf.length);
+				drawData.write(0, buf, 0, buf.length);
+				drawDataSize = new NativeLong(buf.length);
+			} else {
+				drawData = null;
+				drawDataSize = new NativeLong(0);
+			}
+			templateFilter = map.templateFilter;
+			mazeSize = map.mazeSize;
+		}
+		
+		public MapRecipe toMapRecipe() {
+			byte[] buf = null;
+			if(drawData != null && drawDataSize.intValue()>0) {
+				buf = new byte[drawDataSize.intValue()];
+				drawData.read(0, buf, 0, drawDataSize.intValue());
+			}
+			return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, buf);
+		}
+		
 		public int mapgen;
 		public String name;
 		public String seed;
 		public String theme;
 		public Pointer drawData;
-		public int drawDataSize;
+		public NativeLong drawDataSize;
 		public int templateFilter;
 		public int mazeSize;
 	}
@@ -408,7 +701,7 @@
 	static class SchemeStruct extends Structure {
 		public static class ByVal extends SchemeStruct implements Structure.ByValue {}
 		public static class ByRef extends SchemeStruct implements Structure.ByReference {}
-		private static String[] FIELD_ORDER = new String[] {"_referenceCount", "meta", "name", "settings", "mod"};
+		private static String[] FIELD_ORDER = new String[] {"meta", "name", "settings", "mod"};
 		
 		public SchemeStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public SchemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
@@ -442,7 +735,6 @@
 			return new Scheme(metaScheme, name, settingsMap, modsMap);
 		}
 		
-		public int _referenceCount;
 		public MetaschemeStruct.ByRef meta;
 		public String name;
 		public Pointer settings;
@@ -469,6 +761,18 @@
 		public SchemelistStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public SchemelistStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
+		public void fillFrom(List<Scheme> schemeList) {
+			schemeCount = schemeList.size();
+			schemes = new SchemePointerByReference();
+			Structure[] schemePtrStructs = schemes.toArray(schemeCount);
+			
+			for(int i=0; i<this.schemeCount; i++) {
+				SchemePointerByReference spbr = (SchemePointerByReference)schemePtrStructs[i];
+				spbr.scheme = new SchemeStruct.ByRef();
+				spbr.scheme.fillFrom(schemeList.get(i));
+			}
+		}
+
 		/**
 		 * Only use on native-owned structs!
 		 * Calling this method on a Java-owned struct could cause garbage collection of referenced
@@ -490,32 +794,60 @@
 			}
 		}
 		
-		public void fillFrom(List<Scheme> schemeList) {
-			schemeCount = schemeList.size();
-			schemes = new SchemePointerByReference();
-			Structure[] schemePtrStructs = schemes.toArray(schemeCount);
-			
-			for(int i=0; i<this.schemeCount; i++) {
-				SchemePointerByReference spbr = (SchemePointerByReference)schemePtrStructs[i];
-				spbr.scheme = new SchemeStruct.ByRef();
-				spbr.scheme.fillFrom(schemeList.get(i));
-			}
-		}
-		
 		public int schemeCount;
 		public SchemePointerByReference schemes;
 	}
 	
+	/**
+	 * Represents a flib_team*, for use as part of a flib_team**
+	 */
+	static class TeamPointerByReference extends Structure implements Structure.ByReference {
+		private static String[] FIELD_ORDER = new String[] {"team"};
+		
+		public TeamPointerByReference() { super(); setFieldOrder(FIELD_ORDER); }
+		public TeamPointerByReference(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
+		
+		public TeamStruct.ByRef team;
+	}
+	
 	static class TeamlistStruct extends Structure {
 		public static class ByVal extends TeamlistStruct implements Structure.ByValue {}
 		public static class ByRef extends TeamlistStruct implements Structure.ByReference {}
+
 		private static String[] FIELD_ORDER = new String[] {"teamCount", "teams"};
 		
 		public TeamlistStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public TeamlistStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
+		public void fillFrom(List<TeamInGame> teamList, WeaponsetStruct.ByRef weaponset, int initialHealth) {
+			teamCount = teamList.size();
+			teams = new TeamPointerByReference();
+			Structure[] teamPtrStructs = teams.toArray(teamCount);
+			
+			for(int i=0; i<this.teamCount; i++) {
+				TeamPointerByReference tpbr = (TeamPointerByReference)teamPtrStructs[i];
+				tpbr.team = new TeamStruct.ByRef();
+				tpbr.team.fillFrom(teamList.get(i), weaponset, initialHealth);
+			}
+		}
+
+		public List<TeamInGame> toTeamInGameList() {
+			if(teamCount<=0) {
+				return new ArrayList<TeamInGame>();
+			} else {
+				List<TeamInGame> result = new ArrayList<TeamInGame>(teamCount);
+				Structure[] structs = teams.toArray(teamCount);
+				
+				for(int i=0; i<teamCount; i++) {
+					TeamPointerByReference struct = (TeamPointerByReference)structs[i];
+					result.add(struct.team.toTeamInGame());
+				}
+				return result;
+			}
+		}
+		
 		public int teamCount;
-		public Pointer teams;
+		public TeamPointerByReference teams;
 	}
 	
 	static class GameSetupStruct extends Structure {
@@ -526,16 +858,35 @@
 		public GameSetupStruct() { super(); setFieldOrder(FIELD_ORDER); }
 		public GameSetupStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); }
 		
-		public static GameSetupStruct from(GameConfig conf) {
-			GameSetupStruct gss = new GameSetupStruct();
-			gss.gamescheme = new SchemeStruct.ByRef();
-			gss.gamescheme.fillFrom(conf.scheme);
-			gss.map = new MapRecipeStruct.ByRef();
-			// TODO gss.map.fillFrom(conf.map, conf.seed, conf.theme);
-			gss.script = conf.style;
-			gss.teamlist = new TeamlistStruct.ByRef();
-			// TODO gss.teamlist.fillFrom(conf.teams, conf.weapon);
-			return gss;
+		public void fillFrom(GameConfig conf) {
+			script = conf.style;
+			gamescheme = new SchemeStruct.ByRef();
+			gamescheme.fillFrom(conf.scheme);
+			map = new MapRecipeStruct.ByRef();
+			map.fillFrom(conf.map);
+			
+			/*
+			 * At this point we deviate from the usual copying pattern because the frontlib
+			 * expects per-hog weapons and initial health, but the UI models them as per-
+			 * game, so we extract them from the config here and pass them on to be included
+			 * in each hog.
+			 */
+			WeaponsetStruct.ByRef wss = new WeaponsetStruct.ByRef();
+			wss.fillFrom(conf.weaponset);
+			int initialHealth = conf.scheme.getHealth();
+			
+			teamlist = new TeamlistStruct.ByRef();
+			teamlist.fillFrom(conf.teams, wss, initialHealth);
+		}
+		
+		public GameConfig toGameConfig() {
+			Scheme scheme = gamescheme != null ? gamescheme.toScheme() : null;
+			MapRecipe mapRecipe = map != null ? map.toMapRecipe() : null;
+			List<TeamInGame> teams = teamlist != null ? teamlist.toTeamInGameList() : null;
+			
+			WeaponsetStruct weaponsetStruct = teamlist != null && teamlist.teamCount>0 ? teamlist.teams.team.hogs[0].weaponset : null;
+			Weaponset weaponset = weaponsetStruct != null ? weaponsetStruct.toWeaponset() : null;
+			return new GameConfig(script, scheme, mapRecipe, teams, weaponset);
 		}
 
 		public String script;
@@ -544,6 +895,12 @@
 		public TeamlistStruct.ByRef teamlist;
 	}
 	
+	/*
+	 * Callback interfaces. The context parameter is never needed here and
+	 * should always be ignored. Be sure to keep a reference to each callback
+	 * for as long as they might be called by native code, to avoid premature
+	 * garbage collection.
+	 */
 	public static interface VoidCallback extends Callback {
 		void callback(Pointer context);
 	}
@@ -620,15 +977,50 @@
 		void callback(int level, String logMessage);
 	}
 	
+	// frontlib.h
     int flib_init();
     void flib_quit();
 	
+    // hwconsts.h
+    int flib_get_teamcolor(int colorIndex);
+    int flib_get_teamcolor_count();
+    int flib_get_hedgehogs_per_team();
+    int flib_get_weapons_count();
+    
+    // net/netconn.h
+	static final int NETCONN_STATE_CONNECTING = 0;
+	static final int NETCONN_STATE_LOBBY = 1;
+	static final int NETCONN_STATE_ROOM = 2;
+	static final int NETCONN_STATE_DISCONNECTED = 10;
+	
+	static final int NETCONN_DISCONNECT_NORMAL = 0;
+	static final int NETCONN_DISCONNECT_SERVER_TOO_OLD = 1;
+	static final int NETCONN_DISCONNECT_AUTH_FAILED = 2;
+	static final int NETCONN_DISCONNECT_CONNLOST = 3;
+	static final int NETCONN_DISCONNECT_INTERNAL_ERROR = 100;
+	
+	static final int NETCONN_ROOMLEAVE_ABANDONED = 0;
+	static final int NETCONN_ROOMLEAVE_KICKED = 1;
+	
+	static final int NETCONN_MSG_TYPE_PLAYERINFO = 0;
+	static final int NETCONN_MSG_TYPE_SERVERMESSAGE = 1;
+	static final int NETCONN_MSG_TYPE_WARNING = 2;
+	static final int NETCONN_MSG_TYPE_ERROR = 3;
+	
+	static final int NETCONN_MAPCHANGE_FULL = 0;
+	static final int NETCONN_MAPCHANGE_MAP = 1;
+	static final int NETCONN_MAPCHANGE_MAPGEN = 2;
+	static final int NETCONN_MAPCHANGE_DRAWNMAP = 3;
+	static final int NETCONN_MAPCHANGE_MAZE_SIZE = 4;
+	static final int NETCONN_MAPCHANGE_TEMPLATE = 5;
+	static final int NETCONN_MAPCHANGE_THEME = 6;
+	static final int NETCONN_MAPCHANGE_SEED = 7;
+    
 	NetconnPtr flib_netconn_create(String playerName, MetaschemePtr meta, String dataDirPath, String host, int port);
 	void flib_netconn_destroy(NetconnPtr conn);
 
 	void flib_netconn_tick(NetconnPtr conn);
 	boolean flib_netconn_is_chief(NetconnPtr conn);
-	boolean flib_netconn_is_in_room_context(NetconnPtr conn);
 	String flib_netconn_get_playername(NetconnPtr conn);
 	GameSetupPtr flib_netconn_create_gamesetup(NetconnPtr conn);
 	int flib_netconn_send_quit(NetconnPtr conn, String quitmsg);
@@ -644,7 +1036,7 @@
 	int flib_netconn_send_toggleReady(NetconnPtr conn);
 	int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team);
 	int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname);
-	int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size); // TODO check if NativeLong==size_t
+	int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size);
 	int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount);
 	int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex);
 	int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset);
@@ -702,7 +1094,12 @@
 	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
 
-	// Gameconn
+	// ipc/gameconn.h
+	static final int GAME_END_FINISHED = 0;
+	static final int GAME_END_INTERRUPTED = 1;
+	static final int GAME_END_HALTED = 2;
+	static final int GAME_END_ERROR = 3;
+	
 	GameconnPtr flib_gameconn_create(String playerName, GameSetupPtr setup, boolean netgame);
 	GameconnPtr flib_gameconn_create_playdemo(Buffer demo, NativeLong size);
 	GameconnPtr flib_gameconn_create_loadgame(String playerName, Buffer save, NativeLong size);
@@ -715,6 +1112,7 @@
 	int flib_gameconn_send_enginemsg(GameconnPtr conn, Buffer data, NativeLong len);
 	int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg);
 	int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg);
+	int flib_gameconn_send_quit(GameconnPtr conn);
 	
 	void flib_gameconn_onConnect(GameconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_gameconn_onDisconnect(GameconnPtr conn, IntCallback callback, Pointer context);
@@ -723,7 +1121,7 @@
 	void flib_gameconn_onGameRecorded(GameconnPtr conn, BytesBoolCallback callback, Pointer context);
 	void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context);
 	
-	// MapConn
+	// ipc/mapconn.h
 	MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc);
 	void flib_mapconn_destroy(MapconnPtr conn);
 	int flib_mapconn_getport(MapconnPtr conn);
@@ -731,11 +1129,7 @@
 	void flib_mapconn_onFailure(MapconnPtr conn, StrCallback callback, Pointer context);
 	void flib_mapconn_tick(MapconnPtr conn);
 	
-	// GameSetup
-	void flib_gamesetup_destroy(GameSetupPtr gamesetup);
-	GameSetupPtr flib_gamesetup_copy(GameSetupPtr gamesetup);
-	
-	// MapRecipe
+	// model/map.h
 	public static final int MAPGEN_REGULAR = 0;
 	public static final int MAPGEN_MAZE = 1;
 	public static final int MAPGEN_DRAWN = 2;
@@ -754,33 +1148,28 @@
 	public static final int MAZE_SIZE_SMALL_ISLANDS = 3;
 	public static final int MAZE_SIZE_MEDIUM_ISLANDS = 4;
 	public static final int MAZE_SIZE_LARGE_ISLANDS = 5;
-	
-	MapRecipePtr flib_map_create_regular(String seed, String theme, int templateFilter);
-	MapRecipePtr flib_map_create_maze(String seed, String theme, int mazeSize);
-	MapRecipePtr flib_map_create_named(String seed, String name);
-	MapRecipePtr flib_map_create_drawn(String seed, String theme, Buffer drawData, NativeLong drawDataSize);
-	MapRecipePtr flib_map_copy(MapRecipePtr map);
-	MapRecipePtr flib_map_retain(MapRecipePtr map);
-	void flib_map_release(MapRecipePtr map);
-	
-	// Metascheme
+		
+	// model/scheme.h
 	MetaschemePtr flib_metascheme_from_ini(String filename);
 	MetaschemePtr flib_metascheme_retain(MetaschemePtr metainfo);
 	void flib_metascheme_release(MetaschemePtr metainfo);
 
-	// Scheme lists
+	// model/schemelist.h
 	SchemelistPtr flib_schemelist_from_ini(MetaschemePtr meta, String filename);
 	int flib_schemelist_to_ini(String filename, SchemelistPtr list);
 	void flib_schemelist_destroy(SchemelistPtr list);
 	
-	// Team
-	void flib_team_destroy(TeamPtr team);
+	// model/team.h
 	TeamPtr flib_team_from_ini(String filename);
 	int flib_team_to_ini(String filename, TeamPtr team);
-	void flib_team_set_weaponset(TeamPtr team, WeaponsetPtr set);
-	void flib_team_set_health(TeamPtr team, int health);
+	void flib_team_destroy(TeamPtr team);
 	
-	// Logging
+	// model/weapon.h
+	WeaponsetListPtr flib_weaponsetlist_from_ini(String filename);
+	int flib_weaponsetlist_to_ini(String filename, WeaponsetListPtr weaponsets);
+	void flib_weaponsetlist_destroy(WeaponsetListPtr list);
+	
+	// util/logging.h
 	public static final int FLIB_LOGLEVEL_ALL = -100;
 	public static final int FLIB_LOGLEVEL_DEBUG = -1;
 	public static final int FLIB_LOGLEVEL_INFO = 0;