diff --git a/src/main/java/tk/valoeghese/sod/BinaryData.java b/src/main/java/tk/valoeghese/sod/BinaryData.java new file mode 100644 index 0000000..fbb8a97 --- /dev/null +++ b/src/main/java/tk/valoeghese/sod/BinaryData.java @@ -0,0 +1,64 @@ +package tk.valoeghese.sod; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import tk.valoeghese.sod.exception.SODParseException; + +public class BinaryData implements Iterable> { + public BinaryData() { + this.sections = new HashMap<>(); + } + + private final Map sections; + + public DataSection get(String name) { + return this.sections.get(name); + } + + public DataSection getOrCreate(String name) { + return this.sections.computeIfAbsent(name, k -> new DataSection()); + } + + public void put(String name, DataSection section) { + this.sections.put(name, section); + } + + public boolean write(File file) { + try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { + Parser.write(this, dos); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + @Override + public Iterator> iterator() { + return this.sections.entrySet().iterator(); + } + + public static BinaryData read(File file) throws SODParseException { + try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) { + long magic = dis.readLong(); + + if (magic != 0xA77D1E) { + throw new SODParseException("Not a valid SOD file!"); + } + + return Parser.parse(dis); + } catch (IOException e) { + e.printStackTrace(); + //throw new SODParseException("Error in parsing file " + file.toString()); + return new BinaryData(); + } + } +} diff --git a/src/main/java/tk/valoeghese/sod/DataSection.java b/src/main/java/tk/valoeghese/sod/DataSection.java new file mode 100644 index 0000000..a02016e --- /dev/null +++ b/src/main/java/tk/valoeghese/sod/DataSection.java @@ -0,0 +1,106 @@ +package tk.valoeghese.sod; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Represents a section of SOD data. + * @author Valoeghese + */ +public class DataSection implements Iterable { + public DataSection() { + this.data = new ArrayList<>(); + } + + private final List data; + + public void writeByte(byte data) { + this.data.add(data); + } + + public void writeShort(short data) { + this.data.add(data); + } + + public void writeInt(int data) { + this.data.add(data); + } + + public void writeLong(long data) { + this.data.add(data); + } + + public void writeFloat(float data) { + this.data.add(data); + } + + public void writeDouble(double data) { + this.data.add(data); + } + + public void writeString(String data) { + this.data.add(data); + } + + public void writeBoolean(boolean data) { + this.data.add(data ? (byte) 1 : (byte) 0); + } + + public > void writeEnum(T enumValue) { + this.data.add(enumValue.ordinal()); + } + + public > void writeEnumAsString(T enumValue) { + this.data.add(enumValue.toString()); + } + + public int size() { + return this.data.size(); + } + + public byte readByte(int index) { + return (byte) this.data.get(index); + } + + public short readShort(int index) { + return (short) this.data.get(index); + } + + public int readInt(int index) { + return (int) this.data.get(index); + } + + public long readLong(int index) { + return (long) this.data.get(index); + } + + public float readFloat(int index) { + return (float) this.data.get(index); + } + + public double readDouble(int index) { + return (double) this.data.get(index); + } + + public String readString(int index) { + return (String) this.data.get(index); + } + + public boolean readBoolean(int index) { + return ((byte) this.data.get(index)) != 0; + } + + public > T readEnumString(int index, Class type) { + return Enum.valueOf(type, (String) this.data.get(index)); + } + + public > T readEnum(int index, T[] values) { + return values[(int) this.data.get(index)]; + } + + @Override + public Iterator iterator() { + return this.data.iterator(); + } +} diff --git a/src/main/java/tk/valoeghese/sod/Parser.java b/src/main/java/tk/valoeghese/sod/Parser.java new file mode 100644 index 0000000..353169d --- /dev/null +++ b/src/main/java/tk/valoeghese/sod/Parser.java @@ -0,0 +1,142 @@ +package tk.valoeghese.sod; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +import tk.valoeghese.sod.exception.SODParseException; + +final class Parser { + static BinaryData parse(DataInputStream input) throws IOException, SODParseException { + BinaryData data = new BinaryData(); + + DataType dataType; + dataType = DataType.of(input.readByte()); + + if (dataType != DataType.SECTION) { + throw new SODParseException("Data must be segregated into sections!"); + } + + DataSection currentSection = new DataSection(); + data.put(input.readUTF(), currentSection); + + while (input.available() > 0) { + dataType = DataType.of(input.readByte()); + + switch (dataType) { + case BYTE: + currentSection.writeByte(input.readByte()); + break; + case DOUBLE: + currentSection.writeDouble(input.readDouble()); + break; + case FLOAT: + currentSection.writeFloat(input.readFloat()); + break; + case INT: + currentSection.writeInt(input.readInt()); + break; + case LONG: + currentSection.writeLong(input.readLong()); + break; + case SECTION: + currentSection = new DataSection(); + data.put(input.readUTF(), currentSection); + break; + case SHORT: + currentSection.writeShort(input.readShort()); + break; + case STRING: + currentSection.writeString(input.readUTF()); + break; + default: + throw new RuntimeException("This error should never be thrown! If this error occurs, the parser is not properly dealing with a most-likely-invalid data type."); + } + } + + return data; + } + + static void write(BinaryData data, DataOutputStream dos) throws IOException { + dos.writeLong(0xA77D1E); + + Iterator> sectionStream = data.iterator(); + + while (sectionStream.hasNext()) { + Map.Entry section = sectionStream.next(); + dos.writeByte(DataType.SECTION.id); + dos.writeUTF(section.getKey()); + + Iterator dataStream = section.getValue().iterator(); + + while (dataStream.hasNext()) { + Object o = dataStream.next(); + + if (o instanceof Byte) { + dos.writeByte(DataType.BYTE.id); + dos.writeByte((byte) o); + } else if (o instanceof Short) { + dos.writeByte(DataType.SHORT.id); + dos.writeShort((short) o); + } else if (o instanceof Integer) { + dos.writeByte(DataType.INT.id); + dos.writeInt((int) o); + } else if (o instanceof Long) { + dos.writeByte(DataType.LONG.id); + dos.writeLong((long) o); + } else if (o instanceof Float) { + dos.writeByte(DataType.FLOAT.id); + dos.writeFloat((float) o); + } else if (o instanceof Double) { + dos.writeByte(DataType.DOUBLE.id); + dos.writeDouble((double) o); + } else if (o instanceof String) { + dos.writeByte(DataType.STRING.id); + dos.writeUTF((String) o); + } + } + } + } +} + +enum DataType { + SECTION(0), + BYTE(1), + SHORT(2), + INT(3), + LONG(4), + FLOAT(5), + DOUBLE(6), + STRING(7); + + private DataType(int id) { + this.id = (byte) id; + } + + public final byte id; + + public static DataType of(byte id) throws SODParseException { + switch (id) { + case 0: + return SECTION; + case 1: + return BYTE; + case 2: + return SHORT; + case 3: + return INT; + case 4: + return LONG; + case 5: + return FLOAT; + case 6: + return DOUBLE; + case 7: + return STRING; + default: + throw new SODParseException("Unknown data type " + String.valueOf(id)); + } + } +} \ No newline at end of file diff --git a/src/main/java/tk/valoeghese/sod/TestMain.java b/src/main/java/tk/valoeghese/sod/TestMain.java new file mode 100644 index 0000000..66be22e --- /dev/null +++ b/src/main/java/tk/valoeghese/sod/TestMain.java @@ -0,0 +1,53 @@ +package tk.valoeghese.sod; + +import java.io.File; +import java.io.IOException; + +public class TestMain { + + public static void main(String[] args) throws IOException { + //writeTest() + readTest(); + } + + static void readTest() throws IOException { + File f = new File("../test.sod"); + + if (!f.createNewFile()) { + BinaryData bd = BinaryData.read(f); + + DataSection ds1 = bd.get("DS1"); + + for (Object i : ds1) { + System.out.println(i); + } + + DataSection ds2 = bd.get("yeet"); + + for (Object i : ds2) { + System.out.println(i); + } + } + } + + static void writeTest() throws IOException { + File f = new File("../test.sod"); + f.createNewFile(); + BinaryData bd = new BinaryData(); + + DataSection ds1 = bd.getOrCreate("DS1"); + ds1.writeBoolean(false); + ds1.writeDouble(0.666D); + ds1.writeLong(69696969); + ds1.writeString("yaY33T"); + + DataSection ds2 = bd.getOrCreate("yeet"); + ds2.writeByte((byte) 4); + ds2.writeFloat(1.3F); + ds2.writeString("e"); + ds2.writeString("ff"); + + bd.write(f); + } + +} diff --git a/src/main/java/tk/valoeghese/sod/exception/SODParseException.java b/src/main/java/tk/valoeghese/sod/exception/SODParseException.java new file mode 100644 index 0000000..f91639a --- /dev/null +++ b/src/main/java/tk/valoeghese/sod/exception/SODParseException.java @@ -0,0 +1,9 @@ +package tk.valoeghese.sod.exception; + +public class SODParseException extends RuntimeException { + private static final long serialVersionUID = -3337517517501006140L; + + public SODParseException(String message) { + super(message); + } +}