public override Level Read(Stream src, string name, bool metadata) { using (GZipStream s = new GZipStream(src, CompressionMode.Decompress)) { Level lvl = new Level(name, 0, 0, 0); DatReader r = new DatReader(); r.src = new BinaryReader(s); int signature = r.ReadInt32(); // Format version 0 - preclassic to classic 0.12 // (technically this format doesn't have a signature, // but 99% of such maps will start with these 4 bytes) if (signature == 0x01010101) { return(ReadFormat0(lvl, s)); } // All valid .dat maps must start with these 4 bytes if (signature != 0x271BB788) { throw new InvalidDataException("Invalid .dat map signature"); } switch (r.ReadUInt8()) { // Format version 1 - classic 0.13 case 0x01: return(ReadFormat1(lvl, r)); // Format version 2 - classic 0.15 to 0.30 case 0x02: return(ReadFormat2(lvl, r)); } throw new InvalidDataException("Invalid .dat map version"); } }
public override Level Read(Stream src, string name, bool metadata) { using (GZipStream s = new GZipStream(src, CompressionMode.Decompress)) { Level lvl = new Level(name, 0, 0, 0); DatReader r = new DatReader(); r.src = new BinaryReader(s); if (r.ReadInt32() != 0x271BB788 || r.ReadUInt8() != 0x02) { throw new InvalidDataException("Unexpected constant in .dat file"); } if (r.ReadUInt16() != 0xACED) { throw new InvalidDataException("Invalid stream magic"); } if (r.ReadUInt16() != 0x0005) { throw new InvalidDataException("Invalid stream version"); } JObject obj = (JObject)ReadObject(r); ParseRootObject(lvl, obj); return(lvl); } }
static JClassDesc NewClass(DatReader r) { JClassDesc classDesc = ClassDesc(r); r.handles.Add(classDesc); return(classDesc); }
static string NewString(DatReader r) { string value = r.ReadUtf8(); r.handles.Add(value); return(value); }
static void SkipAnnotation(DatReader r) { byte typeCode; while ((typeCode = r.ReadUInt8()) != TC_ENDBLOCKDATA) { ReadContent(r, typeCode); } }
static object PrevObject(DatReader r) { int handle = r.ReadInt32() - baseWireHandle; if (handle >= 0 && handle < r.handles.Count) { return(r.handles[handle]); } throw new InvalidDataException("Invalid stream handle: " + handle); }
static Level ReadFormat1(Level lvl, DatReader r) { r.ReadUtf8(); // level name r.ReadUtf8(); // level author r.ReadInt64(); // created timestamp (currentTimeMillis) lvl.Width = r.ReadUInt16(); lvl.Length = r.ReadUInt16(); lvl.Height = r.ReadUInt16(); lvl.blocks = r.ReadBytes(lvl.Width * lvl.Height * lvl.Length); // TODO readfully SetupClassic013(lvl); return(lvl); }
static object ReadContent(DatReader r, byte typeCode) { if (typeCode == TC_BLOCKDATA) { return(r.ReadBytes(r.ReadUInt8())); } else if (typeCode == TC_BLOCKDATALONG) { return(r.ReadBytes(r.ReadInt32())); } else { return(ReadObject(r, typeCode)); } }
static object ReadObject(DatReader r, byte typeCode) { switch (typeCode) { case TC_STRING: return(NewString(r)); case TC_NULL: return(null); case TC_REFERENCE: return(PrevObject(r)); case TC_OBJECT: return(NewObject(r)); case TC_ARRAY: return(NewArray(r)); } throw new InvalidDataException("Invalid typecode: " + typeCode); }
static void SkipAnnotation(DatReader r) { byte typeCode; while ((typeCode = r.ReadUInt8()) != TC_ENDBLOCKDATA) { if (typeCode == TC_BLOCKDATA) { r.ReadBytes(r.ReadUInt8()); } else { ReadObject(r, typeCode); } } }
// Really annoying map format to parse, because it's just a Java serialised object // http://www.javaworld.com/article/2072752/the-java-serialization-algorithm-revealed.html // https://docs.oracle.com/javase/7/docs/platform/serialization/spec/protocol.html // Good reference tool for comparison // https://github.com/NickstaDB/SerializationDumper static Level ReadFormat2(Level lvl, DatReader r) { if (r.ReadUInt16() != 0xACED) { throw new InvalidDataException("Invalid stream magic"); } if (r.ReadUInt16() != 0x0005) { throw new InvalidDataException("Invalid stream version"); } JObject obj = (JObject)ReadObject(r); ParseRootObject(lvl, obj); return(lvl); }
static unsafe object Value(DatReader r, char type) { if (type == 'B') { return(r.ReadUInt8()); } if (type == 'C') { return((char)r.ReadUInt16()); } if (type == 'D') { long tmp = r.ReadInt64(); return(*(double *)(&tmp)); } if (type == 'F') { int tmp = r.ReadInt32(); return(*(float *)(&tmp)); } if (type == 'I') { return(r.ReadInt32()); } if (type == 'J') { return(r.ReadInt64()); } if (type == 'S') { return(r.ReadInt16()); } if (type == 'Z') { return(r.ReadUInt8() != 0); } if (type == 'L') { return(ReadObject(r)); } if (type == '[') { return(ReadObject(r)); } throw new InvalidDataException("Invalid value code: " + type); }
static JClassDesc ClassDesc(DatReader r) { byte typeCode = r.ReadUInt8(); if (typeCode == TC_CLASSDESC) { return(NewClassDesc(r)); } if (typeCode == TC_NULL) { return(null); } if (typeCode == TC_REFERENCE) { return((JClassDesc)PrevObject(r)); } throw new InvalidDataException("Invalid type code: " + typeCode); }
static JClassDesc NewClassDesc(DatReader r) { JClassDesc desc = new JClassDesc(); desc.Name = r.ReadUtf8(); r.ReadInt64(); // serial UID r.handles.Add(desc); // read class desc info desc.Flags = r.ReadUInt8(); desc.Fields = new JFieldDesc[r.ReadUInt16()]; for (int i = 0; i < desc.Fields.Length; i++) { desc.Fields[i] = FieldDesc(r); } SkipAnnotation(r); desc.SuperClass = ClassDesc(r); return(desc); }
static JClassData ClassData(DatReader r, JClassDesc desc) { if ((desc.Flags & SC_SERIALIZABLE) == 0) { throw new InvalidDataException("Invalid class data flags: " + desc.Flags); } JClassData data = new JClassData(); data.Values = new object[desc.Fields.Length]; for (int i = 0; i < data.Values.Length; i++) { data.Values[i] = Value(r, desc.Fields[i].Type); } if ((desc.Flags & SC_WRITE_METHOD) != 0) { SkipAnnotation(r); } return(data); }
static object ReadObject(DatReader r, byte typeCode) { switch (typeCode) { case TC_STRING: return(NewString(r)); case TC_RESET: r.handles.Clear(); return(null); case TC_NULL: return(null); case TC_REFERENCE: return(PrevObject(r)); case TC_CLASS: return(NewClass(r)); case TC_OBJECT: return(NewObject(r)); case TC_ARRAY: return(NewArray(r)); case TC_CLASSDESC: return(NewClassDesc(r)); } throw new InvalidDataException("Invalid typecode: " + typeCode); }
static JFieldDesc FieldDesc(DatReader r) { JFieldDesc desc = new JFieldDesc(); byte type = r.ReadUInt8(); desc.Type = (char)type; if (type == 'B' || type == 'C' || type == 'D' || type == 'F' || type == 'I' || type == 'J' || type == 'S' || type == 'Z') { desc.Name = r.ReadUtf8(); } else if (type == '[' || type == 'L') { desc.Name = r.ReadUtf8(); desc.ClassName = (string)ReadObject(r); } else { throw new InvalidDataException("Invalid field type: " + type); } return(desc); }
static JObject NewObject(DatReader r) { JObject obj = new JObject(); obj.Desc = ClassDesc(r); r.handles.Add(obj); List <JClassDesc> descs = new List <JClassDesc>(); JClassDesc tmp = obj.Desc; // most superclass data is first while (tmp != null) { descs.Add(tmp); tmp = tmp.SuperClass; } obj.ClassData = new JClassData[descs.Count]; for (int i = descs.Count - 1; i >= 0; i--) { obj.ClassData[i] = ClassData(r, descs[i]); } return(obj); }
static JArray NewArray(DatReader r) { JArray array = new JArray(); array.Desc = ClassDesc(r); r.handles.Add(array); char type = array.Desc.Name[1]; int size = r.ReadInt32(); if (type == 'B') { array.Values = r.ReadBytes(size); } else { object[] values = new object[size]; for (int i = 0; i < values.Length; i++) { values[i] = Value(r, type); } array.Values = values; } return(array); }
static object ReadObject(DatReader r) { return(ReadObject(r, r.ReadUInt8())); }