I waste several hours

This commit is contained in:
Anuken
2022-02-11 11:33:06 -05:00
parent fcb4c232e8
commit 4a216056d0
14 changed files with 334 additions and 24 deletions

View File

@@ -72,10 +72,11 @@ public abstract class SaveFileReader{
"slag", "molten-slag"
);
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream(), byteOutput2 = new ReusableByteOutStream();
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput), dataBytes2 = new DataOutputStream(byteOutput2);
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
protected boolean chunkNested = false;
protected int lastRegionLength;
protected @Nullable CounterInputStream currCounter;
@@ -112,23 +113,41 @@ public abstract class SaveFileReader{
}
/** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */
public void writeChunk(DataOutput output, boolean isByte, IORunner<DataOutput> runner) throws IOException{
ReusableByteOutStream dout = isByte ? byteOutputSmall : byteOutput;
//reset output position
dout.reset();
//write the needed info
runner.accept(isByte ? dataBytesSmall : dataBytes);
int length = dout.size();
//write length (either int or byte) followed by the output bytes
if(!isByte){
output.writeInt(length);
}else{
if(length > 65535){
throw new IOException("Byte write length exceeded: " + length + " > 65535");
public void writeChunk(DataOutput output, boolean isShort, IORunner<DataOutput> runner) throws IOException{
//TODO awful
boolean wasNested = chunkNested;
if(!isShort){
chunkNested = true;
}
ReusableByteOutStream dout =
isShort ? byteOutputSmall :
wasNested ? byteOutput2 :
byteOutput;
try{
//reset output position
dout.reset();
//write the needed info
runner.accept(
isShort ? dataBytesSmall :
wasNested ? dataBytes2 :
dataBytes
);
int length = dout.size();
//write length (either int or byte) followed by the output bytes
if(!isShort){
output.writeInt(length);
}else{
if(length > 65535){
throw new IOException("Byte write length exceeded: " + length + " > 65535");
}
output.writeShort(length);
}
output.writeShort(length);
output.write(dout.getBytes(), 0, length);
}finally{
chunkNested = wasNested;
}
output.write(dout.getBytes(), 0, length);
}
public int readChunk(DataInput input, IORunner<DataInput> runner) throws IOException{
@@ -148,8 +167,8 @@ public abstract class SaveFileReader{
}
/** Skip a chunk completely, discarding the bytes. */
public void skipChunk(DataInput input, boolean isByte) throws IOException{
int length = readChunk(input, isByte, t -> {});
public void skipChunk(DataInput input, boolean isShort) throws IOException{
int length = readChunk(input, isShort, t -> {});
int skipped = input.skipBytes(length);
if(length != skipped){
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
@@ -180,4 +199,10 @@ public abstract class SaveFileReader{
public interface IORunner<T>{
void accept(T stream) throws IOException;
}
public interface CustomChunk{
void write(DataOutput stream) throws IOException;
void read(DataInput stream) throws IOException;
boolean shouldWrite();
}
}

View File

@@ -20,7 +20,7 @@ public class SaveIO{
/** Save format header. */
public static final byte[] header = {'M', 'S', 'A', 'V'};
public static final IntMap<SaveVersion> versions = new IntMap<>();
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6());
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7());
static{
for(SaveVersion version : versionArray){

View File

@@ -23,7 +23,9 @@ import java.util.*;
import static mindustry.Vars.*;
public abstract class SaveVersion extends SaveFileReader{
public int version;
protected static OrderedMap<String, CustomChunk> customChunks = new OrderedMap<>();
public final int version;
//HACK stores the last read build of the save file, valid after read meta call
protected int lastReadBuild;
@@ -31,6 +33,14 @@ public abstract class SaveVersion extends SaveFileReader{
//if null, fall back to EntityMapping's values
protected @Nullable Prov[] entityMapping;
/**
* Registers a custom save chunk reader/writer by name. This is mostly used for mods that need to save extra data.
* @param name a mod-specific, unique name for identifying this chunk. Prefixing is recommended.
* */
public static void addCustomChunk(String name, CustomChunk chunk){
customChunks.put(name, chunk);
}
public SaveVersion(int version){
this.version = version;
}
@@ -56,23 +66,49 @@ public abstract class SaveVersion extends SaveFileReader{
}
@Override
public final void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
region("meta", stream, counter, this::readMeta);
region("content", stream, counter, this::readContentHeader);
try{
region("map", stream, counter, in -> readMap(in, context));
region("entities", stream, counter, this::readEntities);
region("custom", stream, counter, this::readCustomChunks);
}finally{
content.setTemporaryMapper(null);
}
}
public final void write(DataOutputStream stream, StringMap extraTags) throws IOException{
public void write(DataOutputStream stream, StringMap extraTags) throws IOException{
region("meta", stream, out -> writeMeta(out, extraTags));
region("content", stream, this::writeContentHeader);
region("map", stream, this::writeMap);
region("entities", stream, this::writeEntities);
region("custom", stream, this::writeCustomChunks);
}
public void writeCustomChunks(DataOutput stream) throws IOException{
var chunks = customChunks.orderedKeys().select(s -> customChunks.get(s).shouldWrite());
stream.writeInt(chunks.size);
for(var chunkName : chunks){
var chunk = customChunks.get(chunkName);
stream.writeUTF(chunkName);
writeChunk(stream, false, chunk::write);
}
}
public void readCustomChunks(DataInput stream) throws IOException{
int amount = stream.readInt();
for(int i = 0; i < amount; i++){
String name = stream.readUTF();
var chunk = customChunks.get(name);
if(chunk != null){
readChunk(stream, false, chunk::read);
}else{
skipChunk(stream);
}
}
}
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{

View File

@@ -1,10 +1,31 @@
package mindustry.io.versions;
import arc.util.io.*;
import mindustry.io.*;
import mindustry.world.*;
import java.io.*;
import static mindustry.Vars.*;
/** This version does not read custom chunk data. */
public class Save6 extends SaveVersion{
public Save6(){
super(6);
}
@Override
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
region("meta", stream, counter, this::readMeta);
region("content", stream, counter, this::readContentHeader);
try{
region("map", stream, counter, in -> readMap(in, context));
region("entities", stream, counter, this::readEntities);
}finally{
content.setTemporaryMapper(null);
}
}
}

View File

@@ -0,0 +1,10 @@
package mindustry.io.versions;
import mindustry.io.*;
public class Save7 extends SaveVersion{
public Save7(){
super(7);
}
}