/*******************************************************************************
* Copyright (c) 2012 Joshua Huelsman.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Joshua Huelsman - Initial implementation.
* Zhuowei Zhang - Adapted for PTPatchTool and MCPELauncher.
*******************************************************************************/
package com.joshuahuelsman.patchtool;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
public class PTPatch {
public final static byte[] magic = { (byte) 0xff, 0x50, 0x54, 0x50 };
public final static byte[] op_codes = { (byte) 0xaa, (byte) 0xdd, (byte) 0xee };
private byte[] patch_array;
public int count;
public String name;
Header mHeader;
public PTPatch(){
mHeader = new Header();
}
class Header {
byte[] magic = new byte[4];
int minecraft_ver;
int num_patches;
byte[] indices;
}
public void loadPatch(byte[] patch_array) {
this.patch_array = patch_array;
mHeader.minecraft_ver = getMinecraftVersion();
mHeader.num_patches = getNumPatches();
mHeader.indices = getIndices();
count = 0;
}
public void loadPatch(File patchf) throws IOException{
patch_array = new byte[(int) patchf.length()];
InputStream is = new FileInputStream(patchf);
is.read(patch_array);
is.close();
mHeader.minecraft_ver = getMinecraftVersion();
mHeader.num_patches = getNumPatches();
mHeader.indices = getIndices();
count = 0;
}
public int getMinecraftVersion(){
return patch_array[4];
}
public int getNumPatches(){
return patch_array[5];
}
public byte[] getIndices(){
byte[] ret = new byte[mHeader.num_patches * 4];
for(int i = 0; i < (mHeader.num_patches * 4); i++){
ret[i] = patch_array[i + 6];
}
return ret;
}
public boolean checkMagic(){
if(patch_array[0] == magic[0]){
if(patch_array[1] == magic[1]){
if(patch_array[2] == magic[2]){
if(patch_array[3] == magic[3]){
return true;
}
}
}
}
return false;
}
public void checkMinecraftVersion(){
return;
}
public int getNextAddr(){
byte[] i = new byte[4];
i[0] = mHeader.indices[(count*4)];
i[1] = mHeader.indices[(count*4)+1];
i[2] = mHeader.indices[(count*4)+2];
i[3] = mHeader.indices[(count*4)+3];
int index = byteArrayToInt(i);
byte[] b = new byte[4];
b[0] = patch_array[index];
b[1] = patch_array[index + 1];
b[2] = patch_array[index + 2];
b[3] = patch_array[index + 3];
return byteArrayToInt(b);
}
public int getCurrentIndex(){
byte[] i = new byte[4];
i[0] = mHeader.indices[(count*4)];
i[1] = mHeader.indices[(count*4)+1];
i[2] = mHeader.indices[(count*4)+2];
i[3] = mHeader.indices[(count*4)+3];
int index = byteArrayToInt(i);
return index;
}
public byte[] getNextData(){
byte[] array = new byte[getDataLength()];
int index = getCurrentIndex();
int i;
int i2 = 0;
for( i = 0; i < getDataLength(); i++){
array[i2] = patch_array[i + (index + 4)];
i2++;
}
return array;
}
public int getDataLength(){
int start = 0;
int end = 0;
byte[] i = new byte[4];
i[0] = mHeader.indices[(count*4)];
i[1] = mHeader.indices[(count*4)+1];
i[2] = mHeader.indices[(count*4)+2];
i[3] = mHeader.indices[(count*4)+3];
if(count != (mHeader.num_patches - 1)){
byte[] i2 = new byte[4];
i2[0] = mHeader.indices[((count+1)*4)];
i2[1] = mHeader.indices[((count+1)*4)+1];
i2[2] = mHeader.indices[((count+1)*4)+2];
i2[3] = mHeader.indices[((count+1)*4)+3];
end = byteArrayToInt(i2);
}else{
end = patch_array.length;
}
start = (byteArrayToInt(i) + 4);
return (end - start);
}
public void applyPatch(File f) throws IOException{
byte[] barray = new byte[(int) f.length()];
InputStream is = new FileInputStream(f);
is.read(barray);
is.close();
ByteBuffer buf = ByteBuffer.wrap(barray);
for(count = 0; count < mHeader.num_patches; count++){
buf.position(getNextAddr());
buf.put(getNextData());
}
f.delete();
OutputStream os = new FileOutputStream(f);
os.write(buf.array());
os.close();
}
public void applyPatch(byte[] barray) throws IOException{
ByteBuffer buf = ByteBuffer.wrap(barray);
applyPatch(buf);
}
public void applyPatch(ByteBuffer buf) throws IOException{
for(count = 0; count < mHeader.num_patches; count++){
buf.position(getNextAddr());
buf.put(getNextData());
}
}
public void removePatch(ByteBuffer buf, byte[] original) {
ByteBuffer originalBuf = ByteBuffer.wrap(original);
for(count = 0; count < mHeader.num_patches; count++){
int nextAddr = getNextAddr();
buf.position(nextAddr);
originalBuf.position(nextAddr);
byte[] nextData = new byte[getDataLength()];
originalBuf.get(nextData);
buf.put(nextData);
}
}
public byte[] getMetaData() {
count = 0;
int firstIndex = getCurrentIndex();
int metaDataStart = (mHeader.num_patches * 4) + 6;
byte[] retval = new byte[firstIndex - metaDataStart];
System.arraycopy(patch_array, metaDataStart, retval, 0, retval.length);
return retval;
}
public String getDescription() {
try {
byte[] metaData = getMetaData();
return new String(metaData, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return ""; //should this return null? feel free to yell at me if it should
}
}
public static final byte[] intToByteArray(int value) {
return new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value};
}
public static final int byteArrayToInt(byte [] b) {
return (b[0] << 24)
+ ((b[1] & 0xFF) << 16)
+ ((b[2] & 0xFF) << 8)
+ (b[3] & 0xFF);
}
public static byte[] readPatch(String patch)
throws IOException {
File patchf = new File(patch);
byte[] ret = new byte[(int) patchf.length()];
InputStream is = new FileInputStream(patch);
is.read(ret, 0, ret.length);
is.close();
return ret;
}
}