Add option "ignore checksum"

This commit is contained in:
Boris Timofeev 2017-03-06 21:52:11 +03:00
parent b8af6b1da1
commit 4c1e0d844e
19 changed files with 99 additions and 51 deletions

View file

@ -30,7 +30,7 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
#include "xdelta3/xdelta3/xdelta3.h" #include "xdelta3/xdelta3/xdelta3.h"
#include "xdelta3/xdelta3/xdelta3.c" #include "xdelta3/xdelta3/xdelta3.c"
int apply(FILE *patch, FILE *in, FILE *out); int apply(FILE *patch, FILE *in, FILE *out, int ignoreChecksum);
const int ERR_UNABLE_OPEN_PATCH = -5001; const int ERR_UNABLE_OPEN_PATCH = -5001;
const int ERR_UNABLE_OPEN_ROM = -5002; const int ERR_UNABLE_OPEN_ROM = -5002;
@ -41,7 +41,8 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
jobject this, jobject this,
jstring patchPath, jstring patchPath,
jstring romPath, jstring romPath,
jstring outputPath) { jstring outputPath,
jboolean ignoreChecksum) {
int ret = 0; int ret = 0;
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL); const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
const char *romName = (*env)->GetStringUTFChars(env, romPath, NULL); const char *romName = (*env)->GetStringUTFChars(env, romPath, NULL);
@ -70,7 +71,7 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ERR_UNABLE_OPEN_OUTPUT; return ERR_UNABLE_OPEN_OUTPUT;
} }
ret = apply(patchFile, romFile, outputFile); ret = apply(patchFile, romFile, outputFile, (int)ignoreChecksum);
fclose(patchFile); fclose(patchFile);
fclose(romFile); fclose(romFile);
@ -78,7 +79,7 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ret; return ret;
} }
int apply(FILE *patch, FILE *in, FILE *out) { int apply(FILE *patch, FILE *in, FILE *out, int ignoreChecksum) {
int BUFFER_SIZE = 32768; int BUFFER_SIZE = 32768;
int r, ret; int r, ret;
@ -93,6 +94,9 @@ int apply(FILE *patch, FILE *in, FILE *out) {
xd3_init_config(&config, 0); xd3_init_config(&config, 0);
config.winsize = BUFFER_SIZE; config.winsize = BUFFER_SIZE;
if (ignoreChecksum) {
config.flags |= XD3_ADLER32_NOVER;
}
xd3_config_stream(&stream, &config); xd3_config_stream(&stream, &config);
source.blksize = BUFFER_SIZE; source.blksize = BUFFER_SIZE;

View file

@ -69,4 +69,9 @@ public class Settings {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString("output_directory", ""); return prefs.getString("output_directory", "");
} }
public static boolean getIgnoreChecksum(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("ignore_checksum", false);
}
} }

View file

@ -158,7 +158,7 @@ public class WorkerService extends IntentService {
try { try {
if ("ppf".equals(ext)) if ("ppf".equals(ext))
Utils.copyFile(this, romFile, outputFile); Utils.copyFile(this, romFile, outputFile);
patcher.apply(); patcher.apply(Settings.getIgnoreChecksum(this));
} catch (PatchException | IOException e) { } catch (PatchException | IOException e) {
if (Utils.getFreeSpace(outputFile.getParentFile()) == 0) { if (Utils.getFreeSpace(outputFile.getParentFile()) == 0) {
errorMsg = getString(R.string.notify_error_not_enough_space); errorMsg = getString(R.string.notify_error_not_enough_space);

View file

@ -43,7 +43,7 @@ public class APS extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
Patcher aps = null; Patcher aps = null;
switch (checkAPS(patchFile)) { switch (checkAPS(patchFile)) {
case APS_N64_PATCH: case APS_N64_PATCH:
@ -56,7 +56,7 @@ public class APS extends Patcher {
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch)); throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
} }
aps.apply(); aps.apply(ignoreChecksum);
} }
public int checkAPS(File file) throws PatchException, IOException { public int checkAPS(File file) throws PatchException, IOException {

View file

@ -77,7 +77,7 @@ public class APS_GBA extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
long fileSize1, fileSize2, bytesLeft, offset; long fileSize1, fileSize2, bytesLeft, offset;
int crc, patchCrc1, patchCrc2, pCount, oCount; int crc, patchCrc1, patchCrc2, pCount, oCount;
boolean isOriginal = false; boolean isOriginal = false;
@ -138,7 +138,8 @@ public class APS_GBA extends Patcher {
} else if (crc == patchCrc2) { } else if (crc == patchCrc2) {
isModified = true; isModified = true;
} else { } else {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (!ignoreChecksum)
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
} }
if (isOriginal && isModified) if (isOriginal && isModified)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch)); throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
@ -153,7 +154,7 @@ public class APS_GBA extends Patcher {
if (isOriginal) { if (isOriginal) {
Utils.truncateFile(outputFile, fileSize2); Utils.truncateFile(outputFile, fileSize2);
} else { } else if (isModified) {
Utils.truncateFile(outputFile, fileSize1); Utils.truncateFile(outputFile, fileSize1);
} }
} }

View file

@ -47,7 +47,7 @@ public class APS_N64 extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
BufferedInputStream romStream = null; BufferedInputStream romStream = null;
BufferedInputStream patchStream = null; BufferedInputStream patchStream = null;
BufferedOutputStream outputStream = null; BufferedOutputStream outputStream = null;
@ -96,8 +96,10 @@ public class APS_N64 extends Patcher {
int country = patchStream.read(); int country = patchStream.read();
byte[] crc = new byte[8]; byte[] crc = new byte[8];
patchStream.read(crc); patchStream.read(crc);
if (!validateROM(endianness, cardID, country, crc)) if (!ignoreChecksum) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (!validateROM(endianness, cardID, country, crc))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
// skip bytes for future expansion // skip bytes for future expansion
byte[] skip = new byte[5]; byte[] skip = new byte[5];
patchStream.read(skip); patchStream.read(skip);

View file

@ -50,7 +50,7 @@ public class BPS extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (patchFile.length() < 19) { if (patchFile.length() < 19) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
@ -68,9 +68,11 @@ public class BPS extends Patcher {
if (bpsCrc.getPatchFileCRC() != bpsCrc.getRealPatchCRC()) if (bpsCrc.getPatchFileCRC() != bpsCrc.getRealPatchCRC())
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
long realRomCrc = FileUtils.checksumCRC32(romFile); if (!ignoreChecksum) {
if (realRomCrc != bpsCrc.getInputFileCRC()) { long realRomCrc = FileUtils.checksumCRC32(romFile);
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (realRomCrc != bpsCrc.getInputFileCRC()) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
} }
patch = new RandomAccessFile(patchFile, "r").getChannel(); patch = new RandomAccessFile(patchFile, "r").getChannel();
@ -133,9 +135,11 @@ public class BPS extends Patcher {
IOUtils.closeQuietly(output); IOUtils.closeQuietly(output);
} }
long realOutCrc = FileUtils.checksumCRC32(outputFile); if(!ignoreChecksum) {
if (realOutCrc != bpsCrc.getOutputFileCRC()) long realOutCrc = FileUtils.checksumCRC32(outputFile);
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching)); if (realOutCrc != bpsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
}
} }
// decode pointer // decode pointer

View file

@ -42,7 +42,7 @@ public class DPS extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (patchFile.length() < MIN_SIZE_PATCH) { if (patchFile.length() < MIN_SIZE_PATCH) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
@ -63,9 +63,11 @@ public class DPS extends Patcher {
throw new PatchException(context.getString(R.string.notify_error_not_dps_patch)); throw new PatchException(context.getString(R.string.notify_error_not_dps_patch));
// verify rom // verify rom
long romSize = getUInt(buffer, 194); if (!ignoreChecksum) {
if (romSize != romFile.length()) long romSize = getUInt(buffer, 194);
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (romSize != romFile.length())
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
romStream = new RandomAccessFile(romFile, "r"); romStream = new RandomAccessFile(romFile, "r");
outputStream = new RandomAccessFile(outputFile, "rw"); outputStream = new RandomAccessFile(outputFile, "rw");

View file

@ -68,12 +68,12 @@ public class EBP extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
File cleanRom = File.createTempFile("rom", null, context.getCacheDir()); File cleanRom = File.createTempFile("rom", null, context.getCacheDir());
File ipsPatch = File.createTempFile("patch", null, context.getCacheDir()); File ipsPatch = File.createTempFile("patch", null, context.getCacheDir());
try { try {
Utils.copyFile(context, romFile, cleanRom); Utils.copyFile(context, romFile, cleanRom);
prepareCleanRom(cleanRom); prepareCleanRom(cleanRom, ignoreChecksum);
EBPtoIPS(patchFile, ipsPatch); EBPtoIPS(patchFile, ipsPatch);
@ -85,7 +85,7 @@ public class EBP extends Patcher {
} }
} }
private void prepareCleanRom(File file) throws IOException, PatchException { private void prepareCleanRom(File file, boolean ignoreChecksum) throws IOException, PatchException {
// delete smc header // delete smc header
SnesSmcHeader smc = new SnesSmcHeader(); SnesSmcHeader smc = new SnesSmcHeader();
try { try {
@ -95,8 +95,10 @@ public class EBP extends Patcher {
} }
// check rom size and remove unused expanded space // check rom size and remove unused expanded space
if (file.length() < EB_CLEAN_ROM_SIZE) if (!ignoreChecksum) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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)) if (file.length() > EB_CLEAN_ROM_SIZE && checkExpanded(file))
removeExpanded(file); removeExpanded(file);

View file

@ -50,6 +50,10 @@ public class IPS extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
apply();
}
public void apply() throws PatchException, IOException { public void apply() throws PatchException, IOException {
BufferedInputStream romStream = null; BufferedInputStream romStream = null;
BufferedInputStream patchStream = null; BufferedInputStream patchStream = null;

View file

@ -68,7 +68,7 @@ public class PPF extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (patchFile.length() < 61) { if (patchFile.length() < 61) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
} }
@ -78,10 +78,10 @@ public class PPF extends Patcher {
applyPPF1(); applyPPF1();
break; break;
case 2: case 2:
applyPPF2(); applyPPF2(ignoreChecksum);
break; break;
case 3: case 3:
applyPPF3(); applyPPF3(ignoreChecksum);
break; break;
default: default:
throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch)); throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch));
@ -112,15 +112,17 @@ public class PPF extends Patcher {
} }
} }
private void applyPPF2() throws IOException, PatchException { private void applyPPF2(boolean ignoreChecksum) throws IOException, PatchException {
try { try {
patchStream = new RandomAccessFile(patchFile, "r"); patchStream = new RandomAccessFile(patchFile, "r");
// Check size of ROM // Check size of ROM
patchStream.seek(56); patchStream.seek(56);
long romSize = readLittleEndianInt(patchStream); long romSize = readLittleEndianInt(patchStream);
if (romSize != romFile.length()) { if (!ignoreChecksum) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (romSize != romFile.length()) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
} }
outputStream = new RandomAccessFile(outputFile, "rw"); outputStream = new RandomAccessFile(outputFile, "rw");
@ -131,8 +133,10 @@ public class PPF extends Patcher {
outputStream.seek(0x9320); outputStream.seek(0x9320);
patchStream.read(patchBinaryBlock, 0, 1024); patchStream.read(patchBinaryBlock, 0, 1024);
outputStream.read(romBinaryBlock, 0, 1024); outputStream.read(romBinaryBlock, 0, 1024);
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock)) if (!ignoreChecksum) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
// Calculate end of patch data // Calculate end of patch data
long dataEnd = patchFile.length(); long dataEnd = patchFile.length();
@ -160,7 +164,7 @@ public class PPF extends Patcher {
} }
} }
private void applyPPF3() throws IOException, PatchException { private void applyPPF3(boolean ignoreChecksum) throws IOException, PatchException {
try { try {
patchStream = new RandomAccessFile(patchFile, "r"); patchStream = new RandomAccessFile(patchFile, "r");
outputStream = new RandomAccessFile(outputFile, "rw"); outputStream = new RandomAccessFile(outputFile, "rw");
@ -182,8 +186,10 @@ public class PPF extends Patcher {
} }
patchStream.read(patchBinaryBlock, 0, 1024); patchStream.read(patchBinaryBlock, 0, 1024);
outputStream.read(romBinaryBlock, 0, 1024); outputStream.read(romBinaryBlock, 0, 1024);
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock)) if (!ignoreChecksum) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
} }
// Calculate end of patch data // Calculate end of patch data

View file

@ -37,5 +37,5 @@ public abstract class Patcher {
outputFile = output; outputFile = output;
} }
public abstract void apply() throws PatchException, IOException; public abstract void apply(boolean ignoreChecksum) throws PatchException, IOException;
} }

View file

@ -44,7 +44,7 @@ public class UPS extends Patcher {
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (patchFile.length() < 18) { if (patchFile.length() < 18) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
@ -90,7 +90,9 @@ public class UPS extends Patcher {
ySize = tmp; ySize = tmp;
upsCrc.swapInOut(); upsCrc.swapInOut();
} else { } else {
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); if (!ignoreChecksum) {
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
}
} }
romStream = new BufferedInputStream(new FileInputStream(romFile)); romStream = new BufferedInputStream(new FileInputStream(romFile));
@ -125,9 +127,11 @@ public class UPS extends Patcher {
IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(outputStream);
} }
long realOutCrc = FileUtils.checksumCRC32(outputFile); if (!ignoreChecksum) {
if (realOutCrc != upsCrc.getOutputFileCRC()) long realOutCrc = FileUtils.checksumCRC32(outputFile);
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching)); if (realOutCrc != upsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
}
} }
// decode pointer // decode pointer

View file

@ -38,14 +38,14 @@ public class XDelta extends Patcher {
private static final int ERR_WRONG_CHECKSUM = -5010; private static final int ERR_WRONG_CHECKSUM = -5010;
private static final int ERR_INVALID_INPUT = -17712; private static final int ERR_INVALID_INPUT = -17712;
public static native int xdelta3apply(String patchPath, String romPath, String outputPath); public static native int xdelta3apply(String patchPath, String romPath, String outputPath, boolean ignoreChecksum);
public XDelta(Context context, File patch, File rom, File output) { public XDelta(Context context, File patch, File rom, File output) {
super(context, patch, rom, output); super(context, patch, rom, output);
} }
@Override @Override
public void apply() throws PatchException, IOException { public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (checkXDelta1(patchFile)) if (checkXDelta1(patchFile))
throw new PatchException(context.getString(R.string.notify_error_xdelta1_unsupported)); throw new PatchException(context.getString(R.string.notify_error_xdelta1_unsupported));
@ -55,7 +55,7 @@ public class XDelta extends Patcher {
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3)); throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
} }
int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath()); int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath(), ignoreChecksum);
switch (ret) { switch (ret) {
case NO_ERROR: case NO_ERROR:

View file

@ -113,6 +113,9 @@
<string name="settings_patch_directory">Укажите директорию с патчами</string> <string name="settings_patch_directory">Укажите директорию с патчами</string>
<string name="settings_output_directory">Укажите директорию для сохранения</string> <string name="settings_output_directory">Укажите директорию для сохранения</string>
<string name="settings_output_directory_description">Иначе ROM будет сохранён в директорию с ROM\'ами</string> <string name="settings_output_directory_description">Иначе ROM будет сохранён в директорию с ROM\'ами</string>
<string name="settings_patching_header">Применение патчей</string>
<string name="settings_ignore_checksum">Игнорировать контрольную сумму</string>
<string name="settings_ignore_checksum_description">Не показывать ошибку, если используется неподходящий ROM. Используйте осторожно: включив эту опцию можно получить непроходимую игру.</string>
<!-- Help activity --> <!-- Help activity -->
<string name="help_activity_title">Помощь</string> <string name="help_activity_title">Помощь</string>

View file

@ -113,6 +113,9 @@
<string name="settings_patch_directory">Specify patches directory</string> <string name="settings_patch_directory">Specify patches directory</string>
<string name="settings_output_directory">Specify output directory</string> <string name="settings_output_directory">Specify output directory</string>
<string name="settings_output_directory_description">Otherwise ROM will be stored in the ROM directory</string> <string name="settings_output_directory_description">Otherwise ROM will be stored in the ROM directory</string>
<string name="settings_patching_header">Patching</string>
<string name="settings_ignore_checksum">Ignore checksums</string>
<string name="settings_ignore_checksum_description">Do not interrupt patching if you use the wrong ROM. Use caution: this option can break your game.</string>
<!-- Help activity --> <!-- Help activity -->
<string name="help_activity_title">Help</string> <string name="help_activity_title">Help</string>

View file

@ -33,4 +33,12 @@
android:summary="@string/settings_output_directory_description" android:summary="@string/settings_output_directory_description"
android:title="@string/settings_output_directory"/> android:title="@string/settings_output_directory"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/settings_patching_header">
<CheckBoxPreference
android:defaultValue="false"
android:key="ignore_checksum"
android:title="@string/settings_ignore_checksum"
android:summary="@string/settings_ignore_checksum_description"/>
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View file

@ -48,7 +48,7 @@ public class BPSTest {
BPS patcher = new BPS(mockContext, patch, in, out); BPS patcher = new BPS(mockContext, patch, in, out);
try { try {
patcher.apply(); patcher.apply(false);
} catch (PatchException | IOException e) { } catch (PatchException | IOException e) {
fail("Patching failed"); fail("Patching failed");
} }

View file

@ -67,7 +67,7 @@ public class UPSTest {
UPS patcher = new UPS(mockContext, patch, in, out); UPS patcher = new UPS(mockContext, patch, in, out);
try { try {
patcher.apply(); patcher.apply(false);
} catch (PatchException | IOException e) { } catch (PatchException | IOException e) {
fail("Patching failed"); fail("Patching failed");
} }