Compare commits

...

70 commits

Author SHA1 Message Date
Boris Timofeev
5531a55fea version bump to 0.15 2017-12-07 12:52:03 +03:00
Boris Timofeev
0369c6e85a added Chinese and Portuguese translations
Thanks to Nabarl Wang, Davi Lopes, Matheus Silva Sales, Jhonatan Cardoso
2017-12-07 12:11:41 +03:00
Boris Timofeev
faaee235a2 update sdk and libs 2017-12-01 00:20:03 +03:00
Boris Timofeev
af8216dd05 update faq 2017-09-27 18:57:46 +03:00
Boris Timofeev
5cd83abcfc Merge branch 'master' of github.com:btimofeev/UniPatcher 2017-09-27 18:47:20 +03:00
Boris Timofeev
c0c26e59ad Merge pull request #14 from comradekingu/patch-5
General rework
2017-09-26 12:23:23 +03:00
Allan Nordhøy
beb6ed9404 General rework 2017-09-26 11:08:00 +02:00
Boris Timofeev
2cc0e0585d update faq 2017-09-08 10:17:02 +03:00
Boris Timofeev
512f23e680 update faq 2017-09-06 12:33:12 +03:00
Boris Timofeev
655e0954fa update changelog 2017-09-02 13:16:35 +03:00
Boris Timofeev
a959211176 fix crash on Android O 2017-09-02 12:36:13 +03:00
Boris Timofeev
f61cfb5121 vector icons 2017-09-02 12:33:27 +03:00
Boris Timofeev
0cab3fa8b5 placed content in the center of the screen on tablets 2017-09-01 23:25:59 +03:00
Boris Timofeev
550b14896e support startForegroundService from Android O 2017-09-01 18:27:07 +03:00
Boris Timofeev
3f56868998 support notification channels from Android O 2017-09-01 18:12:07 +03:00
Boris Timofeev
70db06eebc update libs and target SDK version to 26 2017-09-01 11:34:11 +03:00
Boris Timofeev
9ec29e3936 added donate snackbar 2017-08-31 16:39:26 +03:00
Boris Timofeev
6fea1b7b8e Globals class was deleted and its contents moved to UniPatcher class 2017-08-31 11:55:39 +03:00
Boris Timofeev
3e2f57dbd7 actions are moved to a separate class 2017-08-31 11:14:13 +03:00
Boris Timofeev
b267f87a99 Merge pull request #13 from comradekingu/patch-4
Specifying free
2017-08-31 08:48:05 +03:00
Boris Timofeev
e6b3dc1331 Merge pull request #12 from comradekingu/patch-3
"Gratis", and new Genesis checksum text
2017-08-31 08:41:31 +03:00
Boris Timofeev
8852b80907 Merge pull request #11 from comradekingu/patch-2
Spelling: July
2017-08-31 08:38:33 +03:00
Allan Nordhøy
a22d786f93 Specifying free 2017-08-31 06:16:29 +02:00
Allan Nordhøy
2c4512e775 "Gratis", and new Genesis checksum text 2017-08-31 06:13:28 +02:00
Allan Nordhøy
b9f9c30009 Spelling: July 2017-08-31 06:07:21 +02:00
Boris Timofeev
b460e3b0a6 removed unnecessary files (array.xml) 2017-08-26 15:04:30 +03:00
Boris Timofeev
c0bdd147cd removed support for all ABI except armeabi-v7a and x86 2017-08-26 14:44:35 +03:00
Boris Timofeev
b1705b3e7d removed version code variables (to support automatic assembly in f-droid) 2017-08-26 14:01:33 +03:00
Boris Timofeev
f137b4950f change license of material design icons by google 2017-08-26 13:52:26 +03:00
Boris Timofeev
e055a65d28 version bump to 0.14.2 2017-07-13 09:30:33 +03:00
Boris Timofeev
c1192dd20d fix typo 2017-07-07 12:10:23 +03:00
Boris Timofeev
c6869d01ba returned a string to the readme 2017-07-07 12:09:01 +03:00
Boris Timofeev
6b27827d4f Merge pull request #9 from comradekingu/patch-1
Clarification of intent
2017-07-07 12:03:46 +03:00
Boris Timofeev
e6174550ab fetch strings from Transifex && update Ukrainian 2017-07-07 12:03:44 +03:00
Boris Timofeev
a36c4ac1c7 added Korean translation
Thanks to Minseo Lee
2017-07-07 11:17:29 +03:00
Boris Timofeev
c3b0d595d1 update libs 2017-07-07 11:08:40 +03:00
Allan Nordhøy
7d9fd3017d Clarification of intent 2017-06-05 12:46:17 +02:00
Boris Timofeev
1cb040b216 Merge branch 'f-droid' 2017-05-31 20:28:27 +03:00
Boris Timofeev
18fe8fe8b1 update japanese translation 2017-05-25 22:05:05 +03:00
Boris Timofeev
9192fb975a set android:extractNativeLibs to true
it should fixes an F-Droid build issue
2017-05-23 23:44:38 +03:00
Boris Timofeev
72264b4ffb version bump to 0.14.1 2017-04-30 19:52:20 +03:00
Boris Timofeev
6c01a85f86 change e-mail to unipatcher@gmail.com 2017-04-29 20:46:43 +03:00
Boris Timofeev
77b4916e0b added French translation
Thanks to Thibaut Panis
2017-04-29 20:45:05 +03:00
Boris Timofeev
e55ed589ad update translations 2017-04-29 20:11:53 +03:00
Boris Timofeev
15604c6163 added Japanese translation
Thanks to Naofumi Fukue
2017-04-28 12:35:45 +03:00
Boris Timofeev
c25ddc247a added Norwegian Bokmål translation
Thanks to Allan Nordhøy
2017-04-28 12:16:12 +03:00
Boris Timofeev
815a64a7ed added a script that updates translations 2017-04-26 17:28:09 +03:00
Boris Timofeev
23b1414549 update libraries 2017-04-26 17:26:03 +03:00
Boris Timofeev
415e2cb2aa update translations 2017-04-26 17:20:08 +03:00
Boris Timofeev
bb41a70bb0 updated gradle 2017-04-26 14:20:11 +03:00
Boris Timofeev
312634cf84 changed some strings (thanks to Allan Nordhøy) 2017-04-26 14:19:31 +03:00
Boris Timofeev
069939444d Changed the position of the screenshots in README 2017-04-04 17:02:20 +03:00
Boris Timofeev
b5ddf27443 support ".xd" file extension 2017-04-04 12:32:42 +03:00
Boris Timofeev
9051da11d3 added f-droid badge 2017-04-04 12:22:26 +03:00
Boris Timofeev
039fbb85a8 Version bump to 0.14 2017-03-23 14:01:58 +03:00
Boris Timofeev
4538bcea11 Fixed bug with deleting directories
Fixes #6
2017-03-22 18:48:27 +03:00
Boris Timofeev
cd86ae9188 Update changelog 2017-03-22 18:18:55 +03:00
Boris Timofeev
a2e6052538 Added name of Spanish translator to about.md 2017-03-22 18:00:46 +03:00
Boris Timofeev
f8e39c52f5 Added Spanish translation 2017-03-22 17:55:40 +03:00
Boris Timofeev
7dad097c6f Support creating XDelta3 patches 2017-03-21 16:04:49 +03:00
Boris Timofeev
a7228702fe Rewrite text for market 2017-03-15 16:23:02 +03:00
Boris Timofeev
540a49979c Revert back mips architecture 2017-03-15 13:01:56 +03:00
Boris Timofeev
61c37de75f Update libs 2017-03-15 12:25:43 +03:00
Boris Timofeev
a987b5326a Added missed strings to tranlations 2017-03-15 12:25:24 +03:00
Boris Timofeev
b14dd6b852 Removed Firebase 2017-03-15 12:23:36 +03:00
Boris Timofeev
4c1e0d844e Add option "ignore checksum" 2017-03-06 21:52:11 +03:00
Boris Timofeev
b8af6b1da1 Removed mips archeticture from build
Build failed on NDK-14

Issue: https://github.com/android-ndk/ndk/issues/290
2017-03-06 11:13:51 +03:00
Boris Timofeev
ff44a72f80 Update libs 2017-03-06 11:13:35 +03:00
Boris Timofeev
f5d8d8d2ec Update gradle 2017-03-06 09:33:23 +03:00
Boris Timofeev
4a36743845 Updated Ukrainian translation 2017-02-17 10:16:18 +03:00
206 changed files with 5207 additions and 664 deletions

View file

@ -1,5 +1,6 @@
[main]
host = https://www.transifex.com
lang_map = he: iw, zh_CN: zh-rCN, zh_TW: zh-rTW, es_MX: es-rMX, pt_BR: pt-rBR, nb_NO: nb-rNO
[unipatcher.strings_xml]
file_filter = app/src/main/res/values-<lang>/strings.xml

View file

@ -8,28 +8,30 @@ UniPatcher is a ROM patcher for Android that supports IPS, IPS32, UPS, BPS, APS
### Additional features:
* Creating XDelta3 patches
* Fix checksum in Sega Mega Drive ROMs
* Add/Delete SMC header in Super Nintendo ROMs
### Screenshots:
<img src="/google-play/screenshot_1.png" width="350">
<img src="/google-play/screenshot_2.png" width="350">
<img src="/google-play/screenshot_1.png" width="350"> <img src="/google-play/screenshot_2.png" width="350">
### Install UniPatcher:
[<img src="/google-play/badges/google-play.png" width="220">](https://play.google.com/store/apps/details?id=org.emunix.unipatcher) [<img src="/google-play/badges/amazon.png" width="220">](http://www.amazon.com/gp/mas/dl/android?p=org.emunix.unipatcher) [<img src="/google-play/badges/slideme.png" width="220">](http://slideme.org/application/unipatcher)
[<img src="/google-play/badges/google-play.png" alt="Get it on Google Play" width="220">](https://play.google.com/store/apps/details?id=org.emunix.unipatcher) [<img src="/google-play/badges/f-droid.png" alt="Get it on F-Droid" width="220">](https://f-droid.org/app/org.emunix.unipatcher)
[<img src="/google-play/badges/amazon.png" alt="Get it on Amazon" width="220">](http://www.amazon.com/gp/mas/dl/android?p=org.emunix.unipatcher) [<img src="/google-play/badges/slideme.png" alt="Get it on SlideMe" width="220">](http://slideme.org/application/unipatcher)
[... or get a Release APK here on GitHub](https://github.com/btimofeev/UniPatcher/releases)
### How to contribute to UniPatcher:
### Contribute:
#### Report a bug or suggest a new feature
#### Report a bug or suggest features
Bugs and new features are being discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
These are discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
#### Help with translations
If you want to translate UniPatcher into another language, you can visit the [Transifex project page](https://www.transifex.com/unipatcher/unipatcher/).
#### Translations
Help translate UniPatcher into another language on the [Transifex project page](https://www.transifex.com/unipatcher/unipatcher/).
### License
UniPatcher is licensed under the GPL version 3. You can find the license text in the COPYING file.

View file

@ -1,13 +1,8 @@
def versionMajor = 0
def versionMinor = 13
def versionPatch = 2
def versionBuild = 0
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileSdkVersion 27
buildToolsVersion '27.0.1'
signingConfigs {
release
@ -16,13 +11,17 @@ android {
defaultConfig {
applicationId "org.emunix.unipatcher"
minSdkVersion 14
targetSdkVersion 25
versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
targetSdkVersion 27
versionCode 150000
versionName "0.15"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
externalNativeBuild {
cmake {
cppFlags ""
arguments "-DANDROID_PLATFORM=android-14"
arguments "-DANDROID_PLATFORM=android-14", "-DCMAKE_BUILD_TYPE=Release"
}
}
}
@ -36,6 +35,8 @@ android {
}
}
flavorDimensions "default"
productFlavors {
free {
buildConfigField "String", "RATE_URL", "\"https://github.com/btimofeev/UniPatcher\""
@ -75,6 +76,11 @@ android {
path "CMakeLists.txt"
}
}
lintOptions {
disable 'MissingTranslation'
}
}
def Properties props = new Properties()
@ -103,21 +109,18 @@ if (propFile.canRead()) {
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.2.28'
testCompile 'org.mockito:mockito-core:2.12.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:25.1.1'
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.android.support:cardview-v7:25.1.1'
compile 'com.android.support:preference-v14:25.1.1'
compile 'com.android.support:recyclerview-v7:25.1.1'
compile 'com.android.support:design:25.1.1'
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-crash:10.0.1'
compile 'com.android.support:support-v4:27.0.2'
compile 'com.android.support:appcompat-v7:27.0.2'
compile 'com.android.support:cardview-v7:27.0.2'
compile 'com.android.support:preference-v14:27.0.2'
compile 'com.android.support:recyclerview-v7:27.0.2'
compile 'com.android.support:design:27.0.2'
compile 'com.android.support:support-v13:27.0.2' // used in material-dialogs
compile 'commons-io:commons-io:2.5'
compile 'org.sufficientlysecure:donations:2.5'
compile 'org.sufficientlysecure:html-textview:3.0'
compile 'org.sufficientlysecure:html-textview:3.5'
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
compile 'com.afollestad.material-dialogs:core:0.9.2.3'
compile 'com.afollestad.material-dialogs:core:0.9.6.0'
}
apply plugin: 'com.google.gms.google-services'

View file

@ -1,53 +0,0 @@
{
"project_info": {
"project_number": "32713218397",
"firebase_url": "https://unipatcher-897b3.firebaseio.com",
"project_id": "unipatcher-897b3",
"storage_bucket": "unipatcher-897b3.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:32713218397:android:4b0afdca1aa174e1",
"android_client_info": {
"package_name": "org.emunix.unipatcher"
}
},
"oauth_client": [
{
"client_id": "32713218397-h8ptc6lelg9l871v10pgq11ni8ejaavg.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "org.emunix.unipatcher",
"certificate_hash": "C31CB23D974F426B38D7AF0848A6F1066FCF1FD9"
}
},
{
"client_id": "32713218397-bhgevevp6i5coukhnkti1g9cecnujuav.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyD_8JprF21wL7wpRV7JymLzlbHR-jUBZKI"
}
],
"services": {
"analytics_service": {
"status": 2,
"analytics_property": {
"tracking_id": "UA-77394676-1"
}
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View file

@ -6,12 +6,11 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:name=".UniPatcher"
android:allowBackup="false"
android:extractNativeLibs="false"
android:extractNativeLibs="true"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -43,6 +42,7 @@
<data android:pathPattern="/.*\\.dps"/>
<data android:pathPattern="/.*\\.xdelta"/>
<data android:pathPattern="/.*\\.xdelta3"/>
<data android:pathPattern="/.*\\.xd"/>
<data android:pathPattern="/.*\\.vcdiff"/>
<data android:host="*"/>
</intent-filter>
@ -69,10 +69,6 @@
android:name=".WorkerService"
android:exported="false"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
</application>
</manifest>
</manifest>

View file

@ -2,7 +2,7 @@
This file based on encode_decode_test.c from XDelta3 sources.
Copyright (C) 2007 Ralf Junker
Copyright (C) 2016 Boris Timofeev
Copyright (C) 2016-2017 Boris Timofeev
This file is part of UniPatcher.
@ -30,18 +30,21 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
#include "xdelta3/xdelta3/xdelta3.h"
#include "xdelta3/xdelta3/xdelta3.c"
int apply(FILE *patch, FILE *in, FILE *out);
int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum);
const int ERR_UNABLE_OPEN_PATCH = -5001;
const int ERR_UNABLE_OPEN_ROM = -5002;
const int ERR_UNABLE_OPEN_OUTPUT = -5003;
const int ERR_UNABLE_OPEN_SOURCE = -5004;
const int ERR_UNABLE_OPEN_MODIFIED = -5005;
const int ERR_WRONG_CHECKSUM = -5010;
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
jobject this,
jstring patchPath,
jstring romPath,
jstring outputPath) {
jstring outputPath,
jboolean ignoreChecksum) {
int ret = 0;
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
const char *romName = (*env)->GetStringUTFChars(env, romPath, NULL);
@ -70,7 +73,7 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ERR_UNABLE_OPEN_OUTPUT;
}
ret = apply(patchFile, romFile, outputFile);
ret = code(0, patchFile, romFile, outputFile, (int)ignoreChecksum);
fclose(patchFile);
fclose(romFile);
@ -78,8 +81,50 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ret;
}
int apply(FILE *patch, FILE *in, FILE *out) {
int BUFFER_SIZE = 32768;
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3create(JNIEnv *env,
jobject this,
jstring patchPath,
jstring sourcePath,
jstring modifiedPath) {
int ret = 0;
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
const char *sourceName = (*env)->GetStringUTFChars(env, sourcePath, NULL);
const char *modifiedName = (*env)->GetStringUTFChars(env, modifiedPath, NULL);
FILE *patchFile = fopen(patchName, "wb");
FILE *sourceFile = fopen(sourceName, "rb");
FILE *modifiedFile = fopen(modifiedName, "rb");
(*env)->ReleaseStringUTFChars(env, patchPath, patchName);
(*env)->ReleaseStringUTFChars(env, sourcePath, sourceName);
(*env)->ReleaseStringUTFChars(env, modifiedPath, modifiedName);
if (!patchFile) {
return ERR_UNABLE_OPEN_PATCH;
}
if (!sourceFile) {
fclose(patchFile);
return ERR_UNABLE_OPEN_SOURCE;
}
if (!modifiedFile) {
fclose(patchFile);
fclose(sourceFile);
return ERR_UNABLE_OPEN_MODIFIED;
}
ret = code(1, modifiedFile, sourceFile, patchFile, 0);
fclose(patchFile);
fclose(sourceFile);
fclose(modifiedFile);
return ret;
}
int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum) {
int BUFFER_SIZE = 0x1000;
int r, ret;
xd3_stream stream;
@ -93,32 +138,37 @@ int apply(FILE *patch, FILE *in, FILE *out) {
xd3_init_config(&config, 0);
config.winsize = BUFFER_SIZE;
if (ignoreChecksum) {
config.flags |= XD3_ADLER32_NOVER;
}
xd3_config_stream(&stream, &config);
source.blksize = BUFFER_SIZE;
source.curblk = malloc(source.blksize);
/* Load 1st block of stream. */
r = fseek(in, 0, SEEK_SET);
r = fseek(src, 0, SEEK_SET);
if (r)
return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, in);
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
source.curblkno = 0;
xd3_set_source(&stream, &source);
Input_Buf = malloc(BUFFER_SIZE);
fseek(patch, 0, SEEK_SET);
fseek(in, 0, SEEK_SET);
do {
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, patch);
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, in);
if (Input_Buf_Read < BUFFER_SIZE) {
xd3_set_flags(&stream, XD3_FLUSH | stream.flags);
}
xd3_avail_input(&stream, Input_Buf, Input_Buf_Read);
process:
ret = xd3_decode_input(&stream);
process:
if (encode)
ret = xd3_encode_input(&stream);
else
ret = xd3_decode_input(&stream);
switch (ret) {
case XD3_INPUT:
@ -132,10 +182,10 @@ int apply(FILE *patch, FILE *in, FILE *out) {
goto process;
case XD3_GETSRCBLK:
r = fseek(in, source.blksize * source.getblkno, SEEK_SET);
r = fseek(src, source.blksize * source.getblkno, SEEK_SET);
if (r)
return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, in);
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
source.curblkno = source.getblkno;
goto process;

View file

@ -0,0 +1,35 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher 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.
UniPatcher 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 UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
public class Action {
public static final int SELECT_ROM_FILE = 1;
public static final int SELECT_PATCH_FILE = 2;
public static final int SELECT_SOURCE_FILE = 3;
public static final int SELECT_MODIFIED_FILE = 4;
public static final int SELECT_HEADER_FILE = 5;
public static final int APPLY_PATCH = 101;
public static final int CREATE_PATCH = 102;
public static final int SMD_FIX_CHECKSUM = 103;
public static final int SNES_ADD_SMC_HEADER = 104;
public static final int SNES_DELETE_SMC_HEADER = 105;
}

View file

@ -1,37 +0,0 @@
/*
Copyright (C) 2013-2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher 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.
UniPatcher 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 UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
public class Globals {
private static String cmdArgument = null;
public static String getCmdArgument() {
return cmdArgument;
}
public static void setCmdArgument(String cmdArgument) {
Globals.cmdArgument = cmdArgument;
}
public static final int ACTION_PATCHING = 1;
public static final int ACTION_SMD_FIX_CHECKSUM = 2;
public static final int ACTION_SNES_ADD_SMC_HEADER = 3;
public static final int ACTION_SNES_DELETE_SMC_HEADER = 4;
}

View file

@ -69,4 +69,33 @@ public class Settings {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString("output_directory", "");
}
public static boolean getIgnoreChecksum(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("ignore_checksum", false);
}
public static void setPatchingSuccessful(Context context, Boolean isSuccessful) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("patching_successful", isSuccessful);
editor.apply();
}
public static boolean getPatchingSuccessful(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("patching_successful", false);
}
public static void setDontShowDonateSnackbarCount(Context context, int count) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("dont_show_donate_snackbar", count);
editor.apply();
}
public static int getDontShowDonateSnackbarCount(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getInt("dont_show_donate_snackbar", 0);
}
}

View file

@ -0,0 +1,57 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher 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.
UniPatcher 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 UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
public class UniPatcher extends Application {
private static String appArgument = null;
public static final String NOTIFICATION_CHANNEL_ID = "notifications";
@Override
public void onCreate() {
super.onCreate();
initNotificationChannel();
}
public static String getAppArgument() {
return appArgument;
}
public static void setAppArgument(String appArgument) {
UniPatcher.appArgument = appArgument;
}
public void initNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(channel);
}
}

View file

@ -22,6 +22,7 @@ package org.emunix.unipatcher;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
@ -48,6 +49,14 @@ public class Utils {
private static final int BUFFER_SIZE = 10240; // 10 Kb
public static void startForegroundService(Context context, Intent intent) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent);
} else {
context.startForegroundService(intent);
}
}
public static String getAppVersion(Context context) {
String versionName = "N/A";
try {
@ -152,7 +161,7 @@ public class Utils {
public static boolean isPatch(File file) {
String[] patches =
{"ips", "ups", "bps", "aps", "ppf", "dps", "ebp", "xdelta", "xdelta3", "vcdiff"};
{"ips", "ups", "bps", "aps", "ppf", "dps", "ebp", "xdelta", "xdelta3", "xd", "vcdiff"};
String ext = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.getDefault());
for (String patch : patches) {
if (ext.equals(patch)) return true;

View file

@ -24,6 +24,7 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.app.NotificationCompat;
@ -43,6 +44,7 @@ import org.emunix.unipatcher.tools.RomException;
import org.emunix.unipatcher.tools.SmdFixChecksum;
import org.emunix.unipatcher.tools.SnesSmcHeader;
import org.emunix.unipatcher.ui.activity.MainActivity;
import org.emunix.unipatcher.ui.notify.CreatePatchNotify;
import org.emunix.unipatcher.ui.notify.Notify;
import org.emunix.unipatcher.ui.notify.PatchingNotify;
import org.emunix.unipatcher.ui.notify.SmdFixChecksumNotify;
@ -74,16 +76,19 @@ public class WorkerService extends IntentService {
try {
int action = intent.getIntExtra("action", 0);
switch (action) {
case Globals.ACTION_PATCHING:
case Action.APPLY_PATCH:
actionPatching(intent);
break;
case Globals.ACTION_SMD_FIX_CHECKSUM:
case Action.CREATE_PATCH:
actionCreatePatch(intent);
break;
case Action.SMD_FIX_CHECKSUM:
actionSmdFixChecksum(intent);
break;
case Globals.ACTION_SNES_ADD_SMC_HEADER:
case Action.SNES_ADD_SMC_HEADER:
actionSnesAddSmcHeader(intent);
break;
case Globals.ACTION_SNES_DELETE_SMC_HEADER:
case Action.SNES_DELETE_SMC_HEADER:
actionSnesDeleteSmcHeader(intent);
break;
}
@ -141,31 +146,88 @@ public class WorkerService extends IntentService {
patcher = new EBP(this, patchFile, romFile, outputFile);
else if ("dps".equals(ext))
patcher = new DPS(this, patchFile, romFile, outputFile);
else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "vcdiff".equals(ext))
else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "xd".equals(ext) || "vcdiff".equals(ext))
patcher = new XDelta(this, patchFile, romFile, outputFile);
else
errorMsg = getString(R.string.notify_error_unknown_patch_format);
Notify notify = new PatchingNotify(this, outputFile.getName());
if (errorMsg != null) {
notify.showResult(errorMsg);
showErrorNotification(errorMsg);
return;
}
Notify notify = new PatchingNotify(this, outputFile.getName());
startForeground(notify.getID(), notify.getNotifyBuilder().build());
try {
if ("ppf".equals(ext))
Utils.copyFile(this, romFile, outputFile);
patcher.apply();
patcher.apply(Settings.getIgnoreChecksum(this));
Settings.setPatchingSuccessful(this, true);
} catch (PatchException | IOException e) {
if (Utils.getFreeSpace(outputFile.getParentFile()) == 0) {
errorMsg = getString(R.string.notify_error_not_enough_space);
} else {
errorMsg = e.getMessage();
}
FileUtils.deleteQuietly(outputFile);
if (outputFile.isFile()) {
FileUtils.deleteQuietly(outputFile);
}
} finally {
stopForeground(true);
}
notify.showResult(errorMsg);
}
private void actionCreatePatch(Intent intent) {
String errorMsg = null;
File sourceFile = new File(intent.getStringExtra("sourcePath"));
File modifiedFile = new File(intent.getStringExtra("modifiedPath"));
File patchFile = new File(intent.getStringExtra("patchPath"));
if (!fileExists(sourceFile) || !fileExists(modifiedFile))
return;
// create output dir
try {
if (!patchFile.getParentFile().exists()) {
FileUtils.forceMkdirParent(patchFile);
}
} catch (IOException | SecurityException e) {
String text = getString(R.string.notify_error_unable_to_create_directory, patchFile.getParent());
showErrorNotification(text);
return;
}
// check access to output dir
try {
if (!patchFile.getParentFile().canWrite()) {
String text = getString(R.string.notify_error_unable_to_write_to_directory, patchFile.getParent());
showErrorNotification(text);
return;
}
} catch (SecurityException e) {
String text = getString(R.string.notify_error_unable_to_write_to_directory, patchFile.getParent());
showErrorNotification(text);
return;
}
XDelta patcher = new XDelta(this, patchFile, sourceFile, modifiedFile);
Notify notify = new CreatePatchNotify(this, patchFile.getName());
startForeground(notify.getID(), notify.getNotifyBuilder().build());
try {
patcher.create();
Settings.setPatchingSuccessful(this, true);
} catch (PatchException | IOException e) {
if (Utils.getFreeSpace(patchFile.getParentFile()) == 0) {
errorMsg = getString(R.string.notify_error_not_enough_space);
} else {
errorMsg = e.getMessage();
}
FileUtils.deleteQuietly(patchFile);
} finally {
stopForeground(true);
}
@ -264,15 +326,21 @@ public class WorkerService extends IntentService {
private void showErrorNotification(String text) {
Intent notificationIntent = new Intent(this, MainActivity.class);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notify = new NotificationCompat.Builder(this)
Notification notify = new NotificationCompat.Builder(this, UniPatcher.NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.notify_error))
.setContentText(text)
.setSmallIcon(R.drawable.ic_stat_patching)
.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp)
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(text))
.build();
nm.notify(0, notify);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
nm.notify(32768, notify);
} else {
startForeground(32768, notify);
stopForeground(STOP_FOREGROUND_DETACH);
}
}
}

View file

@ -43,7 +43,7 @@ public class APS extends Patcher {
}
@Override
public void apply() throws PatchException, IOException {
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
Patcher aps = null;
switch (checkAPS(patchFile)) {
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));
}
aps.apply();
aps.apply(ignoreChecksum);
}
public int checkAPS(File file) throws PatchException, IOException {

View file

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

View file

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

View file

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

View file

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

View file

@ -68,12 +68,12 @@ public class EBP extends Patcher {
}
@Override
public void apply() throws PatchException, IOException {
public void apply(boolean ignoreChecksum) 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);
prepareCleanRom(cleanRom, ignoreChecksum);
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
SnesSmcHeader smc = new SnesSmcHeader();
try {
@ -95,8 +95,10 @@ public class EBP extends Patcher {
}
// 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 (!ignoreChecksum) {
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);

View file

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

View file

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

View file

@ -37,5 +37,5 @@ public abstract class Patcher {
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
public void apply() throws PatchException, IOException {
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (patchFile.length() < 18) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
@ -90,7 +90,9 @@ public class UPS extends Patcher {
ySize = tmp;
upsCrc.swapInOut();
} 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));
@ -125,9 +127,11 @@ public class UPS extends Patcher {
IOUtils.closeQuietly(outputStream);
}
long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != upsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
if (!ignoreChecksum) {
long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != upsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
}
}
// decode pointer

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2016 Boris Timofeev
Copyright (C) 2016-2017 Boris Timofeev
This file is part of UniPatcher.
@ -35,17 +35,20 @@ public class XDelta extends Patcher {
private static final int ERR_UNABLE_OPEN_PATCH = -5001;
private static final int ERR_UNABLE_OPEN_ROM = -5002;
private static final int ERR_UNABLE_OPEN_OUTPUT = -5003;
private static final int ERR_UNABLE_OPEN_SOURCE = -5004;
private static final int ERR_UNABLE_OPEN_MODIFIED = -5005;
private static final int ERR_WRONG_CHECKSUM = -5010;
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 static native int xdelta3create(String patchPath, String sourcePath, String modifiedPath);
public XDelta(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
public void apply() throws PatchException, IOException {
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
if (checkXDelta1(patchFile))
throw new PatchException(context.getString(R.string.notify_error_xdelta1_unsupported));
@ -55,7 +58,7 @@ public class XDelta extends Patcher {
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) {
case NO_ERROR:
@ -78,6 +81,32 @@ public class XDelta extends Patcher {
}
}
public void create() throws PatchException, IOException {
try {
System.loadLibrary("xdelta3");
} catch (UnsatisfiedLinkError e) {
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
}
int ret = xdelta3create(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
switch (ret) {
case NO_ERROR:
return;
case ERR_UNABLE_OPEN_PATCH:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(patchFile.getName()));
case ERR_UNABLE_OPEN_SOURCE:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(romFile.getName()));
case ERR_UNABLE_OPEN_MODIFIED:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(outputFile.getName()));
default:
throw new PatchException(context.getString(R.string.notify_error_unknown));
}
}
public boolean checkXDelta1(File file) throws IOException {
String[] MAGIC_XDELTA1 = {"%XDELTA%", "%XDZ000%", "%XDZ001%",
"%XDZ002%", "%XDZ003%", "%XDZ004%"};

View file

@ -29,6 +29,7 @@ import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
@ -63,6 +64,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
private RecyclerView list;
private FilePickerAdapter listAdapter;
private TextView permissionErrorText;
private CardView card;
private TextView crc32;
private TextView md5;
@ -104,6 +106,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
permissionErrorText = (TextView) findViewById(R.id.empty_view);
list = (RecyclerView) findViewById(R.id.list);
card = (CardView) findViewById(R.id.card);
try {
list.setHasFixedSize(true);
} catch (NullPointerException e) {/* TODO log */}
@ -201,7 +204,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
if (currentDir.getParent() != null && currentDir.getParentFile().canRead()) {
entry = new FileEntry();
entry.setIcon(R.drawable.ic_folder_upload_grey600_24dp);
entry.setIcon(R.drawable.folder_upload);
entry.setName("..");
fileList.add(entry);
}
@ -211,15 +214,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
continue;
if (file.isDirectory()) {
entry = new FileEntry();
entry.setIcon(R.drawable.ic_folder_grey600_24dp);
entry.setIcon(R.drawable.folder);
entry.setName(file.getName());
fileList.add(entry);
} else {
entry = new FileEntry();
if (Utils.isPatch(file)) {
entry.setIcon(R.drawable.ic_healing_grey600_24dp);
entry.setIcon(R.drawable.healing);
} else {
entry.setIcon(R.drawable.ic_insert_drive_file_grey600_24dp);
entry.setIcon(R.drawable.file);
}
entry.setName(file.getName());
fileList.add(entry);
@ -274,11 +277,11 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
private void showPermissionError(boolean on) {
if (on) {
list.setVisibility(View.GONE);
card.setVisibility(View.GONE);
permissionErrorText.setVisibility(View.VISIBLE);
} else {
permissionErrorText.setVisibility(View.GONE);
list.setVisibility(View.VISIBLE);
card.setVisibility(View.VISIBLE);
}
}

View file

@ -25,6 +25,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
@ -35,29 +36,27 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import com.google.firebase.analytics.FirebaseAnalytics;
import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.ui.fragment.ActionFragment;
import org.emunix.unipatcher.ui.fragment.CreatePatchFragment;
import org.emunix.unipatcher.ui.fragment.PatchingFragment;
import org.emunix.unipatcher.ui.fragment.SmdFixChecksumFragment;
import org.emunix.unipatcher.ui.fragment.SnesSmcHeaderFragment;
import java.util.Random;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private FirebaseAnalytics firebaseAnalytics;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -65,10 +64,6 @@ public class MainActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
if (BuildConfig.DEBUG)
firebaseAnalytics.setAnalyticsCollectionEnabled(false);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@ -87,8 +82,6 @@ public class MainActivity extends AppCompatActivity
if (fragment != null) {
boolean ret = fragment.runAction();
}
//Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
// .setAction("Action", null).show();
}
});
@ -100,6 +93,7 @@ public class MainActivity extends AppCompatActivity
}
parseArgument();
showDonateSnackbar();
}
private void setTheme() {
@ -124,10 +118,12 @@ public class MainActivity extends AppCompatActivity
if (id == R.id.nav_apply_patch) {
selectDrawerItem(0);
} else if (id == R.id.nav_smd_fix_checksum) {
} else if (id == R.id.nav_create_patch) {
selectDrawerItem(1);
} else if (id == R.id.nav_snes_add_del_smc_header) {
} else if (id == R.id.nav_smd_fix_checksum) {
selectDrawerItem(2);
} else if (id == R.id.nav_snes_add_del_smc_header) {
selectDrawerItem(3);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
@ -139,8 +135,7 @@ public class MainActivity extends AppCompatActivity
} else if (id == R.id.nav_rate) {
rateApp();
} else if (id == R.id.nav_donate) {
Intent donateIntent = new Intent(this, DonateActivity.class);
startActivity(donateIntent);
showDonateActivity();
} else if (id == R.id.nav_share) {
shareApp();
} else if (id == R.id.nav_help) {
@ -156,9 +151,12 @@ public class MainActivity extends AppCompatActivity
Fragment fragment;
switch (position) {
case 1:
fragment = new SmdFixChecksumFragment();
fragment = new CreatePatchFragment();
break;
case 2:
fragment = new SmdFixChecksumFragment();
break;
case 3:
fragment = new SnesSmcHeaderFragment();
break;
default:
@ -173,13 +171,51 @@ public class MainActivity extends AppCompatActivity
private void parseArgument() {
try {
String arg = getIntent().getData().getPath();
Globals.setCmdArgument(arg);
Log.d(LOG_TAG, "Cmd argument: " + arg);
UniPatcher.setAppArgument(arg);
} catch (NullPointerException e) {
Log.e(LOG_TAG, "NullPointerException in argument fetching");
// The application is not opened from the file manager
}
}
private void showDonateSnackbar() {
// don't show snackbar if the user did not patch the file successfully
if (!Settings.getPatchingSuccessful(this))
return;
// don't show snackbar some time if the user swiped off it before
int count = Settings.getDontShowDonateSnackbarCount(this);
if (count != 0) {
Settings.setDontShowDonateSnackbarCount(this, --count);
return;
}
// don't show snackbar each time you open the application
if (new Random().nextInt(6) != 0)
return;
Snackbar.make(findViewById(R.id.content_frame), R.string.main_activity_donate_snackbar_text, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.main_activity_donate_snackbar_button, new View.OnClickListener() {
@Override
public void onClick(View v) {
showDonateActivity();
}
})
.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event == Snackbar.Callback.DISMISS_EVENT_SWIPE) {
Settings.setDontShowDonateSnackbarCount(getApplicationContext(), 30);
}
}
}
).show();
}
private void showDonateActivity() {
Intent donateIntent = new Intent(this, DonateActivity.class);
startActivity(donateIntent);
}
private void shareApp() {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");

View file

@ -0,0 +1,259 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher 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.
UniPatcher 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 UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.fragment;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.CardView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.WorkerService;
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
import java.io.File;
public class CreatePatchFragment extends ActionFragment implements View.OnClickListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private TextView sourceNameTextView;
private TextView modifiedNameTextView;
private TextView patchNameTextView;
private String sourcePath = null;
private String modifiedPath = null;
private String patchPath = null;
public CreatePatchFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_patch_fragment, container, false);
sourceNameTextView = (TextView) view.findViewById(R.id.sourceFileNameTextView);
modifiedNameTextView = (TextView) view.findViewById(R.id.modifiedFileNameTextView);
patchNameTextView = (TextView) view.findViewById(R.id.patchFileNameTextView);
CardView sourceCardView = (CardView) view.findViewById(R.id.sourceFileCardView);
sourceCardView.setOnClickListener(this);
CardView modifiedCardView = (CardView) view.findViewById(R.id.modifiedFileCardView);
modifiedCardView.setOnClickListener(this);
CardView patchCardView = (CardView) view.findViewById(R.id.patchFileCardView);
patchCardView.setOnClickListener(this);
restoreState(savedInstanceState);
setFonts(view);
// Set action bar title
getActivity().setTitle(R.string.nav_create_patch);
return view;
}
private void setFonts(View view) {
TextView sourceLabel = (TextView) view.findViewById(R.id.sourceFileLabel);
TextView modifiedLabel = (TextView) view.findViewById(R.id.modifiedFileLabel);
TextView patchLabel = (TextView) view.findViewById(R.id.patchFileLabel);
Typeface roboto_light = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Light.ttf");
sourceLabel.setTypeface(roboto_light);
modifiedLabel.setTypeface(roboto_light);
patchLabel.setTypeface(roboto_light);
sourceNameTextView.setTypeface(roboto_light);
modifiedNameTextView.setTypeface(roboto_light);
patchNameTextView.setTypeface(roboto_light);
}
private void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
sourcePath = savedInstanceState.getString("sourcePath");
modifiedPath = savedInstanceState.getString("modifiedPath");
patchPath = savedInstanceState.getString("patchPath");
if (sourcePath != null)
sourceNameTextView.setText(new File(sourcePath).getName());
if (modifiedPath != null)
modifiedNameTextView.setText(new File(modifiedPath).getName());
if (patchPath != null)
patchNameTextView.setText(new File(patchPath).getName());
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("sourcePath", sourcePath);
savedInstanceState.putString("modifiedPath", modifiedPath);
savedInstanceState.putString("patchPath", patchPath);
}
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
switch (view.getId()) {
case R.id.sourceFileCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_source_file));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_SOURCE_FILE);
break;
case R.id.modifiedFileCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_modified_file));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_MODIFIED_FILE);
break;
case R.id.patchFileCardView:
renamePatchFile();
break;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(LOG_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (resultCode == Activity.RESULT_OK) {
String path = data.getStringExtra("path");
File fpath = new File(path);
switch (requestCode) {
case Action.SELECT_SOURCE_FILE:
sourcePath = path;
sourceNameTextView.setVisibility(View.VISIBLE);
sourceNameTextView.setText(fpath.getName());
Settings.setLastRomDir(getActivity(), fpath.getParent());
break;
case Action.SELECT_MODIFIED_FILE:
modifiedPath = path;
modifiedNameTextView.setVisibility(View.VISIBLE);
modifiedNameTextView.setText(fpath.getName());
Settings.setLastRomDir(getActivity(), fpath.getParent());
patchPath = makeOutputPath(path);
patchNameTextView.setText(new File(patchPath).getName());
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private String makeOutputPath(String fullname) {
String dir = Settings.getOutputDir(getActivity());
if (dir.equals("")) { // get ROM directory
dir = FilenameUtils.getFullPath(fullname);
}
String baseName = FilenameUtils.getBaseName(fullname);
return FilenameUtils.concat(dir, baseName.concat(".xdelta"));
}
public boolean runAction() {
if (sourcePath == null & modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_and_modified_not_selected), Toast.LENGTH_LONG).show();
return false;
} else if (sourcePath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_not_selected), Toast.LENGTH_LONG).show();
return false;
} else if (modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
return false;
}
Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Action.CREATE_PATCH);
intent.putExtra("sourcePath", sourcePath);
intent.putExtra("modifiedPath", modifiedPath);
intent.putExtra("patchPath", patchPath);
Utils.startForegroundService(getActivity(), intent);
Toast.makeText(getActivity(), R.string.toast_create_patch_started_check_notify, Toast.LENGTH_SHORT).show();
return true;
}
private void renamePatchFile() {
if (modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
return;
}
AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
dialog.setTitle(R.string.dialog_rename_title);
final EditText input = new EditText(getActivity());
input.setText(patchNameTextView.getText());
// add left and right margins to EditText.
FrameLayout container = new FrameLayout(getActivity());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int dp_24 = Utils.dpToPx(getActivity(), 24);
params.setMargins(dp_24, 0, dp_24, 0);
input.setLayoutParams(params);
container.addView(input);
dialog.setView(container);
dialog.setPositiveButton(R.string.dialog_rename_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String newName = input.getText().toString();
if (newName.equals("")) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_empty_name, Toast.LENGTH_LONG).show();
return;
}
if (newName.contains("/")) {
newName = newName.replaceAll("/", "_");
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show();
}
String newPath = new File(patchPath).getParent().concat(File.separator).concat(newName);
if (FilenameUtils.equals(newPath, sourcePath) || FilenameUtils.equals(newPath, modifiedPath)) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_same_name, Toast.LENGTH_LONG).show();
return;
}
patchNameTextView.setText(newName);
patchPath = newPath;
}
});
dialog.setNegativeButton(R.string.dialog_rename_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
}
}

View file

@ -36,9 +36,10 @@ import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.WorkerService;
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
@ -49,8 +50,6 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final int SELECT_ROM_FILE = 1;
private static final int SELECT_PATCH_FILE = 2;
private TextView romNameTextView;
private TextView patchNameTextView;
private TextView outputNameTextView;
@ -97,7 +96,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
}
private void parseArgument() {
patchPath = Globals.getCmdArgument();
patchPath = UniPatcher.getAppArgument();
if (patchPath != null) {
patchNameTextView.setText(new File(patchPath).getName());
}
@ -147,12 +146,12 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
case R.id.patchCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_patch));
intent.putExtra("directory", Settings.getPatchDir(getActivity()));
startActivityForResult(intent, SELECT_PATCH_FILE);
startActivityForResult(intent, Action.SELECT_PATCH_FILE);
break;
case R.id.romCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, SELECT_ROM_FILE);
startActivityForResult(intent, Action.SELECT_ROM_FILE);
break;
case R.id.outputCardView:
renameOutputRom();
@ -172,7 +171,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
}
switch (requestCode) {
case SELECT_ROM_FILE:
case Action.SELECT_ROM_FILE:
romPath = path;
romNameTextView.setVisibility(View.VISIBLE);
romNameTextView.setText(fpath.getName());
@ -180,7 +179,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
outputPath = makeOutputPath(path);
outputNameTextView.setText(new File(outputPath).getName());
break;
case SELECT_PATCH_FILE:
case Action.SELECT_PATCH_FILE:
patchPath = path;
patchNameTextView.setVisibility(View.VISIBLE);
patchNameTextView.setText(fpath.getName());
@ -214,11 +213,11 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
}
Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Globals.ACTION_PATCHING);
intent.putExtra("action", Action.APPLY_PATCH);
intent.putExtra("romPath", romPath);
intent.putExtra("patchPath", patchPath);
intent.putExtra("outputPath", outputPath);
getActivity().startService(intent);
Utils.startForegroundService(getActivity(), intent);
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify, Toast.LENGTH_SHORT).show();
return true;
@ -248,6 +247,10 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
@Override
public void onClick(DialogInterface dialog, int which) {
String newName = input.getText().toString();
if (newName.equals("")) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_empty_name, Toast.LENGTH_LONG).show();
return;
}
if (newName.contains("/")) {
newName = newName.replaceAll("/", "_");
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show();

View file

@ -31,7 +31,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils;
@ -43,7 +43,6 @@ import java.io.File;
public class SmdFixChecksumFragment extends ActionFragment implements View.OnClickListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final int SELECT_ROM_FILE = 1;
private TextView romNameTextView;
private TextView fixChecksumInfoTextview;
@ -108,7 +107,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
case R.id.romCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, SELECT_ROM_FILE);
startActivityForResult(intent, Action.SELECT_ROM_FILE);
break;
}
}
@ -124,7 +123,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
}
switch (requestCode) {
case SELECT_ROM_FILE:
case Action.SELECT_ROM_FILE:
romPath = path;
romNameTextView.setVisibility(View.VISIBLE);
romNameTextView.setText(new File(path).getName());
@ -142,9 +141,9 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
}
Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Globals.ACTION_SMD_FIX_CHECKSUM);
intent.putExtra("romPath", romPath);
getActivity().startService(intent);
intent.putExtra("action", Action.SMD_FIX_CHECKSUM);
Utils.startForegroundService(getActivity(), intent);
Toast.makeText(getActivity(), R.string.notify_smd_fix_checksum_started_check_notify, Toast.LENGTH_SHORT).show();
return true;

View file

@ -31,7 +31,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils;
@ -43,8 +43,6 @@ import java.io.File;
public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClickListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final int SELECT_ROM_FILE = 1;
private static final int SELECT_HEADER_FILE = 2;
private TextView romNameTextView;
private TextView headerNameTextView;
@ -104,10 +102,10 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
romPath = savedInstanceState.getString("romPath");
headerPath = savedInstanceState.getString("headerPath");
action = savedInstanceState.getInt("action");
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
if (action == Action.SNES_ADD_SMC_HEADER) {
headerInfoTextView.setText(R.string.snes_smc_header_will_be_added);
headerCardView.setVisibility(View.VISIBLE);
} else if (action == Globals.ACTION_SNES_DELETE_SMC_HEADER) {
} else if (action == Action.SNES_DELETE_SMC_HEADER) {
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
headerCardView.setVisibility(View.GONE);
}
@ -133,11 +131,11 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
case R.id.romCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, SELECT_ROM_FILE);
startActivityForResult(intent, Action.SELECT_ROM_FILE);
break;
case R.id.headerCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_header));
startActivityForResult(intent, SELECT_HEADER_FILE);
startActivityForResult(intent, Action.SELECT_HEADER_FILE);
break;
}
}
@ -153,25 +151,25 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
}
switch (requestCode) {
case SELECT_ROM_FILE:
case Action.SELECT_ROM_FILE:
romPath = path;
romNameTextView.setVisibility(View.VISIBLE);
romNameTextView.setText(new File(path).getName());
Settings.setLastRomDir(getActivity(), new File(path).getParent());
SnesSmcHeader checker = new SnesSmcHeader();
if (checker.isHasSmcHeader(new File(path))) {
action = Globals.ACTION_SNES_DELETE_SMC_HEADER;
action = Action.SNES_DELETE_SMC_HEADER;
headerCardView.setVisibility(View.GONE);
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
} else {
action = Globals.ACTION_SNES_ADD_SMC_HEADER;
action = Action.SNES_ADD_SMC_HEADER;
headerCardView.setVisibility(View.VISIBLE);
headerInfoTextView.setText(R.string.snes_smc_header_will_be_added);
}
headerPath = null;
headerNameTextView.setText(R.string.main_activity_tap_to_select);
break;
case SELECT_HEADER_FILE:
case Action.SELECT_HEADER_FILE:
headerPath = path;
headerNameTextView.setText(new File(path).getName());
break;
@ -190,9 +188,9 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
intent.putExtra("action", action);
intent.putExtra("romPath", romPath);
intent.putExtra("headerPath", headerPath);
getActivity().startService(intent);
Utils.startForegroundService(getActivity(), intent);
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
if (action == Action.SNES_ADD_SMC_HEADER) {
Toast.makeText(getActivity(), R.string.notify_snes_add_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.notify_snes_delete_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();

View file

@ -0,0 +1,57 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher 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.
UniPatcher 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 UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.notify;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import org.emunix.unipatcher.R;
public class CreatePatchNotify extends Notify {
public CreatePatchNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_creating_patch));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
setSticked(true);
setProgress(true);
}
@Override
public void setCompleted() {
setProgress(false);
setSticked(false);
notifyBuilder.setTicker(context.getText(R.string.notify_create_patch_complete));
notifyBuilder.setContentTitle(context.getText(R.string.notify_create_patch_complete));
}
@Override
public void setFailed(String message) {
setProgress(false);
setSticked(false);
notifyBuilder.setTicker(context.getText(R.string.notify_error));
notifyBuilder.setContentTitle(context.getString(R.string.notify_error));
notifyBuilder.setContentText(message);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
}
}

View file

@ -24,6 +24,7 @@ import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.ui.activity.MainActivity;
public abstract class Notify {
@ -40,9 +41,8 @@ public abstract class Notify {
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notifyBuilder = new NotificationCompat.Builder(context);
notifyBuilder = new NotificationCompat.Builder(context, UniPatcher.NOTIFICATION_CHANNEL_ID);
notifyBuilder.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT));
//notifyMng = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notifyMng = NotificationManagerCompat.from(context);
}

View file

@ -28,7 +28,7 @@ public class PatchingNotify extends Notify {
public PatchingNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_applying_patch));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));

View file

@ -28,7 +28,7 @@ public class SmdFixChecksumNotify extends Notify {
public SmdFixChecksumNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_smd_fix_checksum_in_progress));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));

View file

@ -27,7 +27,7 @@ import org.emunix.unipatcher.R;
public class SnesAddSmcHeaderNotify extends Notify {
public SnesAddSmcHeaderNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_add_smc_header_in_progress));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));

View file

@ -27,7 +27,7 @@ import org.emunix.unipatcher.R;
public class SnesDeleteSmcHeaderNotify extends Notify {
public SnesDeleteSmcHeaderNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_delete_smc_header_in_progress));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,018 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

Some files were not shown because too many files have changed in this diff Show more