using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
using System.Diagnostics;
using Ionic.Zip;
using Ionic.Zlib;
namespace Minecraft_License_Patcher
{
public partial class Form1 : Form
{
public class Pair<T, U>
{
public Pair()
{
}
public Pair(T first, U second)
{
this.First = first;
this.Second = second;
}
public T First { get; set; }
public U Second { get; set; }
};
public class Version
{
public int Major { get; private set; }
public int Minor { get; private set; }
public int Build { get; private set; }
public int Fix { get; private set; }
public string Release { get; private set; }
private string _raw;
public Version(string verString)
{
_raw = verString;
verString = verString.Substring(10);
int p = verString.IndexOf(" ");
Release = verString.Substring(0, p);
verString = verString.Substring(p + 1);
if (verString.StartsWith("v"))
{
verString = verString.Remove(0, 1);
}
p = verString.IndexOf(".");
Major = int.Parse(verString.Substring(0, p));
verString = verString.Substring(p + 1);
p = verString.IndexOf(".");
if (p == -1)
{
if ((p = verString.IndexOf("_")) > -1)
{
Minor = int.Parse(verString.Substring(0, p));
Fix = int.Parse(verString.Substring(p + 1));
}
else
{
Minor = int.Parse(verString);
Fix = 0;
}
Build = 0;
}
else
{
Minor = int.Parse(verString.Substring(0, p));
verString = verString.Substring(p + 1);
if ((p = verString.IndexOf("_")) > -1)
{
Build = int.Parse(verString.Substring(0, p));
Fix = int.Parse(verString.Substring(p + 1));
}
else
{
Build = int.Parse(verString);
Fix = 0;
}
}
}
public Version(int Major, int Minor, int Build, int Fix, string Release = "Beta")
{
this.Major = Major;
this.Minor = Minor;
this.Build = Build;
this.Fix = Fix;
this.Release = Release;
}
public override string ToString()
{
return ToString();
}
public string ToString(bool rebuild = false, bool includeMinecraftStr = false, bool includeAlphaV = false)
{
if (rebuild || _raw == null)
{
string ver = (includeMinecraftStr ? "Minecraft " : "") + Release + (includeAlphaV && Release == "Alpha" ? " v" : " ") + Major + "." + Minor;
if (Build != 0)
{
ver += "." + Build;
}
if (Fix != 0)
{
ver += "_" + Fix;
}
return ver;
}
else
{
return _raw;
}
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool operator ==(Version x, Version y)
{
return (x.Major == y.Major) &&
(x.Minor == y.Minor) &&
(x.Build == y.Build) &&
(x.Fix == y.Fix) &&
(x.Release == y.Release);
}
public static bool operator !=(Version x, Version y)
{
return !(x == y);
}
public static bool operator >(Version x, Version y)
{
if (x.Release != y.Release)
{
if (x.Release == "Beta" && y.Release == "Alpha")
{
return true;
}
else if (x.Release == "Alpha" && y.Release == "Beta")
{
return false;
}
else
{
throw new NotImplementedException();
}
}
else
{
if (x.Major != y.Major)
{
return x.Major > y.Major;
}
else
{
if (x.Minor != y.Minor)
{
return x.Minor > y.Minor;
}
else
{
if (x.Build != y.Build)
{
return x.Build > y.Build;
}
else
{
return x.Fix > y.Fix;
}
}
}
}
}
public static bool operator >=(Version x, Version y)
{
return (x > y) || (x.Fix == y.Fix);
}
public static bool operator <(Version x, Version y)
{
return !(x > y);
}
public static bool operator <=(Version x, Version y)
{
return (x < y) || (x.Fix == y.Fix);
}
}
private int canPatch = 0;
private Pair<string, Pair<int, int>[]> licenseData;
public Form1()
{
InitializeComponent();
}
public static int ByteIndexOf(byte[] arrayToSearchThrough, byte[] patternToFind)
{
if (patternToFind.Length > arrayToSearchThrough.Length)
return -1;
for (int i = 0; i < arrayToSearchThrough.Length - patternToFind.Length; i++)
{
bool found = true;
for (int j = 0; j < patternToFind.Length; j++)
{
if (arrayToSearchThrough[i + j] != patternToFind[j])
{
found = false;
break;
}
}
if (found)
{
return i;
}
}
return -1;
}
public static void ReplaceBytes(ref byte[] sourceArray, byte replacement, int start, int end)
{
for (int i = start; i < end; i++)
{
sourceArray[i] = replacement;
}
}
private byte[] GetZipEntryData(ZipEntry ze)
{
using (CrcCalculatorStream stream = ze.OpenReader())
{
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
}
private string GetZipEntryString(ZipEntry ze)
{
return Encoding.ASCII.GetString(GetZipEntryData(ze));
}
private Version GetMinecraftVersion(ZipFile jar)
{
Regex versionCheck = new Regex(@"(Minecraft\ [^\s]+\ v?\d+[^\s\01]+)\01");
foreach (ZipEntry ze in jar)
{
Match m = versionCheck.Match(GetZipEntryString(ze));
if (m.Success)
{
return new Version(m.Groups[1].Captures[0].Value);
}
}
return null;
}
private void RemoveMetaData(ZipFile zip)
{
List<ZipEntry> remove = new List<ZipEntry>();
foreach (ZipEntry ze in zip.Entries)
{
if (ze.FileName.StartsWith("META"))
{
remove.Add(ze);
}
}
foreach (ZipEntry ze in remove)
{
zip.RemoveEntry(ze);
}
}
private Pair<string, Pair<int, int>[]> FindLicenseDataString(ZipFile zip, string version)
{
foreach (ZipEntry ze in zip)
{
byte[] buffer = GetZipEntryData(ze);
String[] find =
{
version + " Unlicensed Copy :(",
"(Or logged in from another location)",
"Purchase at minecraft.net",
};
Pair<byte[][], int[]> findBytes = new Pair<byte[][], int[]>();
findBytes.First = new byte[find.Length][];
findBytes.Second = new int[find.Length];
int count = 0;
for (int i = 0; i < find.Length; i++)
{
findBytes.First[i] = Encoding.ASCII.GetBytes(find[i]);
findBytes.Second[i] = ByteIndexOf(buffer, findBytes.First[i]);
if (findBytes.Second[i] > -1)
{
count++;
}
}
if (count == find.Length)
{
Pair<string, Pair<int, int>[]> ret = new Pair<string, Pair<int, int>[]>();
Pair<int, int>[] blocks = new Pair<int, int>[count];
ret.First = ze.FileName;
for (int i = 0; i < count; i++)
{
Pair<int, int> block = new Pair<int, int>(findBytes.Second[i], findBytes.First[i].Length);
blocks[i] = block;
}
ret.Second = blocks;
return ret;
}
}
return null;
}
private void PatchGame()
{
string output = "";
if (backupCb.Checked)
{
string bkpFile = jarPathTb.Text + ".bkp";
if (File.Exists(bkpFile))
{
switch (MessageBox.Show("Backup file already exists, should this file be overwritten?", "Warning", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning))
{
case System.Windows.Forms.DialogResult.Yes:
File.Delete(bkpFile);
break;
case System.Windows.Forms.DialogResult.No:
int i = 0;
do
{
i++;
} while (File.Exists(bkpFile + i));
bkpFile += i;
break;
default:
return;
}
}
File.Copy(jarPathTb.Text, bkpFile);
output = "Backup Minecraft.jar saved as " + bkpFile;
button3.Enabled = button4.Enabled = true;
}
try
{
using (ZipFile zf = new ZipFile(jarPathTb.Text))
{
if (metaInfCb.Enabled && metaInfCb.Checked)
{
RemoveMetaData(zf);
output = "Removed META-INF files\n\n" + output;
}
byte[] buffer = GetZipEntryData(zf[licenseData.First]);
for (int i = 0; i < licenseData.Second.Length; i++)
{
ReplaceBytes(ref buffer, 32, licenseData.Second[0].First, licenseData.Second[0].First + licenseData.Second[0].Second);
}
zf.UpdateEntry(licenseData.First, buffer);
zf.Save();
}
output = "Patching completed sucessfully\n\nLicense strings removed\n" + output;
MessageBox.Show(output, "Completed", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("An error occured during patching (No files modified)\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
if (backupCb.Checked)
{
File.Delete(jarPathTb.Text);
File.Move(jarPathTb.Text + ".bkp", jarPathTb.Text);
button3.Enabled = button4.Enabled = false;
}
}
textBox1_TextChanged(null, null);
}
private void Form1_Load(object sender, EventArgs e)
{
this.MaximumSize = this.Size;
this.MinimumSize = this.Size;
string defaultJar = Environment.GetFolderPath(Environment.SpecialFolde r.ApplicationData) + @"\.minecraft\bin\minecraft.jar";
if (File.Exists(defaultJar))
{
jarPathTb.Text = defaultJar;
if (File.Exists(defaultJar + ".bkp"))
{
button3.Enabled = button4.Enabled = true;
}
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
using (ZipFile zf = new ZipFile(jarPathTb.Text))
{
bool found = false;
foreach (ZipEntry ze in zf)
{
if (ze.FileName.StartsWith("META-INF"))
{
found = true;
break;
}
}
if (found)
{
metaInfCb.Enabled = true;
}
else
{
metaInfCb.Enabled = false;
}
Version v = GetMinecraftVersion(zf);
verLbl.Text = v.ToString(true);
if (v >= new Version(1, 6, 0, 0))
{
licenseData = FindLicenseDataString(zf, v.ToString());
if (licenseData != null)
{
offsetLbl.Text = "0x" + licenseData.Second[0].First.ToString("x") + " (" + licenseData.First.ToString() + ")";
canPatch = -1;
button2.Enabled = true;
}
else
{
offsetLbl.Text = "Error finding strings";
canPatch = 2;
button2.Enabled = false;
}
}
else
{
offsetLbl.Text = "Version too low";
canPatch = 1;
button2.Enabled = false;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "Minecraft.jar|Minecraft.jar|*.jar|*.jar";
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
jarPathTb.Text = ofd.FileName;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
switch (canPatch)
{
case -1:
PatchGame();
break;
case 0:
MessageBox.Show("No Minecraft.jar selected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case 1:
MessageBox.Show("Minecraft.jar version too low for license string", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case 2:
MessageBox.Show("License string not found (has it already been removed?)", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
default:
MessageBox.Show("An unknown state has been reached. Weird.", "Why are you here?!?", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
private void button4_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to delete the backup file:\n" + jarPathTb.Text + ".bkp", "Confirm delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
{
File.Delete(jarPathTb.Text + ".bkp");
button3.Enabled = button4.Enabled = false;
}
}
private void button3_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to restore your backup Minecraft.jar", "Confirm restore", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
{
File.Delete(jarPathTb.Text);
File.Move(jarPathTb.Text + ".bkp", jarPathTb.Text);
button3.Enabled = button4.Enabled = false;
textBox1_TextChanged(null, null);
}
}
}
}