first commit

This commit is contained in:
codestation 2010-12-12 17:12:44 +00:00
commit a4848cb2d4
15 changed files with 1717 additions and 0 deletions

6
.classpath Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/index.bin
/.settings

17
.project Normal file
View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>mhtrans</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

88
src/base/Decoder.java Normal file
View file

@ -0,0 +1,88 @@
/* MHP2GDEC v1.0 - MHP2G xxxx.bin language table extractor
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package base;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
public abstract class Decoder {
public abstract void extract(String filename);
/**
* The "readInt" function of java reads in BigEndian mode but we need
* LittleEndian so i made a custom function for that
*
* @param file
* @return 8 byte integer in LittleEndian mode
* @throws IOException
* if any error occur while reading
*/
protected int readInt(RandomAccessFile file) throws IOException, EOFException {
int ch1 = file.read();
int ch2 = file.read();
int ch3 = file.read();
int ch4 = file.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
}
/**
* Some hex-edited files have some extra zeros at the end of the strings so
* its better to skip them
*
* @param file
* @throws IOException
* if any error occur while reading
*/
protected void advanceNext(RandomAccessFile file) throws IOException {
while (file.readByte() == 0) {
;
}
file.seek(file.getFilePointer() - 1);
}
/**
* The "readUTF8" function of java expects a different format of the string
* so i have to make a custom one
*
* @param file
* @return string extracted from file
* @throws IOException
* if any error occur while reading
*/
protected String readString(RandomAccessFile file) throws IOException {
byte[] buffer = new byte[1024];
byte data = 0;
int counter = 0;
try {
do {
data = file.readByte();
buffer[counter++] = data;
} while (data != 0);
// checks if the string is a edited one
advanceNext(file);
} catch (EOFException e) {
return null;
}
return new String(buffer, 0, counter, "UTF-8");
}
}

122
src/base/Encoder.java Normal file
View file

@ -0,0 +1,122 @@
/* MHP2GDEC v1.0 - MHP2G xxxx.bin language table rebuilder
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package base;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
public abstract class Encoder {
public abstract void compile(String filelist);
/**
* The "readUTF8" function of java expects a different format of the string
* so i have to make a custom one.
*
* @param file
* @return string extracted from file
* @throws IOException
* if any error occur while reading
*/
protected String readString(RandomAccessFile file) throws IOException {
byte[] buffer = new byte[1024];
byte data = 0;
boolean eol = false;
int counter = 0;
try {
while (!eol) {
switch (data = file.readByte()) {
case '\n':
eol = true;
break;
case '\r':
eol = true;
long cur = file.getFilePointer();
if (file.readByte() != '\n') {
file.seek(cur);
eol = false;
}
break;
default:
buffer[counter++] = data;
break;
}
}
} catch (EOFException e) {
return null;
}
return new String(buffer, 0, counter, "UTF-8");
}
/**
* Checks if the file have the unicode BOM mark and skip it (thanks notepad
* grr..)
*
* @param file
* @throws IOException
* if any error occur while reading
*/
protected void checkUnicodeBOM(RandomAccessFile file) throws IOException {
int a = file.readByte();
int b = file.readByte();
int c = file.readByte();
if (a != -17 || b != -69 || c != -65) {
file.seek(0);
}
}
/**
* The "writeInt" function of java writes in BigEndian mode but we need
* LittleEndian so i made a custom function for that
*
* @param file
* @throws IOException
* if any error occur while writing
*/
protected void writeInt(RandomAccessFile file, int value) throws IOException {
int ch1 = (byte) (value >>> 24);
int ch2 = (byte) (value >>> 16);
int ch3 = (byte) (value >>> 8);
int ch4 = (byte) value;
file.write(ch4);
file.write(ch3);
file.write(ch2);
file.write(ch1);
}
/**
* The "readInt" function of java reads in BigEndian mode but we need
* LittleEndian so i made a custom function for that
*
* @param file
* @return 8 byte integer in LittleEndian mode
* @throws IOException
* if any error occur while reading
*/
protected int readInt(RandomAccessFile file) throws IOException, EOFException {
int ch1 = file.read();
int ch2 = file.read();
int ch3 = file.read();
int ch4 = file.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
}
}

184
src/base/Mhtrans.java Normal file
View file

@ -0,0 +1,184 @@
/* MHP2GDEC v1.0 - MHP2G xxxx.bin language table extractor/rebuilder/encrypter/decrypter
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package base;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import crypt.Decrypter;
import crypt.Encrypter;
import dec.ExtractPluginA;
import dec.ExtractPluginB;
import dec.ExtractPluginC;
import enc.RebuildPluginA;
import enc.RebuildPluginB;
import enc.RebuildPluginC;
public class Mhtrans {
public static void extract(String filename, String decoder) {
// (00[1-2][0-9]|47[0-9][0-9])\\..* decoder A
// 53[0-9][0-9]\\..* decoder B
// 54[0-9][0-9]\\..* decoder C
Decoder dec = null;
int type = Integer.parseInt(decoder);
switch(type) {
case 1:
dec = new ExtractPluginA();
break;
case 2:
dec = new ExtractPluginB(false);
break;
case 4:
dec = new ExtractPluginB(true);
break;
case 3:
dec = new ExtractPluginC();
break;
default:
System.err.println("Unknown decoder: " + decoder);
System.exit(1);
}
dec.extract(filename);
}
public static void rebuild(String filename, String encoder) {
String str = checkFile(filename + "/filelist.txt");
Encoder enc = null;
if (str == null)
System.exit(1);
int type = Integer.parseInt(encoder);
switch(type) {
case 1:
enc = new RebuildPluginA();
break;
case 2:
enc = new RebuildPluginB(0);
break;
case 4:
enc = new RebuildPluginB(type);
break;
case 3:
enc = new RebuildPluginC();
break;
default:
System.err.println("Unknown encoder: " + encoder);
System.exit(1);
}
enc.compile(filename);
}
public static void main(String[] args) {
System.out.println("mhtrans v2.0 - MHP2G/MHFU/MHP3 xxxx.bin language table extractor/rebuilder");
System.out.println();
if (args.length < 2) {
System.err.println("Usage: java -jar mhtrans.jar --extract <path to xxxx.bin> <decoder number>");
System.err.println(" java -jar mhtrans.jar --rebuild <path to project folder> <encoder number>");
System.err.println(" java -jar mhtrans.jar --decrypt <path to xxxx.bin>");
System.err.println(" java -jar mhtrans.jar --encrypt <path to xxxx.bin>");
System.err.println(" java -jar mhtrans.jar --dec-ext <path to xxxx.bin> <decoder number>");
System.err.println(" java -jar mhtrans.jar --reb-enc <path to project folder> <encoder number>");
System.err.println(" java -jar mhtrans.jar --gen-index <data.bin>");
System.err.println(" java -jar mhtrans.jar --dec-all <data.bin> <path to output folder>");
//System.err.println(" java MHP2GTRANS --dec-single <data.bin> <path of xxxx.bin>");
//System.err.println(" java MHP2GTRANS --insert <path of xxxx.bin> <data.bin>");
System.exit(1);
} else {
if (args[0].equals("--extract")) {
if(args.length < 3) {
System.err.println("Decoder number missing. Aborting");
System.exit(1);
}
extract(args[1], args[2]);
} else if (args[0].equals("--rebuild")) {
if(args.length < 3) {
System.err.println("Decoder number missing. Aborting");
System.exit(1);
}
rebuild(args[1], args[2]);
} else if (args[0].equals("--decrypt")) {
new Decrypter().decrypt(args[1], args[1] + ".dec");
} else if (args[0].equals("--encrypt")) {
String filename = new File(args[1]).getName();
new Encrypter().encrypt(args[1], filename + ".enc");
} else if (args[0].equals("--dec-ext")) {
if(args.length < 3) {
System.err.println("Decoder number missing. Aborting");
System.exit(1);
}
new Decrypter().decrypt(args[1], args[1] + ".dec");
new File(args[1]).renameTo(new File(args[1] + ".tmp"));
new File(args[1] + ".dec").renameTo(new File(args[1]));
extract(args[1], args[2]);
new File(args[1]).delete();
new File(args[1] + ".tmp").renameTo(new File(args[1]));
} else if (args[0].equals("--reb-enc")) {
if(args.length < 3) {
System.err.println("Decoder number missing. Aborting");
System.exit(1);
}
rebuild(args[1], args[2]);
String filename = new File(args[1]).getName();
new Encrypter().encrypt(filename + ".bin.out", filename + ".bin.enc");
System.out.println("Moving to " + filename + ".bin.enc");
new File(filename + ".bin.out").delete();
} else if(args[0].equals("--gen-index")) {
new Decrypter().decrypt_index(args[1], null);
} else if(args[0].equals("--dec-all")) {
if(args.length < 3) {
System.err.println("Output folder missing. Aborting");
System.exit(1);
}
new Decrypter().decrypt_whole(args[1], args[2]);
// } else if(args[0].equals("--dec-single")) {
// if(args.length < 3) {
// System.err.println("Output xxxx.bin missing. Aborting");
// System.exit(1);
// }
//
// } else if(args[0].equals("--insert")) {
// if(args.length < 3) {
// System.err.println("Path of data.bin missing. Aborting");
// System.exit(1);
// }
} else {
System.err.println("Unknown parameter: " + args[0]);
System.exit(1);
}
}
}
public static String checkFile(String filename) {
try {
BufferedReader file = new BufferedReader(new FileReader(filename));
String name = file.readLine().split(" ")[0];
file.close();
return name;
} catch (FileNotFoundException e) {
System.err.println(e.toString());
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

146
src/crypt/DecryptTable.java Normal file
View file

@ -0,0 +1,146 @@
/* MHP2GDEC v1.0 - MH data.bin/xxxx.bin encrypter/decrypter
Copyright (C) 2008 codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package crypt;
//import java.io.EOFException;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public abstract class DecryptTable {
protected final byte decrypt_table[] = { (byte) 0xCB, (byte) 0x96,
(byte) 0x85, (byte) 0xA6, (byte) 0x5F, (byte) 0x3E, (byte) 0xAB,
(byte) 0x03, (byte) 0x50, (byte) 0xB7, (byte) 0x9C, (byte) 0x5C,
(byte) 0xB2, (byte) 0x40, (byte) 0xEF, (byte) 0xF6, (byte) 0xFF,
(byte) 0x61, (byte) 0x15, (byte) 0x29, (byte) 0xA2, (byte) 0xF1,
(byte) 0xEC, (byte) 0x52, (byte) 0x35, (byte) 0x28, (byte) 0xD9,
(byte) 0x68, (byte) 0x24, (byte) 0x36, (byte) 0xC4, (byte) 0x74,
(byte) 0x26, (byte) 0xE2, (byte) 0xD5, (byte) 0x8C, (byte) 0x47,
(byte) 0x4D, (byte) 0x2C, (byte) 0xFA, (byte) 0x86, (byte) 0x66,
(byte) 0xC1, (byte) 0x4F, (byte) 0x0B, (byte) 0x81, (byte) 0x5B,
(byte) 0x1B, (byte) 0xC0, (byte) 0x0A, (byte) 0xFD, (byte) 0x17,
(byte) 0xA4, (byte) 0xA9, (byte) 0x6D, (byte) 0x63, (byte) 0xAD,
(byte) 0xF3, (byte) 0xF4, (byte) 0x6E, (byte) 0x8D, (byte) 0x89,
(byte) 0x14, (byte) 0xDD, (byte) 0x59, (byte) 0x87, (byte) 0x4A,
(byte) 0x30, (byte) 0xCE, (byte) 0xFE, (byte) 0x3F, (byte) 0x7E,
(byte) 0x06, (byte) 0x49, (byte) 0xA5, (byte) 0x04, (byte) 0x5E,
(byte) 0xD0, (byte) 0xDE, (byte) 0xE8, (byte) 0x0F, (byte) 0xD4,
(byte) 0x13, (byte) 0x1F, (byte) 0xBA, (byte) 0xB9, (byte) 0x69,
(byte) 0x71, (byte) 0x3D, (byte) 0xE4, (byte) 0xDC, (byte) 0x58,
(byte) 0x90, (byte) 0x34, (byte) 0x3A, (byte) 0x3C, (byte) 0xCA,
(byte) 0x10, (byte) 0x76, (byte) 0xC7, (byte) 0xC8, (byte) 0x45,
(byte) 0x33, (byte) 0xC3, (byte) 0x92, (byte) 0x1D, (byte) 0x2B,
(byte) 0x1C, (byte) 0x8F, (byte) 0x6F, (byte) 0x05, (byte) 0x07,
(byte) 0x38, (byte) 0x57, (byte) 0x51, (byte) 0xD6, (byte) 0xDA,
(byte) 0x2D, (byte) 0xB3, (byte) 0xC6, (byte) 0x2E, (byte) 0x64,
(byte) 0x32, (byte) 0x1E, (byte) 0x43, (byte) 0xB1, (byte) 0x5D,
(byte) 0xE1, (byte) 0xBB, (byte) 0x8E, (byte) 0x9D, (byte) 0x72,
(byte) 0x77, (byte) 0xF2, (byte) 0x27, (byte) 0xC9, (byte) 0x7F,
(byte) 0x9E, (byte) 0xAA, (byte) 0x6A, (byte) 0x2F, (byte) 0x6C,
(byte) 0xF9, (byte) 0x48, (byte) 0xE7, (byte) 0xA0, (byte) 0x09,
(byte) 0x56, (byte) 0xB8, (byte) 0xBD, (byte) 0x20, (byte) 0x41,
(byte) 0xCD, (byte) 0x95, (byte) 0x80, (byte) 0xD7, (byte) 0x23,
(byte) 0x0C, (byte) 0x42, (byte) 0xE5, (byte) 0xAE, (byte) 0x8B,
(byte) 0x7D, (byte) 0xBC, (byte) 0x54, (byte) 0x39, (byte) 0xBF,
(byte) 0x65, (byte) 0x01, (byte) 0x88, (byte) 0xE0, (byte) 0x7B,
(byte) 0xB6, (byte) 0x16, (byte) 0x18, (byte) 0x4B, (byte) 0xCC,
(byte) 0x22, (byte) 0x5A, (byte) 0xB5, (byte) 0xEB, (byte) 0xFC,
(byte) 0xF8, (byte) 0x9B, (byte) 0x4E, (byte) 0xE6, (byte) 0xA8,
(byte) 0xBE, (byte) 0x67, (byte) 0x73, (byte) 0x97, (byte) 0x94,
(byte) 0x00, (byte) 0x62, (byte) 0xB4, (byte) 0xD2, (byte) 0x21,
(byte) 0x25, (byte) 0x11, (byte) 0x82, (byte) 0xDB, (byte) 0x93,
(byte) 0x02, (byte) 0x84, (byte) 0x7C, (byte) 0xD3, (byte) 0xB0,
(byte) 0xA3, (byte) 0x91, (byte) 0xA7, (byte) 0xF7, (byte) 0x55,
(byte) 0x70, (byte) 0x7A, (byte) 0x08, (byte) 0x75, (byte) 0x8A,
(byte) 0x53, (byte) 0x79, (byte) 0xFB, (byte) 0x9F, (byte) 0x46,
(byte) 0xF5, (byte) 0x83, (byte) 0xD8, (byte) 0x0E, (byte) 0xE9,
(byte) 0xED, (byte) 0x12, (byte) 0xD1, (byte) 0xDF, (byte) 0xF0,
(byte) 0x37, (byte) 0x2A, (byte) 0x44, (byte) 0x19, (byte) 0x9A,
(byte) 0x31, (byte) 0xCF, (byte) 0xA1, (byte) 0xAF, (byte) 0xE3,
(byte) 0x3B, (byte) 0x1A, (byte) 0x4C, (byte) 0x78, (byte) 0xC2,
(byte) 0x60, (byte) 0xEE, (byte) 0x98, (byte) 0x6B, (byte) 0x0D,
(byte) 0x99, (byte) 0xEA, (byte) 0xC5, (byte) 0xAC };
private long lower_offset;
private long upper_offset;
protected void initSeed(long seed) {
lower_offset = seed & 0xFFFF;
upper_offset = seed >> 0x10 & 0xFFFF;
if (lower_offset == 0) {
lower_offset = 0x7F8D;
}
if (upper_offset == 0) {
upper_offset = 0x2345;
}
}
protected long getBeta() {
lower_offset = (lower_offset * 0x7F8D) % 0xFFF1;
upper_offset = (upper_offset * 0x2345) % 0xFFD9;
return lower_offset + (upper_offset << 0x10);
}
protected void set_table_value(byte table[], int pos, long value) {
table[pos] = (byte) value;
table[pos+1] = (byte) (value >> 8);
table[pos+2] = (byte) (value >> 16);
table[pos+3] = (byte) (value >> 24);
}
protected long get_table_value(byte table[], int pos) {
return (table[pos] & 0xFF)
+ ((long) (table[pos+1] & 0xFF) << 8)
+ ((long) (table[pos+2] & 0xFF) << 16)
+ ((long) (table[pos+3] & 0xFF) << 24);
}
/*
* Can't use the RandomAccessFile readInt func as we need the bytes
* in reverse order
*/
private int readInt(RandomAccessFile file) throws IOException, EOFException {
int ch1 = file.read();
int ch2 = file.read();
int ch3 = file.read();
int ch4 = file.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
}
protected int getOffset(int value) throws EOFException, FileNotFoundException, IOException {
int res = -1;
if (value == 0) {
res = 0;
} else {
RandomAccessFile table = new RandomAccessFile("index.bin", "r");
table.seek(value * 4 - 4);
res = readInt(table);
table.close();
}
return res;
}
protected int extractNumber(String filename) {
return Integer.parseInt(filename.substring(filename.indexOf(".") - 4, filename.indexOf(".")));
}
}

160
src/crypt/Decrypter.java Normal file
View file

@ -0,0 +1,160 @@
/* MHP2GDEC v1.0 - MH data.bin/xxxx.bin decrypter
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package crypt;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Decrypter extends DecryptTable {
public void decrypt_index(String in, ByteArrayOutputStream index_buffer) {
try {
RandomAccessFile filein = new RandomAccessFile(in, "r");
RandomAccessFile fileout = new RandomAccessFile("index.bin", "rw");
fileout.setLength(0);
byte[] buffer = new byte[4];
System.out.println("Decrypting index...");
initSeed(0);
boolean table_end = false;
boolean end_flag = false;
int i = 0;
while(!table_end) {
filein.read(buffer);
buffer[0] = decrypt_table[buffer[0] & 0xFF];
buffer[1] = decrypt_table[buffer[1] & 0xFF];
buffer[2] = decrypt_table[buffer[2] & 0xFF];
buffer[3] = decrypt_table[buffer[3] & 0xFF];
long beta = getBeta();
long alpha = get_table_value(buffer, 0);
long gamma = alpha ^ beta;
if(gamma > 0xFF) {
end_flag = true;
} else if(end_flag) {
table_end = true;
continue;
}
set_table_value(buffer, 0, gamma);
fileout.write(buffer);
if(index_buffer != null)
index_buffer.write(buffer);
i += 4;
}
fileout.close();
System.out.println("Index size: " + i + " bytes");
System.out.println("File count: " + i / 4);
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
}
public void decrypt_whole(String in, String out) {
ByteArrayOutputStream index_buffer = new ByteArrayOutputStream();
decrypt_index(in, index_buffer);
RandomAccessFile filein;
try {
filein = new RandomAccessFile(in, "r");
byte index_table[] = index_buffer.toByteArray();
int files_count = index_table.length / 4;
new File(out).mkdir();
boolean create_subdirectory = true;
int last_subdirectory = 0;
long last_offset = 0;
for(int i = 0; i < files_count; i++) {
if(create_subdirectory) {
last_subdirectory = i / 1000;
new File(out + "/0" + Integer.toString(last_subdirectory)).mkdir();
create_subdirectory = false;
} else {
if(last_subdirectory < i / 1000) {
create_subdirectory = true;
}
}
long offset = last_offset;
last_offset = get_table_value(index_table, i * 4);
long file_length = (get_table_value(index_table, i * 4) - offset) << 11;
String fileout = out +
"/0" + Integer.toString(last_subdirectory) +
"/" + String.format("%04d.bin", i);
System.out.print("Decrypting " + fileout + "(" + file_length + " bytes/offset: " + (offset << 11) + ") ... ");
decrypt_internal(filein, offset, file_length, fileout, false);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void decrypt_internal(RandomAccessFile filein, long pos, long size, String out, boolean single) {
try {
if(!single)
filein.seek(pos << 11);
RandomAccessFile fileout = new RandomAccessFile(out, "rw");
byte buffer[] = new byte[1024];
fileout.setLength(0);
initSeed(pos);
while (size > 0) {
int read = filein.read(buffer);
size -= read;
for(int i = 0; i < read; i += 4) {
buffer[i] = decrypt_table[buffer[i] & 0xFF];
buffer[i+1] = decrypt_table[buffer[i+1] & 0xFF];
buffer[i+2] = decrypt_table[buffer[i+2] & 0xFF];
buffer[i+3] = decrypt_table[buffer[i+3] & 0xFF];
long alpha = get_table_value(buffer, i);
long beta = getBeta();
long gamma = alpha ^ beta;
set_table_value(buffer, i, gamma);
}
fileout.write(buffer);
}
fileout.close();
System.out.println("Finished!");
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
}
public void decrypt(String in, String out) {
RandomAccessFile filein = null;
try {
filein = new RandomAccessFile(in, "r");
System.out.print("Decrypting " + out + " ... ");
decrypt_internal(filein, getOffset(extractNumber(in)), filein.length(), out, true);
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
} finally {
if(filein != null)
try {
filein.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

62
src/crypt/Encrypter.java Normal file
View file

@ -0,0 +1,62 @@
/* MHP2GDEC v1.0 - MHP2G data.bin/xxxx.bin encrypter
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package crypt;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Encrypter extends DecryptTable {
private byte []encrypt_table;
public Encrypter() {
encrypt_table = new byte[256];
for(int i = 0;i < 256; i++) {
encrypt_table[decrypt_table[i] & 0xFF] = (byte)i;
}
}
public void encrypt(String in, String out) {
try {
RandomAccessFile filein = new RandomAccessFile(in, "r");
RandomAccessFile fileout = new RandomAccessFile(out, "rw");
initSeed(getOffset(extractNumber(in)));
byte[] buffer = new byte[4];
System.out.println("Encrypting " + in);
while (filein.read(buffer) >= 0) {
long gamma = get_table_value(buffer, 0);
long beta = getBeta();
long alpha = beta ^ gamma;
set_table_value(buffer, 0, alpha);
buffer[0] = encrypt_table[buffer[0] & 0xFF];
buffer[1] = encrypt_table[buffer[1] & 0xFF];
buffer[2] = encrypt_table[buffer[2] & 0xFF];
buffer[3] = encrypt_table[buffer[3] & 0xFF];
fileout.write(buffer);
}
filein.close();
fileout.close();
System.out.println("Finished!");
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
}
}

120
src/dec/ExtractPluginA.java Normal file
View file

@ -0,0 +1,120 @@
/* MHP2GDEC v1.0 - MHP2G 0016/0017/475x.bin language table extractor
Copyright (C) 2008-2010 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dec;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import base.Decoder;
/**
* ExtractPluginA v1.0 - 0016//0017/475x.bin language table extractor
*
* @author Codestation
*/
public class ExtractPluginA extends Decoder {
@Override
public void extract(String filename) {
int tables_count;
byte[] paddingData;
int[] table_offset;
int offset;
try {
RandomAccessFile file = new RandomAccessFile(filename, "r");
// reading of number of main tables
tables_count = readInt(file);
// skipping 4 bytes of unknown data
file.skipBytes(4);
table_offset = new int[tables_count];
// read the location of each table
for (int i = 0; i < tables_count; i++) {
table_offset[i] = readInt(file);
}
String directory = filename.split("\\.")[0];
new File(directory).mkdir();
// create the list of string tables used in the rebuild
PrintStream filelist = new PrintStream(new FileOutputStream(new File(directory + "/filelist.txt")), true, "UTF-8");
// save the name and size of the file
filelist.println(filename + " " + file.length());
for (int j = 0; j < tables_count; j++) {
if(table_offset[j] == -1) {
//System.out.println("Creating " + directory + "/string_table_" + j + ".txt (empty)");
System.out.println("Can't create " + directory + "/string_table_" + j + ".txt (null table), skipping.");
File f = new File(directory + "/string_table_" + j + ".txt");
f.delete();
//f.createNewFile();
filelist.println("string_table_" + j + ".txt");
continue;
}
file.seek(table_offset[j]);
System.out.println("Creating " + directory + "/string_table_" + j + ".txt");
PrintStream stringout = new PrintStream(new FileOutputStream(new File(directory + "/string_table_" + j + ".txt")), true, "UTF-8");
filelist.println("string_table_" + j + ".txt");
int offsetCounter = 0;
// just skip the offset section (not needed)
while (true) {
offset = readInt(file);
if (offset == -1) {
break;
}
offsetCounter++;
}
int stringCounter = 0;
while (stringCounter < offsetCounter) {
String str = readString(file);
if (str.length() == 1 && str.charAt(0) == 0) {
// some offsets points to empty strings, so i put this
// string to make
// sure that it will created at the moment of repack
stringout.println("<EMPTY STRING>");
} else {
str = str.substring(0, str.length() - 1);
// need one string per line, so better replace the
// newlines
stringout.println(str.replaceAll("\n", "<NEWLINE>"));
}
stringCounter++;
}
// skip the end-byte mark
file.skipBytes(1);
stringout.close();
}
file.seek(file.getFilePointer() - 1);
// calculate the size of the ending padding data and make a file of
// it
int size = (int) (file.length() - file.getFilePointer());
paddingData = new byte[size];
file.read(paddingData, 0, size);
System.out.println("Creating " + directory + "/enddata.bin");
RandomAccessFile end = new RandomAccessFile(directory + "/enddata.bin", "rw");
filelist.println("enddata.bin");
end.write(paddingData, 0, size);
end.setLength(end.getFilePointer());
end.close();
file.close();
filelist.close();
System.out.println("Finished!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

147
src/dec/ExtractPluginB.java Normal file
View file

@ -0,0 +1,147 @@
/* MHP2GDEC v1.0 - MHP2G 53xx.bin language table extractor
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.util.Vector;
import base.Decoder;
/**
* ExtractPluginB v1.0 - 53xx.bin language table extractor
*
* @author Codestation
*/
public class ExtractPluginB extends Decoder {
private int mhp3_skip_bytes;
public ExtractPluginB(boolean newdec) {
mhp3_skip_bytes = newdec ? 4 : 0;
}
@Override
public void extract(String filename) {
byte[] unknownData;
Vector<Integer> table_offset;
try {
RandomAccessFile file = new RandomAccessFile(filename, "r");
table_offset = new Vector<Integer>();
int pointer;
while (true) {
pointer = readInt(file);
if (pointer == 0) {
break;
}
table_offset.add(pointer);
}
String directory = filename.split("\\.")[0];
new File(directory).mkdir();
PrintStream filelist = new PrintStream(new FileOutputStream(new File(directory + "/filelist.txt")), true, "UTF-8");
filelist.println(filename + " " + file.length());
for (int j = 0; j < table_offset.size(); j++) {
file.seek(table_offset.get(j));
System.out.println("Creating " + directory + "/string_table_" + j + ".txt");
PrintStream stringout = new PrintStream(new FileOutputStream(new File(directory + "/string_table_" + j + ".txt")), true, "UTF-8");
filelist.println("string_table_" + j + ".txt");
// int unknown0 = readInt(file);
// int payment = readInt(file);
// int reward = readInt(file);
// int decrease = readInt(file);
// int unknown_fixed = readInt(file);
file.skipBytes(20 + mhp3_skip_bytes);
int offset_table_pointer = readInt(file);
// int unknown1 = readInt(file);
// int unknown2 = readInt(file);
// int unknown3 = readInt(file);
// int unknown4 = readInt(file);
// int unknown5 = readInt(file);
// int unknown6 = readInt(file);
// int unknown7 = readInt(file);
file.seek(offset_table_pointer);
int string_table_pointers = readInt(file);
for (long i = string_table_pointers; i < offset_table_pointer; i += 4) {
file.seek(i);
int current_string = readInt(file);
file.seek(current_string);
String str = readString(file);
if (str.length() == 1 && str.charAt(0) == 0) {
// some offsets points to empty strings, so i put this
// string to make
// sure that it will created at the moment of re-pack
stringout.println("<EMPTY STRING>");
} else {
str = str.substring(0, str.length() - 1);
// need one string per line, so better replace the
// newlines
stringout.println(str.replaceAll("\n", "<NEWLINE>"));
}
}
stringout.close();
file.seek(offset_table_pointer + 7 * 4);
}
// calculate the size of the ending unknown data and make a file of
// it
int size = (int) (file.length() - file.getFilePointer());
unknownData = new byte[size];
file.read(unknownData, 0, size);
System.out.println("Creating " + directory + "/enddata.bin");
RandomAccessFile end = new RandomAccessFile(directory + "/enddata.bin", "rw");
filelist.println("enddata.bin");
end.write(unknownData, 0, size);
end.setLength(end.getFilePointer());
end.close();
file.close();
filelist.close();
System.out.println("Copying " + filename + " to " + directory + "/" + filename + " (needed for rebuild)");
copyfile(filename, directory + "/" + filename);
System.out.println("Finished!");
} catch (IOException e) {
e.printStackTrace();
}
}
private void copyfile(String srFile, String dtFile) {
try {
File f1 = new File(srFile);
File f2 = new File(dtFile);
InputStream in = new FileInputStream(f1);
OutputStream out = new FileOutputStream(f2);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage() + " in the specified directory.");
System.exit(0);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}

117
src/dec/ExtractPluginC.java Normal file
View file

@ -0,0 +1,117 @@
/* MHP2GDEC v1.0 - MHP2G 537x.bin language table extractor
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dec;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.util.Vector;
import base.Decoder;
/**
* MHP2GDEC v1.0 - 537x.bin language table extractor
*
* @author Codestation
*/
public class ExtractPluginC extends Decoder {
@Override
public void extract(String filename) {
Vector<Integer> offset_tables = new Vector<Integer>();
Vector<Integer> unknown_values = new Vector<Integer>();
try {
RandomAccessFile file = new RandomAccessFile(filename, "r");
while (true) {
int unknown = readInt(file);
int offset = readInt(file);
if (unknown == -1 && offset == -1) {
break;
}
unknown_values.add(unknown);
offset_tables.add(offset);
}
String directory = filename.split("\\.")[0];
new File(directory).mkdir();
// create the list of string tables used in the rebuild
PrintStream filelist = new PrintStream(new FileOutputStream(new File(directory + "/filelist.txt")), true, "UTF-8");
// save the name and size of the file
filelist.println(filename + " " + file.length());
int string_table_end = 0;
;
for (int j = 0; j < offset_tables.size(); j++) {
file.seek(offset_tables.get(j));
System.out.println("Creating " + directory + "/string_table_" + j + ".txt");
PrintStream stringout = new PrintStream(new FileOutputStream(new File(directory + "/string_table_" + j + ".txt")), true, "UTF-8");
filelist.println(unknown_values.get(j) + ",string_table_" + j + ".txt");
int offset_table_start = (int) file.getFilePointer();
int string_table_start = 0;
boolean first = false;
while (true) {
int unknown = readInt(file);
int offset = readInt(file);
if (!first) {
first = true;
string_table_start = offset + offset_table_start;
}
int actual_offset = (int) file.getFilePointer();
file.seek(offset + offset_table_start);
String str = readString(file);
string_table_end = (int) file.getFilePointer();
file.seek(actual_offset);
if (str.length() == 1 && str.charAt(0) == 0) {
// some offsets points to empty strings, so i put this
// string to make
// sure that it will created at the moment of re-pack
stringout.println(unknown + ",<EMPTY STRING>");
} else {
str = str.substring(0, str.length() - 1);
// need one string per line, so better replace the
// newlines
stringout.println(unknown + ","
+ str.replaceAll("\n", "<NEWLINE>"));
}
if (file.getFilePointer() >= string_table_start) {
break;
}
}
stringout.close();
}
file.seek(string_table_end);
// calculate the size of the ending unknown data and make a file of
// it
int size = (int) (file.length() - file.getFilePointer());
byte[] unknownData = new byte[size];
file.read(unknownData, 0, size);
file.close();
System.out.println("Creating " + directory + "/enddata.bin");
RandomAccessFile end = new RandomAccessFile(directory + "/enddata.bin", "rw");
filelist.println("enddata.bin");
filelist.close();
end.write(unknownData, 0, size);
end.setLength(end.getFilePointer());
end.close();
System.out.println("Finished!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

194
src/enc/RebuildPluginA.java Normal file
View file

@ -0,0 +1,194 @@
/* MHP2GENC v1.0 - MHP 0016/475x/0017.bin language table rebuilder
Copyright (C) 2008-2010 codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package enc;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
import base.Encoder;
/**
* RebuildPluginA v2.0 - 0016/475x.bin language table rebuilder
*
* @author Codestation
*/
public class RebuildPluginA extends Encoder {
@Override
public void compile(String filepath) {
try {
BufferedReader files = new BufferedReader(new FileReader(filepath + "/filelist.txt"));
String file = files.readLine();
// retrieve the filename and size
String filename = file.split(" ")[0];
long size = Integer.parseInt(file.split(" ")[1]);
// now make a list with the string tables files
Vector<String> filenames = new Vector<String>();
while ((file = files.readLine()) != null) {
filenames.add(file);
}
files.close();
RandomAccessFile out = new RandomAccessFile(filename + ".out", "rw");
out.setLength(0);
// write the first value (number of tables)
writeInt(out, filenames.size() - 1);
int offset = 0;
offset += 8;
// write the second value (unknown...or maybe offset to tables
// offsets?)
writeInt(out, offset);
offset += (filenames.size() - 1) * 4;
// write the first table offset (fixed value)
writeInt(out, offset);
long table_offset = out.getFilePointer();
// first write empty offset values (we don't know the values...yet)
for (int i = 1; i < filenames.size() - 1; i++) {
writeInt(out, 0);
}
for (int i = 0; i < filenames.size() - 1; i++) {
// now start to create each offset table / string table
try {
createStringTable(filepath, filenames.get(i), out);
// write the end-table mark
out.writeByte(0);
if (i < filenames.size() - 2) {
long current = out.getFilePointer();
out.seek(table_offset);
// now we know the value of the next table, so write above
// in the main offset table
writeInt(out, (int) current);
out.seek(current);
table_offset += 4;
}
}catch(FileNotFoundException e) {
// New in MHP3: null tables
System.out.println(filenames.get(i) + " not found, assuming null table pointer.");
if (i < filenames.size() - 2) {
long current = out.getFilePointer();
out.seek(table_offset - 4);
int last_offset = readInt(out);
out.seek(table_offset - 4);
writeInt(out, -1);
out.seek(table_offset);
writeInt(out, last_offset);
out.seek(current);
table_offset += 4;
}
}
}
// we need to know the size of the enddata, so open it now
System.out.println("Reading " + filenames.lastElement());
RandomAccessFile enddata = new RandomAccessFile(filepath + "/" + filenames.lastElement(), "rw");
long enddataSize = enddata.length();
// some checks to make sure that the file size of xxxx.bin is
// correct
if (out.getFilePointer() > size - enddataSize) {
System.out.println("File too big (by " + (out.getFilePointer() - (size - enddataSize))+ " bytes), please reduce some strings :(");
} else if (out.getFilePointer() < size - enddataSize) {
System.out.println("File too small (by " + ((size - enddataSize) - out.getFilePointer())+ " bytes), filling with 0x00 (this is OK) :D");
while (out.getFilePointer() < size - enddataSize) {
out.writeByte(0);
}
} else {
System.out.println("Perfect size of file :O");
}
// now append the padding data at the end of file
int data;
while ((data = enddata.read()) != -1) {
out.write((byte) data);
}
enddata.close();
out.setLength(out.getFilePointer());
out.close();
System.out.println("Finished!");
} catch (FileNotFoundException e) {
System.out.println(e.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param in
* filename of text file
* @param out
* file to write the table
* @throws FileNotFoundException
* if the file is not found
* @throws IOException
* if any error occur while reading/writing
*/
public void createStringTable(String directory, String in, RandomAccessFile out) throws FileNotFoundException, IOException {
System.out.println("Reading " + directory + "/" + in);
RandomAccessFile file = new RandomAccessFile(directory + "/" + in, "r");
if(file.length() == 0) {
// new in MHP3: empty tables
writeInt(out, -1);
file.close();
return;
}
checkUnicodeBOM(file); // thanks notepad :/ (who the hell uses notepad?)
Vector<String> stringTable = new Vector<String>();
try {
while (true) {
// read all strings of file
String str = readString(file);
if (str == null) {
break;
}
// remove the labels and put the original data
str = str.replaceAll("<NEWLINE>", "\n");
str = str.replaceAll("<EMPTY STRING>", "\0");
stringTable.add(str);
}
} catch (EOFException e) {
file.close();
}
int offset = stringTable.size() * 4;
offset += 4;
// now calculate the offsets using the length in bytes of the strings
for (int i = 0; i < stringTable.size(); i++) {
writeInt(out, offset);
if (stringTable.elementAt(i).getBytes("UTF-8").length == 1 && stringTable.elementAt(i).charAt(0) == '\0') {
offset++;
} else {
offset += stringTable.elementAt(i).getBytes("UTF-8").length + 1;
}
}
// end of offset table mark
writeInt(out, -1);
// now write the zero terminated string in the file
for (int i = 0; i < stringTable.size(); i++) {
String str = stringTable.elementAt(i);
if (str.equals("\0")) {
out.writeByte(0);
} else {
out.write(str.getBytes("UTF-8"));
out.writeByte(0);
}
}
}
}

176
src/enc/RebuildPluginB.java Normal file
View file

@ -0,0 +1,176 @@
/* MHP2GENC v1.0 - MHP2G 53xx.bin language table rebuilder
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package enc;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.Vector;
import base.Encoder;
/**
* RebuildPluginB v1.0 - 53xx.bin language table rebuilder
*
* @author codestation
*/
public class RebuildPluginB extends Encoder {
private int encoder = 0;
public RebuildPluginB(int type) {
encoder = type;
}
@Override
public void compile(String filepath) {
try {
BufferedReader files = new BufferedReader(new FileReader(filepath + "/filelist.txt"));
String file = files.readLine();
// retrieve the filename and size
String filename = file.split(" ")[0];
// long size = Integer.parseInt(file.split(" ")[1]);
// now make a list with the string tables files
Vector<String> filenames = new Vector<String>();
while ((file = files.readLine()) != null) {
filenames.add(file);
}
files.close();
copyfile(filepath + "/" + filename, filename + ".out");
RandomAccessFile out = new RandomAccessFile(filename + ".out", "rw");
Vector<Integer> table_offset = new Vector<Integer>();
int pointer;
while (true) {
pointer = readInt(out);
if (pointer == 0) {
break;
}
table_offset.add(pointer);
}
for (int i = 0; i < table_offset.size(); i++) {
patchStringTable(filepath, filenames.get(i), out, table_offset.get(i));
}
out.close();
System.out.println("Finished!");
} catch (FileNotFoundException e) {
System.out.println(e.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
private void patchStringTable(String directory, String in, RandomAccessFile out, int starting_offset) throws FileNotFoundException, IOException {
System.out.println("Reading " + directory + "/" + in);
RandomAccessFile file = new RandomAccessFile(directory + "/" + in, "r");
checkUnicodeBOM(file); // thanks notepad :/ (die notepad, die)
Vector<String> stringTable = new Vector<String>();
try {
while (true) {
// read all strings of file
String str = readString(file);
if (str == null) {
break;
}
// remove the labels and put the original data
str = str.replaceAll("<NEWLINE>", "\n");
str = str.replaceAll("<EMPTY STRING>", "\0");
stringTable.add(str);
}
} catch (EOFException e) {
}
file.close();
out.seek(starting_offset);
out.skipBytes(20 + encoder);
int offset_table_pointer = readInt(out);
int string_start = (int) out.getFilePointer() + 28;
out.seek(offset_table_pointer);
int string_table_pointers = readInt(out);
int diff = string_table_pointers - string_start - calculateTotalSize(stringTable);
if (diff < 0) {
System.err.println(in + " is too big, please remove at least " + -diff + " bytes. Skipped");
return;
}
out.seek(string_table_pointers);
int starting_string = readInt(out);
out.seek(starting_string);
long orig_table_pointer = string_table_pointers;
for (String str : stringTable) {
out.write(str.getBytes("UTF-8"));
out.writeByte(0);
out.writeByte(0);
while (out.getFilePointer() % 4 != 0) {
out.writeByte(0);
}
int tmp = (int) out.getFilePointer();
out.seek(string_table_pointers);
writeInt(out, starting_string);
string_table_pointers += 4;
starting_string = tmp;
out.seek(tmp);
}
long current_offset = out.getFilePointer();
while (current_offset < orig_table_pointer) {
out.writeByte(0);
current_offset++;
}
}
private int calculateTotalSize(Vector<String> st) throws UnsupportedEncodingException {
int total = 0;
for (String str : st) {
int len = str.getBytes("UTF-8").length;
if (len == 1 && str.charAt(0) == 0) {
total++;
} else {
total += len + 1;
}
}
return total;
}
private void copyfile(String srFile, String dtFile) {
try {
File f1 = new File(srFile);
File f2 = new File(dtFile);
InputStream in = new FileInputStream(f1);
OutputStream out = new FileOutputStream(f2);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage() + " in the specified directory.");
System.exit(0);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}

176
src/enc/RebuildPluginC.java Normal file
View file

@ -0,0 +1,176 @@
/* MHP2GENC v1.0 - MHP2G 537x.bin language table rebuilder
Copyright (C) 2008 Codestation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package enc;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
import base.Encoder;
/**
* RebuildPluginC v1.0 - 537x.bin language table rebuilder
*
* @author Codestation
*/
public class RebuildPluginC extends Encoder {
@Override
public void compile(String filepath) {
try {
BufferedReader files = new BufferedReader(new FileReader(filepath + "/filelist.txt"));
String string_file = files.readLine();
// retrieve the filename and size
String filename = string_file.split(" ")[0];
long size = Integer.parseInt(string_file.split(" ")[1]);
// now make a list with the string tables files
Vector<String> filenames = new Vector<String>();
Vector<Integer> unknown = new Vector<Integer>();
while ((string_file = files.readLine()) != null) {
if (string_file.equals("enddata.bin")) {
filenames.add(string_file);
} else {
filenames.add(string_file.split(",")[1]);
unknown.add(Integer.parseInt(string_file.split(",")[0]));
}
}
files.close();
RandomAccessFile out = new RandomAccessFile(filename + ".out", "rw");
writeInt(out, unknown.get(0));
writeInt(out, (unknown.size() + 1) * 8);
for (int i = 1; i < unknown.size(); i++) {
writeInt(out, unknown.get(i));
writeInt(out, 0);
}
writeInt(out, -1);
writeInt(out, -1);
int table_offset = 12;
for (int i = 0; i < filenames.size() - 1; i++) {
// now start to create each offset table / string table
createStringTable(filepath, filenames.get(i), out);
// write the end-table mark
out.writeByte(0);
if (i < filenames.size() - 2) {
long current = out.getFilePointer();
out.seek(table_offset);
// now we know the value of the next table, so write above
// in the main
// offset table
writeInt(out, (int) current);
out.seek(current);
table_offset += 8;
out.seek(current);
}
}
out.writeByte(0);
// we need to know the size of the enddata, so open it now
System.out.println("Reading " + filenames.lastElement());
RandomAccessFile enddata = new RandomAccessFile(filepath + "/" + filenames.lastElement(), "rw");
long enddataSize = enddata.length();
// some checks to make sure that the file size of xxxx.bin is
// correct
if (out.getFilePointer() > size - enddataSize) {
System.out.println("File too big (by " + (out.getFilePointer() - (size - enddataSize))+ " bytes), please reduce some strings :(");
} else if (out.getFilePointer() < size - enddataSize) {
System.out.println("File too small (by " + ((size - enddataSize) - out.getFilePointer())+ " bytes), filling with 0x00 (this is OK) :D");
while (out.getFilePointer() < size - enddataSize) {
out.writeByte(0);
}
} else {
System.out.println("Perfect size of file :O");
}
// now append the unknown data at the end of file
int data;
while ((data = enddata.read()) != -1) {
out.write((byte) data);
}
enddata.close();
out.setLength(out.getFilePointer());
out.close();
System.out.println("Finished!");
} catch (FileNotFoundException e) {
System.out.println(e.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param in
* filename of text file
* @param out
* file to write the table
* @throws FileNotFoundException
* if the file is not found
* @throws IOException
* if any error occur while reading/writing
*/
private void createStringTable(String directory, String in, RandomAccessFile out) throws FileNotFoundException, IOException {
System.out.println("Reading " + directory + "/" + in);
RandomAccessFile file = new RandomAccessFile(directory + "/" + in, "r");
checkUnicodeBOM(file); // thanks notepad :/ (*sigh*)
Vector<String> stringTable = new Vector<String>();
Vector<Integer> unknownTable = new Vector<Integer>();
try {
while (true) {
// read all strings of file
String str = readString(file);
if (str == null) {
break;
}
// remove the labels and put the original data
unknownTable.add(Integer.parseInt(str.split(",")[0]));
str = str.substring(str.indexOf(",") + 1);
str = str.replaceAll("<NEWLINE>", "\n");
str = str.replaceAll("<EMPTY STRING>", "\0");
stringTable.add(str);
}
} catch (EOFException e) {
}
file.close();
int offset = stringTable.size() * 8;
// now calculate the offsets using the length in bytes of the strings
for (int i = 0; i < stringTable.size(); i++) {
writeInt(out, unknownTable.get(i));
writeInt(out, offset);
if (stringTable.elementAt(i).getBytes("UTF-8").length == 1
&& stringTable.elementAt(i).charAt(0) == '\0') {
offset++;
} else {
offset += stringTable.elementAt(i).getBytes("UTF-8").length + 1;
}
}
// now write the zero terminated string in the file
for (int i = 0; i < stringTable.size(); i++) {
String str = stringTable.elementAt(i);
if (str.equals("\0")) {
out.writeByte(0);
} else {
out.write(str.getBytes("UTF-8"));
out.writeByte(0);
}
}
}
}