Implemented slightly improved parser for TXTs (see #26)

This commit is contained in:
Collin Smith
2019-09-29 03:47:15 -07:00
parent 28c6bd95dc
commit d2b48b8119
8 changed files with 240 additions and 42 deletions

View File

@ -41,7 +41,6 @@ import com.riiablo.codec.FontTBL;
import com.riiablo.codec.Index;
import com.riiablo.codec.Palette;
import com.riiablo.codec.StringTBLs;
import com.riiablo.codec.TXT;
import com.riiablo.console.RenderedConsole;
import com.riiablo.cvar.Cvar;
import com.riiablo.cvar.CvarStateAdapter;
@ -52,7 +51,6 @@ import com.riiablo.loader.DC6Loader;
import com.riiablo.loader.DCCLoader;
import com.riiablo.loader.IndexLoader;
import com.riiablo.loader.PaletteLoader;
import com.riiablo.loader.TXTLoader;
import com.riiablo.map.DS1;
import com.riiablo.map.DS1Loader;
import com.riiablo.map.DT1;
@ -257,7 +255,6 @@ public class Client extends Game {
assets.setLoader(FontTBL.BitmapFont.class, new BitmapFontLoader(mpqs));
assets.setLoader(DT1.class, new DT1Loader(mpqs));
assets.setLoader(DS1.class, new DS1Loader(mpqs));
assets.setLoader(TXT.class, new TXTLoader(mpqs));
assets.setLoader(Map.class, new MapLoader(mpqs));
Riiablo.palettes = palettes = new Palettes(assets);

View File

@ -12,9 +12,10 @@ 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 org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutput;
@ -131,8 +132,18 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
@SuppressWarnings("unchecked")
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);
TxtParser in = null;
try {
in = TxtParser.loadFromFile(handle);
return loadTxt(in, excelClass, entryClass, ignore);
} catch (Throwable t) {
throw new GdxRuntimeException("Couldn't load excel: " + handle, t);
} finally {
IOUtils.closeQuietly(in);
}
}
private static <T extends Excel> T loadTxt(TxtParser in, Class<T> excelClass, Class<Entry> entryClass, ObjectSet<String> ignore) throws Exception {
final boolean index = ClassUtils.hasAnnotation(entryClass, Index.class);
Field primaryKey = null, firstKey = null;
@ -182,11 +193,11 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
columnNames[i] = name;
}
columns.put(field, txt.getColumnId(columnNames));
columns.put(field, in.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));
columns.put(field, in.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
for (int i = startIndex, j = 0; i < endIndex; i++, j++) {
@ -195,12 +206,12 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
columnNames[j] = name;
}
columns.put(field, txt.getColumnId(columnNames));
columns.put(field, in.getColumnId(columnNames));
}
} else {
if (startIndex == 0 && endIndex == 0) {
TMP[0] = format;
columns.put(field, txt.getColumnId(TMP));
columns.put(field, in.getColumnId(TMP));
} else {
String[] columnNames = new String[endIndex - startIndex];
if (values.length == 0) {
@ -217,7 +228,7 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
}
}
columns.put(field, txt.getColumnId(columnNames));
columns.put(field, in.getColumnId(columnNames));
}
}
}
@ -239,68 +250,67 @@ public abstract class Excel<T extends Excel.Entry> implements Iterable<T> {
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++) {
for (int j = excel.offset(); in.nextLine() != null;) {
Entry entry = entryClass.newInstance();
String rowName = txt.getRowName(i);
String rowName = in.getString(0);
if (ignore.contains(rowName)) {
if (DEBUG_IGNORED) Gdx.app.debug(TAG, "Skipping row " + i + ", ignoring rows named " + rowName);
if (DEBUG_IGNORED) Gdx.app.debug(TAG, "Skipping row " + in.getIndex() + ", ignoring rows named " + rowName);
continue;
}
String name = index ? null : txt.getString(i, primaryKeyCol);
String name = index ? null : in.getString(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]);
String value = in.getString(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);
String[] value = in.getString(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]);
byte value = in.getByte(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);
byte[] value = in.getByte(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]);
short value = in.getShort(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);
short[] value = in.getShort(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]);
int value = in.getInt(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);
int[] value = in.getInt(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]);
long value = in.getLong(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);
long[] value = in.getLong(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]);
boolean value = in.getBoolean(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);
boolean[] value = in.getBoolean(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 {

View File

@ -0,0 +1,206 @@
package com.riiablo.codec.excel;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectIntMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
public class TxtParser implements Closeable {
private static final String TAG = "TxtParser";
private static final boolean DEBUG = !true;
private static final boolean DEBUG_COLS = DEBUG && true;
private static final boolean DEBUG_ROWS = DEBUG && true;
private static final boolean FORCE_BOOL = true; // logs error if boolean is not 0 or 1
private static final boolean FORCE_COLS = true; // ignores row if token count != columns count
private static final String EXPANSION = "Expansion";
public static final int INDEX_NOT_FOUND = -1;
public static TxtParser loadFromFile(FileHandle handle) {
return loadFromStream(handle.read());
}
public static TxtParser loadFromStream(InputStream in) {
BufferedReader reader = null;
try {
reader = IOUtils.buffer(new InputStreamReader(in, "US-ASCII"));
return new TxtParser(reader);
} catch (Throwable t) {
throw new GdxRuntimeException("Couldn't read excel file", t);
}
}
private BufferedReader in;
private ObjectIntMap<String> ids;
private String columns[];
private int index;
private String line;
private String tokens[];
private TxtParser(BufferedReader in) {
this.in = in;
try {
line = in.readLine();
columns = StringUtils.splitPreserveAllTokens(line, '\t');
if (DEBUG_COLS) Gdx.app.debug(TAG, "cols=" + Arrays.toString(columns));
ids = new ObjectIntMap<>();
for (int i = 0; i < columns.length; i++) {
String key = columns[i].toLowerCase();
if (!ids.containsKey(key)) ids.put(key, i);
}
} catch (Throwable t) {
throw new GdxRuntimeException("Couldn't read txt", t);
}
}
@Override
public void close() throws IOException {
if (in == null) {
return;
}
try {
in.close();
} finally {
in = null;
}
}
public String[] getColumns() {
return columns;
}
public int getNumColumns() {
return columns.length;
}
public String getColumnName(int i) {
return columns[i];
}
public String[] getTokens() {
return tokens;
}
public int getNumTokens() {
return tokens.length;
}
public int getColumnId(String s) {
return ids.get(s.toLowerCase(), INDEX_NOT_FOUND);
}
public int[] getColumnId(String[] s) {
int[] columnIds = new int[s.length];
for (int i = 0; i < s.length; i++) columnIds[i] = getColumnId(s[i]);
return columnIds;
}
public int getIndex() {
return index - 1;
}
public String nextLine() {
try {
index++;
line = in.readLine();
if (line == null) {
return null;
} else if (line.equalsIgnoreCase(EXPANSION)) {
return nextLine();
}
tokens = StringUtils.splitPreserveAllTokens(line, '\t');
if (DEBUG_ROWS) Gdx.app.debug(TAG, (index - 1) + ": " + Arrays.toString(tokens));
if (FORCE_COLS && tokens.length != columns.length) {
Gdx.app.error(TAG, "Skipping row " + Arrays.toString(tokens));
return nextLine();
}
return line;
} catch (IOException e) {
Gdx.app.error(TAG, e.getMessage(), e);
return null;
}
}
public String getString(int i) {
if (i == INDEX_NOT_FOUND) return null;
return tokens[i];
}
public byte getByte(int i) {
return NumberUtils.toByte(getString(i));
}
public short getShort(int i) {
return NumberUtils.toShort(getString(i));
}
public int getInt(int i) {
return NumberUtils.toInt(getString(i));
}
public long getLong(int i) {
return NumberUtils.toLong(getString(i));
}
public boolean getBoolean(int i) {
int value = getInt(i);
if (FORCE_BOOL && (value & 1) != value) Gdx.app.error(TAG, String.format("boolean value != 0 or 1 at row %d col %d (\"%s\", \"%s\"): %d", index, i, getString(0), getColumnName(i), value));
return value != 0;
}
public String[] getString(int[] cols) {
String[] data = new String[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getString(cols[i]);
return data;
}
public byte[] getByte(int[] cols) {
byte[] data = new byte[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getByte(cols[i]);
return data;
}
public short[] getShort(int[] cols) {
short[] data = new short[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getShort(cols[i]);
return data;
}
public int[] getInt(int[] cols) {
int[] data = new int[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getInt(cols[i]);
return data;
}
public long[] getLong(int[] cols) {
long[] data = new long[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getLong(cols[i]);
return data;
}
public boolean[] getBoolean(int[] cols) {
boolean[] data = new boolean[cols.length];
for (int i = 0; i < cols.length; i++) data[i] = getBoolean(cols[i]);
return data;
}
}

View File

@ -34,7 +34,6 @@ import com.riiablo.Riiablo;
import com.riiablo.codec.COF;
import com.riiablo.codec.DCC;
import com.riiablo.codec.Palette;
import com.riiablo.codec.TXT;
import com.riiablo.codec.excel.Excel;
import com.riiablo.codec.excel.LvlPrest;
import com.riiablo.codec.excel.LvlTypes;
@ -90,7 +89,6 @@ public class DS1Viewer extends ApplicationAdapter {
resolver.add(Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II\\d2data.mpq"));
AssetManager assets = Riiablo.assets = new AssetManager();
assets.setLoader(TXT.class, new TXTLoader(resolver));
assets.setLoader(DS1.class, new DS1Loader(resolver));
assets.setLoader(DT1.class, new DT1Loader(resolver));
assets.setLoader(Map.class, new MapLoader(resolver));

View File

@ -29,7 +29,6 @@ import com.riiablo.codec.DCC;
import com.riiablo.codec.FontTBL;
import com.riiablo.codec.Index;
import com.riiablo.codec.Palette;
import com.riiablo.codec.TXT;
import com.riiablo.graphics.PaletteIndexedBatch;
import com.riiablo.loader.BitmapFontLoader;
import com.riiablo.loader.COFLoader;
@ -37,7 +36,6 @@ import com.riiablo.loader.DC6Loader;
import com.riiablo.loader.DCCLoader;
import com.riiablo.loader.IndexLoader;
import com.riiablo.loader.PaletteLoader;
import com.riiablo.loader.TXTLoader;
import com.riiablo.mpq.MPQFileHandleResolver;
public class AnimationTool extends ApplicationAdapter {
@ -78,7 +76,6 @@ public class AnimationTool extends ApplicationAdapter {
Riiablo.assets.setLoader(Palette.class, new PaletteLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(Index.class, new IndexLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(FontTBL.BitmapFont.class, new BitmapFontLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(TXT.class, new TXTLoader(Riiablo.mpqs));
Texture.setAssetManager(Riiablo.assets);
Riiablo.palettes = new Palettes(Riiablo.assets);

View File

@ -32,13 +32,11 @@ import com.riiablo.codec.DC6;
import com.riiablo.codec.FontTBL;
import com.riiablo.codec.Index;
import com.riiablo.codec.Palette;
import com.riiablo.codec.TXT;
import com.riiablo.graphics.PaletteIndexedBatch;
import com.riiablo.loader.BitmapFontLoader;
import com.riiablo.loader.DC6Loader;
import com.riiablo.loader.IndexLoader;
import com.riiablo.loader.PaletteLoader;
import com.riiablo.loader.TXTLoader;
import com.riiablo.mpq.MPQFileHandleResolver;
public class FontMetricsTool extends ApplicationAdapter {
@ -83,7 +81,6 @@ public class FontMetricsTool extends ApplicationAdapter {
Riiablo.assets.setLoader(Palette.class, new PaletteLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(Index.class, new IndexLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(FontTBL.BitmapFont.class, new BitmapFontLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(TXT.class, new TXTLoader(Riiablo.mpqs));
Texture.setAssetManager(Riiablo.assets);
Riiablo.palettes = new Palettes(Riiablo.assets);

View File

@ -34,7 +34,6 @@ import com.riiablo.codec.DCC;
import com.riiablo.codec.FontTBL;
import com.riiablo.codec.Palette;
import com.riiablo.codec.StringTBLs;
import com.riiablo.codec.TXT;
import com.riiablo.entity.Engine;
import com.riiablo.entity.Entity;
import com.riiablo.entity.Player;
@ -43,7 +42,6 @@ import com.riiablo.loader.BitmapFontLoader;
import com.riiablo.loader.COFLoader;
import com.riiablo.loader.DC6Loader;
import com.riiablo.loader.DCCLoader;
import com.riiablo.loader.TXTLoader;
import com.riiablo.map.DT1.Tile;
import com.riiablo.mpq.MPQFileHandleResolver;
@ -113,7 +111,6 @@ public class MapViewer extends ApplicationAdapter {
MPQFileHandleResolver resolver = Riiablo.mpqs = new MPQFileHandleResolver();
AssetManager assets = Riiablo.assets = new AssetManager();
assets.setLoader(TXT.class, new TXTLoader(resolver));
assets.setLoader(DS1.class, new DS1Loader(resolver));
assets.setLoader(DT1.class, new DT1Loader(resolver));
assets.setLoader(COF.class, new COFLoader(resolver));

View File

@ -47,7 +47,6 @@ import com.riiablo.codec.FontTBL;
import com.riiablo.codec.Index;
import com.riiablo.codec.Palette;
import com.riiablo.codec.StringTBLs;
import com.riiablo.codec.TXT;
import com.riiablo.entity.Engine;
import com.riiablo.entity.Entity;
import com.riiablo.entity.Player;
@ -58,7 +57,6 @@ import com.riiablo.loader.DC6Loader;
import com.riiablo.loader.DCCLoader;
import com.riiablo.loader.IndexLoader;
import com.riiablo.loader.PaletteLoader;
import com.riiablo.loader.TXTLoader;
import com.riiablo.mpq.MPQFileHandleResolver;
import java.util.Arrays;
@ -103,7 +101,6 @@ public class WallAggregatorTool extends ApplicationAdapter {
Riiablo.home = home = Gdx.files.absolute(home.path());
Riiablo.mpqs = new MPQFileHandleResolver();
Riiablo.assets = new AssetManager();
Riiablo.assets.setLoader(TXT.class, new TXTLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(DS1.class, new DS1Loader(Riiablo.mpqs));
Riiablo.assets.setLoader(DT1.class, new DT1Loader(Riiablo.mpqs));
Riiablo.assets.setLoader(COF.class, new COFLoader(Riiablo.mpqs));
@ -111,7 +108,6 @@ public class WallAggregatorTool extends ApplicationAdapter {
Riiablo.assets.setLoader(DCC.class, new DCCLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(Palette.class, new PaletteLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(Index.class, new IndexLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(TXT.class, new TXTLoader(Riiablo.mpqs));
Riiablo.assets.setLoader(FontTBL.BitmapFont.class, new BitmapFontLoader(Riiablo.mpqs));
Texture.setAssetManager(Riiablo.assets);