Added support for binary excel loading (see #26)

Refactored Excel parse factory methods to load methods
Added tentative serialization code to MonStats
This commit is contained in:
Collin Smith
2019-09-23 03:51:55 -07:00
parent d7f2a42e9c
commit 6bb974e711
7 changed files with 825 additions and 176 deletions

View File

@ -4,7 +4,6 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.ObjectSet;
import com.riiablo.codec.TXT;
import com.riiablo.codec.excel.ArmType;
import com.riiablo.codec.excel.Armor;
import com.riiablo.codec.excel.BodyLocs;
@ -141,7 +140,7 @@ public class Files {
MagicSuffix = load(MagicSuffix.class, Excel.EXPANSION);
MonAI = load(MonAI.class);
MonMode = load(MonMode.class);
monstats = load2(MonStats.class, Excel.EXPANSION);
monstats = load(MonStats.class, Excel.EXPANSION);
monstats2 = load(MonStats2.class, Excel.EXPANSION);
RarePrefix = load(RarePrefix.class, Excel.EXPANSION);
RareSuffix = load(RareSuffix.class, Excel.EXPANSION);
@ -176,8 +175,7 @@ public class Files {
private <T extends Excel> T loadInternal(Class<T> clazz, String filename) {
FileHandle handle = Gdx.files.internal("data/" + filename + ".txt");
TXT txt = TXT.loadFromFile(handle);
return Excel.parse(txt, clazz);
return Excel.load(clazz, handle);
}
private <T extends Excel> T load(Class<T> clazz, ObjectSet<String> ignore) {
@ -189,8 +187,8 @@ public class Files {
}
private <T extends Excel> T load(Class<T> clazz, String tableName, ObjectSet<String> ignore) {
FileHandle handle = Riiablo.mpqs.resolve(EXCEL_PATH + tableName + ".txt");
TXT txt = TXT.loadFromFile(handle);
return Excel.parse(txt, clazz, ignore);
FileHandle txt = Riiablo.mpqs.resolve(EXCEL_PATH + tableName + ".txt");
FileHandle bin = Gdx.files.internal(EXCEL_PATH + tableName + ".bin");
return Excel.load(clazz, txt, bin, ignore);
}
}

View File

@ -1,16 +1,25 @@
package com.riiablo.codec.excel;
import com.google.common.io.LittleEndianDataInputStream;
import android.support.annotation.CallSuper;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.ObjectIntMap;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectSet;
import com.badlogic.gdx.utils.StreamUtils;
import com.riiablo.codec.TXT;
import com.riiablo.util.ClassUtils;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -28,7 +37,9 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
private static final boolean DEBUG_IGNORED = DEBUG && !true;
private static final boolean DEBUG_ENTRIES = DEBUG && !true;
private static final boolean DEBUG_INDEXES = DEBUG && !true;
private static final boolean DEBUG_KEY = DEBUG && true;
private static final boolean DEBUG_KEY = DEBUG && !true;
private static final boolean DEBUG_BIN = DEBUG && true;
private static final boolean DEBUG_TIME = DEBUG && true;
private static final boolean FORCE_PRIMARY_KEY = false;
@ -46,6 +57,10 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
@Retention(RetentionPolicy.RUNTIME)
public @interface Index {}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Binned {}
public static class Entry {
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@ -65,213 +80,284 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Key {}
public void readBin(DataInput in) throws IOException {}
public void writeBin(DataOutput out) throws IOException {}
}
ObjectIntMap<String> STRING_TO_ID = EMPTY_MAP;
IntMap<T> entries = new IntMap<>();
@SuppressWarnings("unchecked")
public static <T extends Excel> T parse(TXT txt, Class<T> excelClass) {
return parse(txt, excelClass, Excel.<String>emptySet());
public static <T extends Excel> T load(Class<T> excelClass, FileHandle txt) {
return load(excelClass, txt, Excel.<String>emptySet());
}
@SuppressWarnings("unchecked")
public static <T extends Excel> T parse(TXT txt, Class<T> excelClass, ObjectSet<String> ignore) {
public static <T extends Excel> T load(Class<T> excelClass, FileHandle txt, ObjectSet<String> ignore) {
return load(excelClass, txt, null, ignore);
}
public static <T extends Excel> T load(Class<T> excelClass, FileHandle txt, FileHandle bin, ObjectSet<String> ignore) {
try {
if (ignore == null) ignore = emptySet();
Class<Entry> entryClass = getEntryClass(excelClass);
if (entryClass == null) throw new AssertionError(excelClass + " does not implement " + Entry.class);
final boolean index = entryClass.getAnnotation(Index.class) != null;
long start = System.currentTimeMillis();
Field primaryKey = null, firstKey = null;
T excel = excelClass.newInstance();
ObjectMap<Field, int[]> columns = new ObjectMap<>();
String[] TMP = new String[1];
for (Field field : entryClass.getFields()) {
Entry.Column column = field.getAnnotation(Entry.Column.class);
if (column == null) continue;
T excel;
FileHandle file;
if (isBinned(excelClass) && bin != null && bin.exists()) {
if (DEBUG_BIN) Gdx.app.debug(TAG, "Loading bin " + bin);
excel = loadBin(bin, excelClass);
file = bin;
} else {
if (DEBUG_BIN) Gdx.app.debug(TAG, "Loading txt " + txt);
excel = loadTxt(txt, excelClass, entryClass, ignore);
file = txt;
}
Entry.Key key = field.getAnnotation(Entry.Key.class);
if (key != null) {
if (index) {
Gdx.app.error(TAG, "primary key set in class annotated with " + Index.class);
} else if (primaryKey != null) {
boolean primaryDeclared = ClassUtils.isDeclaredField(entryClass, primaryKey);
boolean fieldDeclared = ClassUtils.isDeclaredField(entryClass, field);
if (primaryDeclared != fieldDeclared) {
if (fieldDeclared) {
if (DEBUG_KEY) Gdx.app.debug(TAG, "primary key " + primaryKey.getName() + " -> " + field.getName());
primaryKey = field;
}
} else {
Gdx.app.error(TAG, "more than one primary key for " + entryClass + " " + primaryKey.getName() + " and " + field.getName());
long end = System.currentTimeMillis();
if (DEBUG_TIME) Gdx.app.debug(TAG, "Loaded " + file + " in " + (end - start) + "ms");
excel.init();
return excel;
} catch (Throwable t) {
throw new GdxRuntimeException("Couldn't load excel " + excelClass, t);
}
}
private static <T extends Excel> T loadTxt(FileHandle handle, Class<T> excelClass, Class<Entry> entryClass, ObjectSet<String> ignore) throws Exception {
TXT txt = TXT.loadFromFile(handle);
final boolean index = ClassUtils.hasAnnotation(entryClass, Index.class);
Field primaryKey = null, firstKey = null;
T excel = excelClass.newInstance();
ObjectMap<Field, int[]> columns = new ObjectMap<>();
String[] TMP = new String[1];
for (Field field : entryClass.getFields()) {
Entry.Column column = field.getAnnotation(Entry.Column.class);
if (column == null) continue;
Entry.Key key = field.getAnnotation(Entry.Key.class);
if (key != null) {
if (index) {
Gdx.app.error(TAG, "primary key set in class annotated with " + Index.class);
} else if (primaryKey != null) {
boolean primaryDeclared = ClassUtils.isDeclaredField(entryClass, primaryKey);
boolean fieldDeclared = ClassUtils.isDeclaredField(entryClass, field);
if (primaryDeclared != fieldDeclared) {
if (fieldDeclared) {
if (DEBUG_KEY) Gdx.app.debug(TAG, "primary key " + primaryKey.getName() + " -> " + field.getName());
primaryKey = field;
}
} else {
primaryKey = field;
Gdx.app.error(TAG, "more than one primary key for " + entryClass + " " + primaryKey.getName() + " and " + field.getName());
}
} else {
primaryKey = field;
}
}
if (firstKey == null) firstKey = field;
if (firstKey == null) firstKey = field;
final String format = column.format();
final String values[] = column.values();
final int startIndex = column.startIndex();
final int endIndex = column.endIndex();
final int columnIndex = column.columnIndex();
if (columnIndex >= 0) {
columns.put(field, new int[] { columnIndex });
} else if (format.isEmpty()) {
final String fieldName = field.getName();
if (values.length > 0) {
String[] columnNames = new String[values.length];
for (int i = 0; i < values.length; i++) {
String name = values[i];
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[i] = name;
}
final String format = column.format();
final String values[] = column.values();
final int startIndex = column.startIndex();
final int endIndex = column.endIndex();
final int columnIndex = column.columnIndex();
if (columnIndex >= 0) {
columns.put(field, new int[] { columnIndex });
} else if (format.isEmpty()) {
final String fieldName = field.getName();
if (values.length > 0) {
String[] columnNames = new String[values.length];
for (int i = 0; i < values.length; i++) {
String name = values[i];
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[i] = name;
}
columns.put(field, txt.getColumnId(columnNames));
} else if (startIndex == 0 && endIndex == 0) {
if (DEBUG_COLS) Gdx.app.debug(TAG, fieldName);
TMP[0] = fieldName;
columns.put(field, txt.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
columns.put(field, txt.getColumnId(columnNames));
} else if (startIndex == 0 && endIndex == 0) {
if (DEBUG_COLS) Gdx.app.debug(TAG, fieldName);
TMP[0] = fieldName;
columns.put(field, txt.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
String name = fieldName + i;
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[j] = name;
}
columns.put(field, txt.getColumnId(columnNames));
}
} else {
if (startIndex == 0 && endIndex == 0) {
TMP[0] = format;
columns.put(field, txt.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
if (values.length == 0) {
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
String name = fieldName + i;
String name = String.format(format, i);
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[j] = name;
}
columns.put(field, txt.getColumnId(columnNames));
}
} else {
if (startIndex == 0 && endIndex == 0) {
TMP[0] = format;
columns.put(field, txt.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
if (values.length == 0) {
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
String name = String.format(format, i);
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[j] = name;
}
} else {
for (int i = 0; i < values.length; i++) {
String name = String.format(format, values[i]);
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[i] = name;
}
for (int i = 0; i < values.length; i++) {
String name = String.format(format, values[i]);
if (DEBUG_COLS) Gdx.app.debug(TAG, name);
columnNames[i] = name;
}
columns.put(field, txt.getColumnId(columnNames));
}
columns.put(field, txt.getColumnId(columnNames));
}
}
}
if (primaryKey == null && !index) {
if (FORCE_PRIMARY_KEY) {
throw new IllegalStateException(entryClass + " does not have a " + Entry.Key.class + " set!");
if (primaryKey == null && !index) {
if (FORCE_PRIMARY_KEY) {
throw new IllegalStateException(entryClass + " does not have a " + Entry.Key.class + " set!");
} else {
primaryKey = firstKey;
Gdx.app.error(TAG, entryClass + " does not have a " + Entry.Key.class + " set! Using " + firstKey.getName());
}
}
if (DEBUG_COL_IDS) {
for (ObjectMap.Entry<Field, int[]> entry : columns.entries()) {
Gdx.app.debug(TAG, entry.key.getName() + ": " + Arrays.toString(entry.value));
}
}
final int primaryKeyCol = index ? -1 : columns.get(primaryKey)[0];
final Class primaryKeyType = index ? null : primaryKey.getType();
final int size = txt.getRows();
for (int i = 0, j = excel.offset(); i < size; i++) {
Entry entry = entryClass.newInstance();
String rowName = txt.getRowName(i);
if (ignore.contains(rowName)) {
if (DEBUG_IGNORED) Gdx.app.debug(TAG, "Skipping row " + i + ", ignoring rows named " + rowName);
continue;
}
String name = index ? null : txt.getString(i, primaryKeyCol);
for (ObjectMap.Entry<Field, int[]> row : columns.entries()) {
Field field = row.key;
int[] columnIds = row.value;
Class type = field.getType();
assert type.isArray() || columnIds.length == 1 : "field should only correspond to 1 column: " + field.getName() + ", " + columnIds.length + " columns (is it supposed to be an array?)";
if (type == String.class) {
String value = txt.getString(i, columnIds[0]);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == String[].class) {
String[] value = txt.getString(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == byte.class) {
byte value = txt.getByte(i, columnIds[0]);
field.setByte(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == byte[].class) {
byte[] value = txt.getByte(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == short.class) {
short value = txt.getShort(i, columnIds[0]);
field.setShort(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == short[].class) {
short[] value = txt.getShort(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == int.class) {
int value = txt.getInt(i, columnIds[0]);
field.setInt(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == int[].class) {
int[] value = txt.getInt(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == long.class) {
long value = txt.getLong(i, columnIds[0]);
field.setLong(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == long[].class) {
long[] value = txt.getLong(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == boolean.class) {
boolean value = txt.getBoolean(i, columnIds[0]);
field.setBoolean(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == boolean[].class) {
boolean[] value = txt.getBoolean(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else {
primaryKey = firstKey;
Gdx.app.error(TAG, entryClass + " does not have a " + Entry.Key.class + " set! Using " + firstKey.getName());
throw new UnsupportedOperationException("No support for " + type + " fields");
}
}
if (DEBUG_COL_IDS) {
for (ObjectMap.Entry<Field, int[]> entry : columns.entries()) {
Gdx.app.debug(TAG, entry.key.getName() + ": " + Arrays.toString(entry.value));
}
if (index) {
excel.put(j++, entry);
} else if (primaryKeyType == int.class) {
int id = primaryKey.getInt(entry);
excel.put(id, entry);
} else if (primaryKeyType == String.class) {
String id = name;//(String) primaryKey.get(entry);
excel.put(j, entry);
if (excel.STRING_TO_ID == EMPTY_MAP) excel.STRING_TO_ID = new ObjectIntMap();
if (!excel.STRING_TO_ID.containsKey(id)) excel.STRING_TO_ID.put(id, j);
j++;
}
}
final int primaryKeyCol = index ? -1 : columns.get(primaryKey)[0];
final Class primaryKeyType = index ? null : primaryKey.getType();
final int size = txt.getRows();
for (int i = 0, j = excel.offset(); i < size; i++) {
Entry entry = entryClass.newInstance();
return excel;
}
String rowName = txt.getRowName(i);
if (ignore.contains(rowName)) {
if (DEBUG_IGNORED) Gdx.app.debug(TAG, "Skipping row " + i + ", ignoring rows named " + rowName);
continue;
}
private static <T extends Excel> T loadBin(FileHandle bin, Class<T> excelClass) throws Exception {
byte[] bytes = bin.readBytes();
InputStream in = null;
try {
in = new ByteArrayInputStream(bytes);
LittleEndianDataInputStream dis = new LittleEndianDataInputStream(in);
T excel = excelClass.newInstance();
excel.readBin(dis);
return excel;
} finally {
StreamUtils.closeQuietly(in);
}
}
String name = index ? null : txt.getString(i, primaryKeyCol);
for (ObjectMap.Entry<Field, int[]> row : columns.entries()) {
Field field = row.key;
int[] columnIds = row.value;
Class type = field.getType();
assert type.isArray() || columnIds.length == 1 : "field should only correspond to 1 column: " + field.getName() + ", " + columnIds.length + " columns (is it supposed to be an array?)";
if (type == String.class) {
String value = txt.getString(i, columnIds[0]);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == String[].class) {
String[] value = txt.getString(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == byte.class) {
byte value = txt.getByte(i, columnIds[0]);
field.setByte(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == byte[].class) {
byte[] value = txt.getByte(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == short.class) {
short value = txt.getShort(i, columnIds[0]);
field.setShort(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == short[].class) {
short[] value = txt.getShort(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == int.class) {
int value = txt.getInt(i, columnIds[0]);
field.setInt(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == int[].class) {
int[] value = txt.getInt(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == long.class) {
long value = txt.getLong(i, columnIds[0]);
field.setLong(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == long[].class) {
long[] value = txt.getLong(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else if (type == boolean.class) {
boolean value = txt.getBoolean(i, columnIds[0]);
field.setBoolean(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), value));
} else if (type == boolean[].class) {
boolean[] value = txt.getBoolean(i, columnIds);
field.set(entry, value);
if (DEBUG_ENTRIES) Gdx.app.debug(TAG, String.format("Entry[%d](%s).%s=%s", j, name, field.getName(), Arrays.toString(value)));
} else {
throw new UnsupportedOperationException("No support for " + type + " fields");
}
}
static boolean isBinned(Class c) {
return ClassUtils.hasAnnotation(c, Binned.class);
}
if (index) {
excel.put(j++, entry);
} else if (primaryKeyType == int.class) {
int id = primaryKey.getInt(entry);
excel.put(id, entry);
} else if (primaryKeyType == String.class) {
String id = name;//(String) primaryKey.get(entry);
excel.put(j, entry);
public final boolean isBinned() {
return isBinned(this.getClass());
}
if (excel.STRING_TO_ID == EMPTY_MAP) excel.STRING_TO_ID = new ObjectIntMap();
if (!excel.STRING_TO_ID.containsKey(id)) excel.STRING_TO_ID.put(id, j);
j++;
}
}
public void readBin(DataInput in) throws IOException {}
public void writeBin(DataOutput out) throws IOException {}
public static <T extends Excel> T read(Class<T> excelClass, DataInput in) {
if (!isBinned(excelClass)) {
throw new GdxRuntimeException(excelClass + " is not annotated with " + Excel.Binned.class);
}
try {
T excel = excelClass.newInstance();
excel.readBin(in);
excel.init();
return excel;
} catch (Throwable t) {

View File

@ -1,8 +1,36 @@
package com.riiablo.codec.excel;
import com.riiablo.codec.excel.Excel;
import com.badlogic.gdx.utils.ObjectIntMap;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@Excel.Binned
public class MonStats extends Excel<MonStats.Entry> {
@Override
public void readBin(DataInput in) throws IOException {
STRING_TO_ID = new ObjectIntMap();
int size = in.readInt();
//System.out.println(size);
for (int i = 0; i < size; i++) {
Entry entry = new Entry();
entry.readBin(in);
//System.out.println(entry.hcIdx + ":" + entry.Id);
put(entry.hcIdx, entry);
if (!STRING_TO_ID.containsKey(entry.Id)) STRING_TO_ID.put(entry.Id, entry.hcIdx);
}
}
@Override
public void writeBin(DataOutput out) throws IOException {
out.writeInt(size());
for (MonStats.Entry entry : entries.values()) {
entry.writeBin(out);
}
}
public static class Entry extends Excel.Entry {
@Override
public String toString() {
@ -215,5 +243,374 @@ public class MonStats extends Excel<MonStats.Entry> {
@Column public boolean SplGetModeChart;
@Column public boolean SplEndGeneric;
@Column public boolean SplClientEnd;
@Override
public void readBin(DataInput in) throws IOException {
Id = in.readUTF();
hcIdx = in.readInt();
BaseId = in.readUTF();
NextInClass = in.readUTF();
TransLvl = in.readInt();
NameStr = in.readUTF();
MonStatsEx = in.readUTF();
MonProp = in.readUTF();
MonType = in.readUTF();
AI = in.readUTF();
DescStr = in.readUTF();
Code = in.readUTF();
enabled = in.readBoolean();
rangedtype = in.readBoolean();
placespawn = in.readBoolean();
spawn = in.readUTF();
spawnx = in.readInt();
spawny = in.readInt();
spawnmode = in.readUTF();
minion1 = in.readUTF();
minion2 = in.readUTF();
SetBoss = in.readBoolean();
BossXfer = in.readBoolean();
PartyMin = in.readInt();
PartyMax = in.readInt();
MinGrp = in.readInt();
MaxGrp = in.readInt();
sparsePopulate = in.readInt();
Velocity = in.readInt();
Run = in.readInt();
Rarity = in.readInt();
Level = new int[3];
for (int x = 0; x < 3; x++) Level[x] = in.readInt();
MonSound = in.readUTF();
UMonSound = in.readUTF();
threat = in.readInt();
aidel = new int[3];
for (int x = 0; x < 3; x++) aidel[x] = in.readInt();
aidist = new int[3];
for (int x = 0; x < 3; x++) aidist[x] = in.readInt();
aip1 = new int[3];
for (int x = 0; x < 3; x++) aip1[x] = in.readInt();
aip2 = new int[3];
for (int x = 0; x < 3; x++) aip2[x] = in.readInt();
aip3 = new int[3];
for (int x = 0; x < 3; x++) aip3[x] = in.readInt();
aip4 = new int[3];
for (int x = 0; x < 3; x++) aip4[x] = in.readInt();
aip5 = new int[3];
for (int x = 0; x < 3; x++) aip5[x] = in.readInt();
aip6 = new int[3];
for (int x = 0; x < 3; x++) aip6[x] = in.readInt();
aip7 = new int[3];
for (int x = 0; x < 3; x++) aip7[x] = in.readInt();
aip8 = new int[3];
for (int x = 0; x < 3; x++) aip8[x] = in.readInt();
MissA1 = in.readUTF();
MissA2 = in.readUTF();
MissS1 = in.readUTF();
MissS2 = in.readUTF();
MissS3 = in.readUTF();
MissS4 = in.readUTF();
MissC = in.readUTF();
MissSQ = in.readUTF();
Align = in.readInt();
isSpawn = in.readBoolean();
isMelee = in.readBoolean();
npc = in.readBoolean();
interact = in.readBoolean();
inventory = in.readBoolean();
inTown = in.readBoolean();
lUndead = in.readBoolean();
hUndead = in.readBoolean();
demon = in.readBoolean();
flying = in.readBoolean();
opendoors = in.readBoolean();
boss = in.readBoolean();
primeevil = in.readBoolean();
killable = in.readBoolean();
switchai = in.readBoolean();
noAura = in.readBoolean();
nomultishot = in.readBoolean();
neverCount = in.readBoolean();
petIgnore = in.readBoolean();
deathDmg = in.readBoolean();
genericSpawn = in.readBoolean();
zoo = in.readBoolean();
SendSkills = in.readInt();
Skill1 = in.readUTF();
Sk1mode = in.readUTF();
Sk1lvl = in.readInt();
Skill2 = in.readUTF();
Sk2mode = in.readUTF();
Sk2lvl = in.readInt();
Skill3 = in.readUTF();
Sk3mode = in.readUTF();
Sk3lvl = in.readInt();
Skill4 = in.readUTF();
Sk4mode = in.readUTF();
Sk4lvl = in.readInt();
Skill5 = in.readUTF();
Sk5mode = in.readUTF();
Sk5lvl = in.readInt();
Skill6 = in.readUTF();
Sk6mode = in.readUTF();
Sk6lvl = in.readInt();
Skill7 = in.readUTF();
Sk7mode = in.readUTF();
Sk7lvl = in.readInt();
Skill8 = in.readUTF();
Sk8mode = in.readUTF();
Sk8lvl = in.readInt();
Drain = new int[3];
for (int x = 0; x < 3; x++) Drain[x] = in.readInt();
coldeffect = new int[3];
for (int x = 0; x < 3; x++) coldeffect[x] = in.readInt();
ResDm = new int[3];
for (int x = 0; x < 3; x++) ResDm[x] = in.readInt();
ResMa = new int[3];
for (int x = 0; x < 3; x++) ResMa[x] = in.readInt();
ResFi = new int[3];
for (int x = 0; x < 3; x++) ResFi[x] = in.readInt();
ResLi = new int[3];
for (int x = 0; x < 3; x++) ResLi[x] = in.readInt();
ResCo = new int[3];
for (int x = 0; x < 3; x++) ResCo[x] = in.readInt();
ResPo = new int[3];
for (int x = 0; x < 3; x++) ResPo[x] = in.readInt();
DamageRegen = in.readInt();
SkillDamage = in.readUTF();
noRatio = in.readBoolean();
NoShldBlock = in.readBoolean();
ToBlock = new int[3];
for (int x = 0; x < 3; x++) ToBlock[x] = in.readInt();
Crit = in.readInt();
minHP = new int[3];
for (int x = 0; x < 3; x++) minHP[x] = in.readInt();
maxHP = new int[3];
for (int x = 0; x < 3; x++) maxHP[x] = in.readInt();
AC = new int[3];
for (int x = 0; x < 3; x++) AC[x] = in.readInt();
Exp = new int[3];
for (int x = 0; x < 3; x++) Exp[x] = in.readInt();
A1MinD = new int[3];
for (int x = 0; x < 3; x++) A1MinD[x] = in.readInt();
A1MaxD = new int[3];
for (int x = 0; x < 3; x++) A1MaxD[x] = in.readInt();
A1TH = new int[3];
for (int x = 0; x < 3; x++) A1TH[x] = in.readInt();
A2MinD = new int[3];
for (int x = 0; x < 3; x++) A2MinD[x] = in.readInt();
A2MaxD = new int[3];
for (int x = 0; x < 3; x++) A2MaxD[x] = in.readInt();
A2TH = new int[3];
for (int x = 0; x < 3; x++) A2TH[x] = in.readInt();
S1MinD = new int[3];
for (int x = 0; x < 3; x++) S1MinD[x] = in.readInt();
S1MaxD = new int[3];
for (int x = 0; x < 3; x++) S1MaxD[x] = in.readInt();
S1TH = new int[3];
for (int x = 0; x < 3; x++) S1TH[x] = in.readInt();
El1Mode = in.readUTF();
El1Type = in.readUTF();
El1Pct = new int[3];
for (int x = 0; x < 3; x++) El1Pct[x] = in.readInt();
El1MinD = new int[3];
for (int x = 0; x < 3; x++) El1MinD[x] = in.readInt();
El1MaxD = new int[3];
for (int x = 0; x < 3; x++) El1MaxD[x] = in.readInt();
El1Dur = new int[3];
for (int x = 0; x < 3; x++) El1Dur[x] = in.readInt();
El2Mode = in.readUTF();
El2Type = in.readUTF();
El2Pct = new int[3];
for (int x = 0; x < 3; x++) El2Pct[x] = in.readInt();
El2MinD = new int[3];
for (int x = 0; x < 3; x++) El2MinD[x] = in.readInt();
El2MaxD = new int[3];
for (int x = 0; x < 3; x++) El2MaxD[x] = in.readInt();
El2Dur = new int[3];
for (int x = 0; x < 3; x++) El2Dur[x] = in.readInt();
El3Mode = in.readUTF();
El3Type = in.readUTF();
El3Pct = new int[3];
for (int x = 0; x < 3; x++) El3Pct[x] = in.readInt();
El3MinD = new int[3];
for (int x = 0; x < 3; x++) El3MinD[x] = in.readInt();
El3MaxD = new int[3];
for (int x = 0; x < 3; x++) El3MaxD[x] = in.readInt();
El3Dur = new int[3];
for (int x = 0; x < 3; x++) El3Dur[x] = in.readInt();
TreasureClass1 = new String[3];
for (int x = 0; x < 3; x++) TreasureClass1[x] = in.readUTF();
TreasureClass2 = new String[3];
for (int x = 0; x < 3; x++) TreasureClass2[x] = in.readUTF();
TreasureClass3 = new String[3];
for (int x = 0; x < 3; x++) TreasureClass3[x] = in.readUTF();
TreasureClass4 = new String[3];
for (int x = 0; x < 3; x++) TreasureClass4[x] = in.readUTF();
TCQuestId = in.readInt();
TCQuestCP = in.readInt();
SplEndDeath = in.readInt();
SplGetModeChart = in.readBoolean();
SplEndGeneric = in.readBoolean();
SplClientEnd = in.readBoolean();
}
@Override
public void writeBin(DataOutput out) throws IOException {
out.writeUTF(Id);
out.writeInt(hcIdx);
out.writeUTF(BaseId);
out.writeUTF(NextInClass);
out.writeInt(TransLvl);
out.writeUTF(NameStr);
out.writeUTF(MonStatsEx);
out.writeUTF(MonProp);
out.writeUTF(MonType);
out.writeUTF(AI);
out.writeUTF(DescStr);
out.writeUTF(Code);
out.writeBoolean(enabled);
out.writeBoolean(rangedtype);
out.writeBoolean(placespawn);
out.writeUTF(spawn);
out.writeInt(spawnx);
out.writeInt(spawny);
out.writeUTF(spawnmode);
out.writeUTF(minion1);
out.writeUTF(minion2);
out.writeBoolean(SetBoss);
out.writeBoolean(BossXfer);
out.writeInt(PartyMin);
out.writeInt(PartyMax);
out.writeInt(MinGrp);
out.writeInt(MaxGrp);
out.writeInt(sparsePopulate);
out.writeInt(Velocity);
out.writeInt(Run);
out.writeInt(Rarity);
for (int x : Level) out.writeInt(x);
out.writeUTF(MonSound);
out.writeUTF(UMonSound);
out.writeInt(threat);
for (int x : aidel) out.writeInt(x);
for (int x : aidist) out.writeInt(x);
for (int x : aip1) out.writeInt(x);
for (int x : aip2) out.writeInt(x);
for (int x : aip3) out.writeInt(x);
for (int x : aip4) out.writeInt(x);
for (int x : aip5) out.writeInt(x);
for (int x : aip6) out.writeInt(x);
for (int x : aip7) out.writeInt(x);
for (int x : aip8) out.writeInt(x);
out.writeUTF(MissA1);
out.writeUTF(MissA2);
out.writeUTF(MissS1);
out.writeUTF(MissS2);
out.writeUTF(MissS3);
out.writeUTF(MissS4);
out.writeUTF(MissC);
out.writeUTF(MissSQ);
out.writeInt(Align);
out.writeBoolean(isSpawn);
out.writeBoolean(isMelee);
out.writeBoolean(npc);
out.writeBoolean(interact);
out.writeBoolean(inventory);
out.writeBoolean(inTown);
out.writeBoolean(lUndead);
out.writeBoolean(hUndead);
out.writeBoolean(demon);
out.writeBoolean(flying);
out.writeBoolean(opendoors);
out.writeBoolean(boss);
out.writeBoolean(primeevil);
out.writeBoolean(killable);
out.writeBoolean(switchai);
out.writeBoolean(noAura);
out.writeBoolean(nomultishot);
out.writeBoolean(neverCount);
out.writeBoolean(petIgnore);
out.writeBoolean(deathDmg);
out.writeBoolean(genericSpawn);
out.writeBoolean(zoo);
out.writeInt(SendSkills);
out.writeUTF(Skill1);
out.writeUTF(Sk1mode);
out.writeInt(Sk1lvl);
out.writeUTF(Skill2);
out.writeUTF(Sk2mode);
out.writeInt(Sk2lvl);
out.writeUTF(Skill3);
out.writeUTF(Sk3mode);
out.writeInt(Sk3lvl);
out.writeUTF(Skill4);
out.writeUTF(Sk4mode);
out.writeInt(Sk4lvl);
out.writeUTF(Skill5);
out.writeUTF(Sk5mode);
out.writeInt(Sk5lvl);
out.writeUTF(Skill6);
out.writeUTF(Sk6mode);
out.writeInt(Sk6lvl);
out.writeUTF(Skill7);
out.writeUTF(Sk7mode);
out.writeInt(Sk7lvl);
out.writeUTF(Skill8);
out.writeUTF(Sk8mode);
out.writeInt(Sk8lvl);
for (int x : Drain) out.writeInt(x);
for (int x : coldeffect) out.writeInt(x);
for (int x : ResDm) out.writeInt(x);
for (int x : ResMa) out.writeInt(x);
for (int x : ResFi) out.writeInt(x);
for (int x : ResLi) out.writeInt(x);
for (int x : ResCo) out.writeInt(x);
for (int x : ResPo) out.writeInt(x);
out.writeInt(DamageRegen);
out.writeUTF(SkillDamage);
out.writeBoolean(noRatio);
out.writeBoolean(NoShldBlock);
for (int x : ToBlock) out.writeInt(x);
out.writeInt(Crit);
for (int x : minHP) out.writeInt(x);
for (int x : maxHP) out.writeInt(x);
for (int x : AC) out.writeInt(x);
for (int x : Exp) out.writeInt(x);
for (int x : A1MinD) out.writeInt(x);
for (int x : A1MaxD) out.writeInt(x);
for (int x : A1TH) out.writeInt(x);
for (int x : A2MinD) out.writeInt(x);
for (int x : A2MaxD) out.writeInt(x);
for (int x : A2TH) out.writeInt(x);
for (int x : S1MinD) out.writeInt(x);
for (int x : S1MaxD) out.writeInt(x);
for (int x : S1TH) out.writeInt(x);
out.writeUTF(El1Mode);
out.writeUTF(El1Type);
for (int x : El1Pct) out.writeInt(x);
for (int x : El1MinD) out.writeInt(x);
for (int x : El1MaxD) out.writeInt(x);
for (int x : El1Dur) out.writeInt(x);
out.writeUTF(El2Mode);
out.writeUTF(El2Type);
for (int x : El2Pct) out.writeInt(x);
for (int x : El2MinD) out.writeInt(x);
for (int x : El2MaxD) out.writeInt(x);
for (int x : El2Dur) out.writeInt(x);
out.writeUTF(El3Mode);
out.writeUTF(El3Type);
for (int x : El3Pct) out.writeInt(x);
for (int x : El3MinD) out.writeInt(x);
for (int x : El3MaxD) out.writeInt(x);
for (int x : El3Dur) out.writeInt(x);
for (String x : TreasureClass1) out.writeUTF(x);
for (String x : TreasureClass2) out.writeUTF(x);
for (String x : TreasureClass3) out.writeUTF(x);
for (String x : TreasureClass4) out.writeUTF(x);
out.writeInt(TCQuestId);
out.writeInt(TCQuestCP);
out.writeInt(SplEndDeath);
out.writeBoolean(SplGetModeChart);
out.writeBoolean(SplEndGeneric);
out.writeBoolean(SplClientEnd);
}
}
}

View File

@ -12,4 +12,8 @@ public class ClassUtils {
return false;
}
}
public static boolean hasAnnotation(Class c, Class annotationClass) {
return c.getAnnotation(annotationClass) != null;
}
}

View File

@ -10,6 +10,7 @@ import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
@ -100,8 +101,8 @@ public class DS1Viewer extends ApplicationAdapter {
Riiablo.cofs = new COFs(assets);
//COFD2.loadFromFile(resolver.resolve("data\\global\\cmncof_a1.d2"));
TXT txt = TXT.loadFromFile(Gdx.files.local("data/ds1types.txt"));
DS1Types = Excel.parse(txt, com.riiablo.map.DS1Types.class);
FileHandle txt = Gdx.files.local("data/ds1types.txt");
DS1Types = Excel.load(com.riiablo.map.DS1Types.class, txt);
VisUI.load();

View File

@ -0,0 +1,98 @@
package com.riiablo.codec.excel;
import com.google.common.io.LittleEndianDataInputStream;
import com.google.common.io.LittleEndianDataOutputStream;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.headless.HeadlessApplication;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.StreamUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
public class BinGenerationTool extends ApplicationAdapter {
public static void main(String[] args) throws Exception {
new HeadlessApplication(new BinGenerationTool(args));
}
final String[] args;
BinGenerationTool(String[] args) {
this.args = args;
}
@Override
public void create() {
try {
generate(args[0], args[1]);
} catch (Throwable t) {
throw new GdxRuntimeException(t);
}
Gdx.app.exit();
}
public void generate(String fileName, String className) throws Exception {
Class<Excel> excelClass = (Class<Excel>) Class.forName(className);
if (!Excel.isBinned(excelClass)) {
System.out.println(excelClass + " is not annotated with " + Excel.Binned.class);
return;
}
File file = new File(fileName);
//InputStream in = FileUtils.openInputStream(file);
//TXT txt = TXT.loadFromStream(in);
FileHandle txt = new FileHandle(file);
Excel excel = Excel.load(excelClass, txt, null, Excel.EXPANSION);
String binFileName = generateBinName(fileName);
File binFile = new File(binFileName);
OutputStream out = FileUtils.openOutputStream(binFile);
BufferedOutputStream buffer = new BufferedOutputStream(out);
LittleEndianDataOutputStream dos = null;
try {
dos = new LittleEndianDataOutputStream(buffer);
excel.writeBin(dos);
} finally {
StreamUtils.closeQuietly(dos);
}
InputStream in = FileUtils.openInputStream(binFile);
BufferedInputStream bufferIn = new BufferedInputStream(in);
LittleEndianDataInputStream dis = null;
try {
dis = new LittleEndianDataInputStream(bufferIn);
Excel copy = excelClass.newInstance();
copy.readBin(dis);
} finally {
StreamUtils.closeQuietly(dis);
}
}
private static String generateBinName(String fileName) {
return FilenameUtils.getPath(fileName) + FilenameUtils.getBaseName(fileName) + ".bin";
}
private static Method getMethod(Class clazz, Class annotation) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getAnnotation(annotation) != null) {
return method;
}
}
return null;
}
}

View File

@ -0,0 +1,65 @@
package com.riiablo.codec.excel;
import java.lang.reflect.Field;
public class ExcelGenerationTool {
public static void main(String[] args) throws Exception {
for (String arg : args) {
generate(arg + "$Entry");
}
}
public static void generate(String _package) throws Exception {
Class clazz = Class.forName(_package);
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
Class type = field.getType();
if (type.isArray()) {
type = type.getComponentType();
System.out.printf("for (%s x : %s) out.write%s(x);%n", type.getSimpleName(), field.getName(), getMethod(field));
} else {
System.out.printf("out.write%s(%s);%n", getMethod(field), field.getName());
}
}
System.out.println("-----------------------------------");
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
Excel.Entry.Column column = field.getAnnotation(Excel.Entry.Column.class);
Class type = field.getType();
if (type.isArray()) {
type = type.getComponentType();
System.out.printf("%s = new %s[%d];%n", field.getName(), type.getSimpleName(), column.endIndex() - column.startIndex());
System.out.printf("for (int x = %d; x < %d; x++) %s[x] = in.read%s();%n", column.startIndex(), column.endIndex(), field.getName(), getMethod(field));
} else {
System.out.printf("%s = in.read%s();%n", field.getName(), getMethod(field));
}
}
}
private static String getMethod(Field field) {
Class type = field.getType();
if (type.isArray()) {
type = type.getComponentType();
}
if (type == String.class) {
return "UTF";
} else if (type == byte.class) {
return "Byte";
} else if (type == short.class) {
return "Short";
} else if (type == int.class) {
return "Int";
} else if (type == long.class) {
return "Long";
} else if (type == boolean.class) {
return "Boolean";
} else {
throw new UnsupportedOperationException("No support for " + type + " fields");
}
}
}