/// <summary> /// Writes the json file /// </summary> /// <param name="path"></param> /// <param name="info"></param> 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(); } } } }
/// <summary> /// Writes the json file /// </summary> /// <param name="info"></param> public void WriteJsonFile(MinecraftVersionInfo info) { WriteJsonFile(PathJson, info); }
/// <summary> /// 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 <paramref name="version"/>. /// 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. /// </summary> /// <param name="version"></param> /// <param name="postfix"></param> /// <returns></returns> 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 <JavaConstantClassInfo>(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); }