--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sat Mar 31 20:39:19 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sun Apr 01 04:27:46 2012 +0200
@@ -18,322 +18,167 @@
package org.hedgewars.hedgeroid.Datastructures;
-import java.io.BufferedReader;
+import java.util.Map;
+
import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.TreeMap;
import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
+import org.ini4j.Ini;
+import org.ini4j.InvalidFileFormatException;
+import org.ini4j.Profile.Section;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
public class Scheme implements Parcelable, Comparable<Scheme>{
-
public static final String DIRECTORY_SCHEME = "schemes";
+ private static final Map<String, BasicSettingMeta> basicSettingsMeta = new TreeMap<String, BasicSettingMeta>();
+ private static final Map<String, GameModMeta> gameModsMeta = new TreeMap<String, GameModMeta>();
- private String name;
- //private ArrayList<Integer> basic;
- private Integer gamemod;
- private ArrayList<Integer> basic;;
- private static ArrayList<LinkedHashMap<String, ?>> basicflags = new ArrayList<LinkedHashMap<String, ?>>();//TODO why is it static?
- public int health;
-
- public Scheme(String _name, ArrayList<Integer> _basic, int _gamemod){
+ private final String name;
+ private final int gamemod;
+ private final Map<String, Integer> basic = new TreeMap<String, Integer>();
+
+ public Scheme(String _name, Map<String, Integer> _basic, int _gamemod) {
name = _name;
gamemod = _gamemod;
- basic = _basic;
+ basic.putAll(_basic);
}
public Scheme(Parcel in){
- readFromParcel(in);
+ name = in.readString();
+ gamemod = in.readInt();
+ in.readMap(basic, Integer.class.getClassLoader());
}
- public void sendToEngine(EngineProtocolNetwork epn)throws IOException{
+ public int getHealth() {
+ return basic.get("InitialHealth");
+ }
+
+ public void sendToEngine(EngineProtocolNetwork epn) throws IOException{
epn.sendToEngine(String.format("e$gmflags %d", gamemod));
- for(int pos = 0; pos < basic.size(); pos++){
- LinkedHashMap<String, ?> basicflag = basicflags.get(pos);
+ for(Map.Entry<String, Integer> entry : basic.entrySet()) {
+ BasicSettingMeta basicflag = basicSettingsMeta.get(entry.getKey());
- String command = (String)basicflag.get("command");
- Integer value = basic.get(pos);
-
- if(command.equals("inithealth")){//Health is a special case, it doesn't need to be send
- health = value; //to the engine yet, we'll do that with the other HH info
- continue;
+ //Health is a special case, it doesn't need to be send
+ //to the engine yet, we'll do that with the other HH info
+ if(!basicflag.command.equals("inithealth")){
+ epn.sendToEngine(String.format("%s %d", basicflag.command, entry.getValue()));
}
-
- Boolean checkOverMax = (Boolean) basicflag.get("checkOverMax");
- Boolean times1000 = (Boolean) basicflag.get("times1000");
- Integer max = (Integer) basicflag.get("max");
-
- if(checkOverMax && value >= max) value = max;
- if(times1000) value *= 1000;
-
- epn.sendToEngine(String.format("%s %d", command, value));
}
}
+
public String toString(){
return name;
}
-
- public static final int STATE_START = 0;
- public static final int STATE_ROOT = 1;
- public static final int STATE_NAME = 2;
- public static final int STATE_BASICFLAGS = 3;
- public static final int STATE_GAMEMOD = 4;
- public static final int STATE_BASICFLAG_INTEGER = 5;
- public static final int STATE_GAMEMOD_TRUE = 6;
- public static final int STATE_GAMEMOD_FALSE = 7;
-
- public static ArrayList<Scheme> getSchemes(Context c) throws IllegalArgumentException{
- String dir = c.getFilesDir().getAbsolutePath() + '/' + DIRECTORY_SCHEME + '/';
- String[] files = new File(dir).list(fnf);
- if(files == null) files = new String[]{};
+ public static List<Scheme> getSchemes(Context c) throws IllegalArgumentException {
+ File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME);
+ File[] files = schemeDir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String filename) {
+ return filename.toLowerCase().startsWith("scheme_");
+ }
+ });
+ if(files == null) files = new File[0];
Arrays.sort(files);
- ArrayList<Scheme> schemes = new ArrayList<Scheme>();
-
- try {
- XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
- XmlPullParser xmlPuller = xmlPullFactory.newPullParser();
-
- for(String file : files){
- BufferedReader br = new BufferedReader(new FileReader(dir + file), 1024);
- xmlPuller.setInput(br);
- String name = null;
- ArrayList<Integer> basic = new ArrayList<Integer>();
- Integer gamemod = 0;
- int health = 0;
- int mask = 0x000000004;
+ List<Scheme> schemes = new ArrayList<Scheme>();
- int eventType = xmlPuller.getEventType();
- int state = STATE_START;
- while(eventType != XmlPullParser.END_DOCUMENT){
- switch(state){
- case STATE_START:
- if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().equals("scheme")) state = STATE_ROOT;
- else if(eventType != XmlPullParser.START_DOCUMENT) throwException(file, eventType);
- break;
- case STATE_ROOT:
- if(eventType == XmlPullParser.START_TAG){
- if(xmlPuller.getName().equals("basicflags")) state = STATE_BASICFLAGS;
- else if(xmlPuller.getName().toLowerCase().equals("gamemod")) state = STATE_GAMEMOD;
- else if(xmlPuller.getName().toLowerCase().equals("name")) state = STATE_NAME;
- else throwException(file, eventType);
- }else if(eventType == XmlPullParser.END_TAG) state = STATE_START;
- else throwException(xmlPuller.getText(), eventType);
- break;
- case STATE_BASICFLAGS:
- if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().toLowerCase().equals("integer")) state = STATE_BASICFLAG_INTEGER;
- else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
- else throwException(file, eventType);
- break;
- case STATE_GAMEMOD:
- if(eventType == XmlPullParser.START_TAG){
- if(xmlPuller.getName().toLowerCase().equals("true")) state = STATE_GAMEMOD_TRUE;
- else if(xmlPuller.getName().toLowerCase().equals("false")) state = STATE_GAMEMOD_FALSE;
- else throwException(file, eventType);
- }else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
- else throwException(file, eventType);
- break;
- case STATE_NAME:
- if(eventType == XmlPullParser.TEXT) name = xmlPuller.getText().trim();
- else if(eventType == XmlPullParser.END_TAG) state = STATE_ROOT;
- else throwException(file, eventType);
- break;
- case STATE_BASICFLAG_INTEGER:
- if(eventType == XmlPullParser.TEXT) basic.add(Integer.parseInt(xmlPuller.getText().trim()));
- else if(eventType == XmlPullParser.END_TAG) state = STATE_BASICFLAGS;
- else throwException(file, eventType);
- break;
- case STATE_GAMEMOD_FALSE:
- if(eventType == XmlPullParser.TEXT) gamemod <<= 1;
- else if(eventType == XmlPullParser.END_TAG) state = STATE_GAMEMOD;
- else throwException(file, eventType);
- break;
- case STATE_GAMEMOD_TRUE:
- if(eventType == XmlPullParser.TEXT){
- gamemod |= mask;
- gamemod <<= 1;
- }else if(eventType == XmlPullParser.END_TAG) state = STATE_GAMEMOD;
- else throwException(file, eventType);
- break;
+ for(File file : files) {
+ try {
+ Ini ini = new Ini(file);
+
+ String name = ini.get("Scheme", "name");
+ if(name==null) {
+ name = file.getName();
+ }
+ Section basicSettingsSection = ini.get("BasicSettings");
+ Section gameModsSection = ini.get("GameMods");
+ if(basicSettingsSection == null || gameModsSection == null) {
+ Log.e(Scheme.class.getCanonicalName(), "Scheme file "+file+" is missing the BasicSettings or GameMods section - skipping.");
+ continue;
+ }
+
+ Map<String, Integer> basicSettings = new TreeMap<String, Integer>();
+ for(Entry<String, BasicSettingMeta> entry : basicSettingsMeta.entrySet()) {
+ String key = entry.getKey();
+ BasicSettingMeta settingMeta = entry.getValue();
+ Integer value = null;
+ if(basicSettingsSection.containsKey(key)) {
+ try {
+ value = Integer.valueOf(basicSettingsSection.get(key));
+ } catch (NumberFormatException e) {
+ // ignore
+ }
}
- eventType = getEventType(xmlPuller);
- }//end while(eventtype != END_DOCUMENT
- schemes.add(new Scheme(name, basic, gamemod));
- }//end for(string file : files
- return schemes;
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+
+ if(value==null) {
+ Log.w(Scheme.class.getCanonicalName(), "Scheme file "+file+" setting "+key+" is missing or invalid, using default.");
+ value = settingMeta.def;
+ }
+
+ if(settingMeta.checkOverMax) {
+ value = Math.min(value, settingMeta.max);
+ }
+ if(settingMeta.times1000) {
+ value *= 1000;
+ }
+
+ basicSettings.put(key, value);
+ }
+
+ int gamemods = 0;
+ for(Entry<String, GameModMeta> entry : gameModsMeta.entrySet()) {
+ String key = entry.getKey();
+ GameModMeta modMeta = entry.getValue();
+ if(Boolean.parseBoolean(gameModsSection.get(key))) {
+ gamemods |= (1 << modMeta.bitmaskIndex);
+ }
+ }
+
+ schemes.add(new Scheme(name, basicSettings, gamemods));
+ } catch (InvalidFileFormatException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- return new ArrayList<Scheme>();//TODO handle correctly
+ return schemes;
}
- private static FilenameFilter fnf = new FilenameFilter(){
- public boolean accept(File dir, String filename) {
- return filename.toLowerCase().startsWith("scheme_");
- }
- };
-
/**
- * This method will parse the basic flags from a prespecified xml file.
- * I use a raw xml file rather than one parsed by aatp at compile time
- * to keep it generic with other frontends, ie in the future we could
- * use one provided by the Data folder.
- */
- public static void parseBasicFlags(Context c){
- String filename = String.format("%s/%s/basicflags", c.getFilesDir().getAbsolutePath(), DIRECTORY_SCHEME);
-
- XmlPullParser xmlPuller = null;
- BufferedReader br = null;
- try {
- XmlPullParserFactory xmlPullFactory = XmlPullParserFactory.newInstance();
- xmlPuller = xmlPullFactory.newPullParser();
- br = new BufferedReader(new FileReader(filename), 1024);
- xmlPuller.setInput(br);
-
- int eventType = getEventType(xmlPuller);
- boolean continueParsing = true;
- do{
- switch(eventType){
-
- case XmlPullParser.START_TAG:
- if(xmlPuller.getName().toLowerCase().equals("flag")){
- basicflags.add(parseFlag(xmlPuller));
- }else if(xmlPuller.getName().toLowerCase().equals("basicflags")){
- eventType = getEventType(xmlPuller);
- }else{
- skipCurrentTag(xmlPuller);
- eventType = getEventType(xmlPuller);
- }
- break;
- case XmlPullParser.START_DOCUMENT://ignore all tags not being "flag"
- case XmlPullParser.END_TAG:
- case XmlPullParser.TEXT:
- default:
- continueParsing = true;
- case XmlPullParser.END_DOCUMENT:
- continueParsing = false;
- }
- }while(continueParsing);
-
- }catch(IOException e){
- e.printStackTrace();
- }catch (XmlPullParserException e) {
- e.printStackTrace();
- }finally{
- if(br != null)
- try {
- br.close();
- } catch (IOException e) {}
- }
-
- }
-
- /*
- * * Parses a Tag structure from xml as example we use
- *<flag>
- * <checkOverMax>
- * <boolean>false</boolean>
- * </checkOverMax>
- *</flag>
- *
- * It returns a LinkedHashMap with key/value pairs
+ * This method will parse the basic flags from a prespecified ini file.
+ * In the future we could use one provided by the Data folder.
*/
- private static LinkedHashMap<String, Object> parseFlag(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
- LinkedHashMap<String, Object> hash = new LinkedHashMap<String, Object>();
-
- int eventType = xmlPuller.getEventType();//Get the event type which triggered this method
- if(eventType == XmlPullParser.START_TAG && xmlPuller.getName().toLowerCase().equals("flag")){//valid start of flag tag
- String lcKey = null;
- String lcType = null;
- String value = null;
-
- eventType = getEventType(xmlPuller);//<checkOverMax>
- while(eventType == XmlPullParser.START_TAG){
- lcKey = xmlPuller.getName();//checkOverMax
- if(getEventType(xmlPuller) == XmlPullParser.START_TAG){//<boolean>
- lcType = xmlPuller.getName().toLowerCase();
- if(getEventType(xmlPuller) == XmlPullParser.TEXT){
- value = xmlPuller.getText();
- if(getEventType(xmlPuller) == XmlPullParser.END_TAG && //</boolean>
- getEventType(xmlPuller) == XmlPullParser.END_TAG){//</checkOverMax>
- if(lcType.equals("boolean")) hash.put(lcKey, new Boolean(value));
- else if(lcType.equals("string"))hash.put(lcKey, value);
- else if(lcType.equals("integer")){
- try{
- hash.put(lcKey, new Integer(value));
- }catch (NumberFormatException e){
- throw new XmlPullParserException("Wrong integer value in xml file");
- }
- }else{
- throwException("basicflags", eventType);
- }
- }//</boolean> / </checkOverMax>
- }//if TEXT
- }//if boolean
- eventType = getEventType(xmlPuller);//start new loop
+ public static void parseConfiguration(Context c) {
+ File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME);
+ File settingsFile = new File(schemeDir, "basicsettings");
+ File gameModsFile = new File(schemeDir, "gamemods");
+
+ try {
+ Ini ini = new Ini(settingsFile);
+ for(Entry<String, Section> sectionEntry : ini.entrySet()) {
+ basicSettingsMeta.put(sectionEntry.getKey(), new BasicSettingMeta(sectionEntry.getValue()));
}
- eventType = getEventType(xmlPuller);//</flag>
+
+ ini = new Ini(gameModsFile);
+ for(Entry<String, Section> sectionEntry : ini.entrySet()) {
+ gameModsMeta.put(sectionEntry.getKey(), new GameModMeta(sectionEntry.getValue()));
+ }
+ } catch (InvalidFileFormatException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
-
- return hash;
- }
-
- private static void skipCurrentTag(XmlPullParser xmlPuller) throws XmlPullParserException, IOException{
- int eventType = xmlPuller.getEventType();
- if(eventType != XmlPullParser.START_TAG)return;
- String tag = xmlPuller.getName().toLowerCase();
-
- while(true){
- eventType = getEventType(xmlPuller);//getNext()
- switch(eventType){
- case XmlPullParser.START_DOCUMENT://we're inside of a start tag so START_ or END_DOCUMENT is just wrong
- case XmlPullParser.END_DOCUMENT:
- throw new XmlPullParserException("invalid xml file");
- case XmlPullParser.START_TAG://if we get a new tag recursively handle it
- skipCurrentTag(xmlPuller);
- break;
- case XmlPullParser.TEXT:
- break;
- case XmlPullParser.END_TAG:
- if(!xmlPuller.getName().toLowerCase().equals(tag)){//if the end tag doesn't match the start tag
- throw new XmlPullParserException("invalid xml file");
- }else{
- return;//skip completed
- }
-
- }
- }
- }
-
- /**
- * Skips whitespaces..
- */
- private static int getEventType(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
- int eventType = xmlPuller.next();
- while(eventType == XmlPullParser.TEXT && xmlPuller.isWhitespace()){
- eventType = xmlPuller.next();
- }
- return eventType;
- }
- private static void throwException(String file, int eventType){
- throw new IllegalArgumentException(String.format("Xml file: %s malformed with error: %d.", file, eventType));
}
public int describeContents() {
@@ -343,13 +188,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(gamemod);
- dest.writeList(basic);
- }
-
- public void readFromParcel(Parcel src){
- name = src.readString();
- gamemod = src.readInt();
- basic = src.readArrayList(ArrayList.class.getClassLoader());
+ dest.writeMap(basic);
}
public static final Parcelable.Creator<Scheme> CREATOR = new Parcelable.Creator<Scheme>() {
@@ -366,3 +205,48 @@
return name.compareTo(another.name);
}
}
+
+class BasicSettingMeta {
+ final String command;
+ final String title;
+ final int def;
+ final int min;
+ final int max;
+ final boolean times1000;
+ final boolean checkOverMax;
+
+ public BasicSettingMeta(Ini.Section section) {
+ command = getRequired(section, "command");
+ title = section.get("title", "");
+ def = Integer.parseInt(getRequired(section, "default"));
+ min = Integer.parseInt(getRequired(section, "min"));
+ max = Integer.parseInt(getRequired(section, "max"));
+ times1000 = Boolean.parseBoolean(section.get("times1000", "false"));
+ checkOverMax = Boolean.parseBoolean(section.get("checkOverMax", "false"));
+ }
+
+ private String getRequired(Ini.Section section, String key) {
+ String result = section.get(key);
+ if(result==null) {
+ throw new IllegalArgumentException("basicsettings.ini, section "+section.getName()+" is missing required setting "+key+".");
+ }
+ return result;
+ }
+}
+
+// TODO: Extend with additional metadata
+class GameModMeta {
+ final int bitmaskIndex;
+
+ public GameModMeta(Ini.Section section) {
+ bitmaskIndex = Integer.parseInt(getRequired(section, "bitmaskIndex"));
+ }
+
+ private String getRequired(Ini.Section section, String key) {
+ String result = section.get(key);
+ if(result==null) {
+ throw new IllegalArgumentException("gamemods.ini, section "+section.getName()+" is missing required setting "+key+".");
+ }
+ return result;
+ }
+}
\ No newline at end of file