ClassiCube/ClassicalSharp/Map/Formats/MapDat.Importer.cs
2016-12-02 15:31:34 +11:00

183 lines
5.8 KiB
C#

// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using ClassicalSharp.Entities;
using ClassicalSharp.Network;
using OpenTK;
namespace ClassicalSharp.Map {
/// <summary> Imports a world from a dat map file (original minecraft classic map) </summary>
public sealed class MapDatImporter : IMapFormatImporter {
const byte TC_NULL = 0x70, TC_REFERENCE = 0x71;
const byte TC_CLASSDESC = 0x72, TC_OBJECT = 0x73;
const byte TC_STRING = 0x74, TC_ARRAY = 0x75;
const byte TC_ENDBLOCKDATA = 0x78;
BinaryReader reader;
public byte[] Load(Stream stream, Game game, out int width, out int height, out int length) {
byte[] map = null;
width = 0;
height = 0;
length = 0;
LocalPlayer p = game.LocalPlayer;
p.Spawn = Vector3.Zero;
GZipHeaderReader gsHeader = new GZipHeaderReader();
while (!gsHeader.ReadHeader(stream)) { }
using (DeflateStream gs = new DeflateStream(stream, CompressionMode.Decompress)) {
reader = new BinaryReader(gs);
ClassDescription obj = ReadData();
for (int i = 0; i < obj.Fields.Length; i++) {
FieldDescription field = obj.Fields[i];
if (field.FieldName == "width")
width = (int)field.Value;
else if (field.FieldName == "height")
length = (int)field.Value;
else if (field.FieldName == "depth")
height = (int)field.Value;
else if (field.FieldName == "blocks")
map = (byte[])field.Value;
else if (field.FieldName == "xSpawn")
p.Spawn.X = (int)field.Value;
else if (field.FieldName == "ySpawn")
p.Spawn.Y = (int)field.Value;
else if (field.FieldName == "zSpawn")
p.Spawn.Z = (int)field.Value;
}
}
return map;
}
ClassDescription ReadData() {
if (ReadInt32() != 0x271BB788 || reader.ReadByte() != 0x02) {
throw new InvalidDataException("Unexpected constant in .lvl file");
}
// Java serialization constants
if ((ushort)ReadInt16() != 0xACED || ReadInt16() != 0x0005) {
throw new InvalidDataException("Unexpected java serialisation constant(s).");
}
byte typeCode = reader.ReadByte();
if (typeCode != TC_OBJECT) ParseError(TC_OBJECT, typeCode);
ClassDescription desc = ReadClassDescription();
ReadClassData(desc.Fields);
return desc;
}
void ReadClassData(FieldDescription[] fields) {
for (int i = 0; i < fields.Length; i++) {
switch (fields[i].Type) {
case FieldType.Byte:
fields[i].Value = reader.ReadByte(); break;
case FieldType.Float:
fields[i].Value = ReadInt32(); break; // wrong, but we don't support it anyways
case FieldType.Integer:
fields[i].Value = ReadInt32(); break;
case FieldType.Long:
fields[i].Value = ReadInt64(); break;
case FieldType.Boolean:
fields[i].Value = reader.ReadByte() != 0; break;
case FieldType.Object:
if (fields[i].FieldName == "blockMap") {
byte typeCode = reader.ReadByte();
// Skip all blockMap data with awful hacks
if (typeCode == TC_OBJECT) {
reader.ReadBytes(315);
int count = ReadInt32();
byte[] temp = new byte[17];
Stream stream = reader.BaseStream;
for (int j = 0; j < count; j++) {
stream.Read(temp, 0, temp.Length);
}
reader.ReadBytes(152);
}
} break;
case FieldType.Array:
ReadArray(ref fields[i]); break;
}
}
}
void ReadArray(ref FieldDescription field) {
byte typeCode = reader.ReadByte();
if (typeCode == TC_NULL) return;
if (typeCode != TC_ARRAY) ParseError(TC_ARRAY, typeCode);
ClassDescription desc = ReadClassDescription();
field.Value = reader.ReadBytes(ReadInt32());
}
ClassDescription ReadClassDescription() {
ClassDescription desc = default(ClassDescription);
byte typeCode = reader.ReadByte();
if (typeCode == TC_NULL) return desc;
if (typeCode != TC_CLASSDESC) ParseError(TC_CLASSDESC, typeCode);
desc.ClassName = ReadString();
reader.ReadUInt64(); // serial version UID
reader.ReadByte(); // flags
FieldDescription[] fields = new FieldDescription[ReadInt16()];
for (int i = 0; i < fields.Length; i++) {
fields[i] = ReadFieldDescription();
}
desc.Fields = fields;
typeCode = reader.ReadByte();
if (typeCode != TC_ENDBLOCKDATA) ParseError(TC_ENDBLOCKDATA, typeCode);
ReadClassDescription(); // super class description
return desc;
}
FieldDescription ReadFieldDescription() {
FieldDescription desc = default(FieldDescription);
byte fieldType = reader.ReadByte();
desc.Type = (FieldType)fieldType;
desc.FieldName = ReadString();
if (desc.Type == FieldType.Array || desc.Type == FieldType.Object) {
byte typeCode = reader.ReadByte();
if (typeCode == TC_STRING)
desc.ClassName1 = ReadString();
else if (typeCode == TC_REFERENCE)
reader.ReadInt32(); // handle
else
ParseError(TC_STRING, typeCode);
}
return desc;
}
void ParseError(int expected, int typeCode) {
throw new InvalidDataException(
String.Format("Expected {0}, got {1}", expected.ToString("X2"), typeCode.ToString("X2")));
}
struct ClassDescription {
public string ClassName;
public FieldDescription[] Fields;
}
struct FieldDescription {
public FieldType Type;
public string FieldName;
public string ClassName1;
public object Value;
}
enum FieldType {
Byte = 'B', Float = 'F', Integer = 'I', Long = 'J',
Boolean = 'Z', Array = '[', Object = 'L',
}
long ReadInt64() { return IPAddress.HostToNetworkOrder(reader.ReadInt64()); }
int ReadInt32() { return IPAddress.HostToNetworkOrder(reader.ReadInt32()); }
short ReadInt16() { return IPAddress.HostToNetworkOrder(reader.ReadInt16()); }
string ReadString() { return Encoding.ASCII.GetString(reader.ReadBytes(ReadInt16())); }
}
}