mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-08 14:48:47 -04:00
Update JavaDeserialiser.cs
Added TC_ENUM and associated class. Added TC_CLASS to ReadObject(). Removed unnecessary spaces on empty lines. Unify code styling.
This commit is contained in:
parent
4d838ecb6b
commit
86af8c8f27
@ -1,97 +1,103 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015-2024 MCGalaxy
|
Copyright 2015-2024 MCGalaxy
|
||||||
|
|
||||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||||
not use this file except in compliance with the Licenses. You may
|
not use this file except in compliance with the Licenses. You may
|
||||||
obtain a copy of the Licenses at
|
obtain a copy of the Licenses at
|
||||||
|
|
||||||
https://opensource.org/license/ecl-2-0/
|
https://opensource.org/license/ecl-2-0/
|
||||||
https://www.gnu.org/licenses/gpl-3.0.html
|
https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
Unless required by applicable law or agreed to in writing,
|
||||||
software distributed under the Licenses are distributed on an "AS IS"
|
software distributed under the Licenses are distributed on an "AS IS"
|
||||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
or implied. See the Licenses for the specific language governing
|
or implied. See the Licenses for the specific language governing
|
||||||
permissions and limitations under the Licenses.
|
permissions and limitations under the Licenses.
|
||||||
*/
|
*/
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace MCGalaxy.Levels.IO
|
namespace MCGalaxy.Levels.IO {
|
||||||
{
|
public class JClassDesc {
|
||||||
public class JClassDesc
|
|
||||||
{
|
|
||||||
public string Name;
|
public string Name;
|
||||||
public byte Flags;
|
public byte Flags;
|
||||||
public JFieldDesc[] Fields;
|
public JFieldDesc[] Fields;
|
||||||
public JClassDesc SuperClass;
|
public JClassDesc SuperClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JClassData
|
public class JClassData {
|
||||||
{
|
|
||||||
public object[] Values;
|
public object[] Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JObject
|
public class JClass {
|
||||||
{
|
public JClassDesc Desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JObject {
|
||||||
public JClassDesc Desc;
|
public JClassDesc Desc;
|
||||||
public JClassData[] ClassData;
|
public JClassData[] ClassData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JArray
|
public class JArray {
|
||||||
{
|
|
||||||
public JClassDesc Desc;
|
public JClassDesc Desc;
|
||||||
public object Values;
|
public object Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JFieldDesc
|
public class JFieldDesc {
|
||||||
{
|
|
||||||
public char Type;
|
public char Type;
|
||||||
public string Name, ClassName;
|
public string Name, ClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class JEnum {
|
||||||
|
public JClassDesc Desc;
|
||||||
|
public object Name;
|
||||||
|
public override string ToString() { return Desc.Name + "." + Name; }
|
||||||
|
}
|
||||||
|
|
||||||
// Java serialised objects are quite complicated and annoying to parse
|
// Java serialised objects are quite complicated and annoying to parse
|
||||||
// http://www.javaworld.com/article/2072752/the-java-serialization-algorithm-revealed.html
|
// http://www.javaworld.com/article/2072752/the-java-serialization-algorithm-revealed.html
|
||||||
// https://docs.oracle.com/javase/7/docs/platform/serialization/spec/protocol.html
|
// https://docs.oracle.com/javase/7/docs/platform/serialization/spec/protocol.html
|
||||||
// Good reference tool for comparison
|
// Good reference tool for comparison
|
||||||
// https://github.com/NickstaDB/SerializationDumper
|
// https://github.com/NickstaDB/SerializationDumper
|
||||||
public sealed class JavaReader
|
public sealed class JavaReader {
|
||||||
{
|
|
||||||
public BinaryReader src;
|
public BinaryReader src;
|
||||||
public List<object> handles = new List<object>();
|
public List<object> handles = new List<object>();
|
||||||
public byte[] ReadBytes(int count) { return src.ReadBytes(count); }
|
public byte[] ReadBytes(int count) { return src.ReadBytes(count); }
|
||||||
|
|
||||||
public byte ReadUInt8() { return src.ReadByte(); }
|
public byte ReadUInt8() { return src.ReadByte(); }
|
||||||
public short ReadInt16() { return IPAddress.HostToNetworkOrder(src.ReadInt16()); }
|
public short ReadInt16() { return IPAddress.HostToNetworkOrder(src.ReadInt16()); }
|
||||||
public ushort ReadUInt16() { return (ushort)IPAddress.HostToNetworkOrder(src.ReadInt16()); }
|
public ushort ReadUInt16() { return (ushort)IPAddress.HostToNetworkOrder(src.ReadInt16()); }
|
||||||
|
|
||||||
public int ReadInt32() { return IPAddress.HostToNetworkOrder(src.ReadInt32()); }
|
public int ReadInt32() { return IPAddress.HostToNetworkOrder(src.ReadInt32()); }
|
||||||
public long ReadInt64() { return IPAddress.HostToNetworkOrder(src.ReadInt64()); }
|
public long ReadInt64() { return IPAddress.HostToNetworkOrder(src.ReadInt64()); }
|
||||||
public string ReadUtf8() { return Encoding.UTF8.GetString(src.ReadBytes(ReadUInt16())); }
|
public string ReadUtf8() { return Encoding.UTF8.GetString(src.ReadBytes(ReadUInt16())); }
|
||||||
|
|
||||||
public object ReadObject() { return ReadObject(ReadUInt8()); }
|
public object ReadObject() { return ReadObject(ReadUInt8()); }
|
||||||
|
|
||||||
|
|
||||||
const byte TC_NULL = 0x70;
|
const byte TC_NULL = 0x70;
|
||||||
const byte TC_REFERENCE = 0x71;
|
const byte TC_REFERENCE = 0x71;
|
||||||
const byte TC_CLASSDESC = 0x72;
|
const byte TC_CLASSDESC = 0x72;
|
||||||
const byte TC_OBJECT = 0x73;
|
const byte TC_OBJECT = 0x73;
|
||||||
const byte TC_STRING = 0x74;
|
const byte TC_STRING = 0x74;
|
||||||
const byte TC_ARRAY = 0x75;
|
const byte TC_ARRAY = 0x75;
|
||||||
const byte TC_CLASS = 0x76;
|
const byte TC_CLASS = 0x76;
|
||||||
const byte TC_BLOCKDATA = 0x77;
|
const byte TC_BLOCKDATA = 0x77;
|
||||||
const byte TC_ENDBLOCKDATA = 0x78;
|
const byte TC_ENDBLOCKDATA = 0x78;
|
||||||
const byte TC_RESET = 0x79;
|
const byte TC_RESET = 0x79; // Unimplemented
|
||||||
const byte TC_BLOCKDATALONG = 0x7A;
|
const byte TC_BLOCKDATALONG = 0x7A; // Unimplemented
|
||||||
|
const byte TC_EXCEPTION = 0x7B; // Unimplemented
|
||||||
|
const byte TC_LONGSTRING = 0x7C; // Unimplemented
|
||||||
|
const byte TC_PROXYCLASSDESC = 0x7D; // Unimplemented
|
||||||
|
const byte TC_ENUM = 0x7E;
|
||||||
|
|
||||||
const int baseWireHandle = 0x7E0000;
|
const int baseWireHandle = 0x7E0000;
|
||||||
const byte SC_WRITE_METHOD = 0x01, SC_SERIALIZABLE = 0x02;
|
const byte SC_WRITE_METHOD = 0x01, SC_SERIALIZABLE = 0x02;
|
||||||
|
|
||||||
|
|
||||||
object ReadObject(byte typeCode) {
|
object ReadObject(byte typeCode) {
|
||||||
switch (typeCode) {
|
switch (typeCode) {
|
||||||
case TC_STRING: return NewString();
|
case TC_STRING: return NewString();
|
||||||
@ -99,109 +105,123 @@ namespace MCGalaxy.Levels.IO
|
|||||||
case TC_REFERENCE: return PrevObject();
|
case TC_REFERENCE: return PrevObject();
|
||||||
case TC_OBJECT: return NewObject();
|
case TC_OBJECT: return NewObject();
|
||||||
case TC_ARRAY: return NewArray();
|
case TC_ARRAY: return NewArray();
|
||||||
|
case TC_ENUM: return NewEnum();
|
||||||
|
case TC_CLASS: return NewClass();
|
||||||
}
|
}
|
||||||
throw new InvalidDataException("Invalid typecode: " + typeCode);
|
throw new InvalidDataException("Invalid typecode: " + typeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
string NewString() {
|
string NewString() {
|
||||||
string value = ReadUtf8();
|
string value = ReadUtf8();
|
||||||
handles.Add(value);
|
handles.Add(value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
object PrevObject() {
|
object PrevObject() {
|
||||||
int handle = ReadInt32() - baseWireHandle;
|
int handle = ReadInt32() - baseWireHandle;
|
||||||
if (handle >= 0 && handle < handles.Count) return handles[handle];
|
if (handle >= 0 && handle < handles.Count) return handles[handle];
|
||||||
throw new InvalidDataException("Invalid stream handle: " + handle);
|
throw new InvalidDataException("Invalid stream handle: " + handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
JObject NewObject() {
|
JObject NewObject() {
|
||||||
JObject obj = new JObject();
|
JObject obj = new JObject();
|
||||||
obj.Desc = ClassDesc();
|
obj.Desc = ClassDesc();
|
||||||
handles.Add(obj);
|
handles.Add(obj);
|
||||||
|
|
||||||
List<JClassDesc> descs = new List<JClassDesc>();
|
List<JClassDesc> descs = new List<JClassDesc>();
|
||||||
JClassDesc tmp = obj.Desc;
|
JClassDesc tmp = obj.Desc;
|
||||||
|
|
||||||
// most superclass data is first
|
// most superclass data is first
|
||||||
while (tmp != null) {
|
while (tmp != null) {
|
||||||
descs.Add(tmp);
|
descs.Add(tmp);
|
||||||
tmp = tmp.SuperClass;
|
tmp = tmp.SuperClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.ClassData = new JClassData[descs.Count];
|
obj.ClassData = new JClassData[descs.Count];
|
||||||
for (int i = descs.Count - 1; i >= 0; i--)
|
for (int i = descs.Count - 1; i >= 0; i--) {
|
||||||
{
|
|
||||||
obj.ClassData[i] = ClassData(descs[i]);
|
obj.ClassData[i] = ClassData(descs[i]);
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
JArray NewArray() {
|
JArray NewArray() {
|
||||||
JArray array = new JArray();
|
JArray array = new JArray();
|
||||||
array.Desc = ClassDesc();
|
array.Desc = ClassDesc();
|
||||||
handles.Add(array);
|
handles.Add(array);
|
||||||
char type = array.Desc.Name[1];
|
char type = array.Desc.Name[1];
|
||||||
int size = ReadInt32();
|
int size = ReadInt32();
|
||||||
|
|
||||||
if (type == 'B') {
|
if (type == 'B') {
|
||||||
array.Values = ReadBytes(size);
|
array.Values = ReadBytes(size);
|
||||||
} else {
|
} else {
|
||||||
object[] values = new object[size];
|
object[] values = new object[size];
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++) {
|
||||||
{
|
|
||||||
values[i] = Value(type);
|
values[i] = Value(type);
|
||||||
}
|
}
|
||||||
array.Values = values;
|
array.Values = values;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
JClassDesc NewClassDesc() {
|
JClassDesc NewClassDesc() {
|
||||||
JClassDesc desc = new JClassDesc();
|
JClassDesc desc = new JClassDesc();
|
||||||
desc.Name = ReadUtf8();
|
desc.Name = ReadUtf8();
|
||||||
ReadInt64(); // serial UID
|
ReadInt64(); // serial UID
|
||||||
handles.Add(desc);
|
handles.Add(desc);
|
||||||
|
|
||||||
// read class desc info
|
// read class desc info
|
||||||
desc.Flags = ReadUInt8();
|
desc.Flags = ReadUInt8();
|
||||||
desc.Fields = new JFieldDesc[ReadUInt16()];
|
desc.Fields = new JFieldDesc[ReadUInt16()];
|
||||||
for (int i = 0; i < desc.Fields.Length; i++)
|
for (int i = 0; i < desc.Fields.Length; i++) {
|
||||||
{
|
|
||||||
desc.Fields[i] = FieldDesc();
|
desc.Fields[i] = FieldDesc();
|
||||||
}
|
}
|
||||||
|
|
||||||
SkipAnnotation();
|
SkipAnnotation();
|
||||||
desc.SuperClass = ClassDesc();
|
desc.SuperClass = ClassDesc();
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JEnum NewEnum() { // TC_ENUM classDesc newHandle enumConstantName
|
||||||
|
JEnum je = new JEnum();
|
||||||
|
je.Desc = ClassDesc(); // classDesc
|
||||||
|
handles.Add(je); // newHandle
|
||||||
|
je.Name = ReadObject(); // enumConstantName
|
||||||
|
return je;
|
||||||
|
}
|
||||||
|
|
||||||
|
JClass NewClass() { // TC_CLASS classDesc newHandle
|
||||||
|
JClass jc = new JClass();
|
||||||
|
jc.Desc = ClassDesc(); // classDesc
|
||||||
|
handles.Add(jc); // newHandle
|
||||||
|
return jc;
|
||||||
|
}
|
||||||
|
|
||||||
JClassDesc ClassDesc() {
|
JClassDesc ClassDesc() {
|
||||||
byte typeCode = ReadUInt8();
|
byte typeCode = ReadUInt8();
|
||||||
if (typeCode == TC_CLASSDESC) return NewClassDesc();
|
if (typeCode == TC_CLASSDESC) return NewClassDesc();
|
||||||
if (typeCode == TC_NULL) return null;
|
if (typeCode == TC_NULL) return null;
|
||||||
if (typeCode == TC_REFERENCE) return (JClassDesc)PrevObject();
|
if (typeCode == TC_REFERENCE) return (JClassDesc)PrevObject();
|
||||||
|
|
||||||
throw new InvalidDataException("Invalid type code: " + typeCode);
|
throw new InvalidDataException("Invalid type code: " + typeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
JClassData ClassData(JClassDesc desc) {
|
JClassData ClassData(JClassDesc desc) {
|
||||||
if ((desc.Flags & SC_SERIALIZABLE) == 0) {
|
if ((desc.Flags & SC_SERIALIZABLE) == 0) {
|
||||||
throw new InvalidDataException("Invalid class data flags: " + desc.Flags);
|
throw new InvalidDataException("Invalid class data flags: " + desc.Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
JClassData data = new JClassData();
|
JClassData data = new JClassData();
|
||||||
data.Values = new object[desc.Fields.Length];
|
data.Values = new object[desc.Fields.Length];
|
||||||
for (int i = 0; i < data.Values.Length; i++) {
|
for (int i = 0; i < data.Values.Length; i++) {
|
||||||
data.Values[i] = Value(desc.Fields[i].Type);
|
data.Values[i] = Value(desc.Fields[i].Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((desc.Flags & SC_WRITE_METHOD) != 0) {
|
if ((desc.Flags & SC_WRITE_METHOD) != 0) {
|
||||||
SkipAnnotation();
|
SkipAnnotation();
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe object Value(char type) {
|
unsafe object Value(char type) {
|
||||||
if (type == 'B') return ReadUInt8();
|
if (type == 'B') return ReadUInt8();
|
||||||
if (type == 'C') return (char)ReadUInt16();
|
if (type == 'C') return (char)ReadUInt16();
|
||||||
@ -213,15 +233,15 @@ namespace MCGalaxy.Levels.IO
|
|||||||
if (type == 'Z') return ReadUInt8() != 0;
|
if (type == 'Z') return ReadUInt8() != 0;
|
||||||
if (type == 'L') return ReadObject();
|
if (type == 'L') return ReadObject();
|
||||||
if (type == '[') return ReadObject();
|
if (type == '[') return ReadObject();
|
||||||
|
|
||||||
throw new InvalidDataException("Invalid value code: " + type);
|
throw new InvalidDataException("Invalid value code: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
JFieldDesc FieldDesc() {
|
JFieldDesc FieldDesc() {
|
||||||
JFieldDesc desc = new JFieldDesc();
|
JFieldDesc desc = new JFieldDesc();
|
||||||
byte type = ReadUInt8();
|
byte type = ReadUInt8();
|
||||||
desc.Type = (char)type;
|
desc.Type = (char)type;
|
||||||
|
|
||||||
if (type == 'B' || type == 'C' || type == 'D' || type == 'F' || type == 'I' || type == 'J' || type == 'S' || type == 'Z') {
|
if (type == 'B' || type == 'C' || type == 'D' || type == 'F' || type == 'I' || type == 'J' || type == 'S' || type == 'Z') {
|
||||||
desc.Name = ReadUtf8();
|
desc.Name = ReadUtf8();
|
||||||
} else if (type == '[' || type == 'L') {
|
} else if (type == '[' || type == 'L') {
|
||||||
@ -232,11 +252,10 @@ namespace MCGalaxy.Levels.IO
|
|||||||
}
|
}
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkipAnnotation() {
|
void SkipAnnotation() {
|
||||||
byte typeCode;
|
byte typeCode;
|
||||||
while ((typeCode = ReadUInt8()) != TC_ENDBLOCKDATA)
|
while ((typeCode = ReadUInt8()) != TC_ENDBLOCKDATA) {
|
||||||
{
|
|
||||||
if (typeCode == TC_BLOCKDATA) {
|
if (typeCode == TC_BLOCKDATA) {
|
||||||
ReadBytes(ReadUInt8());
|
ReadBytes(ReadUInt8());
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user