Hedgeroid:
authorMedo <smaxein@googlemail.com>
Sun, 12 Aug 2012 22:46:23 +0200
changeset 7485 0481bd74267c
parent 7482 d70a5b0d1190
child 7488 7e09947b6aa5
Hedgeroid: - (hopefully) completed the frontlib JNA mappings - added documentation - Changed more code to use frontlib for ini reading/writing - tried to make everything work again that was working before - Teamlist can now be used to add and remove teams - Netplay now correctly handles team additions when being room chief - Fixed TeamCreatorActivity so that editing teams works
project_files/Android-build/SDL-android-project/assets/assetsversion.txt
project_files/Android-build/SDL-android-project/res/layout/team_creation.xml
project_files/Android-build/SDL-android-project/res/layout/team_selector.xml
project_files/Android-build/SDL-android-project/res/raw/weapon_clean
project_files/Android-build/SDL-android-project/res/raw/weapon_crazy
project_files/Android-build/SDL-android-project/res/raw/weapon_default
project_files/Android-build/SDL-android-project/res/raw/weapon_mines
project_files/Android-build/SDL-android-project/res/raw/weapon_portals
project_files/Android-build/SDL-android-project/res/raw/weapon_promode
project_files/Android-build/SDL-android-project/res/raw/weapon_shoppa
project_files/Android-build/SDL-android-project/res/raw/weapons_builtin.ini
project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml
project_files/Android-build/SDL-android-project/res/values/strings.xml
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Map.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamFile.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weapon.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Teamlist.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java
--- a/project_files/Android-build/SDL-android-project/assets/assetsversion.txt	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/assets/assetsversion.txt	Sun Aug 12 22:46:23 2012 +0200
@@ -1,1 +1,1 @@
-4
\ No newline at end of file
+5
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml	Sun Aug 12 22:46:23 2012 +0200
@@ -15,8 +15,6 @@
 	  	android:layout_width="fill_parent"
 	  	android:layout_height="fill_parent"
 	  	android:layout_weight="1">
-	  	<include layout="@layout/backbutton"/>
-	  	<include layout="@layout/savebutton"/>
 	  	<ScrollView
 		  	android:layout_width="fill_parent"
 		  	android:layout_height="fill_parent"
--- a/project_files/Android-build/SDL-android-project/res/layout/team_selector.xml	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/layout/team_selector.xml	Sun Aug 12 22:46:23 2012 +0200
@@ -5,8 +5,6 @@
   android:layout_height="fill_parent">
   
   <include layout="@layout/background"/>
- 
-  <include layout="@layout/backbutton"/>
 
   <ImageButton
    	android:id="@+id/btnAdd"
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_clean	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Clean</name>
-    <QT>
-    101000900001000001100000000000000000000000000000100000
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_crazy	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Crazy</name>
-    <QT>
-    999999999999999999299999999999999929999999990999999229
-    </QT>
-    <probability>
-    111111011111111111111111111111111111111111110111111111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111010111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_default	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Default</name>
-    <QT>
-    939192942219912103223511100120100000021111010101111991
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000022000000060000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_mines	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Mines</name>
-    <QT>
-    000000990009000000030000000000000000000000000000000000
-    </QT>
-    <probability>
-    000000000000000000000000000000000000000000000000000000
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000060000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_portals	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Portals</name>
-    <QT>
-    900000900200000000210000000000000011000009000000000000
-    </QT>
-    <probability>
-    040504054160065554655446477657666666615551010111541111
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000060000
-    </delay>
-    <crate>
-    131111031211111112311411111111111111121111110111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_promode	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Pro Mode</name>
-    <QT>
-    909000900000000000000900000000000000000000000000000000
-    </QT>
-    <probability>
-    000000000000000000000000000000000000000000000000000000
-    </probability>
-    <delay>
-    000000000000020550000004000700400000000020000000000000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111110010111111111
-    </crate>
-</weapon>
-
--- a/project_files/Android-build/SDL-android-project/res/raw/weapon_shoppa	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<weapon>
-    <name>Shoppa</name>
-    <QT>
-    000000990000000000000000000000000000000000000000000000
-    </QT>
-    <probability>
-    444441004424440221011212122242200000000200040001001111
-    </probability>
-    <delay>
-    000000000000000000000000000000000000000000000000000000
-    </delay>
-    <crate>
-    111111111111111111111111111111111111111110110111111111
-    </crate>
-</weapon>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/res/raw/weapons_builtin.ini	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,8 @@
+[General]
+%44efault=9391929422199121032235111001201000000211110101011111011040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002200000006000001311110312111111123114111111111111111211111101111111010
+%43razy=9999999999999999992999999999999999299999999909999992099111111011111111111111111111111111111111111110111111101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211110101111111011
+%50ro%20%4dode=9090009000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000000000001111111111111111111111111111111111111111100101111111011
+%53hoppa=0000009900000000000000000000000000000000000000000000000444441004424440221011212122242200000000200040001001100000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111101101111111001
+%43lean%20%53late=1010009000010000011000000000000000000000000000001000000040504054160065554655446477657666666615551010111541101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211111101111111011
+%4dinefield=0000009900090000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000006000001111111111111111111111111111111111111111111101111111011
+%54hinking%20with%20%50ortals=9000009002000000002100000000000000110000090000000000000040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002000000006000001311110312111111123114111111111111111211111101111111011
--- a/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml	Sun Aug 12 22:46:23 2012 +0200
@@ -1,16 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-
-<array name="weapons">
-    <item>@raw/weapon_default</item>
-    <item>@raw/weapon_clean</item>
-    <item>@raw/weapon_crazy</item>
-    <item>@raw/weapon_mines</item>
-    <item>@raw/weapon_portals</item>
-    <item>@raw/weapon_promode</item>
-    <item>@raw/weapon_shoppa</item>
-</array>
-
 <array name="teams">
 	<item>@raw/team_one</item>
 	<item>@raw/team_two</item>
--- a/project_files/Android-build/SDL-android-project/res/values/strings.xml	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml	Sun Aug 12 22:46:23 2012 +0200
@@ -123,7 +123,8 @@
     <string name="error_auth_failed">Unable to authenticate for your username.</string>
     <string name="error_connection_lost">The connection to the server was lost.</string>
     <string name="error_save_failed">Saving has failed.</string>
-    
+    <string name="error_missing_sdcard_or_files">Error: Either the sdcard is not available, or files are missing from the Data directory.</string>
+    <string name="error_team_attribute_not_found">Error: This team uses assets which we do not have. Try downloading the big data package from the main menu.</string>
     
     <!-- Dialogs -->
     <string name="dialog_connecting_title">Please wait</string>
@@ -134,6 +135,7 @@
     <string name="dialog_password_remember">remember password</string>
     <string name="dialog_create_room_hint">Room name</string>
     <string name="dialog_create_room_title">Create new room</string>
+    <string name="dialog_addteam_title">Add team</string>
     
     <string name="toast_disconnected">Disconnected: %1$s</string>
     <string name="toast_room_abandoned">The room was closed because the owner left.</string>
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java	Sun Aug 12 22:46:23 2012 +0200
@@ -20,15 +20,14 @@
 package org.hedgewars.hedgeroid.Datastructures;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
 import org.hedgewars.hedgeroid.R;
-import org.hedgewars.hedgeroid.StartGameActivity;
 import org.hedgewars.hedgeroid.Utils;
-import org.hedgewars.hedgeroid.Datastructures.Map.MapType;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -36,59 +35,75 @@
 
 public class FrontendDataUtils {
 
-
-	public static ArrayList<Map> getMaps(Context c){
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Maps directory doesn't exist
+	 */
+	public static ArrayList<MapFile> getMaps(Context c) throws FileNotFoundException {
 		File[] files = Utils.getFilesFromRelativeDir(c,"Maps");
-		ArrayList<Map> ret = new ArrayList<Map>();
+		ArrayList<MapFile> ret = new ArrayList<MapFile>();
 
-		for(File f : files){
-			if(Utils.hasFileWithSuffix(f, ".lua")){
-				ret.add(new Map(f,MapType.TYPE_MISSION, c));
-			}else{
-				ret.add(new Map(f, MapType.TYPE_DEFAULT,c));
-			}
+		for(File f : files) {
+			boolean isMission = Utils.hasFileWithSuffix(f, ".lua");
+			ret.add(new MapFile(f.getName(), isMission));
 		}
-		Collections.sort(ret);
+		Collections.sort(ret, MapFile.MISSIONS_FIRST_NAME_ORDER);
 
 		return ret;
 	}
 
-	public static List<String> getGameplay(Context c){
-		String[] files = Utils.getFileNamesFromRelativeDir(c, "Scripts/Multiplayer");
+	/**
+	 * Returns a list of all multiplayer scripts (game styles)
+	 * @throws FileNotFoundException if the sdcard isn't available or the Scripts/Multiplayer directory doesn't exist
+	 */
+	public static List<String> getGameStyles(Context c) throws FileNotFoundException {
+		File[] files = Utils.getFilesFromRelativeDir(c, "Scripts/Multiplayer");
 		ArrayList<String> ret = new ArrayList<String>();
-		
-		for(int i = 0; i < files.length; i++){
-			if(files[i].endsWith(".lua")){
-				ret.add(files[i].replace('_', ' ').substring(0, files[i].length()-4)); //replace _ by a space and removed the last four characters (.lua)
+		/*
+		 * Caution: It is important that the "empty" style has this exact name, because
+		 * it will be interpreted as "don't load a script" by the frontlib, and also by
+		 * the QtFrontend in a netgame. This should probably be improved some time
+		 * (maybe TODO add a dummy script called "Normal" to the MP scripts?) 
+		 */
+		ret.add("Normal");
+		for(int i = 0; i < files.length; i++) {
+			String name = files[i].getName();
+			if(name.endsWith(".lua")){
+				//replace _ by a space and removed the last four characters (.lua)
+				ret.add(name.replace('_', ' ').substring(0, name.length()-4));
 			}
 		}
-		ret.add(0,"None");
-		Collections.sort(ret);
-		return ret;	
+		Collections.sort(ret, String.CASE_INSENSITIVE_ORDER);
+		return ret;
 	}
 
-	public static List<String> getThemes(Context c){
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Themes directory doesn't exist
+	 */
+	public static List<String> getThemes(Context c) throws FileNotFoundException {
 		List<String> list = Utils.getDirsWithFileSuffix(c, "Themes", "icon.png");
-		Collections.sort(list);
+		Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
 		return list;
 	}
 
-	public static List<Weapon> getWeapons(Context c){
+	public static List<Weaponset> getWeaponsets(Context c) {
 		// TODO stub, re-implement
 		/*List<Weapon> list = Weapon.getWeapons(c);
 		Collections.sort(list);*/
 		return Collections.emptyList();
 	}
 
-	public static ArrayList<HashMap<String, ?>> getGraves(Context c){
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Graves/";
-		ArrayList<String> names = Utils.getFilesFromDirWithSuffix(c,"Graphics/Graves", ".png", true);
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist
+	 */
+	public static ArrayList<HashMap<String, ?>> getGraves(Context c) throws FileNotFoundException {
+		File gravePath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Graves");
+		ArrayList<String> names = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Graves", ".png", true);
 		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size());
 
 		for(String s : names){
 			HashMap<String, Object> map = new HashMap<String, Object>();
 			map.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(gravePath, s + ".png").getAbsolutePath());
 			int width = b.getWidth();
 			if(b.getHeight() > width){//some pictures contain more 'frames' underneath each other, if so we only use the first frame
                                 Bitmap tmp = Bitmap.createBitmap(width, width, b.getConfig());
@@ -104,22 +119,28 @@
 		return data;
 	}
 
-	public static ArrayList<HashMap<String, ?>> getFlags(Context c){
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Flags/";
-		ArrayList<String> names = Utils.getFilesFromDirWithSuffix(c, "Graphics/Flags", ".png", true);
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist
+	 */
+	public static ArrayList<HashMap<String, ?>> getFlags(Context c) throws FileNotFoundException {
+		File flagsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Flags");
+		ArrayList<String> names = Utils.getFileNamesFromDirWithSuffix(c, "Graphics/Flags", ".png", true);
 		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(names.size());
 
 		for(String s : names){
 			HashMap<String, Object> map = new HashMap<String, Object>();
 			map.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(flagsPath, s + ".png").getAbsolutePath());
 			map.put("img", b);
 			data.add(map);
 		}
 		return data;
 	}
 
-	public static ArrayList<String> getVoices(Context c){
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Sounds/voices directory doesn't exist
+	 */
+	public static ArrayList<String> getVoices(Context c) throws FileNotFoundException {
 		File[] files = Utils.getFilesFromRelativeDir(c, "Sounds/voices");
 		ArrayList<String> ret = new ArrayList<String>();
 
@@ -129,9 +150,14 @@
 		return ret;
 	}
 
-	public static ArrayList<String> getForts(Context c){
-		return Utils.getFilesFromDirWithSuffix(c,"Forts", "L.png", true);
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Forts directory doesn't exist
+	 */
+	public static ArrayList<String> getForts(Context c) throws FileNotFoundException {
+		return Utils.getFileNamesFromDirWithSuffix(c,"Forts", "L.png", true);
 	}
+	
+	// TODO wat
 	public static ArrayList<HashMap<String, ?>> getTypes(Context c){
 		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(6);
 		String[] levels = {c.getString(R.string.human), c.getString(R.string.bot5), c.getString(R.string.bot4), c.getString(R.string.bot3), c.getString(R.string.bot2), c.getString(R.string.bot1)};
@@ -147,9 +173,12 @@
 		return data;
 	}
 
-	public static ArrayList<HashMap<String, ?>> getHats(Context c){
-		ArrayList<String> files = Utils.getFilesFromDirWithSuffix(c,"Graphics/Hats", ".png", true);
-		String pathPrefix = Utils.getDataPath(c) + "Graphics/Hats/";
+	/**
+	 * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Hats directory doesn't exist
+	 */
+	public static ArrayList<HashMap<String, ?>> getHats(Context c) throws FileNotFoundException {
+		ArrayList<String> files = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Hats", ".png", true);
+		File hatsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Hats");
 		int size = files.size();
 		ArrayList<HashMap<String, ?>> data = new ArrayList<HashMap<String, ?>>(size);
 
@@ -157,7 +186,7 @@
 		for(String s : files){
 			hashmap = new HashMap<String, Object>();
 			hashmap.put("txt", s);
-			Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap
+			Bitmap b = BitmapFactory.decodeFile(new File(hatsPath, s + ".png").getAbsolutePath());
 			b = Bitmap.createBitmap(b, 0,0,b.getWidth()/2, b.getWidth()/2);
 			hashmap.put("img", b);
 			data.add(hashmap);
@@ -166,8 +195,8 @@
 		return data;
 	}
 
-	public static List<TeamFile> getTeamFiles(Context c) {
-		List<TeamFile> ret = new ArrayList<TeamFile>();
+	public static List<Team> getTeams(Context c) {
+		List<Team> ret = new ArrayList<Team>();
 		
 		File teamsDir = new File(c.getFilesDir(), Team.DIRECTORY_TEAMS);
 		File[] teamFileNames = teamsDir.listFiles();
@@ -175,15 +204,10 @@
 			for(File file : teamFileNames){
 				Team team = Team.load(file);
 				if(team != null){
-					ret.add(new TeamFile(team, file));
+					ret.add(team);
 				}
 			}
 		}
 		return ret;
 	}
-
-	public static Scheme[] getSchemes(StartGameActivity startGameActivity) {
-		// TODO Auto-generated method stub
-		return null;
-	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,54 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Game configuration from the point of view of the UI. This differs slightly from the
+ * frontlib view, because the engine allows setting a separate initial health and weapon set
+ * for each hog, while the Android UI currently only supports both attributes on a per-game
+ * basis (initial health is contained in the scheme).
+ * 
+ * This difference means that creating a GameConfig object from a frontlib object can, in
+ * theory, lose information. This does not cause problems at the moment because for now
+ * weaponset and initial health are always per-game, but that might change in the future.
+ */
+public final class GameConfig {
+	public static final String DEFAULT_STYLE = "Normal";
+	public static final String DEFAULT_SCHEME = "Default";
+	public static final String DEFAULT_WEAPONSET = "Default";
+	
+	public final String style;
+	public final Scheme scheme;
+	public final MapRecipe map;
+	public final List<TeamInGame> teams;
+	public final Weaponset weaponset;
+	
+	public GameConfig(String style, Scheme scheme, MapRecipe map, List<TeamInGame> teams, Weaponset weaponset) {
+		this.style = style;
+		this.scheme = scheme;
+		this.map = map;
+		this.teams = Collections.unmodifiableList(new ArrayList<TeamInGame>(teams));
+		this.weaponset = weaponset;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Map.java	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-import java.io.File;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-public class Map implements Comparable<Map> {
-
-	private static final String MISSION_PREFIX = "Mission: ";
-
-	private String name;
-	private String path;
-	private String previewPath;
-	private MapType type;
-
-	public Map(File mapDir, MapType _type, Context c){
-		type = _type;
-
-		name = mapDir.getName();
-		path = mapDir.getAbsolutePath();
-		previewPath = path + "/preview.png";
-		
-		/*switch(type){
-		case TYPE_DEFAULT:
-			
-			break;
-		case TYPE_GENERATED:
-			//TODO
-			break;
-		case TYPE_MISSION:
-			name = MISSION_PREFIX + mapDir.getName();
-			path = mapDir.getAbsolutePath();
-			break;
-		}*/
-
-		
-	}
-
-	public String toString(){
-		switch(type){
-		default:
-		case TYPE_DEFAULT:
-			return name;
-		case TYPE_GENERATED:
-			return "bla";
-		case TYPE_MISSION:
-			return MISSION_PREFIX + name;
-		}
-	}
-	
-	public MapType getType(){
-		return type;
-	}
-
-	public Drawable getDrawable(){
-		switch(type){
-		case TYPE_MISSION:
-		case TYPE_DEFAULT:
-			return Drawable.createFromPath(previewPath);
-		case TYPE_GENERATED:
-
-		default:
-			return null;
-		}
-	}
-
-	public int compareTo(Map another) {
-		switch(type){
-		case TYPE_GENERATED:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return name.compareTo(another.name);
-			case TYPE_MISSION:
-				return -1;
-			case TYPE_DEFAULT:
-				return -1;
-			}
-		case TYPE_MISSION:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return 1;
-			case TYPE_MISSION:
-				return name.compareTo(another.name);
-			case TYPE_DEFAULT:
-				return -1;
-			}
-		case TYPE_DEFAULT:
-			switch(another.getType()){
-			case TYPE_GENERATED:
-				return 1;
-			case TYPE_MISSION:
-				return 1;
-			case TYPE_DEFAULT:
-				return name.compareTo(another.name);
-			}
-		}
-		return 0;//default case this should never happen
-	}
-
-	public enum MapType{
-		TYPE_DEFAULT, TYPE_MISSION, TYPE_GENERATED
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,52 @@
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Comparator;
+
+import org.hedgewars.hedgeroid.Utils;
+
+import android.content.Context;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+/**
+ * Represents a map from the data directory
+ */
+public final class MapFile {
+	public static final String MISSION_PREFIX = "Mission: "; // TODO move text generation to UI to allow translation
+	public static final String MAP_DIRECTORY = "Maps";
+	
+	public final String name;
+	public final boolean isMission;
+	
+	public MapFile(String name, boolean isMission) {
+		this.name = name;
+		this.isMission = isMission;
+	}
+	
+	/**
+	 * @throws FileNotFoundException if the sdcard is not available. Does NOT throw if the requested map file does not exist.
+	 */
+	public static File getFileForMapname(Context ctx, String mapname) throws FileNotFoundException {
+		return new File(new File(Utils.getDataPathFile(ctx), MAP_DIRECTORY), mapname);
+	}
+	
+	public static final Comparator<MapFile> MISSIONS_FIRST_NAME_ORDER = new Comparator<MapFile>() {
+		public int compare(MapFile lhs, MapFile rhs) {
+			if(lhs.isMission != rhs.isMission) {
+				return lhs.isMission && !rhs.isMission ? -1 : 1;
+			} else {
+				return lhs.name.compareToIgnoreCase(rhs.name);
+			}
+		}
+	};
+	
+	@Override
+	public String toString() {
+		return (isMission ? MISSION_PREFIX : "") + name;
+	}
+
+	public File getPreviewFile(Context c) throws FileNotFoundException {
+		return new File(new File(new File(Utils.getDataPathFile(c), MAP_DIRECTORY), name), "preview.png");
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,80 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+package org.hedgewars.hedgeroid.Datastructures;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.frontlib.Frontlib;
+
+import android.content.res.Resources;
+
+public final class MapRecipe {
+	public static final String MAPNAME_REGULAR = "+rnd+";
+	public static final String MAPNAME_MAZE = "+maze+";
+	public static final String MAPNAME_DRAWN = "+drawn+";
+
+	public final int mapgen;			// Frontlib.MAPGEN_xxx
+	public final int templateFilter;	// Frontlib.TEMPLATEFILTER_xxx, only used when mapgen==MAPGEN_REGULAR
+	public final int mazeSize;			// Frontlib.MAZE_SIZE_xxx, only used when mapgen==MAPGEN_MAZE
+	public final String name, seed, theme;
+	
+	private final byte[] drawData;		// For drawn maps, can be null.
+
+	public MapRecipe(int mapgen, int templateFilter, int mazeSize, String name, String seed, String theme, byte[] drawData) {
+		this.mapgen = mapgen;
+		this.templateFilter = templateFilter;
+		this.mazeSize = mazeSize;
+		this.name = name;
+		this.seed = seed;
+		this.theme = theme;
+		this.drawData = drawData==null ? null : drawData.clone();
+	}
+	
+	public static MapRecipe makeMap(String name, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_NAMED, 0, 0, name, seed, theme, null);
+	}
+	
+	public static MapRecipe makeRandomMap(int templateFilter, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_REGULAR, templateFilter, 0, MAPNAME_REGULAR, seed, theme, null);
+	}
+	
+	public static MapRecipe makeRandomMaze(int mazeSize, String seed, String theme) {
+		return new MapRecipe(Frontlib.MAPGEN_MAZE, 0, mazeSize, MAPNAME_MAZE, seed, theme, null);
+	}
+	
+	public static MapRecipe makeDrawnMap(String seed, String theme, byte[] drawData) {
+		return new MapRecipe(Frontlib.MAPGEN_DRAWN, 0, 0, MAPNAME_DRAWN, seed, theme, drawData);
+	}
+	
+	public byte[] getDrawData() {
+		return drawData==null ? null : drawData.clone();
+	}
+	
+	public static String formatMapName(Resources res, String map) {
+		if(map.charAt(0)=='+') {
+			if(map.equals(MAPNAME_REGULAR)) {
+				return res.getString(R.string.map_regular);
+			} else if(map.equals(MAPNAME_MAZE)) {
+				return res.getString(R.string.map_maze);
+			} else if(map.equals(MAPNAME_DRAWN)) {
+				return res.getString(R.string.map_drawn);
+			}
+		}
+		return map;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,14 +1,8 @@
 package org.hedgewars.hedgeroid.Datastructures;
 
-import org.hedgewars.hedgeroid.R;
-
 import android.content.res.Resources;
 
 public final class RoomlistRoom {
-	public static final String MAP_REGULAR = "+rnd+";
-	public static final String MAP_MAZE = "+maze+";
-	public static final String MAP_DRAWN = "+drawn+";
-	
 	public final String name, map, scheme, weapons, owner;
 	public final int playerCount, teamCount;
 	public final boolean inProgress;
@@ -25,21 +19,8 @@
 		this.inProgress = inProgress;
 	}
 	
-	public static String formatMapName(Resources res, String map) {
-		if(map.charAt(0)=='+') {
-			if(map.equals(MAP_REGULAR)) {
-				return res.getString(R.string.map_regular);
-			} else if(map.equals(MAP_MAZE)) {
-				return res.getString(R.string.map_maze);
-			} else if(map.equals(MAP_DRAWN)) {
-				return res.getString(R.string.map_drawn);
-			}
-		}
-		return map;
-	}
-	
 	public String formatMapName(Resources res) {
-		return formatMapName(res, map);
+		return MapRecipe.formatMapName(res, map);
 	}
 
 	@Override
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java	Sun Aug 12 22:46:23 2012 +0200
@@ -38,13 +38,19 @@
 	}
 	
 	public int getHealth() {
-		return settings.get("health");
+		Integer health = settings.get("health");
+		return health==null ? 100 : health.intValue();
 	}
 
-	@Override
+	/*@Override
 	public String toString() {
 		return "Scheme [metascheme=" + metascheme + ", name=" + name
 				+ ", settings=" + settings + ", mods=" + mods + "]";
+	}*/
+	
+	@Override
+	public String toString() {
+		return name; // TODO change back once StartGameActivity does not need this anymore
 	}
 	
 	public static final Comparator<Scheme> caseInsensitiveNameComparator = new Comparator<Scheme>() {
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java	Sun Aug 12 22:46:23 2012 +0200
@@ -32,6 +32,12 @@
 		return new File(c.getFilesDir(), "schemes_builtin.ini");
 	}
 	
+	public static Map<String, Scheme> loadAllSchemes(Context c) throws IOException {
+		Map<String, Scheme> result = loadUserSchemes(c);
+		result.putAll(loadBuiltinSchemes(c));
+		return result;
+	}
+	
 	public static Map<String, Scheme> loadUserSchemes(Context c) throws IOException {
 		return loadSchemes(c, getUserSchemesFile(c));
 	}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java	Sun Aug 12 22:46:23 2012 +0200
@@ -22,26 +22,28 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
+import org.hedgewars.hedgeroid.Utils;
 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
 import org.hedgewars.hedgeroid.frontlib.Flib;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
 
-import android.util.Log;
+import android.content.Context;
 
 public final class Team {
 	public static final String DIRECTORY_TEAMS = "teams";
 
-	public static final int maxNumberOfHogs = PascalExports.HWgetMaxNumberOfHogs();
+	public static final int HEDGEHOGS_PER_TEAM = Flib.INSTANCE.flib_get_hedgehogs_per_team();
 	public static final int maxNumberOfTeams = PascalExports.HWgetMaxNumberOfTeams();
 
 	public final String name, grave, flag, voice, fort;
 	public final List<Hog> hogs;
 
 	public Team(String name, String grave, String flag, String voice, String fort, List<Hog> hogs) {
-		if(hogs.size() != maxNumberOfHogs) {
-			throw new IllegalArgumentException("A team must consist of "+maxNumberOfHogs+" hogs.");
+		if(hogs.size() != HEDGEHOGS_PER_TEAM) {
+			throw new IllegalArgumentException("A team must consist of "+HEDGEHOGS_PER_TEAM+" hogs.");
 		}
 		this.name = name;
 		this.grave = grave;
@@ -52,7 +54,6 @@
 	}
 
 	public void save(File f) throws IOException {
-		Log.d("Team", "saving to "+f.getAbsolutePath());
 		TeamPtr teamPtr = TeamPtr.createJavaOwned(this);
 		if(Flib.INSTANCE.flib_team_to_ini(f.getAbsolutePath(), teamPtr) != 0) {
 			throw new IOException("Error saving team "+name);
@@ -69,11 +70,21 @@
 			return null;
 		}
 	}
-
+	
+	public static File getTeamfileByName(Context c, String teamName) {
+		return new File(new File(c.getFilesDir(), DIRECTORY_TEAMS), Utils.replaceBadChars(teamName)+".hwt");
+	}
+	
 	@Override
 	public String toString() {
 		return "Team [name=" + name + ", grave=" + grave + ", flag=" + flag
 				+ ", voice=" + voice + ", fort=" + fort + ", hogs=" + hogs
 				+ "]";
 	}
+	
+	public static Comparator<Team> NAME_ORDER = new Comparator<Team>() {
+		public int compare(Team lhs, Team rhs) {
+			return lhs.name.compareToIgnoreCase(rhs.name);
+		}
+	};
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamFile.java	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package org.hedgewars.hedgeroid.Datastructures;
-
-import java.io.File;
-
-public final class TeamFile {
-	public final Team team;
-	public final File file;
-	
-	public TeamFile(Team team, File file) {
-		this.team = team;
-		this.file = file;
-	}
-
-	@Override
-	public String toString() {
-		return "TeamFile [team=" + team + ", file=" + file + "]";
-	}
-}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,5 +1,10 @@
 package org.hedgewars.hedgeroid.Datastructures;
 
+/**
+ * A team with per-game configuration. This is similar to the frontlib "team" structure,
+ * except that it does not include weaponset and initial health, which are handled on a
+ * per-game basis in the UI, but per-hog in the frontlib.
+ */
 public final class TeamInGame {
 	public final Team team;
 	public final TeamIngameAttributes ingameAttribs;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java	Sun Aug 12 22:46:23 2012 +0200
@@ -3,19 +3,19 @@
 import java.util.ArrayList;
 import java.util.Random;
 
+import org.hedgewars.hedgeroid.frontlib.Flib;
+
 public final class TeamIngameAttributes {
-	public static final int[] TEAM_COLORS = {
-		0xd12b42, /* red    */ 
-		0x4980c1, /* blue   */ 
-		0x6ab530, /* green  */ 
-		0xbc64c4, /* purple */ 
-		0xe76d14, /* orange */ 
-		0x3fb6e6, /* cyan   */ 
-		0xe3e90c, /* yellow */ 
-		0x61d4ac, /* mint   */ 
-		0xf1c3e1, /* pink   */ 
-		/* add new colors here */
-	};
+	public static final int DEFAULT_HOG_COUNT = 4;
+	public static final int[] TEAM_COLORS;
+	
+	static {
+		int[] teamColors = new int[Flib.INSTANCE.flib_get_teamcolor_count()];
+		for(int i=0; i<teamColors.length; i++) {
+			teamColors[i] = Flib.INSTANCE.flib_get_teamcolor(i);
+		}
+		TEAM_COLORS = teamColors;
+	}
 	
 	public final String ownerName;
 	public final int colorIndex, hogCount;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weapon.java	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.Datastructures;
-
-import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
-
-public class Weapon implements Comparable<Weapon>{
-	public static final String DIRECTORY_WEAPON = "weapons";
-	public static final int maxWeapons = PascalExports.HWgetNumberOfWeapons();
-	
-	private String name;
-	private String QT;
-	private String prob;
-	private String delay;
-	private String crate;
-	
-	public Weapon(String _name, String _QT, String _prob, String _delay, String _crate){
-		name = _name;
-		
-		//Incase there's a newer ammoStore which is bigger we append with zeros
-		StringBuffer sb = new StringBuffer();
-		while(_QT.length() + sb.length() < maxWeapons){
-			sb.append('0');
-		}
-		
-		QT = String.format("e%s %s%s", "ammloadt", _QT, sb);
-		prob = String.format("e%s %s%s", "ammprob", _prob, sb);
-		delay = String.format("e%s %s%s", "ammdelay", _delay, sb);
-		crate = String.format("e%s %s%s", "ammreinf", _crate, sb);
-	}
-	
-	public String toString(){
-		return name;
-	}
-		
-	public int compareTo(Weapon another) {
-		return name.compareTo(another.name);
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,22 @@
+package org.hedgewars.hedgeroid.Datastructures;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+
+public final class Weaponset {
+	public static final int WEAPONS_COUNT = Flib.INSTANCE.flib_get_weapons_count();
+	
+	public final String name, loadout, crateProb, crateAmmo, delay;
+	
+	public Weaponset(String name, String loadout, String crateProb, String crateAmmo, String delay) {
+		this.name = name;
+		this.loadout = loadout;
+		this.crateProb = crateProb;
+		this.crateAmmo = crateAmmo;
+		this.delay = delay;
+	}
+
+	@Override
+	public String toString() {
+		return name; // TODO use the generated one once StartGameActivity doesn't need this anymore
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,63 @@
+package org.hedgewars.hedgeroid.Datastructures;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.frontlib.Flib;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetListPtr;
+
+import android.content.Context;
+
+public final class Weaponsets {
+	private Weaponsets() {
+		throw new AssertionError("This class is not meant to be instantiated");
+	}
+	
+	public static File getUserWeaponsetsFile(Context c) {
+		return new File(c.getFilesDir(), "weapons_user.ini");
+	}
+	
+	public static File getBuiltinWeaponsetsFile(Context c) {
+		return new File(c.getFilesDir(), "weapons_builtin.ini");
+	}
+	
+	public static List<Weaponset> loadAllWeaponsets(Context c) throws IOException {
+		List<Weaponset> result = loadBuiltinWeaponsets(c);
+		result.addAll(loadUserWeaponsets(c));
+		return result;
+	}
+	
+	public static List<Weaponset> loadUserWeaponsets(Context c) throws IOException {
+		return loadWeaponsets(c, getUserWeaponsetsFile(c));
+	}
+	
+	public static List<Weaponset> loadBuiltinWeaponsets(Context c) throws IOException {
+		return loadWeaponsets(c, getBuiltinWeaponsetsFile(c));
+	}
+	
+	public static List<Weaponset> loadWeaponsets(Context c, File weaponsetFile) throws IOException {
+		if(!weaponsetFile.isFile()) {
+			// No file == no weaponsets, no error
+			return new ArrayList<Weaponset>();
+		}
+		WeaponsetListPtr weaponsetListPtr = null;
+		try {
+			weaponsetListPtr = Flib.INSTANCE.flib_weaponsetlist_from_ini(weaponsetFile.getAbsolutePath());
+			if(weaponsetListPtr == null) {
+				throw new IOException("Unable to read weaponsets from "+weaponsetFile);
+			}
+			return weaponsetListPtr.deref();
+		} finally {
+			if(weaponsetListPtr != null) {
+				Flib.INSTANCE.flib_weaponsetlist_destroy(weaponsetListPtr);
+			}
+		}
+	}
+	
+	public static void saveUserWeaponsets(Context c, List<Weaponset> weaponsets) throws IOException {
+		WeaponsetListPtr ptr = WeaponsetListPtr.createJavaOwned(weaponsets);
+		Flib.INSTANCE.flib_weaponsetlist_to_ini(getUserWeaponsetsFile(c).getAbsolutePath(), ptr);
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java	Sun Aug 12 22:46:23 2012 +0200
@@ -7,10 +7,9 @@
 import org.hedgewars.hedgeroid.MainActivity;
 import org.hedgewars.hedgeroid.R;
 import org.hedgewars.hedgeroid.Utils;
-import org.hedgewars.hedgeroid.Datastructures.Scheme;
 import org.hedgewars.hedgeroid.Datastructures.Schemes;
 import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
 
 import android.content.res.AssetManager;
 import android.os.AsyncTask;
@@ -46,7 +45,7 @@
 	protected Boolean doInBackground(Object... params) {
 		try {
 			Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.schemes_builtin), Schemes.getBuiltinSchemesFile(act));
-			Utils.resRawToFilesDir(act, R.array.weapons, Weapon.DIRECTORY_WEAPON);
+			Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.weapons_builtin), Weaponsets.getBuiltinWeaponsetsFile(act));
 			Utils.resRawToFilesDir(act, R.array.teams, Team.DIRECTORY_TEAMS);
 			copyFileOrDir(act.getAssets(), Utils.getDataPathFile(act), "Data");
 			copyFileOrDir(act.getAssets(), new File(Utils.getCachePath(act), VERSION_FILENAME), VERSION_FILENAME);
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java	Sun Aug 12 22:37:57 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
- * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-package org.hedgewars.hedgeroid.EngineProtocol;
-
-import java.util.ArrayList;
-
-import org.hedgewars.hedgeroid.Datastructures.GameMode;
-import org.hedgewars.hedgeroid.Datastructures.Map;
-import org.hedgewars.hedgeroid.Datastructures.Scheme;
-import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
-
-public class GameConfig {
-	public GameMode mode = GameMode.MODE_LOCAL;
-	public Map map = null;
-	public String theme = null;
-	public Scheme scheme = null;
-	public Weapon weapon = null;
-	
-	public String style = null;
-	public String training = null;
-	public String seed = null;
-	
-	public ArrayList<Team> teams = new ArrayList<Team>();
-	
-	public GameConfig(){
-		
-	}
-}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java	Sun Aug 12 22:46:23 2012 +0200
@@ -31,10 +31,6 @@
 		System.loadLibrary("hwengine");
 	}
 	
-	public static native int HWversionInfoNetProto();
-	public static native String HWversionInfoVersion();
-	public static native int HWgetNumberOfWeapons();
 	public static native int HWgetMaxNumberOfTeams();
-	public static native int HWgetMaxNumberOfHogs();
     public static native int HWterminate(boolean b);	
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -24,6 +24,7 @@
 
 import org.hedgewars.hedgeroid.Downloader.DownloadAssets;
 import org.hedgewars.hedgeroid.Downloader.DownloadListActivity;
+import org.hedgewars.hedgeroid.frontlib.Flib;
 import org.hedgewars.hedgeroid.netplay.LobbyActivity;
 import org.hedgewars.hedgeroid.netplay.Netplay;
 import org.hedgewars.hedgeroid.netplay.Netplay.State;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -6,13 +6,16 @@
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.egl.EGLSurface;
 
-import org.hedgewars.hedgeroid.EngineProtocol.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports;
 import org.hedgewars.hedgeroid.frontlib.Flib;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback;
 import org.hedgewars.hedgeroid.netplay.TickHandler;
 
+import com.sun.jna.Pointer;
+
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -424,7 +427,18 @@
 
 	private final int surfaceWidth, surfaceHeight;
 	private final GameConfig config;
-
+	private GameconnPtr conn;
+	HandlerThread thread = new HandlerThread("IPC thread");
+	
+	private final IntCallback dccb = new IntCallback() {
+		public void callback(Pointer context, int arg1) {
+			Log.d("SDLMain", "Disconnected: "+arg1);
+			Flib.INSTANCE.flib_gameconn_destroy(conn);
+			conn = null;
+			thread.quit();
+		}
+	};
+	
 	public SDLMain(int width, int height, GameConfig _config) {
 		config = _config;
 		surfaceWidth = width;
@@ -433,35 +447,40 @@
 
 	public void run() {
 		//Set up the IPC socket server to communicate with the engine
-		HandlerThread thread = new HandlerThread("IPC thread");
-		thread.start(); // TODO ensure it gets stopped
+		
 		final GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Xeli", GameSetupPtr.createJavaOwned(config), false);
 		if(conn == null) {
 			throw new AssertionError();
 		}
+		Flib.INSTANCE.flib_gameconn_onDisconnect(conn, dccb, null);
 		int port = Flib.INSTANCE.flib_gameconn_getport(conn);
 
 		String path = Utils.getDataPath(SDLActivity.mSingleton);//This represents the data directory
 		path = path.substring(0, path.length()-1);//remove the trailing '/'
 
+		thread.start(); // TODO ensure it gets stopped
+		new TickHandler(thread.getLooper(), 500, new Runnable() {
+			public void run() {
+				Log.d("SDLMain", "pre-tick");
+				Flib.INSTANCE.flib_gameconn_tick(conn);
+				Log.d("SDLMain", "post-tick");
+			}
+		}).start();
+		
+		Log.d("SDLMain", "Starting engine");
 		// Runs SDL_main() with added parameters
 		SDLActivity.nativeInit(new String[] { String.valueOf(port),
 				String.valueOf(surfaceWidth), String.valueOf(surfaceHeight),
 				"0", "en.txt", "xeli", "1", "1", "1", path, ""  });
-
-		new TickHandler(thread.getLooper(), 50, new Runnable() {
-			public void run() {
-				Flib.INSTANCE.flib_gameconn_tick(conn);
-			}
-		});
+		Log.d("SDLMain", "Engine stopped");
 		try {
-			thread.quit();
 			thread.join();
 		} catch (InterruptedException e) {
 			throw new AssertionError();
 		}
+		Log.v("SDLMain", "thread joined");
 		Flib.INSTANCE.flib_gameconn_destroy(conn);
-		Log.v("SDL", "SDL thread terminated");
+		Log.v("SDLMain", "SDL thread terminated");
 	}
 }
 
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -19,19 +19,29 @@
 
 package org.hedgewars.hedgeroid;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
 import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
-import org.hedgewars.hedgeroid.Datastructures.Map;
-import org.hedgewars.hedgeroid.Datastructures.Map.MapType;
+import org.hedgewars.hedgeroid.Datastructures.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.MapFile;
+import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
 import org.hedgewars.hedgeroid.Datastructures.Scheme;
-import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.Weapon;
-import org.hedgewars.hedgeroid.EngineProtocol.GameConfig;
+import org.hedgewars.hedgeroid.Datastructures.Schemes;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.Weaponset;
+import org.hedgewars.hedgeroid.Datastructures.Weaponsets;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Parcelable;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.AdapterView;
@@ -43,17 +53,16 @@
 import android.widget.Toast;
 
 public class StartGameActivity extends Activity {
-
 	public static final int ACTIVITY_TEAM_SELECTOR = 0;
 	
-	private GameConfig config = null;
 	private ImageButton start, back, team;
 	private Spinner maps, gameplay, gamescheme, weapons, themes;
 	private ImageView themeIcon, mapPreview, teamCount;
+	
+	private List<TeamInGame> teams = new ArrayList<TeamInGame>();
 
 	public void onCreate(Bundle savedInstanceState){
 		super.onCreate(savedInstanceState);
-		config = new GameConfig();
 
 		setContentView(R.layout.starting_game);
 
@@ -75,22 +84,35 @@
 		back.setOnClickListener(backClicker);
 		team.setOnClickListener(teamClicker);
 
-		ArrayAdapter<?> adapter = new ArrayAdapter<Map>(this, R.layout.listview_item, FrontendDataUtils.getMaps(this));
+		List<MapFile> mapFiles;
+		try {
+			mapFiles = FrontendDataUtils.getMaps(this);
+		} catch (FileNotFoundException e) {
+			Log.e("StartGameActivity", e.getMessage(), e);
+			mapFiles = Collections.emptyList();
+		}
+		ArrayAdapter<?> adapter = new ArrayAdapter<MapFile>(this, R.layout.listview_item, mapFiles);
 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 		maps.setAdapter(adapter);
 		maps.setOnItemSelectedListener(mapsClicker);
-		//set to first nonmap
+		//set to first nonmission
 		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Map)adapter.getItem(i)).getType() == MapType.TYPE_DEFAULT){
+			if(!((MapFile)adapter.getItem(i)).isMission){
 				maps.setSelection(i, false);
 				break;
 			}
 		}
 
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, FrontendDataUtils.getGameplay(this));
+		List<String> gameStyles;
+		try {
+			gameStyles = FrontendDataUtils.getGameStyles(this);
+		} catch (FileNotFoundException e) {
+			Log.e("StartGameActivity", e.getMessage(), e);
+			gameStyles = Collections.emptyList();
+		}
+		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, gameStyles);
 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 		gameplay.setAdapter(adapter);
-		gameplay.setOnItemSelectedListener(gameplayClicker);
 		//set to first nonmap
 		for(int i = 0; i < adapter.getCount(); i++){
 			if(((String)adapter.getItem(i)).equals("None")){
@@ -99,30 +121,48 @@
 			}
 		}
 
-		adapter = new ArrayAdapter<Scheme>(this, R.layout.listview_item, FrontendDataUtils.getSchemes(this));
+		List<Scheme> schemes;
+		try {
+			schemes = new ArrayList<Scheme>(Schemes.loadAllSchemes(this).values());
+		} catch (IOException e) {
+			Log.e("StartGameActivity", e.getMessage(), e);
+			schemes = Collections.emptyList();
+		} 
+		adapter = new ArrayAdapter<Scheme>(this, R.layout.listview_item, schemes);
 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 		gamescheme.setAdapter(adapter);
-		gamescheme.setOnItemSelectedListener(schemeClicker);
-		//set to first nonmap
 		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Scheme)adapter.getItem(i)).toString().equals("Default")){
+			if(((Scheme)adapter.getItem(i)).name.equals("Default")){
 				gamescheme.setSelection(i, false);
 				break;
 			}
 		}
 		
-		
-		adapter = new ArrayAdapter<Weapon>(this, R.layout.listview_item, FrontendDataUtils.getWeapons(this));
+		List<Weaponset> weaponsets;
+		try {
+			weaponsets = Weaponsets.loadAllWeaponsets(this);
+		} catch(IOException e) {
+			Log.e("StartGameActivity", e.getMessage(), e);
+			weaponsets = Collections.emptyList();
+		}
+		adapter = new ArrayAdapter<Weaponset>(this, R.layout.listview_item, weaponsets);
 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 		weapons.setAdapter(adapter);
-		weapons.setOnItemSelectedListener(weaponClicker);
 		for(int i = 0; i < adapter.getCount(); i++){
-			if(((Weapon)adapter.getItem(i)).toString().equals("Crazy")){
+			if(((Weaponset)adapter.getItem(i)).name.equals("Crazy")){
 				weapons.setSelection(i, false);
 				break;
 			}
 		}
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, FrontendDataUtils.getThemes(this));
+		
+		List<String> themeList;
+		try {
+			themeList = FrontendDataUtils.getThemes(this);
+		} catch(FileNotFoundException e) {
+			Log.e("StartGameActivity", e.getMessage(), e);
+			themeList = Collections.emptyList();
+		}
+		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, themeList);
 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 		themes.setAdapter(adapter);
 		themes.setOnItemSelectedListener(themesClicker);
@@ -130,7 +170,7 @@
 
 	private void startTeamsActivity(){
 		Intent i = new Intent(StartGameActivity.this, TeamSelectionActivity.class);
-		// TODO i.putParcelableArrayListExtra("teams", config.teams);
+		TeamSelectionActivity.activityParams = new ArrayList<TeamInGame>(teams);
 		startActivityForResult(i, ACTIVITY_TEAM_SELECTOR);
 	}
 
@@ -138,12 +178,9 @@
 		switch(requestCode){
 		case ACTIVITY_TEAM_SELECTOR:
 			if(resultCode == Activity.RESULT_OK){
-				Parcelable[] parcelables = (Parcelable[])data.getParcelableArrayExtra("teams");
-				config.teams.clear();
-				for(Parcelable t : parcelables){
-					// TODO config.teams.add((Team)t);
-				}
-				teamCount.getDrawable().setLevel(config.teams.size());
+				teams = new ArrayList<TeamInGame>(TeamSelectionActivity.activityReturn);
+				TeamSelectionActivity.activityReturn = Collections.emptyList();
+				teamCount.getDrawable().setLevel(teams.size());
 			}
 			break;
 		}
@@ -156,7 +193,6 @@
 			String themeName = (String) arg0.getAdapter().getItem(position);
 			Drawable themeIconDrawable = Drawable.createFromPath(Utils.getDataPath(StartGameActivity.this) + "Themes/" + themeName + "/icon@2X.png");
 			themeIcon.setImageDrawable(themeIconDrawable);
-			config.theme = themeName;
 		}
 
 		public void onNothingSelected(AdapterView<?> arg0) {
@@ -167,9 +203,13 @@
 	private OnItemSelectedListener mapsClicker = new OnItemSelectedListener(){
 
 		public void onItemSelected(AdapterView<?> arg0, View view, int position,long rowId) {
-			Map map = (Map)arg0.getAdapter().getItem(position);
-			mapPreview.setImageDrawable(map.getDrawable());
-			config.map = map;
+			MapFile map = (MapFile)arg0.getAdapter().getItem(position);
+			try {
+				File previewFile = map.getPreviewFile(getApplicationContext());
+				mapPreview.setImageDrawable(Drawable.createFromPath(previewFile.getAbsolutePath()));
+			} catch (FileNotFoundException e) {
+				mapPreview.setImageDrawable(null);
+			}
 		}
 
 		public void onNothingSelected(AdapterView<?> arg0) {
@@ -177,38 +217,19 @@
 
 	};
 
-	private OnItemSelectedListener weaponClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			config.weapon = (Weapon)arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-	private OnItemSelectedListener schemeClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			config.scheme = (Scheme)arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-	private OnItemSelectedListener gameplayClicker = new OnItemSelectedListener(){
-		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-			//config = ()arg0.getAdapter().getItem(arg2);
-		}
-		public void onNothingSelected(AdapterView<?> arg0) {
-
-		}
-	};
-
 	private OnClickListener startClicker = new OnClickListener(){
 		public void onClick(View v) {
-			if(config.teams.size() < 2){
-				Toast.makeText(StartGameActivity.this, R.string.not_enough_teams, Toast.LENGTH_LONG).show();
+			if(teams.size() < 2) {
+				Toast.makeText(getApplicationContext(), R.string.not_enough_teams, Toast.LENGTH_LONG).show();
 				startTeamsActivity();
 			} else {
-				SDLActivity.startConfig = config;
+				String style = (String)gameplay.getSelectedItem();
+				Scheme scheme = (Scheme)gamescheme.getSelectedItem();
+				String mapName = ((MapFile)maps.getSelectedItem()).name;
+				String theme = (String)themes.getSelectedItem();
+				MapRecipe map = MapRecipe.makeMap(mapName, UUID.randomUUID().toString(), theme);
+				Weaponset weaponset = (Weaponset)weapons.getSelectedItem();
+				SDLActivity.startConfig = new GameConfig(style, scheme, map, teams, weaponset);
 				Intent i = new Intent(StartGameActivity.this, SDLActivity.class);
 				startActivity(i);
 			}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -20,11 +20,13 @@
 package org.hedgewars.hedgeroid;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 
 import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
 import org.hedgewars.hedgeroid.Datastructures.Hog;
@@ -37,7 +39,6 @@
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.ArrayAdapter;
@@ -49,23 +50,29 @@
 import android.widget.ScrollView;
 import android.widget.SimpleAdapter;
 import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import android.widget.Toast;
 
-public class TeamCreatorActivity extends Activity implements Runnable{
-
+/**
+ * Edit or create a team. If a team should be edited, it is supplied in the extras
+ * as parameter oldTeamName.
+ */
+public class TeamCreatorActivity extends Activity implements Runnable {
+	public static final String PARAMETER_EXISTING_TEAMNAME = "existingTeamName";
+	
 	private TextView name;
 	private Spinner difficulty, grave, flag, voice, fort;
 	private ImageView imgFort;
 	private ArrayList<ImageButton> hogDice = new ArrayList<ImageButton>();
 	private ArrayList<Spinner> hogHat = new ArrayList<Spinner>();
 	private ArrayList<EditText> hogName = new ArrayList<EditText>();
-	private ImageButton back, save, voiceButton;
+	private ImageButton voiceButton;
 	private ScrollView scroller;
 	private MediaPlayer mp = null;
-	private boolean settingsChanged = false;
-	private boolean saved = false;
-	private String fileName = null;
+	private boolean initComplete = false;
+	
+	private String existingTeamName = null;
 
 	private final List<HashMap<String, ?>> flagsData = new ArrayList<HashMap<String, ?>>();
 	private final List<HashMap<String, ?>> typesData = new ArrayList<HashMap<String, ?>>();
@@ -76,6 +83,16 @@
 
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
+		initComplete = false;
+		
+		// Restore state and read parameters 
+		if(savedInstanceState != null) {
+			existingTeamName = savedInstanceState.getString(PARAMETER_EXISTING_TEAMNAME);
+		} else {
+			existingTeamName = getIntent().getStringExtra(PARAMETER_EXISTING_TEAMNAME);
+		}
+		
+		// Set up view
 		setContentView(R.layout.team_creation);
 
 		name = (TextView) findViewById(R.id.txtName);
@@ -87,15 +104,11 @@
 
 		imgFort = (ImageView) findViewById(R.id.imgFort);
 
-		back = (ImageButton) findViewById(R.id.btnBack);
-		save = (ImageButton) findViewById(R.id.btnSave);
 		voiceButton = (ImageButton) findViewById(R.id.btnPlay);
 
 		scroller = (ScrollView) findViewById(R.id.scroller);
 
-		save.setOnClickListener(saveClicker);
-		back.setOnClickListener(backClicker);
-
+		// Wire view elements
 		LinearLayout ll = (LinearLayout) findViewById(R.id.HogsContainer);
 		for (int i = 0; i < ll.getChildCount(); i++) {
 			RelativeLayout team_creation_entry = (RelativeLayout) ll.getChildAt(i);
@@ -108,107 +121,116 @@
 					.findViewById(R.id.txtTeam1));
 		}
 
-		SimpleAdapter sa = new SimpleAdapter(this, gravesData,
-				R.layout.spinner_textimg_entry, new String[] { "txt", "img" },
-				new int[] { R.id.spinner_txt, R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
-		grave.setAdapter(sa);
-		grave.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, flagsData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
-		flag.setAdapter(sa);
-		flag.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, typesData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		difficulty.setAdapter(sa);
-		difficulty.setOnFocusChangeListener(focusser);
-
-		sa = new SimpleAdapter(this, hatsData, R.layout.spinner_textimg_entry,
-				new String[] { "txt", "img" }, new int[] { R.id.spinner_txt,
-				R.id.spinner_img });
-		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
-		sa.setViewBinder(viewBinder);
+		grave.setAdapter(createMapSpinnerAdapter(gravesData));
+		flag.setAdapter(createMapSpinnerAdapter(flagsData));
+		difficulty.setAdapter(createMapSpinnerAdapter(typesData));
+		SpinnerAdapter hatAdapter = createMapSpinnerAdapter(hatsData);
 		for (Spinner spin : hogHat) {
-			spin.setAdapter(sa);
+			spin.setAdapter(hatAdapter);
 		}
 
-		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.listview_item, voicesData);
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		voice.setAdapter(adapter);
-		voice.setOnFocusChangeListener(focusser);
+
+		voice.setAdapter(createListSpinnerAdapter(voicesData));
 		voiceButton.setOnClickListener(voiceClicker);
 
-		adapter = new ArrayAdapter<String>(this, R.layout.listview_item, fortsData);
-		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-		fort.setAdapter(adapter);
+		fort.setAdapter(createListSpinnerAdapter(fortsData));
 		fort.setOnItemSelectedListener(fortSelector);
-		fort.setOnFocusChangeListener(focusser);
 
 		new Thread(this).start();
 	}
 
-	public void run(){
-		final ArrayList<HashMap<String, ?>> gravesDataNew = FrontendDataUtils.getGraves(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(gravesData, gravesDataNew);
-				((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> flagsDataNew = FrontendDataUtils.getFlags(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(flagsData, flagsDataNew);
-				((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> typesDataNew = FrontendDataUtils.getTypes(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(typesData, typesDataNew);
-				((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<HashMap<String, ?>> hatsDataNew = FrontendDataUtils.getHats(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(hatsData, hatsDataNew);
-				((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<String> voicesDataNew = FrontendDataUtils.getVoices(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(voicesData, voicesDataNew);
-				((ArrayAdapter<String>)voice.getAdapter()).notifyDataSetChanged();
-			}
-		});
-		
-		final ArrayList<String> fortsDataNew = FrontendDataUtils.getForts(this);
-		this.runOnUiThread(new Runnable(){
-			public void run() {
-				copy(fortsData, fortsDataNew);
-				((ArrayAdapter<String>)fort.getAdapter()).notifyDataSetChanged();
-			}
-		});
+	private SpinnerAdapter createMapSpinnerAdapter(List<? extends Map<String, ?>> data) {
+		SimpleAdapter sa = new SimpleAdapter(this, data,
+				R.layout.spinner_textimg_entry, new String[] { "txt", "img" },
+				new int[] { R.id.spinner_txt, R.id.spinner_img });
+		sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry);
+		sa.setViewBinder(viewBinder);
+		return sa;
+	}
+	
+	private SpinnerAdapter createListSpinnerAdapter(List<String> data) {
+		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.listview_item, data);
+		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+		return adapter;
 	}
 	
-	private static <T> void copy(List<T> dest, List<T> src){
-		for(T t: src) dest.add(t);
+	public void run(){
+		try {
+			final ArrayList<HashMap<String, ?>> gravesDataNew = FrontendDataUtils.getGraves(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					gravesData.addAll(gravesDataNew);
+					((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final ArrayList<HashMap<String, ?>> flagsDataNew = FrontendDataUtils.getFlags(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					flagsData.addAll(flagsDataNew);
+					((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final ArrayList<HashMap<String, ?>> typesDataNew = FrontendDataUtils.getTypes(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					typesData.addAll(typesDataNew);
+					((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final ArrayList<HashMap<String, ?>> hatsDataNew = FrontendDataUtils.getHats(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					hatsData.addAll(hatsDataNew);
+					((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final ArrayList<String> voicesDataNew = FrontendDataUtils.getVoices(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					voicesData.addAll(voicesDataNew);
+					((ArrayAdapter<?>)voice.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			final ArrayList<String> fortsDataNew = FrontendDataUtils.getForts(this);
+			runOnUiThread(new Runnable(){
+				public void run() {
+					fortsData.addAll(fortsDataNew);
+					((ArrayAdapter<?>)fort.getAdapter()).notifyDataSetChanged();
+				}
+			});
+			
+			if(existingTeamName!=null) {
+				final Team loadedTeam = Team.load(Team.getTeamfileByName(getApplicationContext(), existingTeamName));
+				if(loadedTeam==null) {
+					existingTeamName = null;
+				} else {
+					runOnUiThread(new Runnable(){
+						public void run() {
+							setTeamValues(loadedTeam);
+						}
+					});
+				}
+			}
+			runOnUiThread(new Runnable(){
+				public void run() {
+					initComplete = true;
+				}
+			});
+		} catch(FileNotFoundException e) {
+			this.runOnUiThread(new Runnable(){
+				public void run() {
+					Toast.makeText(getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
+					finish();
+				}
+			});
+		}
 	}
-
+	
 	public void onDestroy() {
 		super.onDestroy();
 		if (mp != null) {
@@ -217,93 +239,88 @@
 		}
 	}
 
-	private OnFocusChangeListener focusser = new OnFocusChangeListener() {
-		public void onFocusChange(View v, boolean hasFocus) {
-			settingsChanged = true;
-		}
-
-	};
+	@Override
+	protected void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		outState.putString(PARAMETER_EXISTING_TEAMNAME, existingTeamName);
+	}
 
 	public void onBackPressed() {
-		onFinishing();
+		if(initComplete) {
+			saveTeam();
+		}
+		setResult(RESULT_OK);
 		super.onBackPressed();
-
 	}
 
-	private OnClickListener backClicker = new OnClickListener() {
-		public void onClick(View v) {
-			onFinishing();
-			finish();
+	private int getDifficultyLevelFromText(String text) {
+		if (text.equals(getString(R.string.human))) {
+			return 0;
+		} else if (text.equals(getString(R.string.bot5))) {
+			return 1;
+		} else if (text.equals(getString(R.string.bot4))) {
+			return 2;
+		} else if (text.equals(getString(R.string.bot3))) {
+			return 3;
+		} else if (text.equals(getString(R.string.bot2))) {
+			return 4;
+		} else {
+			return 5;
+		}
+	}
+	
+	private String getTxtFromDifficulty(int level) {
+		switch(level) {
+		case 0: return getString(R.string.human);
+		case 1: return getString(R.string.bot5);
+		case 2: return getString(R.string.bot4);
+		case 3: return getString(R.string.bot3);
+		case 4: return getString(R.string.bot2);
+		default: return getString(R.string.bot1);
+		}
+	}
+	
+	private void saveTeam() {
+		String teamName = name.getText().toString();
+		String teamFlag = (String)((Map<String, Object>) flag.getSelectedItem()).get("txt");
+		String teamFort = fort.getSelectedItem().toString();
+		String teamGrave = (String)((Map<String, Object>) grave.getSelectedItem()).get("txt");
+		String teamVoice = voice.getSelectedItem().toString();
+		String levelString = (String)((Map<String, Object>) difficulty.getSelectedItem()).get("txt");
+		int levelInt = getDifficultyLevelFromText(levelString);
+		
+		List<Hog> hogs = new ArrayList<Hog>();
+		for (int i = 0; i < hogName.size(); i++) {
+			String name = hogName.get(i).getText().toString();
+			String hat = ((Map<String, Object>) hogHat.get(i).getSelectedItem()).get("txt").toString();
+			hogs.add(new Hog(name, hat, levelInt));
+		}
+		
+		Team team = new Team(teamName, teamGrave, teamFlag, teamVoice, teamFort, hogs);
+		File teamsDir = new File(getFilesDir(), Team.DIRECTORY_TEAMS);
+		if (!teamsDir.exists()) teamsDir.mkdir();
+		
+		File newFile = Team.getTeamfileByName(this, teamName);
+		File oldFile = null;
+		if(existingTeamName != null) {
+			oldFile = Team.getTeamfileByName(this, existingTeamName);
+		}
+		try {
+			team.save(newFile);
+			Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show();
+			// If the team was renamed, delete the old file.
+			if(oldFile != null && oldFile.isFile() && !oldFile.equals(newFile)) {
+				oldFile.delete();
+			}
+			existingTeamName = teamName;
+		} catch(IOException e) {
+			Toast.makeText(getApplicationContext(), R.string.error_save_failed, Toast.LENGTH_SHORT).show();
 		}
 	};
 
-	private void onFinishing() {
-		if (settingsChanged) {
-			setResult(RESULT_OK);
-		} else {
-			setResult(RESULT_CANCELED);
-		}
-	}
-
-	private OnClickListener saveClicker = new OnClickListener() {
-		public void onClick(View v) {
-			String teamName = name.getText().toString();
-			String teamFlag = (String)((Map<String, Object>) flag.getSelectedItem()).get("txt");
-			String teamFort = fort.getSelectedItem().toString();
-			String teamGrave = (String)((Map<String, Object>) grave.getSelectedItem()).get("txt");
-			String teamVoice = voice.getSelectedItem().toString();
-			String levelString = (String)((Map<String, Object>) difficulty.getSelectedItem()).get("txt");
-			int levelInt;
-			if (levelString.equals(getString(R.string.human))) {
-				levelInt = 0;
-			} else if (levelString.equals(getString(R.string.bot5))) {
-				levelInt = 1;
-			} else if (levelString.equals(getString(R.string.bot4))) {
-				levelInt = 2;
-			} else if (levelString.equals(getString(R.string.bot3))) {
-				levelInt = 3;
-			} else if (levelString.equals(getString(R.string.bot2))) {
-				levelInt = 4;
-			} else {
-				levelInt = 5;
-			}
-			
-			List<Hog> hogs = new ArrayList<Hog>();
-			for (int i = 0; i < hogName.size(); i++) {
-				String name = hogName.get(i).getText().toString();
-				String hat = ((Map<String, Object>) hogHat.get(i).getSelectedItem()).get("txt").toString();
-				hogs.add(new Hog(name, hat, levelInt));
-			}
-			
-			Team team = new Team(teamName, teamGrave, teamFlag, teamVoice, teamFort, hogs);
-			File teamsDir = new File(getFilesDir(), Team.DIRECTORY_TEAMS);
-			if (!teamsDir.exists()) teamsDir.mkdir();
-			if(fileName == null){
-				fileName = createNewFilename(Utils.replaceBadChars(team.name));
-			}
-			try {
-				team.save(new File(teamsDir, fileName));
-				Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show();
-				saved = true;
-			} catch(IOException e) {
-				Toast.makeText(getApplicationContext(), R.string.error_save_failed, Toast.LENGTH_SHORT).show();
-			}
-		}
-	};
-
-	private String createNewFilename(String suggestedName){
-		File f = new File(getFilesDir(), suggestedName);
-		if(f.exists()){
-			return createNewFilename(suggestedName + (int)(Math.random()*10));
-		} else {
-			return suggestedName + (int)(Math.random()*10);
-		}
-	}
-	
 	private OnItemSelectedListener fortSelector = new OnItemSelectedListener() {
 		public void onItemSelected(AdapterView<?> arg0, View arg1,
 				int position, long arg3) {
-			settingsChanged = true;
 			String fortName = (String) arg0.getAdapter().getItem(position);
 			Drawable fortIconDrawable = Drawable.createFromPath(Utils
 					.getDataPath(TeamCreatorActivity.this)
@@ -351,54 +368,48 @@
 		}
 	};
 
+	@SuppressWarnings("unchecked")
 	private void setTeamValues(Team t){
-
-		if (t != null) {
+		if (t == null) {
+			return;
+		}
+		
+		try {
 			name.setText(t.name);
-			int position = ((ArrayAdapter<String>) voice.getAdapter()).getPosition(t.voice);
-			voice.setSelection(position);
-
-			position = ((ArrayAdapter<String>) fort.getAdapter()).getPosition(t.fort);
-			fort.setSelection(position);
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : typesData) {
-				if (t.hogs.get(0) != null && hashmap.get("txt").equals(t.hogs.get(0).level)) {
-					difficulty.setSelection(position);
-					break;
-				}
-			}
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : gravesData) {
-				if (hashmap.get("txt").equals(t.grave)) {
-					grave.setSelection(position);
-					break;
-				}
-			}
-
-			position = 0;
-			for (HashMap<String, ?> hashmap : typesData) {
-				if (hashmap.get("txt").equals(t.flag)) {
-					flag.setSelection(position);
-					break;
-				}
-			}
-
-			for (int i = 0; i < Team.maxNumberOfHogs; i++) {
-				position = 0;
-				for (HashMap<String, ?> hashmap : hatsData) {
-					if (hashmap.get("txt").equals(t.hogs.get(i).hat)) {
-						hogHat.get(i).setSelection(position);
-					}
-				}
-
+			voice.setSelection(findPosition((ArrayAdapter<String>) voice.getAdapter(), t.voice));
+			fort.setSelection(findPosition((ArrayAdapter<String>) fort.getAdapter(), t.fort));
+			difficulty.setSelection(findPosition(typesData, getTxtFromDifficulty(t.hogs.get(0).level))); // TODO store actual difficulty int in typesData
+			grave.setSelection(findPosition(gravesData, t.grave));
+			flag.setSelection(findPosition(flagsData, t.flag));
+	
+			for (int i = 0; i < Team.HEDGEHOGS_PER_TEAM; i++) {
+				hogHat.get(i).setSelection(findPosition(hatsData, t.hogs.get(i).hat));
 				hogName.get(i).setText(t.hogs.get(i).name);
 			}
-			//this.fileName = t.file;
+		} catch(NoSuchElementException e) {
+			Toast.makeText(getApplicationContext(), R.string.error_team_attribute_not_found, Toast.LENGTH_LONG).show();
+			finish();
 		}
 	}
 
+	int findPosition(ArrayAdapter<String> adapter, String key) throws NoSuchElementException {
+		int position = adapter.getPosition(key);
+		if(position<0) {
+			throw new NoSuchElementException();
+		}
+		return position;
+	}
+	
+	int findPosition(List<? extends Map<String, ?>> data, String txtValue) throws NoSuchElementException {
+		int position = 0;
+		for (Map<String, ?> map : data) {
+			if (map.get("txt").equals(txtValue)) {
+				return position;
+			}
+			position++;
+		}
+		throw new NoSuchElementException();
+	}
 
 	private SimpleAdapter.ViewBinder viewBinder = new SimpleAdapter.ViewBinder() {
 
@@ -413,5 +424,4 @@
 			}
 		}
 	};
-
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -19,23 +19,18 @@
 
 package org.hedgewars.hedgeroid;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
 import org.hedgewars.hedgeroid.Datastructures.Team;
-import org.hedgewars.hedgeroid.Datastructures.TeamFile;
 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Pair;
 import android.view.ContextMenu;
 import android.view.MenuItem;
 import android.view.View;
@@ -46,7 +41,6 @@
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.ListView;
-import android.widget.RelativeLayout;
 import android.widget.SimpleAdapter;
 import android.widget.SimpleAdapter.ViewBinder;
 import android.widget.TextView;
@@ -54,10 +48,10 @@
 public class TeamSelectionActivity extends Activity implements Runnable{
 	private static final int ACTIVITY_TEAMCREATION = 0;
 	
-	private static volatile List<Pair<TeamFile, TeamIngameAttributes>> activityParams;
-	private static volatile List<Pair<TeamFile, TeamIngameAttributes>> activityReturn;
+	public static volatile List<TeamInGame> activityParams;
+	public static volatile List<TeamInGame> activityReturn;
 
-	private ImageButton addTeam, back;
+	private ImageButton addTeam;
 	private ListView availableTeams, selectedTeams;
 	private List<HashMap<String, Object>> availableTeamsList, selectedTeamsList;
 	private TextView txtInfo;
@@ -68,12 +62,10 @@
 		setContentView(R.layout.team_selector);
 
 		addTeam = (ImageButton) findViewById(R.id.btnAdd);
-		back = (ImageButton) findViewById(R.id.btnBack);
 		txtInfo = (TextView) findViewById(R.id.txtInfo);
 		selectedTeams = (ListView) findViewById(R.id.selectedTeams);
 		availableTeams = (ListView) findViewById(R.id.availableTeams);
 		addTeam.setOnClickListener(addTeamClicker);
-		back.setOnClickListener(backClicker);
 
 		availableTeamsList = new ArrayList<HashMap<String, Object>>();
 		SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry_simple, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty});
@@ -93,23 +85,33 @@
 	}
 
 	public void run(){
-		List<HashMap<String, Object>> teamsList = getTeams(this);//teams from xml
-		ArrayList<String> teamsStartGame = getIntent().getStringArrayListExtra("selectedTeamNames");
-
-		for(HashMap<String, Object> hashmap : teamsList){
+		List<Team> teams = FrontendDataUtils.getTeams(this);
+		List<TeamInGame> existingTeams = activityParams;
+		final List<TeamInGame> newSelectedList = new ArrayList<TeamInGame>();
+		final List<Team> newAvailableList = new ArrayList<Team>();
+		
+		for(Team team : teams){
 			boolean added = false;
-			for(String teamName : teamsStartGame){
-				if(((Team)hashmap.get("team")).name.equals(teamName)){ // add to available or add to selected
-					selectedTeamsList.add(hashmap);
+			for(TeamInGame existingTeam : existingTeams){
+				if(team.name.equals(existingTeam.team.name)){ // add to available or add to selected
+					newSelectedList.add(new TeamInGame(team, existingTeam.ingameAttribs));
 					added = true;
 					break;
 				}
 			}
-			if(!added) availableTeamsList.add(hashmap);
+			if(!added) newAvailableList.add(team);
 		}
 
 		this.runOnUiThread(new Runnable(){
 			public void run() {
+				availableTeamsList.clear();
+				selectedTeamsList.clear();
+				for(TeamInGame t : newSelectedList) {
+					selectedTeamsList.add(toMap(t));
+				}
+				for(Team t : newAvailableList) {
+					availableTeamsList.add(toMap(t));
+				}
 				((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged();
 				((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();		
 			}
@@ -145,39 +147,36 @@
 	 * Updates the list view when TeamCreationActivity is shutdown and the user returns to this point
 	 */
 	private void updateListViews(){
-		unregisterForContextMenu(availableTeams);
-		availableTeamsList = getTeams(this);
+		List<Team> teams = FrontendDataUtils.getTeams(this);
+		availableTeamsList.clear();
+		for(Team team : teams) {
+			availableTeamsList.add(toMap(team));
+		}
+		
 		ArrayList<HashMap<String, Object>> toBeRemoved = new ArrayList<HashMap<String, Object>>();
+		ArrayList<HashMap<String, Object>> toBeRemovedFromSelected = new ArrayList<HashMap<String, Object>>();
 		for(HashMap<String, Object> hashmap : selectedTeamsList){
 			String name = (String)hashmap.get("txt");
-
+			boolean exists = false;
 			for(HashMap<String, Object> hash : availableTeamsList){
 				if(name.equals((String)hash.get("txt"))){
 					toBeRemoved.add(hash);
+					exists = true;
+					break;
 				}
 			}
+			if(!exists) {
+				toBeRemovedFromSelected.add(hashmap);
+			}
 		}
 		for(HashMap<String, Object> hash: toBeRemoved) availableTeamsList.remove(hash);
-
-		SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty});
-		availableTeams.setAdapter(adapter);
-		registerForContextMenu(availableTeams);
-		availableTeams.setOnItemClickListener(availableClicker);
-
-
+		for(HashMap<String, Object> hash: toBeRemovedFromSelected) selectedTeamsList.remove(hash);
+		((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged();
+		((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();
 	}
 
-	private void setTeamColor(int position, int color){
-		View iv = ((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount);
-		setTeamColor(iv, color);
-	}
-	private void setTeamColor(View iv, int color){
-		iv.setBackgroundColor(0xFF000000 + color);
-	}
-
-	private void setTeamHogCount(int position, int count){
-		ImageView iv = (ImageView)((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount);
-		setTeamHogCount(iv, count);
+	private void setTeamColor(View iv, int colorIndex){
+		iv.setBackgroundColor(0xFF000000 + TeamIngameAttributes.TEAM_COLORS[colorIndex]);
 	}
 
 	private void setTeamHogCount(ImageView iv, int count){
@@ -227,13 +226,6 @@
 		}
 	};
 
-	private OnClickListener backClicker = new OnClickListener(){
-		public void onClick(View v){
-			returnTeams();
-			finish();
-		}
-	};
-
 	private OnItemClickListener availableClicker = new OnItemClickListener(){
 		public void onItemClick(AdapterView<?> arg0, View arg1, int position,long arg3) {
 			selectAvailableTeamsItem(position);
@@ -260,19 +252,19 @@
 	public boolean onContextItemSelected(MenuItem item){
 		AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
 		int position = menuInfo.position;
-		TeamFile teamfile = (TeamFile)availableTeamsList.get(position).get("teamfile");
+		Team team = (Team)availableTeamsList.get(position).get("team");
 		switch(item.getItemId()){
 		case 0://select
 			selectAvailableTeamsItem(position);
 			return true;
 		case 1://delete
-			teamfile.file.delete();
+			Team.getTeamfileByName(getApplicationContext(), team.name).delete();
 			availableTeamsList.remove(position);
 			((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged();
 			return true;
 		case 2://edit
 			Intent i = new Intent(TeamSelectionActivity.this, TeamCreatorActivity.class);
-			i.putExtra("teamfile", teamfile.file);
+			i.putExtra(TeamCreatorActivity.PARAMETER_EXISTING_TEAMNAME, team.name);
 			startActivityForResult(i, ACTIVITY_TEAMCREATION);
 			return true;
 		}
@@ -281,14 +273,12 @@
 
 	private void selectAvailableTeamsItem(int position){
 		HashMap<String, Object> hash = (HashMap<String, Object>) availableTeamsList.get(position);
-		Team t = (Team)hash.get("team");
 		int[] illegalcolors = new int[selectedTeamsList.size()];
 		for(int i = 0; i < selectedTeamsList.size(); i++){
-			illegalcolors[i] = ((Team)selectedTeamsList.get(i).get("team")).color;
+			illegalcolors[i] = (Integer)selectedTeamsList.get(i).get("color");
 		}
-		t.setRandomColor(illegalcolors);
-		hash.put("color", t.color);
-		hash.put("count", t.hogCount);
+		hash.put("color", TeamIngameAttributes.randomColorIndex(illegalcolors));
+		hash.put("count", TeamIngameAttributes.DEFAULT_HOG_COUNT);
 
 		selectedTeamsList.add(hash);
 		availableTeamsList.remove(position);
@@ -298,47 +288,36 @@
 		txtInfo.setText(String.format(getResources().getString(R.string.teams_info_template), selectedTeamsList.size()));
 	}
 
-	private void returnTeams(){
-		int teamsCount = selectedTeamsList.size();
-		Intent i = new Intent();
-		Parcelable[] teams = new Parcelable[teamsCount];
-		for(int x = 0 ; x < teamsCount; x++){
-			teams[x] = (Team)selectedTeamsList.get(x).get("team");
+	private void returnTeams() {
+		List<TeamInGame> result = new ArrayList<TeamInGame>();
+		for(HashMap<String, Object> item : selectedTeamsList) {
+			result.add(new TeamInGame((Team)item.get("team"), new TeamIngameAttributes("Player", (Integer)item.get("color"), (Integer)item.get("count"), false)));
 		}
-		i.putExtra("teams", teams);
-		setResult(Activity.RESULT_OK, i);
-
-	}
-	
-	private static List<HashMap<String, Object>> getTeams(Context c){
-		List<HashMap<String, Object>> ret = new ArrayList<HashMap<String, Object>>();
-		List<TeamFile> teamfiles = FrontendDataUtils.getTeamFiles(c);
-		for(TeamFile tf : teamfiles) {
-			ret.add(teamfileToMap(tf));
-		}
-		return ret;
+		activityReturn = result;
+		setResult(Activity.RESULT_OK);
 	}
 	
 	private static final int[] botlevelDrawables = new int[] {
 		R.drawable.human, R.drawable.bot5, R.drawable.bot4, R.drawable.bot3, R.drawable.bot2, R.drawable.bot1
 	};
-	
-	private static HashMap<String, Object> teamfileToMap(TeamFile tf){
-		HashMap<String, Object> hashmap = new HashMap<String, Object>();
-		Team t = tf.team;
-		hashmap.put("team", t);
-		hashmap.put("teamfile", tf);
-		hashmap.put("txt", t.name);
-		hashmap.put("color", t.color);
-		hashmap.put("count", t.hogCount);
 		
+	private static HashMap<String, Object> toMap(Team t) {
+		HashMap<String, Object> map = new HashMap<String, Object>();
+		map.put("team", t);
+		map.put("txt", t.name);
 		int botlevel = t.hogs.get(0).level;
 		if(botlevel<0 || botlevel>=botlevelDrawables.length) {
-			hashmap.put("img", R.drawable.bot1);
+			map.put("img", R.drawable.bot1);
 		} else {
-			hashmap.put("img", botlevelDrawables[botlevel]);
-		}
-		return hashmap;
-		return null;
+			map.put("img", botlevelDrawables[botlevel]);
+		}	
+		return map;
+	}
+	
+	private static HashMap<String, Object> toMap(TeamInGame t) {
+		HashMap<String, Object> map = toMap(t.team);
+		map.put("color", t.ingameAttribs.colorIndex);
+		map.put("count", t.ingameAttribs.hogCount);
+		return map;
 	}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java	Sun Aug 12 22:46:23 2012 +0200
@@ -54,7 +54,7 @@
 	 * get the path to which we should download all the data files
 	 * @param c context 
 	 * @return The directory
-	 * @throws FileNotFoundException if external storage is not avaliable at the moment
+	 * @throws FileNotFoundException if external storage is not available at the moment
 	 */
 	public static File getCachePath(Context c) throws FileNotFoundException {
 		File cachePath = null;
@@ -105,37 +105,19 @@
 	}
 
 	/**
-	 * Get files from dirName, dir name is relative to {@link getDownloadPath}
-	 * @param dirName
-	 * @param c context
-	 * @return string of files
-	 */
-	public static String[] getFileNamesFromRelativeDir(Context c, String dirName){
-		String prefix = getDataPath(c);
-		File f = new File(prefix + dirName);
-
-		if(f.exists() && f.isDirectory()) return f.list();
-		else{
-
-			Log.e("Utils::", "Couldn't find dir: " + dirName);
-			return new String[0];
-		}
-	}
-
-	/**
 	 * Return a File array with all the files from dirName
 	 * @param c
 	 * @param dirName
 	 * @return
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dirName" does not exist
 	 */
-	public static File[] getFilesFromRelativeDir(Context c, String dirName){
-		String prefix = getDataPath(c);
-		File f = new File(prefix + dirName);
+	public static File[] getFilesFromRelativeDir(Context c, String dirName) throws FileNotFoundException {
+		File f = new File(getDataPathFile(c), dirName);
 
-		if(f.exists() && f.isDirectory()) return f.listFiles();
-		else {
-			Log.e("Utils::", "Dir not found: " + dirName);
-			return new File[0];
+		if(f.isDirectory()) {
+			return f.listFiles();
+		} else {
+			throw new FileNotFoundException("Directory "+dirName+" does not exist.");
 		}
 	}
 
@@ -161,8 +143,9 @@
 	 * @param path
 	 * @param fileSuffix
 	 * @return
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "path" does not exist
 	 */
-	public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix){
+	public static List<String> getDirsWithFileSuffix(Context c, String path, String fileSuffix) throws FileNotFoundException{
 		File[] files = getFilesFromRelativeDir(c,path);
 		ArrayList<String> ret = new ArrayList<String>();
 
@@ -174,16 +157,13 @@
 
 	/**
 	 * Get all files from directory dir which have the given suffix
-	 * @param c
-	 * @param dir
-	 * @param suffix
-	 * @param removeSuffix
-	 * @return
+	 * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dir" does not exist
 	 */
-	public static ArrayList<String> getFilesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix){
-		String[] files = Utils.getFileNamesFromRelativeDir(c, dir);
+	public static ArrayList<String> getFileNamesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix) throws FileNotFoundException{
+		File[] files = Utils.getFilesFromRelativeDir(c, dir);
 		ArrayList<String> ret = new ArrayList<String>();
-		for(String s : files){
+		for(File file : files){
+			String s = file.getName();
 			if(s.endsWith(suffix)){
 				if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length()));
 				else ret.add(s);
--- 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;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java	Sun Aug 12 22:46:23 2012 +0200
@@ -2,10 +2,13 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.TreeMap;
 
 import org.hedgewars.hedgeroid.R;
 import org.hedgewars.hedgeroid.Utils;
 import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom;
+import org.hedgewars.hedgeroid.Datastructures.Team;
 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
 import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
 import org.hedgewars.hedgeroid.frontlib.Flib;
@@ -82,6 +85,7 @@
 	public final MessageLog lobbyChatlog;
 	public final MessageLog roomChatlog;
 	public final Teamlist roomTeamlist = new Teamlist();
+	private final Map<String, Team> roomRequestedTeams = new TreeMap<String, Team>();
 	
 	public Netplay(Context appContext) {
 		this.appContext = appContext;
@@ -133,6 +137,13 @@
 	public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); }
 	public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); }
 	public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); }
+	public void sendAddTeam(Team newTeam) {
+		roomRequestedTeams.put(newTeam.name, newTeam);
+		sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ADD_TEAM, newTeam);
+	}
+	public void sendRemoveTeam(String teamName) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_REMOVE_TEAM, teamName); }
+	public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); }
+	public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); }
 	
 	public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); }
 	
@@ -202,18 +213,17 @@
 	}
 	
 	private boolean sendToNet(int what) {
-		if(connection != null) {
-			Handler handler = connection.toNetHandler;
-			return handler.sendMessage(handler.obtainMessage(what));
-		} else {
-			return false;
-		}
+		return sendToNet(what, 0, null);
 	}
 	
 	private boolean sendToNet(int what, Object obj) {
+		return sendToNet(what, 0, obj);
+	}
+	
+	private boolean sendToNet(int what, int arg1, Object obj) {
 		if(connection != null) {
 			Handler handler = connection.toNetHandler;
-			return handler.sendMessage(handler.obtainMessage(what, obj));
+			return handler.sendMessage(handler.obtainMessage(what, arg1, 0, obj));
 		} else {
 			return false;
 		}
@@ -338,6 +348,7 @@
 				roomChatlog.clear();
 				roomPlayerlist.clear();
 				roomTeamlist.clear();
+				roomRequestedTeams.clear();
 				changeState(State.ROOM);
 				chief = (Boolean)msg.obj;
 				Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY);
@@ -358,7 +369,14 @@
 				break;
 			}
 			case MSG_TEAM_ADDED: {
-				roomTeamlist.addTeamWithNewId((TeamInGame)msg.obj);
+				Team newTeam = (Team)msg.obj;
+				TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false);
+				TeamInGame tig = new TeamInGame(newTeam, attrs);
+				roomTeamlist.addTeamWithNewId(tig);
+				if(chief) {
+					sendTeamColorIndex(newTeam.name, attrs.colorIndex);
+					sendTeamHogCount(newTeam.name, attrs.hogCount);
+				}
 				break;
 			}
 			case MSG_TEAM_DELETED: {
@@ -366,7 +384,18 @@
 				break;
 			}
 			case MSG_TEAM_ACCEPTED: {
-				// TODO depends: adding teams
+				Team requestedTeam = roomRequestedTeams.remove(msg.obj);
+				if(requestedTeam!=null) {
+					TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false);
+					TeamInGame tig = new TeamInGame(requestedTeam, attrs);
+					roomTeamlist.addTeamWithNewId(tig);
+					if(chief) {
+						sendTeamColorIndex(requestedTeam.name, attrs.colorIndex);
+						sendTeamHogCount(requestedTeam.name, attrs.hogCount);
+					}
+				} else {
+					Log.e("Netplay", "Got accepted message for team that was never requested.");
+				}
 				break;
 			}
 			case MSG_TEAM_COLOR_CHANGED: {
@@ -412,7 +441,7 @@
 		private final Context appContext;
 		private final FromNetHandler fromNetHandler;
 		private final TickHandler tickHandler;
-		
+
 		/**
 		 * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting,
 		 * in the same message (the looper is shut down on disconnect, so there will be no messages after that).
@@ -589,7 +618,7 @@
 		
 		private final TeamCallback teamAddedCb = new TeamCallback() {
 			public void callback(Pointer context, TeamPtr team) {
-				sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref());
+				sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref().team);
 			}
 		};
 		
@@ -674,8 +703,11 @@
 			public static final int MSG_SEND_CREATE_ROOM = 8;
 			public static final int MSG_SEND_LEAVE_ROOM = 9;
 			public static final int MSG_SEND_KICK = 10;
-			
-			public static final int MSG_DISCONNECT = 11;
+			public static final int MSG_SEND_ADD_TEAM = 11;
+			public static final int MSG_SEND_REMOVE_TEAM = 12;
+			public static final int MSG_DISCONNECT = 13;
+			public static final int MSG_SEND_TEAM_COLOR_INDEX = 14;
+			public static final int MSG_SEND_TEAM_HOG_COUNT = 15;
 			
 			public ToNetHandler(Looper looper) {
 				super(looper);
@@ -697,7 +729,7 @@
 					break;
 				}
 				case MSG_SEND_ROOMLIST_REQUEST: {
-					FLIB.flib_netconn_send_request_roomlist(conn); // TODO restrict to lobby state?
+					FLIB.flib_netconn_send_request_roomlist(conn);
 					break;
 				}
 				case MSG_SEND_PLAYER_INFO_REQUEST: {
@@ -732,11 +764,33 @@
 					FLIB.flib_netconn_send_kick(conn, (String)msg.obj);
 					break;
 				}
+				case MSG_SEND_ADD_TEAM: {
+					FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj));
+					break;
+				}
+				case MSG_SEND_REMOVE_TEAM: {
+					if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) {
+						sendFromNet(FromNetHandler.MSG_TEAM_DELETED, msg.obj);
+					}
+					break;
+				}
 				case MSG_DISCONNECT: {
 					FLIB.flib_netconn_send_quit(conn, (String)msg.obj);
 					shutdown(false, "User quit");
 					break;
 				}
+				case MSG_SEND_TEAM_COLOR_INDEX: {
+					if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) {
+						sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj);
+					}
+					break;
+				}
+				case MSG_SEND_TEAM_HOG_COUNT: {
+					if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) {
+						sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj);
+					}
+					break;
+				}
 				default: {
 					Log.e("ToNetHandler", "Unknown message type: "+msg.what);
 					break;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,6 +1,7 @@
 package org.hedgewars.hedgeroid.netplay;
 
 import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Team;
 import org.hedgewars.hedgeroid.netplay.Netplay.State;
 import org.hedgewars.hedgeroid.netplay.NetplayStateFragment.NetplayStateListener;
 
@@ -10,7 +11,7 @@
 import android.widget.TabHost;
 import android.widget.Toast;
 
-public class RoomActivity extends FragmentActivity implements NetplayStateListener {
+public class RoomActivity extends FragmentActivity implements NetplayStateListener, TeamAddDialog.Listener {
 	private TabHost tabHost;
 	private Netplay netplay;
 	
@@ -72,4 +73,8 @@
 			throw new IllegalStateException("Unknown connection state: "+newState);
     	}
     }
+    
+	public void onTeamAddDialogSubmitted(Team newTeam) {
+		netplay.sendAddTeam(newTeam);
+	}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java	Sun Aug 12 22:46:23 2012 +0200
@@ -39,7 +39,7 @@
 	
 	private static CharSequence formatExtra(Resources res, RoomlistRoom room) {
 		String ownermsg = res.getString(R.string.roomlist_owner, room.owner);
-		String mapmsg = res.getString(R.string.roomlist_map, RoomlistRoom.formatMapName(res, room.map));
+		String mapmsg = res.getString(R.string.roomlist_map, room.formatMapName(res));
 		String scheme = room.scheme.equals(room.weapons) ? room.scheme : room.scheme + " / " + room.weapons;
 		String schememsg = res.getString(R.string.roomlist_scheme, scheme);
 		return ownermsg + ". " + mapmsg + ", " + schememsg;
@@ -74,7 +74,7 @@
 				teamCountView.setText(String.valueOf(room.teamCount));
 			}
 			ownerView.setText(room.owner);
-			mapView.setText(RoomlistRoom.formatMapName(context.getResources(), room.map));
+			mapView.setText(room.formatMapName(context.getResources()));
 			schemeView.setText(room.scheme);
 			weaponView.setText(room.weapons);
 		} else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java	Sun Aug 12 22:46:23 2012 +0200
@@ -0,0 +1,92 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class TeamAddDialog extends DialogFragment {
+	private static final String STATE_TEAMS_ALREADY_IN_GAME = "teamAlreadyInGame";
+	private ArrayList<String> teamsAlreadyInGame;
+	private List<Team> availableTeams;
+	private Listener listener;
+	
+	public static interface Listener {
+		void onTeamAddDialogSubmitted(Team newTeam);
+	}
+	
+	public TeamAddDialog() {
+		// Only for reflection-based instantiation by the framework
+	}
+	
+	TeamAddDialog(Collection<String> teamsAlreadyInGame) {
+		this.teamsAlreadyInGame = new ArrayList<String>(teamsAlreadyInGame);
+	}
+	
+	@Override
+	public void onAttach(Activity activity) {
+		super.onAttach(activity);
+		try {
+			listener = (Listener) activity;
+		} catch(ClassCastException e) {
+			throw new ClassCastException("Activity " + activity + " must implement TeamAddDialog.Listener to use TeamAddDialog.");
+		}
+	}
+	
+	@Override
+	public void onDetach() {
+		super.onDetach();
+		listener = null;
+	}
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		if(savedInstanceState != null) {
+			teamsAlreadyInGame = savedInstanceState.getStringArrayList(STATE_TEAMS_ALREADY_IN_GAME);
+		}
+		availableTeams = new ArrayList<Team>();
+		List<Team> teams = FrontendDataUtils.getTeams(getActivity());
+		for(Team team : teams) {
+			if(!teamsAlreadyInGame.contains(team.name)) {
+				availableTeams.add(team);
+			}
+		}
+		Collections.sort(availableTeams, Team.NAME_ORDER);
+	}
+	
+	@Override
+	public Dialog onCreateDialog(Bundle savedInstanceState) {
+		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		builder.setTitle(R.string.dialog_addteam_title);
+		builder.setIcon(R.drawable.human);
+		String[] teamNames = new String[availableTeams.size()];
+		for(int i=0; i<availableTeams.size(); i++) {
+			teamNames[i] = availableTeams.get(i).name;
+		}
+		builder.setItems(teamNames, new OnClickListener() {
+			public void onClick(DialogInterface dialog, int which) {
+				listener.onTeamAddDialogSubmitted(availableTeams.get(which));
+			}
+		});
+		return builder.create();
+	}
+	
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		outState.putStringArrayList(STATE_TEAMS_ALREADY_IN_GAME, teamsAlreadyInGame);
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Teamlist.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Teamlist.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,6 +1,9 @@
 package org.hedgewars.hedgeroid.netplay;
 
+import java.util.Collection;
+
 import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
+import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes;
 
 import android.util.Pair;
 
@@ -10,4 +13,15 @@
 	public void addTeamWithNewId(TeamInGame team) {
 		put(team.team.name, Pair.create(team, nextId++));
 	}
+	
+	public int getUnusedOrRandomColorIndex() {
+		Collection<Pair<TeamInGame, Long>> teams = getMap().values();
+		int[] illegalColors = new int[teams.size()];
+		int i=0;
+		for(Pair<TeamInGame, Long> item : teams) {
+			illegalColors[i] = item.first.ingameAttribs.colorIndex;
+			i++;
+		}
+		return TeamIngameAttributes.randomColorIndex(illegalColors);
+	}
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java	Sun Aug 12 22:37:57 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java	Sun Aug 12 22:46:23 2012 +0200
@@ -1,17 +1,31 @@
 package org.hedgewars.hedgeroid.netplay;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 import org.hedgewars.hedgeroid.R;
+import org.hedgewars.hedgeroid.Datastructures.Team;
+import org.hedgewars.hedgeroid.Datastructures.TeamInGame;
 
+import android.database.DataSetObserver;
 import android.os.Bundle;
 import android.support.v4.app.ListFragment;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView;
+import android.widget.Button;
 
-public class TeamlistFragment extends ListFragment {
+public class TeamlistFragment extends ListFragment implements OnItemClickListener {
 	private Netplay netplay;
 	private TeamlistAdapter adapter;
-
+	private Button addTeamButton;
+	private DataSetObserver teamlistObserver;
+	
 	@Override
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
@@ -24,12 +38,48 @@
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
-		return inflater.inflate(R.layout.fragment_teamlist, container, false);
+		View v = inflater.inflate(R.layout.fragment_teamlist, container, false);
+		addTeamButton = (Button)v.findViewById(R.id.addTeamButton);
+		addTeamButton.setOnClickListener(new OnClickListener() {
+			public void onClick(View v) {
+				new TeamAddDialog(getCurrentTeamNames()).show(getFragmentManager(), "team_add_dialog");
+			}
+		});
+		
+		teamlistObserver = new DataSetObserver() {
+			@Override
+			public void onChanged() {
+				addTeamButton.setEnabled(netplay.roomTeamlist.getMap().size() < Team.maxNumberOfTeams);
+			}
+		};
+		netplay.roomTeamlist.registerObserver(teamlistObserver);
+		teamlistObserver.onChanged();
+		
+		return v;
 	}
 	
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
 		adapter.invalidate();
+		netplay.roomTeamlist.unregisterObserver(teamlistObserver);
+	}
+
+	@Override
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
+		getListView().setOnItemClickListener(this);
+	}
+	
+	private Collection<String> getCurrentTeamNames() {
+		List<String> names = new ArrayList<String>();
+		for(Pair<TeamInGame, Long> teamWithId : netplay.roomTeamlist.getMap().values()) {
+			names.add(teamWithId.first.team.name);
+		}
+		return names;
+	}
+	
+	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+		netplay.sendRemoveTeam(adapter.getItem(position).team.name);
 	}
 }