diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a516d1e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+################################################################################
+# Diese .gitignore-Datei wurde von Microsoft(R) Visual Studio automatisch erstellt.
+################################################################################
+
+/JavaDecompiler/bin
+/JavaDecompiler/obj
+/MinecraftTabPatcher/bin
+/MinecraftTabPatcher/obj
+/.vs
+*.user
diff --git a/JavaDecompiler/Exceptions/JavaClassMagicNumberException.cs b/JavaDecompiler/Exceptions/JavaClassMagicNumberException.cs
new file mode 100644
index 0000000..b95af88
--- /dev/null
+++ b/JavaDecompiler/Exceptions/JavaClassMagicNumberException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace JavaDecompiler.Exceptions
+{
+ ///
+ /// DS 2019-08-09: The exception if the given class file has an invalid magic number.
+ ///
+ public class JavaClassMagicNumberException : Exception
+ {
+ }
+}
diff --git a/JavaDecompiler/JavaAccessFlag.cs b/JavaDecompiler/JavaAccessFlag.cs
new file mode 100644
index 0000000..4242707
--- /dev/null
+++ b/JavaDecompiler/JavaAccessFlag.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The access flag for classes and methods
+ ///
+ [Flags]
+ public enum JavaAccessFlag : ushort
+ {
+ Public = 0x0001,
+ Final = 0x0010,
+ Super = 0x0020,
+ Interface = 0x0200,
+ Abstract = 0x0400,
+ Synthetic = 0x1000,
+ Annotation = 0x2000,
+ Enum = 0x4000,
+ }
+}
diff --git a/JavaDecompiler/JavaAttributeInfo.cs b/JavaDecompiler/JavaAttributeInfo.cs
new file mode 100644
index 0000000..af8f455
--- /dev/null
+++ b/JavaDecompiler/JavaAttributeInfo.cs
@@ -0,0 +1,270 @@
+using System.IO;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The attribute info for method, fields and classes
+ ///
+ public abstract class JavaAttributeInfo
+ {
+ ///
+ /// Gets and sets the index of the name
+ ///
+ public ushort NameIndex { get; set; }
+
+ ///
+ /// Gets the data length
+ ///
+ protected uint Length { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Recalculates the length.
+ /// This will be executed when the class file is saved.
+ ///
+ ///
+ public abstract uint CalculateLength();
+
+ ///
+ /// Reads the attribute info
+ ///
+ ///
+ ///
+ public abstract void Read(BinaryReader reader, JavaClass javaClass);
+
+ ///
+ /// Writes the attribute info data
+ ///
+ ///
+ protected abstract void WriteData(BinaryWriter writer);
+
+ ///
+ /// Writes the attribute info
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ // Updates the length
+ Length = CalculateLength();
+
+ writer.Write(NameIndex);
+ writer.Write(Length);
+
+ // Writes the data
+ WriteData(writer);
+ }
+
+ ///
+ /// Creates and read the attribute info
+ ///
+ ///
+ ///
+ ///
+ public static JavaAttributeInfo CreateAndRead(BinaryReader reader, JavaClass javaClass)
+ {
+ var nameIndex = reader.ReadUInt16();
+ var length = reader.ReadUInt32();
+
+ // Gets the type
+ var type = javaClass.GetConstantUtf8(nameIndex);
+ var attribute = Create(type);
+ attribute.NameIndex = nameIndex;
+ attribute.Length = length;
+ attribute.Read(reader, javaClass);
+ return attribute;
+ }
+
+ ///
+ /// Creates a new attribute by the type
+ ///
+ ///
+ ///
+ private static JavaAttributeInfo Create(string type)
+ {
+ switch (type)
+ {
+ case "Code":
+ return new JavaAttributeCodeInfo();
+ default:
+ return new JavaAttributeUnknwonInfo();
+ }
+ }
+
+ #endregion Read & write
+ }
+
+ #region Child classes
+
+ // TODO: Add all the other classes if needed
+
+ #region Code
+
+ ///
+ /// DS 2019-08-09: The attribute info for the code
+ ///
+ public class JavaAttributeCodeInfo : JavaAttributeInfo
+ {
+ ///
+ /// Gets and sets the max stack size
+ ///
+ public ushort MaxStack { get; set; }
+
+ ///
+ /// Gets and sets the max size for local variables
+ ///
+ public ushort MaxLocals { get; set; }
+
+ ///
+ /// Gets and sets the java byte code
+ ///
+ public byte[] Code { get; set; }
+
+ ///
+ /// Gets and sets the exceptions
+ ///
+ public JavaExceptionTable[] Exceptions { get; set; }
+
+ ///
+ /// Gets and sets the attributes
+ ///
+ public JavaAttributeInfo[] Attributes { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Recalculates the length of the data
+ ///
+ ///
+ public override uint CalculateLength()
+ {
+ uint size = sizeof(ushort) * 2; // MaxStack and MaxLocals
+ size += sizeof(uint) + (uint)Code.Length; // Code
+ size += sizeof(ushort) + sizeof(ushort) * 4 * (uint)Exceptions.Length; // Exception table
+
+ // Attributes
+ size += sizeof(ushort);
+ var attributes = Attributes.Length;
+ for (int i = 0; i < attributes; i++)
+ {
+ size += sizeof(ushort) + sizeof(uint) + Attributes[i].CalculateLength();
+ }
+
+ return size;
+ }
+
+ ///
+ /// Reads the attribute info
+ ///
+ ///
+ ///
+ public override void Read(BinaryReader reader, JavaClass javaClass)
+ {
+ MaxStack = reader.ReadUInt16();
+ MaxLocals = reader.ReadUInt16();
+
+ // Reads the code
+ var len = reader.ReadUInt32();
+ Code = reader.ReadBytes((int)len);
+
+ // Reads the exception
+ var exceptions = reader.ReadUInt16();
+ Exceptions = new JavaExceptionTable[exceptions];
+ for (int i = 0; i < exceptions; i++)
+ {
+ Exceptions[i] = JavaExceptionTable.CreateAndRead(reader, javaClass);
+ }
+
+ // Reads the attributes
+ var attributes = reader.ReadUInt16();
+ Attributes = new JavaAttributeInfo[attributes];
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i] = JavaAttributeInfo.CreateAndRead(reader, javaClass);
+ }
+ }
+
+ ///
+ /// Writes the attribute info data
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(MaxStack);
+ writer.Write(MaxLocals);
+
+ // Writes the code
+ writer.Write((uint)Code.Length);
+ writer.Write(Code);
+
+ // Writes the exceptions
+ var exceptions = (ushort)Exceptions.Length;
+ writer.Write(exceptions);
+ for (int i = 0; i < exceptions; i++)
+ {
+ Exceptions[i].Write(writer);
+ }
+
+ // Writes the attributes
+ var attributes = (ushort)Attributes.Length;
+ writer.Write(attributes);
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i].Write(writer);
+ }
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Code
+
+ #region Unknwon
+
+ ///
+ /// DS 2019-08-09: The attribute info for unknown types
+ ///
+ public class JavaAttributeUnknwonInfo : JavaAttributeInfo
+ {
+ ///
+ /// Gets and sets the unknown data
+ ///
+ public byte[] Data { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Recalculates the length of the data
+ ///
+ ///
+ public override uint CalculateLength()
+ {
+ return (uint)Data.Length;
+ }
+
+ ///
+ /// Reads the attribute info
+ ///
+ ///
+ ///
+ public override void Read(BinaryReader reader, JavaClass javaClass)
+ {
+ Data = reader.ReadBytes((int)Length);
+ }
+
+ ///
+ /// Writes the attribute info data
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(Data);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Unknown
+
+ #endregion Child classes
+}
diff --git a/JavaDecompiler/JavaClass.cs b/JavaDecompiler/JavaClass.cs
new file mode 100644
index 0000000..6ce4275
--- /dev/null
+++ b/JavaDecompiler/JavaClass.cs
@@ -0,0 +1,356 @@
+using JavaDecompiler.Exceptions;
+using JavaDecompiler.Utils;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The parser for the java class files.
+ /// The format is described here: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
+ ///
+ public class JavaClass
+ {
+ #region Const
+
+ ///
+ /// The magic number for all java classes
+ ///
+ public const uint MagicNumber = 0xCAFEBABE;
+
+ #endregion Const
+
+ ///
+ /// The minor version of java
+ ///
+ public ushort MinorVersion { get; set; }
+
+ ///
+ /// The major version of java
+ ///
+ public ushort MajorVersion { get; set; }
+
+ ///
+ /// The constant pool
+ ///
+ public JavaConstantInfo[] ConstantPool { get; set; }
+
+ ///
+ /// The access flag for this class
+ ///
+ public JavaAccessFlag AccessFlag { get; set; }
+
+ ///
+ /// The constant index for the current class.
+ ///
+ public ushort ThisClass { get; set; }
+
+ ///
+ /// The constant index for the super class.
+ /// Zero means the super class is java.object.
+ ///
+ public ushort SuperClass { get; set; }
+
+ ///
+ /// The constant indices for all interfaces.
+ ///
+ public ushort[] Interfaces { get; set; }
+
+ ///
+ /// The field info for this class
+ ///
+ public JavaFieldInfo[] Fields { get; set; }
+
+ ///
+ /// The method info for this class
+ ///
+ public JavaMethodInfo[] Methods { get; set; }
+
+ ///
+ /// The attributes for this class
+ ///
+ public JavaAttributeInfo[] Attributes { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the class file from the given file path
+ ///
+ ///
+ public void Read(string path)
+ {
+ using (var stream = new FileStream(path, FileMode.Open))
+ {
+ Read(stream);
+ }
+ }
+
+ ///
+ /// Reads the class file by the given stream.
+ ///
+ ///
+ public void Read(Stream stream)
+ {
+ Read(new BinaryReaderBigEndian(stream));
+ }
+
+ ///
+ /// Reads the class file by the given binary reader.
+ /// Remember: This must be an big endian reader!
+ ///
+ ///
+ public void Read(BinaryReader reader)
+ {
+ // Reads the magic number
+ var magic = reader.ReadUInt32();
+ if (magic != MagicNumber)
+ {
+ throw new JavaClassMagicNumberException();
+ }
+ MinorVersion = reader.ReadUInt16();
+ MajorVersion = reader.ReadUInt16();
+
+ // Reads the constant pool.
+ // The first constnat (0) is always zero.
+ // The first valid index is 1.
+ var constants = reader.ReadUInt16();
+ ConstantPool = new JavaConstantInfo[constants];
+ for (int i = 1; i < constants; i++)
+ {
+ var constant = JavaConstantInfo.CreateAndRead(reader);
+ ConstantPool[i] = constant;
+
+ // There is an odd special case for double and long.
+ // For some reasons you have to add an empty index
+ // if you read an double or long constant.
+ if (constant.Type == ConstantInfoType.Double ||
+ constant.Type == ConstantInfoType.Long)
+ {
+ i++;
+ }
+ }
+
+ AccessFlag = (JavaAccessFlag)reader.ReadUInt16();
+ ThisClass = reader.ReadUInt16();
+ SuperClass = reader.ReadUInt16();
+
+ // Reads the interfaces
+ var interfaces = reader.ReadUInt16();
+ Interfaces = new ushort[interfaces];
+ for (int i = 0; i < interfaces; i++)
+ {
+ Interfaces[i] = reader.ReadUInt16();
+ }
+
+ // Reads the fields
+ var fields = reader.ReadUInt16();
+ Fields = new JavaFieldInfo[fields];
+ for (int i = 0; i < fields; i++)
+ {
+ Fields[i] = JavaFieldInfo.CreateAndRead(reader, this);
+ }
+
+ // Reads the methods
+ var methods = reader.ReadUInt16();
+ Methods = new JavaMethodInfo[methods];
+ for (int i = 0; i < methods; i++)
+ {
+ Methods[i] = JavaMethodInfo.CreateAndRead(reader, this);
+ }
+
+ // Reads the attributes
+ var attributes = reader.ReadUInt16();
+ Attributes = new JavaAttributeInfo[attributes];
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i] = JavaAttributeInfo.CreateAndRead(reader, this);
+ }
+ }
+
+ ///
+ /// Writes the class file to the given file path
+ ///
+ ///
+ public void Write(string path)
+ {
+ using (var stream = new FileStream(path, FileMode.Create))
+ {
+ Write(stream);
+ }
+ }
+
+ ///
+ /// Writes the class file to the given stream.
+ ///
+ ///
+ public void Write(Stream stream)
+ {
+ using (var writer = new BinaryWriterBigEndian(stream, true))
+ {
+ Write(writer);
+ }
+ }
+
+ ///
+ /// Writes the class file to the given binary writer.
+ /// Remember: This must be an big endian writer!
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ writer.Write(MagicNumber);
+ writer.Write(MinorVersion);
+ writer.Write(MajorVersion);
+
+ // Writes the constant pool
+ var constants = (ushort)ConstantPool.Length;
+ writer.Write(constants);
+ for (int i = 0; i < constants; i++)
+ {
+ var constant = ConstantPool[i];
+ if (constant != null)
+ {
+ constant.Write(writer);
+ }
+ }
+
+ writer.Write((ushort)AccessFlag);
+ writer.Write(ThisClass);
+ writer.Write(SuperClass);
+
+ // Writes the interfaces
+ var interfaces = (ushort)Interfaces.Length;
+ writer.Write(interfaces);
+ for (int i = 0; i < interfaces; i++)
+ {
+ writer.Write(Interfaces[i]);
+ }
+
+ // Writes the fields
+ var fields = (ushort)Fields.Length;
+ writer.Write(fields);
+ for (int i = 0; i < fields; i++)
+ {
+ Fields[i].Write(writer);
+ }
+
+ // Writes the methods
+ var methods = (ushort)Methods.Length;
+ writer.Write(methods);
+ for (int i = 0; i < methods; i++)
+ {
+ Methods[i].Write(writer);
+ }
+
+ // Writes the attributes
+ var attributes = (ushort)Attributes.Length;
+ writer.Write(attributes);
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i].Write(writer);
+ }
+ }
+
+ #endregion Read & write
+
+ #region Method
+
+ ///
+ /// Returns a method with the given name
+ ///
+ ///
+ ///
+ public JavaMethodInfo GetMethod(string name)
+ {
+ var nameIndex = GetConstantUtf8Index(name);
+
+ return Methods.FirstOrDefault(m => m.NameIndex == nameIndex);
+ }
+
+ ///
+ /// Returns all methods with the given name
+ ///
+ ///
+ ///
+ public IEnumerable GetMethods(string name)
+ {
+ var nameIndex = GetConstantUtf8Index(name);
+
+ return Methods.Where(m => m.NameIndex == nameIndex);
+ }
+
+ ///
+ /// Returns a method with the given name and descriptor
+ ///
+ ///
+ ///
+ public JavaMethodInfo GetMethod(string name, string descriptor)
+ {
+ var nameIndex = GetConstantUtf8Index(name);
+ var descriptorIndex = GetConstantUtf8Index(descriptor);
+
+ return Methods.FirstOrDefault(m => m.NameIndex == nameIndex && m.DescriptorIndex == descriptorIndex);
+ }
+
+
+ #endregion Method
+
+ #region Constants
+
+ ///
+ /// Returns the constant with the given index
+ ///
+ ///
+ ///
+ public JavaConstantInfo GetConstant(int index)
+ {
+ return ConstantPool[index];
+ }
+
+ ///
+ /// Returns the constant with the given index and the given type
+ ///
+ ///
+ ///
+ ///
+ public T GetConstant(int index) where T : JavaConstantInfo
+ {
+ return ConstantPool[index] as T;
+ }
+
+ ///
+ /// Returns the utf8 text at the given index
+ ///
+ ///
+ ///
+ public string GetConstantUtf8(int index)
+ {
+ return GetConstant(index).Value;
+ }
+
+ ///
+ /// Returns the index from the constant pool of the given utf8
+ ///
+ ///
+ ///
+ public ushort GetConstantUtf8Index(string value)
+ {
+ var len = ConstantPool.Length;
+ for (ushort i = 0; i < len; i++)
+ {
+ var constant = ConstantPool[i];
+ if (constant is JavaConstantUtf8Info utf8)
+ {
+ if (utf8.Value == value)
+ {
+ return i;
+ }
+ }
+ }
+ return 0;
+ }
+
+ #endregion Constants
+ }
+}
diff --git a/JavaDecompiler/JavaConstantInfo.cs b/JavaDecompiler/JavaConstantInfo.cs
new file mode 100644
index 0000000..e1809fb
--- /dev/null
+++ b/JavaDecompiler/JavaConstantInfo.cs
@@ -0,0 +1,697 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The type of the constant info
+ ///
+ public enum ConstantInfoType : byte
+ {
+ Class = 7,
+ FieldRef = 9,
+ MethodRef = 10,
+ InterfaceMethodRef = 11,
+ String = 8,
+ Integer = 3,
+ Float = 4,
+ Long = 5,
+ Double = 6,
+ NameAndType = 12,
+ Utf8 = 1,
+ MethodHandle = 15,
+ MethodType = 16,
+ InvokeDynamic = 18,
+ }
+
+ ///
+ /// DS 2019-08-09: The constant info parent class
+ ///
+ public abstract class JavaConstantInfo
+ {
+ ///
+ /// Gets the constant type
+ ///
+ public abstract ConstantInfoType Type { get; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public abstract void Read(BinaryReader reader);
+
+ ///
+ /// Writes the constant info data
+ ///
+ ///
+ protected abstract void WriteData(BinaryWriter writer);
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ writer.Write((byte)Type);
+ WriteData(writer);
+ }
+
+ ///
+ /// Creates and reads a java constant info
+ ///
+ ///
+ ///
+ public static JavaConstantInfo CreateAndRead(BinaryReader reader)
+ {
+ // Reads the type
+ var type = (ConstantInfoType)reader.ReadByte();
+ var constant = Create(type);
+ constant.Read(reader);
+ return constant;
+ }
+
+ ///
+ /// Creates a java constant info by its type
+ ///
+ ///
+ ///
+ private static JavaConstantInfo Create(ConstantInfoType type)
+ {
+ switch (type)
+ {
+ case ConstantInfoType.Class:
+ return new JavaConstantClassInfo();
+ case ConstantInfoType.String:
+ return new JavaConstantStringInfo();
+ case ConstantInfoType.NameAndType:
+ return new JavaConstantNameAndTypeInfo();
+ case ConstantInfoType.Utf8:
+ return new JavaConstantUtf8Info();
+ case ConstantInfoType.FieldRef:
+ return new JavaConstantFieldRefInfo();
+ case ConstantInfoType.MethodRef:
+ return new JavaConstantMethodRefInfo();
+ case ConstantInfoType.InterfaceMethodRef:
+ return new JavaConstantInterfaceMethodRefInfo();
+ case ConstantInfoType.MethodHandle:
+ return new JavaConstantMethodHandleInfo();
+ case ConstantInfoType.MethodType:
+ return new JavaConstantMethodTypeInfo();
+ case ConstantInfoType.InvokeDynamic:
+ return new JavaConstantInvokeDynamicInfo();
+ case ConstantInfoType.Integer:
+ return new JavaConstantIntegerInfo();
+ case ConstantInfoType.Long:
+ return new JavaConstantLongInfo();
+ case ConstantInfoType.Float:
+ return new JavaConstantFloatInfo();
+ case ConstantInfoType.Double:
+ return new JavaConstantDoubleInfo();
+ default:
+ throw new ArgumentException();
+ }
+ }
+
+ #endregion Read & write
+ }
+
+ #region Child classes
+
+ #region Class
+
+ ///
+ /// DS 2019-08-09: The constant info for classes
+ ///
+ public class JavaConstantClassInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Class;
+
+ ///
+ /// Gets and sets the index of the name
+ ///
+ public ushort NameIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ NameIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(NameIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Class
+
+ #region String
+
+ ///
+ /// DS 2019-08-09: The constant info for strings
+ ///
+ public class JavaConstantStringInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.String;
+
+ ///
+ /// Gets and sets the index of the string
+ ///
+ public ushort StringIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ StringIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(StringIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion String
+
+ #region Name and type
+
+ ///
+ /// DS 2019-08-09: The constant info for name and types
+ ///
+ public class JavaConstantNameAndTypeInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.NameAndType;
+
+ ///
+ /// Gets and sets the index of the name
+ ///
+ public ushort NameIndex { get; set; }
+
+ ///
+ /// Gets and sets the index of the descriptor
+ ///
+ public ushort DescriptorIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ NameIndex = reader.ReadUInt16();
+ DescriptorIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(NameIndex);
+ writer.Write(DescriptorIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Name and type
+
+ #region Utf8
+
+ ///
+ /// DS 2019-08-09: The constant info for utf8 texts
+ ///
+ public class JavaConstantUtf8Info : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Utf8;
+
+ ///
+ /// Gets the text value.
+ /// TODO: Write a setter. Java uses a wierd big endian version of utf8 when dealing with mulibytes.
+ /// I will ignore this for now. We can only read single byte chars. Any other char will be gibberish
+ /// and can not be converted back!
+ /// You are not able to modify the string directly but you can modify the bytes.
+ ///
+ public string Value { get; private set; }
+
+ ///
+ /// Gets and sets the bytes of the utf8 string
+ ///
+ public byte[] Bytes { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ var len = reader.ReadUInt16();
+ Bytes = reader.ReadBytes(len);
+ Value = Encoding.UTF8.GetString(Bytes);
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ // TODO: Write a custom encoding for java utf8 and convert the string back to bytes.
+ writer.Write((ushort)Bytes.Length);
+ writer.Write(Bytes);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Utf8
+
+ #region Ref
+
+ ///
+ /// DS 2019-08-09: The constant info for references
+ ///
+ public abstract class JavaConstantRefInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets and sets the index of the class
+ ///
+ public ushort ClassIndex { get; set; }
+
+ ///
+ /// Gets and sets the index of the name and type
+ ///
+ public ushort NameAndTypeIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ ClassIndex = reader.ReadUInt16();
+ NameAndTypeIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(ClassIndex);
+ writer.Write(NameAndTypeIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ ///
+ /// DS 2019-08-09: The constant info for field references
+ ///
+ public class JavaConstantFieldRefInfo : JavaConstantRefInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.FieldRef;
+ }
+
+ ///
+ /// DS 2019-08-09: The constant info for method references
+ ///
+ public class JavaConstantMethodRefInfo : JavaConstantRefInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.MethodRef;
+ }
+
+ ///
+ /// DS 2019-08-09: The constant info for interface method references
+ ///
+ public class JavaConstantInterfaceMethodRefInfo : JavaConstantRefInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.InterfaceMethodRef;
+ }
+
+ #endregion Ref
+
+ #region Method handler
+
+ ///
+ /// DS 2019-08-09: The constant info for method handles
+ ///
+ public class JavaConstantMethodHandleInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.MethodHandle;
+
+ ///
+ /// Gets and sets the type of the reference
+ ///
+ public JavaMethodHandlerType ReferenceType { get; set; }
+
+ ///
+ /// Gets and sets the index of the reference
+ ///
+ public ushort ReferenceIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ ReferenceType = (JavaMethodHandlerType)reader.ReadByte();
+ ReferenceIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write((byte)ReferenceType);
+ writer.Write(ReferenceIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Method handler
+
+ #region Method type
+
+ ///
+ /// DS 2019-08-09: The constant info for method types
+ ///
+ public class JavaConstantMethodTypeInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.MethodType;
+
+ ///
+ /// Gets and sets the index of the descriptor
+ ///
+ public ushort DescriptorIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ DescriptorIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(DescriptorIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Method type
+
+ #region Invoke dynamic
+
+ ///
+ /// DS 2019-08-09: The constant info for invoke dynamics
+ ///
+ public class JavaConstantInvokeDynamicInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.InvokeDynamic;
+
+ ///
+ /// Gets and sets the index of the method attribute index
+ ///
+ public ushort BootstrapMethodAttrIndex { get; set; }
+
+ ///
+ /// Gets and sets the index for the name and type
+ ///
+ public ushort NameAndTypeIndex { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ BootstrapMethodAttrIndex = reader.ReadUInt16();
+ NameAndTypeIndex = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(BootstrapMethodAttrIndex);
+ writer.Write(NameAndTypeIndex);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Invoke dynamic
+
+ #region Integer
+
+ ///
+ /// DS 2019-08-09: The constant info for integers
+ ///
+ public class JavaConstantIntegerInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Integer;
+
+ ///
+ /// Gets and sets the value
+ ///
+ public int Value { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ Value = reader.ReadInt32();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(Value);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Integer
+
+ #region Long
+
+ ///
+ /// DS 2019-08-09: The constant info for longs
+ ///
+ public class JavaConstantLongInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Long;
+
+ ///
+ /// Gets and sets the value
+ ///
+ public long Value { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ Value = reader.ReadInt64();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(Value);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Long
+
+ #region Float
+
+ ///
+ /// DS 2019-08-09: The constant info for floats
+ ///
+ public class JavaConstantFloatInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Float;
+
+ ///
+ /// Gets and sets the value
+ ///
+ public float Value { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ Value = reader.ReadSingle();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(Value);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Float
+
+ #region Double
+
+ ///
+ /// DS 2019-08-09: The constant info for doubles
+ ///
+ public class JavaConstantDoubleInfo : JavaConstantInfo
+ {
+ ///
+ /// Gets the constant info type
+ ///
+ public override ConstantInfoType Type => ConstantInfoType.Double;
+
+ ///
+ /// Gets and sets the value
+ ///
+ public double Value { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the constant info
+ ///
+ ///
+ public override void Read(BinaryReader reader)
+ {
+ Value = reader.ReadDouble();
+ }
+
+ ///
+ /// Writes the constant info
+ ///
+ ///
+ protected override void WriteData(BinaryWriter writer)
+ {
+ writer.Write(Value);
+ }
+
+ #endregion Read & write
+ }
+
+ #endregion Double
+
+ #endregion Chils classes
+}
diff --git a/JavaDecompiler/JavaDecompiler.csproj b/JavaDecompiler/JavaDecompiler.csproj
new file mode 100644
index 0000000..5c44a62
--- /dev/null
+++ b/JavaDecompiler/JavaDecompiler.csproj
@@ -0,0 +1,11 @@
+
+
+
+ netcoreapp2.1
+
+
+
+
+
+
+
diff --git a/JavaDecompiler/JavaExceptionTable.cs b/JavaDecompiler/JavaExceptionTable.cs
new file mode 100644
index 0000000..2e232bf
--- /dev/null
+++ b/JavaDecompiler/JavaExceptionTable.cs
@@ -0,0 +1,72 @@
+using System.IO;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The java exception table for the code attributes
+ ///
+ public class JavaExceptionTable
+ {
+ ///
+ /// Gets and sets the start position of the exception handler
+ ///
+ public ushort StartPC { get; set; }
+
+ ///
+ /// Gets and sets the end position of the exception handler
+ ///
+ public ushort EndPC { get; set; }
+
+ ///
+ /// Gets and sets the handler
+ ///
+ public ushort HandlerPC { get; set; }
+
+ ///
+ /// Gets and sets the catch type
+ ///
+ public ushort CatchType { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the exception table
+ ///
+ ///
+ ///
+ public void Read(BinaryReader reader, JavaClass javaClass)
+ {
+ StartPC = reader.ReadUInt16();
+ EndPC = reader.ReadUInt16();
+ HandlerPC = reader.ReadUInt16();
+ CatchType = reader.ReadUInt16();
+ }
+
+ ///
+ /// Writes the exception table
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ writer.Write(StartPC);
+ writer.Write(EndPC);
+ writer.Write(HandlerPC);
+ writer.Write(CatchType);
+ }
+
+ ///
+ /// Creates and reads the exception table
+ ///
+ ///
+ ///
+ ///
+ public static JavaExceptionTable CreateAndRead(BinaryReader reader, JavaClass javaClass)
+ {
+ var exceptionTable = new JavaExceptionTable();
+ exceptionTable.Read(reader, javaClass);
+ return exceptionTable;
+ }
+
+ #endregion Read & write
+ }
+}
diff --git a/JavaDecompiler/JavaFieldInfo.cs b/JavaDecompiler/JavaFieldInfo.cs
new file mode 100644
index 0000000..40a3118
--- /dev/null
+++ b/JavaDecompiler/JavaFieldInfo.cs
@@ -0,0 +1,87 @@
+using System.IO;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The field info of a java class
+ ///
+ public class JavaFieldInfo
+ {
+ ///
+ /// Gets and sets the access flag
+ ///
+ public JavaAccessFlag AccessFlag { get; set; }
+
+ ///
+ /// Gets and sets the index of the name
+ ///
+ public ushort NameIndex { get; set; }
+
+ ///
+ /// Gets and sets the index of the descriptor
+ ///
+ public ushort DescriptorIndex { get; set; }
+
+ ///
+ /// Gets and sets the attributes
+ ///
+ public JavaAttributeInfo[] Attributes { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the field info
+ ///
+ ///
+ ///
+ public void Read(BinaryReader reader, JavaClass javaClass)
+ {
+ AccessFlag = (JavaAccessFlag)reader.ReadUInt16();
+ NameIndex = reader.ReadUInt16();
+ DescriptorIndex = reader.ReadUInt16();
+
+ // Reads the attributes
+ var attributes = reader.ReadUInt16();
+ Attributes = new JavaAttributeInfo[attributes];
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i] = JavaAttributeInfo.CreateAndRead(reader, javaClass);
+ }
+
+ }
+
+ ///
+ /// Writes the field info
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ writer.Write((ushort)AccessFlag);
+ writer.Write(NameIndex);
+ writer.Write(DescriptorIndex);
+
+ // Writes the attributes
+ var attributes = (ushort)Attributes.Length;
+ writer.Write(attributes);
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i].Write(writer);
+ }
+ }
+
+ ///
+ /// Creates and reads the field info
+ ///
+ ///
+ ///
+ ///
+ public static JavaFieldInfo CreateAndRead(BinaryReader reader, JavaClass javaClass)
+ {
+ var field = new JavaFieldInfo();
+ field.Read(reader, javaClass);
+ return field;
+ }
+
+ #endregion Read & write
+ }
+}
diff --git a/JavaDecompiler/JavaFile.cs b/JavaDecompiler/JavaFile.cs
new file mode 100644
index 0000000..19580a7
--- /dev/null
+++ b/JavaDecompiler/JavaFile.cs
@@ -0,0 +1,136 @@
+using ICSharpCode.SharpZipLib.Zip;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The primary java file (jar). This wile contains all ressources and class files.
+ /// A jar file is basically just a fancy zip file.
+ ///
+ public class JavaFile : IDisposable
+ {
+ ///
+ /// Creates the zip file from the given stream
+ ///
+ ///
+ public JavaFile(Stream stream)
+ {
+ m_Zip = new ZipFile(stream);
+ }
+
+ ///
+ /// Creates the zip file from the given stream
+ ///
+ ///
+ public JavaFile(string path) : this(new FileStream(path, FileMode.Open, FileAccess.Read))
+ {
+ }
+
+ #region Zip
+
+ ///
+ /// The zip file
+ ///
+ private ZipFile m_Zip;
+
+ ///
+ /// Gets the internal zip reader
+ ///
+ public ZipFile Zip
+ {
+ get { return m_Zip; }
+ }
+
+ ///
+ /// Returns the reader file stream for a file inside the jar file
+ ///
+ ///
+ ///
+ public Stream GetFileStream(string path)
+ {
+ var entry = m_Zip.GetEntry(path);
+ return GetFileStream(entry);
+ }
+
+ ///
+ /// Returns the reader file stream for a file inside the jar file
+ ///
+ ///
+ ///
+ public Stream GetFileStream(ZipEntry entry)
+ {
+ return m_Zip.GetInputStream(entry);
+ }
+
+ ///
+ /// Returns a list of all class files
+ ///
+ ///
+ public IEnumerable GetClassFiles()
+ {
+ var len = m_Zip.Count;
+ for (int i = 0; i < len; i++)
+ {
+ var entry = m_Zip[i];
+ if (Path.GetExtension(entry.Name) == ".class")
+ {
+ yield return entry.Name;
+ }
+ }
+ }
+
+ ///
+ /// Returns a class file by its name
+ ///
+ ///
+ ///
+ public JavaClass GetClass(string path)
+ {
+ using (var stream = GetFileStream(path))
+ {
+ var javaClass = new JavaClass();
+ javaClass.Read(stream);
+ return javaClass;
+ }
+ }
+
+ ///
+ /// Returns a class file from the zip entry
+ ///
+ ///
+ ///
+ public JavaClass GetClass(ZipEntry entry)
+ {
+ using (var stream = GetFileStream(entry))
+ {
+ var javaClass = new JavaClass();
+ javaClass.Read(stream);
+ return javaClass;
+ }
+ }
+
+ ///
+ /// Close the file
+ ///
+ public void Close()
+ {
+ m_Zip.Close();
+ }
+
+ #endregion Zip
+
+ #region IDisposable
+
+ ///
+ /// Dispose the file
+ ///
+ public void Dispose()
+ {
+ Close();
+ }
+
+ #endregion IDisposable
+ }
+}
diff --git a/JavaDecompiler/JavaMethodHandlerType.cs b/JavaDecompiler/JavaMethodHandlerType.cs
new file mode 100644
index 0000000..3c63790
--- /dev/null
+++ b/JavaDecompiler/JavaMethodHandlerType.cs
@@ -0,0 +1,18 @@
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The type of the method hander refernece
+ ///
+ public enum JavaMethodHandlerType : byte
+ {
+ GetField = 1,
+ GetStatic = 2,
+ PutField = 3,
+ PutStatic = 4,
+ InvokeVirtual = 5,
+ InvokeStatic = 6,
+ InvokeSpecial = 7,
+ NewInvokeSpecial = 8,
+ InvokeInterface = 9,
+ }
+}
diff --git a/JavaDecompiler/JavaMethodInfo.cs b/JavaDecompiler/JavaMethodInfo.cs
new file mode 100644
index 0000000..7e5eeaf
--- /dev/null
+++ b/JavaDecompiler/JavaMethodInfo.cs
@@ -0,0 +1,100 @@
+using System.IO;
+using System.Linq;
+
+namespace JavaDecompiler
+{
+ ///
+ /// DS 2019-08-09: The method info of a java class
+ ///
+ public class JavaMethodInfo
+ {
+ ///
+ /// Gets and sets the access flag
+ ///
+ public JavaAccessFlag AccessFlag { get; set; }
+
+ ///
+ /// Gets and sets the index of the name
+ ///
+ public ushort NameIndex { get; set; }
+
+ ///
+ /// Gets and sets the index of the descriptor
+ ///
+ public ushort DescriptorIndex { get; set; }
+
+ ///
+ /// Gets and sets the attributes
+ ///
+ public JavaAttributeInfo[] Attributes { get; set; }
+
+ #region Read & write
+
+ ///
+ /// Reads the method info
+ ///
+ ///
+ ///
+ public void Read(BinaryReader reader, JavaClass javaClass)
+ {
+ AccessFlag = (JavaAccessFlag)reader.ReadUInt16();
+ NameIndex = reader.ReadUInt16();
+ DescriptorIndex = reader.ReadUInt16();
+
+ // Reads the attributes
+ var attributes = reader.ReadUInt16();
+ Attributes = new JavaAttributeInfo[attributes];
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i] = JavaAttributeInfo.CreateAndRead(reader, javaClass);
+ }
+ }
+
+ ///
+ /// Writes the method info
+ ///
+ ///
+ public void Write(BinaryWriter writer)
+ {
+ writer.Write((ushort)AccessFlag);
+ writer.Write(NameIndex);
+ writer.Write(DescriptorIndex);
+
+ // Writes the attributes
+ var attributes = (ushort)Attributes.Length;
+ writer.Write(attributes);
+ for (int i = 0; i < attributes; i++)
+ {
+ Attributes[i].Write(writer);
+ }
+ }
+
+ ///
+ /// Creates and reads the method info
+ ///
+ ///
+ ///
+ ///
+ public static JavaMethodInfo CreateAndRead(BinaryReader reader, JavaClass javaClass)
+ {
+ var method = new JavaMethodInfo();
+ method.Read(reader, javaClass);
+ return method;
+ }
+
+ #endregion Read & write
+
+ #region Utils
+
+ ///
+ /// Returns the code attribute of this method
+ ///
+ ///
+ public JavaAttributeCodeInfo GetCodeAttribute()
+ {
+ return (JavaAttributeCodeInfo)Attributes.FirstOrDefault(a => a is JavaAttributeCodeInfo);
+ }
+
+ #endregion Utils
+ }
+}
diff --git a/JavaDecompiler/Utils/BinaryReaderBigEndian.cs b/JavaDecompiler/Utils/BinaryReaderBigEndian.cs
new file mode 100644
index 0000000..e4df687
--- /dev/null
+++ b/JavaDecompiler/Utils/BinaryReaderBigEndian.cs
@@ -0,0 +1,127 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace JavaDecompiler.Utils
+{
+ ///
+ /// DS 2019-08-09: A binary reader that reads files in big endian.
+ /// Not all method are supported yet!
+ ///
+ public class BinaryReaderBigEndian : BinaryReader
+ {
+ ///
+ /// Creates the binary reader from stream
+ ///
+ ///
+ public BinaryReaderBigEndian(Stream input) : base(input, Encoding.UTF8)
+ {
+ }
+
+ ///
+ /// Creates the binary reader from stream
+ ///
+ ///
+ ///
+ public BinaryReaderBigEndian(Stream input, bool leaveOpen) : base(input, Encoding.UTF8, leaveOpen)
+ {
+ }
+
+ ///
+ /// Reads the next float
+ ///
+ ///
+ public override float ReadSingle()
+ {
+ var data = base.ReadBytes(4);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToSingle(data, 0);
+ }
+
+ ///
+ /// Reads the next double
+ ///
+ ///
+ public override double ReadDouble()
+ {
+ var data = base.ReadBytes(8);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToDouble(data, 0);
+ }
+
+ ///
+ /// Reasd the next short
+ ///
+ ///
+ public override short ReadInt16()
+ {
+ var data = base.ReadBytes(2);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToInt16(data, 0);
+ }
+
+ ///
+ /// Reads the next integer
+ ///
+ ///
+ public override int ReadInt32()
+ {
+ var data = base.ReadBytes(4);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToInt32(data, 0);
+ }
+
+ ///
+ /// Reads the next long
+ ///
+ ///
+ public override long ReadInt64()
+ {
+ var data = base.ReadBytes(8);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToInt64(data, 0);
+ }
+
+ ///
+ /// Reads the next unsigned short
+ ///
+ ///
+ public override ushort ReadUInt16()
+ {
+ var data = base.ReadBytes(2);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToUInt16(data, 0);
+ }
+
+ ///
+ /// Reads the next unsigned integer
+ ///
+ ///
+ public override uint ReadUInt32()
+ {
+ var data = base.ReadBytes(4);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToUInt32(data, 0);
+ }
+
+ ///
+ /// Reads the next unsigned long
+ ///
+ ///
+ public override ulong ReadUInt64()
+ {
+ var data = base.ReadBytes(8);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ return BitConverter.ToUInt64(data, 0);
+ }
+
+ }
+}
diff --git a/JavaDecompiler/Utils/BinaryWriterBigEndian.cs b/JavaDecompiler/Utils/BinaryWriterBigEndian.cs
new file mode 100644
index 0000000..88355a1
--- /dev/null
+++ b/JavaDecompiler/Utils/BinaryWriterBigEndian.cs
@@ -0,0 +1,126 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace JavaDecompiler.Utils
+{
+ ///
+ /// DS 2019-08-09: A binary writer that writes files in big endian.
+ /// Not all method are supported yet!
+ ///
+ public class BinaryWriterBigEndian : BinaryWriter
+ {
+ ///
+ /// Creates the binary writer from stream
+ ///
+ ///
+ public BinaryWriterBigEndian(Stream output) : base(output, Encoding.UTF8)
+ {
+ }
+
+ ///
+ /// Creates the binary writer from stream with the given encoding
+ ///
+ ///
+ ///
+ public BinaryWriterBigEndian(Stream output, bool leaveOpen) : base(output, Encoding.UTF8, leaveOpen)
+ {
+ }
+
+ ///
+ /// Writes the float
+ ///
+ ///
+ public override void Write(float value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the double
+ ///
+ ///
+ public override void Write(double value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the short
+ ///
+ ///
+ public override void Write(short value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the integer
+ ///
+ ///
+ public override void Write(int value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the long
+ ///
+ ///
+ public override void Write(long value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the unsigned short
+ ///
+ ///
+ public override void Write(ushort value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the unsigned integer
+ ///
+ ///
+ public override void Write(uint value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+
+ ///
+ /// Writes the unsigned long
+ ///
+ ///
+ public override void Write(ulong value)
+ {
+ var data = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+ base.Write(data);
+ }
+ }
+}
diff --git a/MinecraftTabPatcher.sln b/MinecraftTabPatcher.sln
new file mode 100644
index 0000000..40788f1
--- /dev/null
+++ b/MinecraftTabPatcher.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29020.237
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinecraftTabPatcher", "MinecraftTabPatcher\MinecraftTabPatcher.csproj", "{FFE5EE18-FD80-4F48-BAC5-C63C11016AD1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JavaDecompiler", "JavaDecompiler\JavaDecompiler.csproj", "{5D26679D-D5C0-406E-B911-769BAAD73FD1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FFE5EE18-FD80-4F48-BAC5-C63C11016AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FFE5EE18-FD80-4F48-BAC5-C63C11016AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FFE5EE18-FD80-4F48-BAC5-C63C11016AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FFE5EE18-FD80-4F48-BAC5-C63C11016AD1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D26679D-D5C0-406E-B911-769BAAD73FD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D26679D-D5C0-406E-B911-769BAAD73FD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D26679D-D5C0-406E-B911-769BAAD73FD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D26679D-D5C0-406E-B911-769BAAD73FD1}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {46FD4B4F-1EA3-44CA-92D7-7BDFFCB445DC}
+ EndGlobalSection
+EndGlobal
diff --git a/MinecraftTabPatcher/MinecraftTabPatcher.csproj b/MinecraftTabPatcher/MinecraftTabPatcher.csproj
new file mode 100644
index 0000000..36b9bf4
--- /dev/null
+++ b/MinecraftTabPatcher/MinecraftTabPatcher.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ netcoreapp2.1
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MinecraftTabPatcher/MinecraftVersion.cs b/MinecraftTabPatcher/MinecraftVersion.cs
new file mode 100644
index 0000000..0a52f66
--- /dev/null
+++ b/MinecraftTabPatcher/MinecraftVersion.cs
@@ -0,0 +1,199 @@
+using JavaDecompiler;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace MinecraftTabPatcher
+{
+ ///
+ /// DS 2019-08-10: The class of the minecraft version.
+ /// A version is a jar file inside the minecraft directory.
+ /// Minecraft can have multiple versions each with multiple
+ /// profiles.
+ /// The versions are located at:
+ /// Windows: %appdata%/.minecraft/versions
+ ///
+ public class MinecraftVersion
+ {
+ ///
+ /// Gets the id of the version
+ ///
+ public string ID { get; private set; }
+
+ ///
+ /// Gets the path of the version
+ ///
+ public string Path { get; private set; }
+
+ ///
+ /// Gets the path of the jar file
+ ///
+ public string PathJar
+ {
+ get { return System.IO.Path.Combine(Path, ID + ".jar"); }
+ }
+
+ ///
+ /// Gets the path of the json file
+ ///
+ public string PathJson
+ {
+ get { return System.IO.Path.Combine(Path, ID + ".json"); }
+ }
+
+ ///
+ /// Gets if this version is valid.
+ /// This will check if the jar and json file exists.
+ ///
+ public bool IsValid
+ {
+ get { return File.Exists(PathJar) && File.Exists(PathJson); }
+ }
+
+ ///
+ /// Creates the miencraft version by the given path
+ ///
+ ///
+ public MinecraftVersion(string path)
+ {
+ Path = path;
+
+ // The id is the directory name
+ ID = System.IO.Path.GetFileName(path);
+
+ // Creates the directory
+ Directory.CreateDirectory(path);
+ }
+
+ ///
+ /// Opens the jar file
+ ///
+ ///
+ public JavaFile OpenJavaFile()
+ {
+ return new JavaFile(PathJar);
+ }
+
+ ///
+ /// Writes the json file
+ ///
+ ///
+ public void WriteJsonFile(MinecraftVersionInfo info)
+ {
+ WriteJsonFile(PathJson, info);
+ }
+
+ #region Static
+
+ ///
+ /// Writes the json file
+ ///
+ ///
+ ///
+ public static void WriteJsonFile(string path, MinecraftVersionInfo info)
+ {
+ // Opens the json file
+ using (var stream = new FileStream(path, FileMode.Create))
+ {
+ using (var streamWriter = new StreamWriter(stream))
+ {
+ // Creates the json writer
+ using (var writer = new JsonTextWriter(streamWriter))
+ {
+ writer.Formatting = Formatting.Indented;
+
+ writer.WriteStartObject();
+ writer.WritePropertyName("id");
+ writer.WriteValue(info.ID);
+
+ if (!string.IsNullOrEmpty(info.InheritsFrom))
+ {
+ writer.WritePropertyName("inheritsFrom");
+ writer.WriteValue(info.InheritsFrom);
+ }
+
+ if (!string.IsNullOrEmpty(info.Type))
+ {
+ writer.WritePropertyName("type");
+ writer.WriteValue(info.Type);
+ }
+
+ if (!string.IsNullOrEmpty(info.MainClass))
+ {
+ writer.WritePropertyName("mainClass");
+ writer.WriteValue(info.MainClass);
+ }
+
+ if (info.MinimumLauncherVersion != 0)
+ {
+ writer.WritePropertyName("minimumLauncherVersion");
+ writer.WriteValue(info.MinimumLauncherVersion.ToString());
+ }
+
+ // Writes the empty arguments object.
+ writer.WritePropertyName("arguments");
+ writer.WriteStartObject();
+ writer.WriteEndObject();
+
+ // Prevents the launcher from redownloading the jar file
+ // by creating an empty downloads object.
+ writer.WritePropertyName("downloads");
+ writer.WriteStartObject();
+ writer.WriteEndObject();
+
+ writer.WriteEndObject();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Returns the directory for the current version
+ ///
+ ///
+ public static string GetSystemVersionDirectory()
+ {
+ // TODO: Other systems
+ return Environment.ExpandEnvironmentVariables("%appdata%/.minecraft/versions");
+ }
+
+ ///
+ /// Returns all minecraft version from the system version directory.
+ ///
+ ///
+ public static IEnumerable GetVersions()
+ {
+ var path = GetSystemVersionDirectory();
+
+ // Directory is not valid
+ if (!Directory.Exists(path))
+ yield break;
+
+ foreach(var version in GetVersions(path))
+ {
+ yield return version;
+ }
+ }
+
+ ///
+ /// Returns all minecraft version from the version directory.
+ ///
+ ///
+ ///
+ public static IEnumerable GetVersions(string path)
+ {
+ // Checks every sub directory for a minecraft version
+ foreach (var directory in Directory.EnumerateDirectories(path))
+ {
+ var version = new MinecraftVersion(directory);
+ if (version.IsValid)
+ {
+ yield return version;
+ }
+ }
+ }
+
+ #endregion Static
+ }
+}
diff --git a/MinecraftTabPatcher/MinecraftVersionInfo.cs b/MinecraftTabPatcher/MinecraftVersionInfo.cs
new file mode 100644
index 0000000..708e182
--- /dev/null
+++ b/MinecraftTabPatcher/MinecraftVersionInfo.cs
@@ -0,0 +1,33 @@
+namespace MinecraftTabPatcher
+{
+ ///
+ /// DS 2019-08-11: A small struct to store the content of the minecraft version json file.
+ ///
+ public struct MinecraftVersionInfo
+ {
+ ///
+ /// Gets and sets the id
+ ///
+ public string ID { get; set; }
+
+ ///
+ /// Gets and sets the version id of the parent version
+ ///
+ public string InheritsFrom { get; set; }
+
+ ///
+ /// Gets and sets the type
+ ///
+ public string Type { get; set; }
+
+ ///
+ /// Gets and sets the main class
+ ///
+ public string MainClass { get; set; }
+
+ ///
+ /// Gets and sets the minimum launcher version
+ ///
+ public int MinimumLauncherVersion { get; set; }
+ }
+}
diff --git a/MinecraftTabPatcher/Program.cs b/MinecraftTabPatcher/Program.cs
new file mode 100644
index 0000000..1ef5970
--- /dev/null
+++ b/MinecraftTabPatcher/Program.cs
@@ -0,0 +1,266 @@
+using ICSharpCode.SharpZipLib.Core;
+using ICSharpCode.SharpZipLib.Zip;
+using JavaDecompiler;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace MinecraftTabPatcher
+{
+ ///
+ /// DS 2019-08-09: The main application entry point
+ ///
+ class Program
+ {
+ ///
+ /// You start here
+ ///
+ ///
+ public static void Main(string[] args)
+ {
+ // Gets the version path
+ var versionPath = MinecraftVersion.GetSystemVersionDirectory();
+
+ Console.WriteLine("Minecraft-Tab-Patcher");
+ Console.WriteLine("------------------------------------------------------");
+ Console.WriteLine("This tool can patch any existing minecraft version and");
+ Console.WriteLine("remove the ability to 'tab' to the next ui element. ");
+ Console.WriteLine("The only purpose of this patch is to allow the user");
+ Console.WriteLine("to enter and exit the inventory with the 'tab' key.");
+ Console.WriteLine("Since minecraft 1.13 'tab' will focus the new recipe");
+ Console.WriteLine("book when trying to close the inventory.");
+ Console.WriteLine("------------------------------------------------------");
+ Console.WriteLine("Minecraft version directory:");
+ Console.WriteLine(versionPath);
+ Console.WriteLine("------------------------------------------------------");
+ Console.WriteLine("Please continue at your own risk!");
+ Console.WriteLine("");
+ Console.ReadKey();
+
+
+
+ Console.Clear();
+ var versions = MinecraftVersion.GetVersions(versionPath).OrderByDescending(v => v.ID).ToArray();
+ if (versions.Length == 0)
+ {
+ Console.WriteLine("Could not find any installed minecraft versions at:");
+ Console.WriteLine(versionPath);
+ Console.ReadKey();
+ return;
+ }
+
+ int selection = 0;
+ bool loop = true;
+ do {
+ Console.WriteLine("Select the minecraft version you want to patch.");
+ Console.WriteLine("Use the arrow keys to navigate and press enter to confirm.");
+ for (int i = 0; i < versions.Length; i++)
+ {
+ var version = versions[i];
+ if (selection == i)
+ Console.WriteLine(" -> {0}", version.ID);
+ else
+ Console.WriteLine(" {0}", version.ID);
+ }
+
+ var key = Console.ReadKey();
+ switch (key.Key)
+ {
+ // Cancel
+ case ConsoleKey.Escape:
+ return;
+
+ // Move selection down
+ case ConsoleKey.DownArrow:
+ selection = (selection + 1) % versions.Length;
+ break;
+
+ // Move selection up
+ case ConsoleKey.UpArrow:
+ selection = selection > 0 ? selection - 1 : versions.Length - 1;
+ break;
+
+ // Confirm
+ case ConsoleKey.Enter:
+ loop = false;
+ break;
+ }
+ Console.Clear();
+ } while (loop);
+
+
+ // The selected version
+ MinecraftVersion selectedVersion = versions[selection];
+
+ // Patch the version
+ var patchedVersion = Patch(selectedVersion, "tabFix");
+
+ Console.WriteLine();
+ Console.WriteLine("------------------------------------------------------");
+ Console.WriteLine("Open the minecraft launcher and create a new profile.");
+ Console.WriteLine("Select the new version '{0}' and click 'save'.", patchedVersion.ID);
+ Console.WriteLine("Now launch the game with your new profile selected!");
+ Console.WriteLine("Bye!");
+ Console.WriteLine("");
+ Console.ReadKey();
+ }
+
+
+ ///
+ /// The original code. This code must be replaced.
+ ///
+ static readonly byte[] OrginalCode = new byte[] { 0x11, 0x01, 0x02 };
+
+ ///
+ /// Patches the given minecraft version and returns the new patched version.
+ /// The patcher will create a new version directory and create a new jar file.
+ /// The jar file is based on the original .
+ /// The patcher will copy any assets from the orignal jar file.
+ /// The content of the META-INF directory will not be copied.
+ /// All classes will be analysed and scanned for a keyPressed method.
+ /// The patcher will replace 0x110102 from the keyPressed methods.
+ /// 0x11 (sipush) is the java bytecode instruction that loads the next two
+ /// bytes as short value onto the stack. Where 0x0102 is the short value for
+ /// 258 which is the minecraft keycode for tab. 0x0102 will be replaced with
+ /// 0xFFFF which is an invalid keycode.
+ ///
+ ///
+ ///
+ ///
+ private static MinecraftVersion Patch(MinecraftVersion version, string postfix)
+ {
+ Console.WriteLine("Start pathching '{0}'...", version.ID);
+ Console.WriteLine("------------------------------------------------------");
+
+ // Creates a new version
+ var parentPath = Path.GetDirectoryName(version.Path);
+
+ // Searches for the next free directory.
+ // Is this really necessary?
+ var template = version.ID + "-" + postfix;
+ var id = template;
+ var path = Path.Combine(parentPath, id);
+ var counter = 1;
+ while (Directory.Exists(path))
+ {
+ counter++;
+ id = template + counter;
+ path = Path.Combine(parentPath, id);
+ }
+
+ Console.WriteLine("Creating patched version '{0}'...", id);
+
+ // Creates the patched version
+ var patchedVersion = new MinecraftVersion(path);
+
+ // The copy buffer
+ var buffer = new byte[1024 * 8];
+
+ // Opens the java file
+ using (var javaFile = version.OpenJavaFile())
+ {
+ // Creates the output file
+ using (var output = new FileStream(patchedVersion.PathJar, FileMode.Create))
+ {
+ // Creates the output zip stream
+ using (var outputZip = new ZipOutputStream(output))
+ {
+ // Do not use 64 bit zip
+ outputZip.UseZip64 = UseZip64.Off;
+
+ var files = javaFile.Zip.Count;
+ for (int i = 0; i < files; i++)
+ {
+ var entry = javaFile.Zip[i];
+
+
+ // Ignore the META-INF folder
+ if (entry.Name.Contains("META-INF/"))
+ {
+ continue;
+ }
+
+ // Creates the output entry file
+ var outputEntry = new ZipEntry(entry.Name);
+ outputEntry.DateTime = entry.DateTime;
+ outputEntry.Size = entry.Size;
+ outputZip.PutNextEntry(outputEntry);
+
+ // This is a class file
+ if (Path.GetExtension(entry.Name) == ".class")
+ {
+ // Loads the class
+ var javaClass = javaFile.GetClass(entry);
+
+ // Gets the class info
+ var javaClassInfo = javaClass.GetConstant(javaClass.ThisClass);
+ var javaClassName = javaClass.GetConstantUtf8(javaClassInfo.NameIndex);
+
+ // Gets the method
+ var javaMethod = javaClass.GetMethod("keyPressed");
+
+
+ if (javaMethod != null)
+ {
+ // Gets the method info
+ var javaMethodName = javaClass.GetConstantUtf8(javaMethod.NameIndex);
+ var javaMethodDescriptor = javaClass.GetConstantUtf8(javaMethod.DescriptorIndex);
+
+ // Gets the code attribute of the method
+ var javaCodeAttribute = javaMethod.GetCodeAttribute();
+ if (javaCodeAttribute != null)
+ {
+ var code = javaCodeAttribute.Code;
+ var index = 0;
+ while ((index = Utils.BinaryIndexOf(code, OrginalCode, index)) >= 0)
+ {
+ Console.WriteLine("Patching bytecode from '{0}.{1}{2}' at position {3}...", javaClassName, javaMethodName, javaMethodDescriptor, index);
+
+ // Change the code
+ code[index + 1] = 0xFF;
+ code[index + 2] = 0xFF;
+
+
+ index++;
+ }
+ }
+ }
+
+ // Writes the class
+ javaClass.Write(outputZip);
+ }
+ else
+ {
+ // Just copy the file
+ using (var inputStream = javaFile.GetFileStream(entry))
+ {
+ StreamUtils.Copy(inputStream, outputZip, buffer);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Console.WriteLine("Creating json file...");
+
+ // Creates the json file
+ var patchedInfo = new MinecraftVersionInfo()
+ {
+ ID = id,
+ InheritsFrom = version.ID,
+ Type = "custom",
+ MainClass = "net.minecraft.client.main.Main",
+ MinimumLauncherVersion = 21,
+ };
+
+ // Write the version json
+ patchedVersion.WriteJsonFile(patchedInfo);
+
+ Console.WriteLine("Version got patched!");
+
+ return patchedVersion;
+ }
+
+ }
+}
diff --git a/MinecraftTabPatcher/Utils.cs b/MinecraftTabPatcher/Utils.cs
new file mode 100644
index 0000000..74877e4
--- /dev/null
+++ b/MinecraftTabPatcher/Utils.cs
@@ -0,0 +1,41 @@
+namespace MinecraftTabPatcher
+{
+ ///
+ /// DS 2019-08-09: A class for utility methods
+ ///
+ public static class Utils
+ {
+ ///
+ /// Returns the index of in .
+ /// Returns -1 if the search array was not found.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int BinaryIndexOf(byte[] array, byte[] search, int start = 0)
+ {
+ int found = 0;
+ int foundMax = search.Length;
+ int len = array.Length;
+ for (int i = start; i < len; i++)
+ {
+ var c = array[i];
+
+ if (c == search[found])
+ {
+ found++;
+ if (found == foundMax)
+ {
+ return i - foundMax + 1;
+ }
+ }
+ else
+ {
+ found = 0;
+ }
+ }
+ return -1;
+ }
+ }
+}