From d39682952de54c049b8d76bb4a4ad88ff768822b Mon Sep 17 00:00:00 2001 From: Boris Timofeev Date: Mon, 19 Dec 2016 15:48:44 +0300 Subject: [PATCH] Support EBP patches --- app/src/main/AndroidManifest.xml | 1 + app/src/main/assets/patch/ebp/wrong1.ips | Bin 0 -> 72 bytes app/src/main/assets/patch/ebp/wrong2.ips | Bin 0 -> 58 bytes app/src/main/assets/patch/ebp/wrong3.ips | Bin 0 -> 32 bytes app/src/main/assets/patch/ebp/wrong4.ips | Bin 0 -> 3202 bytes app/src/main/assets/patch/ebp/wrong5.ips | Bin 0 -> 106 bytes app/src/main/assets/patch/ebp/wrong6.ips | Bin 0 -> 14754 bytes .../java/org/emunix/unipatcher/Utils.java | 8 + .../org/emunix/unipatcher/WorkerService.java | 5 +- .../java/org/emunix/unipatcher/patch/EBP.java | 275 ++++++++++++++++++ .../unipatcher/tools/SnesSmcHeader.java | 12 +- .../ui/activity/FilePickerActivity.java | 12 +- app/src/main/res/raw/about.html | 2 +- app/src/main/res/raw/changelog.html | 5 + app/src/main/res/raw/faq.html | 2 +- app/src/main/res/values/strings.xml | 3 +- google-play/en/google-play.txt | 1 + 17 files changed, 307 insertions(+), 19 deletions(-) create mode 100644 app/src/main/assets/patch/ebp/wrong1.ips create mode 100644 app/src/main/assets/patch/ebp/wrong2.ips create mode 100644 app/src/main/assets/patch/ebp/wrong3.ips create mode 100644 app/src/main/assets/patch/ebp/wrong4.ips create mode 100644 app/src/main/assets/patch/ebp/wrong5.ips create mode 100644 app/src/main/assets/patch/ebp/wrong6.ips create mode 100644 app/src/main/java/org/emunix/unipatcher/patch/EBP.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 423a369..15e85e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ + diff --git a/app/src/main/assets/patch/ebp/wrong1.ips b/app/src/main/assets/patch/ebp/wrong1.ips new file mode 100644 index 0000000000000000000000000000000000000000..8e59ea6fc20ca7519710c90f14f40995075de439 GIT binary patch literal 72 zcmWG=3~}~g(2!+dx*)~SJePsdkfC`61EUecLJbgmp(6w12ZsM=7?>3l6d3+r1&T5L af6l=2{{8=h%zv*ju)hEF_wWZ-e>VWgDH$UG literal 0 HcmV?d00001 diff --git a/app/src/main/assets/patch/ebp/wrong2.ips b/app/src/main/assets/patch/ebp/wrong2.ips new file mode 100644 index 0000000000000000000000000000000000000000..17264c01d6a4b172eb9751bb8365db754b664204 GIT binary patch literal 58 zcmWG=3~}~g(2!+dx*)~SJePsdkfC`61EUecLJbgmp(6w12gd);8Cc%G|9_DA?==S2 N_n-bA{^08G1_0N;6z>24 literal 0 HcmV?d00001 diff --git a/app/src/main/assets/patch/ebp/wrong3.ips b/app/src/main/assets/patch/ebp/wrong3.ips new file mode 100644 index 0000000000000000000000000000000000000000..6e149454608fa5d52fc244a82a2fab304230c446 GIT binary patch literal 32 gcmWG=3~}~gXkNs?_<^B$Er?vG3nm?bq^rLh0HhKM6aWAK literal 0 HcmV?d00001 diff --git a/app/src/main/assets/patch/ebp/wrong4.ips b/app/src/main/assets/patch/ebp/wrong4.ips new file mode 100644 index 0000000000000000000000000000000000000000..a9a53505ec19fefca14b2413a785cd1b76bf167f GIT binary patch literal 3202 zcmb7GeT*B$6`vjZa*jhT4%(Ty>-u5}YD9&aD*=Hjp{mI>QPfmq96}wE>#60B7Kw_w z*FCq6xm`r03U`pTt|2j4@(w4dAbhxr`7 zCxMk>c7S}N>>4e^>0Zk;Mt=SwQq++NbRFX4INSG-nz<`?zcPphm4W0p6`cE75{ya> z!w`1Sgq;(WR^$xET`vme-b72)A8 zrGpU|fcw{-TY(4r2=pr8v!&x9HKVk^%;ue!~7KEF&)+mjGvXv`E1a zdXLESMMx)v0nS`k@mpP8-E%FsW*(6ct5)e&s|%88sa`j2da-N3Y^lo03Da@h3URtf zV4GI7!mk38{mmCWO!TB@(b0H$@VaXgvSZ{Kpc6(-@QUTKXUQ_QF?J!lk|Kc0>?>?v zl$2=B+VmW)+U!O4F`be`GOn>^Q1SJU)oSz%`>I58cwI5Oia$C{4v^DwE7^fqyk=?f z|EE3DA=zfTeyCeluaM(Bn^F)s5r5?HNaWB>P+Igh*sP4{tpO*3B>^ z7dg}?y-9|Sp6A&Cb{t|^tho-k9Yhd(ZuoSR6jBdecnyl^5(Hf87AGQUlmIZeC3e0m zotD)#F9p_nf)G{&_379gc&ow+c|b0MVlrfRN)VEHI%3Zb#IQ$40p9MpX5c!BMVjN0 zgRv$(e&h%oXp(FSeTF1`Q4!`WDJP9#qs*hZ4Xd?8`Z0(iDyH6&WnGgM4z(fo|^(o76x(#ba#(!8a zPG5**^YdNTrcg3zShhc20uBJVqs&y2Q01wD4rGa0VaYp?{u)X@FU6@7k3+1gRpHQ= zn{<7{BcJpl6XIG($rGfogvBEbc6JHz0TZq?c9JgvO+LAlbT4~zvA6`T?$ZyOcEg5a zyBPI)(CI~J4*yu*lYg5a z{{^G}z>r2NNw86!N&?lC{zmF8xTo@{f~9(Xd<=@-&3d z@CwM%i{Oc-$SM11K|3ai9N1s?-6q$YdJ7NX3D+rv+;F)W&hMnu*-1L$RfbnV>N~;Z zVci$aD1_jF3UayKBHacJBa)7H0+~?~m_aX3eU7d#i@Hgk zhiAxmLgxRAvJV=N$pmMs0-0i|KLNL84i8DnzZ*g(DDc<=I?Kpwv;#!Fj-CmHO%L^m z-!P%EkhDW*TksOyk;uT~CbpnQb*S%Iz0zpd@rB%B+QRxAc8U~qUTp}A{oeE7MUlhz z_1Z%D-Np#mHv*!;1s(TFww70QrxtYA=T(z!l0lID-?Ui2t&@M5A-x#2&hZ@H+{>Ex z=?mZ#8x2jT!PfSA72R2LwFZ4ycEFCOAB!e*!HVRsTDncNs_Ba|c^kal+QZBJMo=dO z6Al{mYWn@K3GJ5rvg@U6kpQjJc_mWSDs|WE!o23|9_ur}cB^dDXM#pK+K=ndo$)Jq rK6JCQ;80SpRD>mb`XeJR-)k|*%Rc>por+ePn{mvl=&Z15+vfiP^)|}O literal 0 HcmV?d00001 diff --git a/app/src/main/assets/patch/ebp/wrong5.ips b/app/src/main/assets/patch/ebp/wrong5.ips new file mode 100644 index 0000000000000000000000000000000000000000..a70b0b25aaac43ba21cf85be7bc6baba0868928b GIT binary patch literal 106 zcmWG=3~}~g(2!+dx*)~SJeNVx@cf5*gAaUC{U7R$z)Xgg00!2z5>Bn<3=1_F*dX!@ z3mq94KQR2?!@$76!ocwV4+E29Bjf+)3@q>8|3Aq5_ZkE1`%ixlf6#jXR3Q9?!PVal E02iq#hX4Qo literal 0 HcmV?d00001 diff --git a/app/src/main/assets/patch/ebp/wrong6.ips b/app/src/main/assets/patch/ebp/wrong6.ips new file mode 100644 index 0000000000000000000000000000000000000000..0b9c91a1ba41ad60161b6aad7d4a54e63da74644 GIT binary patch literal 14754 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. +*/ + +package org.emunix.unipatcher.patch; + +import android.content.Context; +import android.support.annotation.NonNull; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.emunix.unipatcher.R; +import org.emunix.unipatcher.Utils; +import org.emunix.unipatcher.tools.RomException; +import org.emunix.unipatcher.tools.SnesSmcHeader; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; + +public class EBP extends Patch { + + private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH" + private static final byte[] EARTH_BOUND = {0x45, 0x41, 0x52, 0x54, 0x48, 0x20, 0x42, 0x4f, 0x55, 0x4e, 0x44}; + private static final String EB_CLEAN_MD5 = "a864b2e5c141d2dec1c4cbed75a42a85"; + private static final int EB_CLEAN_ROM_SIZE = 0x300000; + private static final HashMap EB_WRONG_MD5; + + static { + EB_WRONG_MD5 = new HashMap<>(); + EB_WRONG_MD5.put("8c28ce81c7d359cf9ccaa00d41f8ad33", "patch/ebp/wrong1.ips"); + EB_WRONG_MD5.put("b2dcafd3252cc4697bf4b89ea3358cd5", "patch/ebp/wrong2.ips"); + EB_WRONG_MD5.put("0b8c04fc0182e380ff0e3fe8fdd3b183", "patch/ebp/wrong3.ips"); + EB_WRONG_MD5.put("2225f8a979296b7dcccdda17b6a4f575", "patch/ebp/wrong4.ips"); + EB_WRONG_MD5.put("eb83b9b6ea5692cefe06e54ea3ec9394", "patch/ebp/wrong5.ips"); + EB_WRONG_MD5.put("cc9fa297e7bf9af21f7f179e657f1aa1", "patch/ebp/wrong6.ips"); + } + + public EBP(Context context, File patch, File rom, File output) { + super(context, patch, rom, output); + } + + @Override + public void apply() throws PatchException, IOException { + File cleanRom = File.createTempFile("rom", null, context.getCacheDir()); + File ipsPatch = File.createTempFile("patch", null, context.getCacheDir()); + try { + Utils.copyFile(context, romFile, cleanRom); + prepareCleanRom(cleanRom); + + EBPtoIPS(patchFile, ipsPatch); + + IPS ips = new IPS(context, ipsPatch, cleanRom, outputFile); + ips.apply(); + } finally { + FileUtils.deleteQuietly(ipsPatch); + FileUtils.deleteQuietly(cleanRom); + } + } + + private void prepareCleanRom(File file) throws IOException, PatchException { + // delete smc header + SnesSmcHeader smc = new SnesSmcHeader(); + try { + smc.deleteSnesSmcHeader(context, file, false); + } catch (RomException e) { + // no header + } + + // check rom size and remove unused expanded space + if (file.length() < EB_CLEAN_ROM_SIZE) + throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); + if (file.length() > EB_CLEAN_ROM_SIZE && checkExpanded(file)) + removeExpanded(file); + + // try to fix the ROM if it's incorrect + if (!checkMD5(file)) + repairRom(file); + + // if we couldn't fix the ROM, try to remove a 0xff byte at the end. + if (!checkMD5(file)) { + int length = (int)file.length(); + byte[] buffer = new byte[length]; + FileInputStream in = new FileInputStream(file); + int count = in.read(buffer); + in.close(); + if (count != file.length()) + throw new IOException("Unable read file"); + if (buffer[length - 1] == 0xff) + buffer[length - 1] = 0; + + if (checkMD5(buffer)) { + RandomAccessFile f = new RandomAccessFile(file, "rw"); + f.seek(length - 1); + f.write(0); + f.close(); + } + } + + if (!checkMD5(file) || !checkEarthBound(file)) { + throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); + } + } + + private boolean checkExpanded(File file) throws IOException { + byte[] byteArray = new byte[EB_CLEAN_ROM_SIZE]; + FileInputStream f = new FileInputStream(file); + int count = f.read(byteArray); + IOUtils.closeQuietly(f); + if (count < EB_CLEAN_ROM_SIZE) + throw new IOException("Unable to read 0x300000 bytes from ROM"); + // ExHiROM expanded ROMs have two bytes different from LoROM. + byteArray[0xffd5] = 0x31; + byteArray[0xffd7] = 0x0c; + + // If the normal area is unmodified, then the expanded area is unused and can be deleted. + return checkMD5(byteArray); + } + + private void removeExpanded(File file) throws IOException { + if (file.length() > 0x400000) { + RandomAccessFile f = new RandomAccessFile(file, "rw"); + f.seek(0xffd5); + f.write(0x31); + f.seek(0xffd7); + f.write(0x0c); + f.close(); + } + FileChannel fc = new FileOutputStream(file, true).getChannel(); + fc.truncate(EB_CLEAN_ROM_SIZE); + fc.close(); + } + + private void repairRom(File file) throws IOException, PatchException { + String md5 = calculateMD5(file); + if (EB_WRONG_MD5.containsKey(md5)) { + + // copy patch from assets + InputStream in = context.getAssets().open(EB_WRONG_MD5.get(md5)); + File patch = File.createTempFile("patch", null, context.getCacheDir()); + FileUtils.copyToFile(in, patch); + IOUtils.closeQuietly(in); + + // fix rom + File tmpFile = File.createTempFile("rom", null, context.getCacheDir()); + FileUtils.copyFile(file, tmpFile); + IPS ips = new IPS(context, patch, tmpFile, file); + ips.apply(); + + FileUtils.deleteQuietly(tmpFile); + FileUtils.deleteQuietly(patch); + } + } + + private boolean checkMD5(byte[] array) throws IOException { + try { + MessageDigest md5Digest = MessageDigest.getInstance("MD5"); + md5Digest.update(array); + String md5 = Utils.bytesToHexString(md5Digest.digest()); + return md5.equals(EB_CLEAN_MD5); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e.getMessage()); + } + } + + private boolean checkMD5(File file) throws IOException { + String md5 = calculateMD5(file); + return md5.equals(EB_CLEAN_MD5); + } + + @NonNull + private String calculateMD5(File file) throws IOException { + FileInputStream f = new FileInputStream(file); + try { + MessageDigest md5Digest = MessageDigest.getInstance("MD5"); + byte[] byteArray = new byte[32768]; + int count; + while ((count = f.read(byteArray)) != -1) + md5Digest.update(byteArray, 0, count); + return Utils.bytesToHexString(md5Digest.digest()); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e.getMessage()); + } finally { + IOUtils.closeQuietly(f); + } + } + + private boolean checkEarthBound(File file) throws IOException { + byte[] buffer = new byte[11]; + RandomAccessFile f = new RandomAccessFile(file, "r"); + f.seek(0xffc0); + f.read(buffer); + f.close(); + return Arrays.equals(EARTH_BOUND, buffer); + } + + private void EBPtoIPS(File ebpFile, File ipsFile) throws IOException, PatchException { + BufferedInputStream ebp = null; + BufferedOutputStream ips = null; + try { + ebp = new BufferedInputStream(new FileInputStream(ebpFile)); + ips = new BufferedOutputStream(new FileOutputStream(ipsFile)); + + int size; + byte[] buffer = new byte[65536]; + + if (ebpFile.length() < 14) { + throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); + } + + // check magic string + byte[] magic = new byte[5]; + size = ebp.read(magic); + if (size != 5 || !Arrays.equals(magic, MAGIC_NUMBER)) + throw new PatchException(context.getString(R.string.notify_error_not_ebp_patch)); + + ips.write(magic); + + while (true) { + size = ebp.read(buffer, 0, 3); + if (size < 3) + throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); + ips.write(buffer, 0, 3); + if (buffer[0] == 0x45 && buffer[1] == 0x4f && buffer[2] == 0x46) // EOF + break; + size = ebp.read(buffer, 0, 2); + if (size < 2) + throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); + ips.write(buffer, 0, 2); + size = (((int)buffer[0] & 0xff) << 8) + ((int)buffer[1] & 0xff); + if (size != 0) { + int c = ebp.read(buffer, 0, size); + if (c < size) + throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); + ips.write(buffer, 0, size); + } else { + size = ebp.read(buffer, 0, 3); + if (size < 3) + throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); + ips.write(buffer, 0, 3); + } + } + } finally { + IOUtils.closeQuietly(ips); + IOUtils.closeQuietly(ebp); + } + } +} diff --git a/app/src/main/java/org/emunix/unipatcher/tools/SnesSmcHeader.java b/app/src/main/java/org/emunix/unipatcher/tools/SnesSmcHeader.java index 5a81e25..7d35899 100644 --- a/app/src/main/java/org/emunix/unipatcher/tools/SnesSmcHeader.java +++ b/app/src/main/java/org/emunix/unipatcher/tools/SnesSmcHeader.java @@ -39,9 +39,9 @@ public class SnesSmcHeader { return (romSize & 0x7fff) == 512; } - public void deleteSnesSmcHeader(Context context, File romfile) throws IOException, RomException { + public void deleteSnesSmcHeader(Context context, File romfile, boolean saveHeader) throws IOException, RomException { if (!isHasSmcHeader(romfile)) { - throw new RomException(); + throw new RomException("ROM don't have SMC header"); } FileInputStream inputRom = null; @@ -51,17 +51,19 @@ public class SnesSmcHeader { try { tmpfile = File.createTempFile(romfile.getName(), null, romfile.getParentFile()); - File headerfile = new File(romfile.getPath()+".smc_header"); inputRom = new FileInputStream(romfile); outputRom = new FileOutputStream(tmpfile); - outputHeader = new FileOutputStream(headerfile); // write smc header in a file byte[] header = new byte[HEADER_SIZE]; int length; length = inputRom.read(header); - outputHeader.write(header, 0, length); + if (saveHeader) { + File headerfile = new File(romfile.getPath()+".smc_header"); + outputHeader = new FileOutputStream(headerfile); + outputHeader.write(header, 0, length); + } // write headerless rom in tmp file byte[] buffer = new byte[32768]; diff --git a/app/src/main/java/org/emunix/unipatcher/ui/activity/FilePickerActivity.java b/app/src/main/java/org/emunix/unipatcher/ui/activity/FilePickerActivity.java index f8fe238..85835c1 100644 --- a/app/src/main/java/org/emunix/unipatcher/ui/activity/FilePickerActivity.java +++ b/app/src/main/java/org/emunix/unipatcher/ui/activity/FilePickerActivity.java @@ -420,8 +420,8 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA fis.close(); String crc32 = Long.toHexString(crc32Digest.getValue()); - String md5 = bytesToHexString(md5Digest.digest()); - String sha1 = bytesToHexString(sha1Digest.digest()); + String md5 = Utils.bytesToHexString(md5Digest.digest()); + String sha1 = Utils.bytesToHexString(sha1Digest.digest()); HashMap checksum = new HashMap<>(); checksum.put(CRC32, crc32); @@ -429,13 +429,5 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA checksum.put(SHA1, sha1); return checksum; } - - private String bytesToHexString(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < bytes.length ;i++) { - sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1)); - } - return sb.toString(); - } } } diff --git a/app/src/main/res/raw/about.html b/app/src/main/res/raw/about.html index 484e331..8b916ff 100644 --- a/app/src/main/res/raw/about.html +++ b/app/src/main/res/raw/about.html @@ -1,5 +1,5 @@ -

UniPatcher is a ROM patcher that supports IPS, UPS, BPS, PPF, DPS and XDelta3 patch types.

+

UniPatcher is a ROM patcher that supports IPS, UPS, BPS, PPF, DPS, EBP and XDelta3 patch types.

Additional features:

  • Fix checksum in Sega Mega Drive ROMs
  • diff --git a/app/src/main/res/raw/changelog.html b/app/src/main/res/raw/changelog.html index db0d72e..d382b6c 100644 --- a/app/src/main/res/raw/changelog.html +++ b/app/src/main/res/raw/changelog.html @@ -1,5 +1,10 @@ +

    0.11 (December, 2016)

    +
      +
    • Support EBP patches (for EarthBound game)
    • +
    +

    0.10.1 (December 6, 2016)

    • Added dark theme
    • diff --git a/app/src/main/res/raw/faq.html b/app/src/main/res/raw/faq.html index 76e898c..c01dc0e 100644 --- a/app/src/main/res/raw/faq.html +++ b/app/src/main/res/raw/faq.html @@ -6,7 +6,7 @@

      UniPatcher is an Android tool for applying patches to ROM images of various video game consoles.

      What patch formats are supported?

      -

      The app supports IPS, UPS, BPS, PPF, DPS and XDelta3 patches.

      +

      The app supports IPS, UPS, BPS, PPF, DPS, EBP and XDelta3 patches.

      Can I hack or crack Android game using this app?

      No. UniPatcher is not designed to hack the Android games.

      diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2542535..2b5f8b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -64,6 +64,7 @@ Not a valid UPS patch Not a valid BPS patch Not a valid PPF patch + Not a valid EBP patch Not a valid DPS patch Not a valid XDelta3 patch I am not able to work with XDelta1 patches @@ -123,7 +124,7 @@ There are no email clients installed Share UniPatcher - Download the best ROM patcher for Android. It supports IPS, UPS, BPS, PPF, DPS and XDelta3 patch types.\n\n + Download the best ROM patcher for Android. It supports IPS, UPS, BPS, PPF, DPS, EBP and XDelta3 patch types.\n\n Apply patch diff --git a/google-play/en/google-play.txt b/google-play/en/google-play.txt index ee6e6dc..32ce938 100644 --- a/google-play/en/google-play.txt +++ b/google-play/en/google-play.txt @@ -12,6 +12,7 @@ Utility to apply patches to game rom. • BPS • PPF • DPS +• EBP • XDelta 3