mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-10-06 12:37:28 -04:00
183 lines
6.1 KiB
C#
183 lines
6.1 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() ) ); }
|
|
}
|
|
} |