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:
123DontMessWitMe 2025-07-13 07:34:58 -04:00
parent 4d838ecb6b
commit 86af8c8f27

View File

@ -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 {