Compare commits

..

No commits in common. "master" and "0.12" have entirely different histories.
master ... 0.12

241 changed files with 1536 additions and 6435 deletions

View file

@ -1,6 +1,5 @@
[main] [main]
host = https://www.transifex.com 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] [unipatcher.strings_xml]
file_filter = app/src/main/res/values-<lang>/strings.xml file_filter = app/src/main/res/values-<lang>/strings.xml

View file

@ -1,37 +1,29 @@
[![GPL Licence](https://badges.frapsoft.com/os/gpl/gpl.png?v=103)](https://opensource.org/licenses/GPL-3.0/)
[![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14) [![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14)
UniPatcher UniPatcher
---------- ----------
UniPatcher is a ROM patcher for Android that supports IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP and XDelta3 patch types. UniPatcher is a ROM patcher for Android that supports IPS, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP and XDelta3 patch types.
### Additional features: ### Additional features:
* Creating XDelta3 patches
* Fix checksum in Sega Mega Drive ROMs * Fix checksum in Sega Mega Drive ROMs
* Add/Delete SMC header in Super Nintendo ROMs * Add/Delete SMC header in Super Nintendo ROMs
### Screenshots: ### 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: ### Install 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="https://play.google.com/intl/de_de/badges/images/generic/en_badge_web_generic.png" width="192">](https://play.google.com/store/apps/details?id=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) ### How to contribute to UniPatcher:
[... or get a Release APK here on GitHub](https://github.com/btimofeev/UniPatcher/releases) #### Report a bug or suggest a new feature
### Contribute: Bugs and new features are being discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
#### Report a bug or suggest features #### Help with translations
The translations are managed on [Transifex](https://www.transifex.com/unipatcher/unipatcher/).
These are discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
#### 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,2 +1,4 @@
cmake_minimum_required(VERSION 3.4.1) cmake_minimum_required(VERSION 3.4.1)
add_library( xdelta3 SHARED src/main/cpp/xdelta3.c ) add_library( xdelta3 SHARED src/main/cpp/xdelta3.c )
find_library( log-lib log )
target_link_libraries( xdelta3 ${log-lib} )

View file

@ -1,8 +1,13 @@
def versionMajor = 0
def versionMinor = 12
def versionPatch = 0
def versionBuild = 0
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 27 compileSdkVersion 25
buildToolsVersion '27.0.1' buildToolsVersion '25.0.2'
signingConfigs { signingConfigs {
release release
@ -11,17 +16,13 @@ android {
defaultConfig { defaultConfig {
applicationId "org.emunix.unipatcher" applicationId "org.emunix.unipatcher"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 27 targetSdkVersion 25
versionCode 150000 versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild
versionName "0.15" versionName "${versionMajor}.${versionMinor}.${versionPatch}"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
externalNativeBuild { externalNativeBuild {
cmake { cmake {
cppFlags "" cppFlags ""
arguments "-DANDROID_PLATFORM=android-14", "-DCMAKE_BUILD_TYPE=Release" arguments "-DANDROID_PLATFORM=android-14"
} }
} }
} }
@ -35,52 +36,11 @@ android {
} }
} }
flavorDimensions "default"
productFlavors {
free {
buildConfigField "String", "RATE_URL", "\"https://github.com/btimofeev/UniPatcher\""
buildConfigField "String", "SHARE_URL", "\"https://github.com/btimofeev/UniPatcher\""
buildConfigField "String", "PAYPAL_USER", "\"btimofeev@emunix.org\""
buildConfigField "String", "PAYPAL_CURRENCY_CODE", "\"USD\""
buildConfigField "String", "BITCOIN_ADDRESS", "\"16coztryz7xbNNDNhhf98wuHmi3hEintsW\""
}
google {
buildConfigField "String", "RATE_URL", "\"market://details?id=org.eminix.unipatcher\""
buildConfigField "String", "SHARE_URL", "\"https://play.google.com/store/apps/details?id=org.eminix.unipatcher\""
buildConfigField "String", "GOOGLE_PLAY_PUBKEY", "\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA384jTCBEuJ8nCWaC4S6AFrnMQN4mBlmkOXHV3Xg5hlFOl8TkVwiCfqz8r20yJpEy0IJ1+3QRnlq59zadUxbkD+PacJlGB/r2b3mbKfu+m0K+e/0aL6eWupjMSIyPgpnbN3uswiBEGUb4ytzYF53ZKTbLARnruQdMnjV6+VyfwMgpor/48anVQawDARBj/AIAj6VGtRHLmg6DmKDyOGQ7uCgXSv+ysnBKJjtIX/L/5nQgL8Q+9jsr2knuWY7j9BmrtpUXaDH3Kb50M1TOCKiqxPGa8lInOOIndABWxcpqmSMXP06SPYOanUlEH7lT0jjqpHpFNx8hRTT9xf652rgMJwIDAQAB\""
}
amazon {
buildConfigField "String", "RATE_URL", "\"amzn://apps/android?p=org.emunix.unipatcher\""
buildConfigField "String", "SHARE_URL", "\"http://www.amazon.com/gp/mas/dl/android?p=org.emunix.unipatcher\""
buildConfigField "String", "PAYPAL_USER", "\"btimofeev@emunix.org\""
buildConfigField "String", "PAYPAL_CURRENCY_CODE", "\"USD\""
buildConfigField "String", "BITCOIN_ADDRESS", "\"16coztryz7xbNNDNhhf98wuHmi3hEintsW\""
}
slideme {
buildConfigField "String", "RATE_URL", "\"sam://details?id=org.emunix.unipatcher\""
buildConfigField "String", "SHARE_URL", "\"http://slideme.org/application/unipatcher\""
buildConfigField "String", "PAYPAL_USER", "\"btimofeev@emunix.org\""
buildConfigField "String", "PAYPAL_CURRENCY_CODE", "\"USD\""
buildConfigField "String", "BITCOIN_ADDRESS", "\"16coztryz7xbNNDNhhf98wuHmi3hEintsW\""
}
}
sourceSets{
amazon.java.srcDir 'src/free/java'
slideme.java.srcDir 'src/free/java'
}
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path "CMakeLists.txt" path "CMakeLists.txt"
} }
} }
lintOptions {
disable 'MissingTranslation'
}
} }
def Properties props = new Properties() def Properties props = new Properties()
@ -109,18 +69,22 @@ if (propFile.canRead()) {
dependencies { dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.12.0' testCompile 'org.mockito:mockito-core:2.2.28'
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:27.0.2' compile 'com.android.support:support-v4:25.1.0'
compile 'com.android.support:appcompat-v7:27.0.2' compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:cardview-v7:27.0.2' compile 'com.android.support:cardview-v7:25.1.0'
compile 'com.android.support:preference-v14:27.0.2' compile 'com.android.support:preference-v14:25.1.0'
compile 'com.android.support:recyclerview-v7:27.0.2' compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:design:27.0.2' compile 'com.android.support:design:25.1.0'
compile 'com.android.support:support-v13:27.0.2' // used in material-dialogs compile 'com.google.firebase:firebase-ads:10.0.1'
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-crash:10.0.1'
compile 'com.anjlab.android.iab.v3:library:1.0.38'
compile 'commons-io:commons-io:2.5' 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 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
compile 'com.afollestad.material-dialogs:core:0.9.6.0' compile 'com.afollestad.material-dialogs:core:0.9.2.3'
} }
apply plugin: 'com.google.gms.google-services'

53
app/google-services.json Normal file
View file

@ -0,0 +1,53 @@
{
"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

@ -1,72 +0,0 @@
/*
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.activity;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.R;
import org.sufficientlysecure.donations.DonationsFragment;
public class DonateActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (NullPointerException e) {
/* empty */
}
getSupportActionBar().setTitle(R.string.donate_activity_title);
DonationsFragment fragment = (DonationsFragment) getSupportFragmentManager().findFragmentByTag("donationsFragment");
if (fragment == null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
fragment = DonationsFragment.newInstance(BuildConfig.DEBUG,
false, null, null, null,
true, BuildConfig.PAYPAL_USER, BuildConfig.PAYPAL_CURRENCY_CODE, getString(R.string.donation),
false, null, null,
true, BuildConfig.BITCOIN_ADDRESS);
ft.replace(R.id.donate_fragment, fragment, "donationsFragment");
ft.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.emunix.unipatcher">
<uses-permission android:name="com.android.vending.BILLING"/>
<application/>
</manifest>

View file

@ -1,91 +0,0 @@
/*
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.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.R;
import org.sufficientlysecure.donations.DonationsFragment;
public class DonateActivity extends AppCompatActivity {
private static final String[] GOOGLE_PLAY_CATALOG = new String[]{"donate_1", "donate_3",
"donate_5", "donate_10", "donate_25", "donate_50", "donate_100"};
private static final String[] GOOGLE_PLAY_COST = new String[]{"$1", "$3", "$5", "$10",
"$25", "$50", "$100"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (NullPointerException e) {
/* empty */
}
getSupportActionBar().setTitle(R.string.donate_activity_title);
DonationsFragment fragment = (DonationsFragment) getSupportFragmentManager().findFragmentByTag("donationsFragment");
if (fragment == null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
fragment = DonationsFragment.newInstance(BuildConfig.DEBUG,
true, BuildConfig.GOOGLE_PLAY_PUBKEY, GOOGLE_PLAY_CATALOG, GOOGLE_PLAY_COST,
false, null, null, null,
false, null, null,
false, null);
ft.replace(R.id.donate_fragment, fragment, "donationsFragment");
ft.commit();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag("donationsFragment");
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View file

@ -6,15 +6,17 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_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.WAKE_LOCK" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application <application
android:name=".UniPatcher"
android:allowBackup="false" android:allowBackup="false"
android:extractNativeLibs="true"
android:fullBackupContent="false" android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="false" android:supportsRtl="false"
android:extractNativeLibs="false"
android:theme="@style/AppTheme.NoActionBar"> android:theme="@style/AppTheme.NoActionBar">
<activity <activity
android:name=".ui.activity.MainActivity" android:name=".ui.activity.MainActivity"
@ -42,7 +44,6 @@
<data android:pathPattern="/.*\\.dps" /> <data android:pathPattern="/.*\\.dps" />
<data android:pathPattern="/.*\\.xdelta" /> <data android:pathPattern="/.*\\.xdelta" />
<data android:pathPattern="/.*\\.xdelta3" /> <data android:pathPattern="/.*\\.xdelta3" />
<data android:pathPattern="/.*\\.xd"/>
<data android:pathPattern="/.*\\.vcdiff" /> <data android:pathPattern="/.*\\.vcdiff" />
<data android:host="*" /> <data android:host="*" />
</intent-filter> </intent-filter>
@ -61,9 +62,14 @@
android:name=".ui.activity.HelpActivity" android:name=".ui.activity.HelpActivity"
android:label="@string/help_activity_title" /> android:label="@string/help_activity_title" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity <activity
android:name=".ui.activity.DonateActivity" android:name="com.google.android.gms.ads.AdActivity"
android:label="@string/donate_activity_title"/> android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
<service <service
android:name=".WorkerService" android:name=".WorkerService"

View file

@ -2,7 +2,7 @@
This file based on encode_decode_test.c from XDelta3 sources. This file based on encode_decode_test.c from XDelta3 sources.
Copyright (C) 2007 Ralf Junker Copyright (C) 2007 Ralf Junker
Copyright (C) 2016-2017 Boris Timofeev Copyright (C) 2016 Boris Timofeev
This file is part of UniPatcher. This file is part of UniPatcher.
@ -23,6 +23,7 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
#include <jni.h> #include <jni.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <android/log.h>
#define SIZEOF_SIZE_T 4 #define SIZEOF_SIZE_T 4
#define SIZEOF_UNSIGNED_LONG_LONG 8 #define SIZEOF_UNSIGNED_LONG_LONG 8
@ -30,21 +31,19 @@ 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 code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum); int apply(FILE *patch, FILE *in, FILE *out);
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;
const int ERR_UNABLE_OPEN_OUTPUT = -5003; 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; const int ERR_WRONG_CHECKSUM = -5010;
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env, int Java_org_emunix_unipatcher_patch_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);
@ -58,22 +57,25 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
(*env)->ReleaseStringUTFChars(env, romPath, romName); (*env)->ReleaseStringUTFChars(env, romPath, romName);
(*env)->ReleaseStringUTFChars(env, outputPath, outputName); (*env)->ReleaseStringUTFChars(env, outputPath, outputName);
if (!patchFile) { if (!patchFile)
{
return ERR_UNABLE_OPEN_PATCH; return ERR_UNABLE_OPEN_PATCH;
} }
if (!romFile) { if (!romFile)
{
fclose(patchFile); fclose(patchFile);
return ERR_UNABLE_OPEN_ROM; return ERR_UNABLE_OPEN_ROM;
} }
if (!outputFile) { if (!outputFile)
{
fclose(patchFile); fclose(patchFile);
fclose(romFile); fclose(romFile);
return ERR_UNABLE_OPEN_OUTPUT; return ERR_UNABLE_OPEN_OUTPUT;
} }
ret = code(0, patchFile, romFile, outputFile, (int)ignoreChecksum); ret = apply(patchFile, romFile, outputFile);
fclose(patchFile); fclose(patchFile);
fclose(romFile); fclose(romFile);
@ -81,50 +83,9 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ret; return ret;
} }
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3create(JNIEnv *env, int apply(FILE *patch, FILE *in, FILE *out)
jobject this, {
jstring patchPath, int BUFFER_SIZE = 32768;
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; int r, ret;
xd3_stream stream; xd3_stream stream;
@ -138,39 +99,37 @@ int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum) {
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;
source.curblk = malloc(source.blksize); source.curblk = malloc(source.blksize);
/* Load 1st block of stream. */ /* Load 1st block of stream. */
r = fseek(src, 0, SEEK_SET); r = fseek(in, 0, SEEK_SET);
if (r) if (r)
return r; return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, src); source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
source.curblkno = 0; source.curblkno = 0;
xd3_set_source(&stream, &source); xd3_set_source(&stream, &source);
Input_Buf = malloc(BUFFER_SIZE); Input_Buf = malloc(BUFFER_SIZE);
fseek(in, 0, SEEK_SET); fseek(patch, 0, SEEK_SET);
do { do
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, in); {
if (Input_Buf_Read < BUFFER_SIZE) { Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, patch);
if (Input_Buf_Read < BUFFER_SIZE)
{
xd3_set_flags(&stream, XD3_FLUSH | stream.flags); xd3_set_flags(&stream, XD3_FLUSH | stream.flags);
} }
xd3_avail_input(&stream, Input_Buf, Input_Buf_Read); xd3_avail_input(&stream, Input_Buf, Input_Buf_Read);
process: process:
if (encode)
ret = xd3_encode_input(&stream);
else
ret = xd3_decode_input(&stream); ret = xd3_decode_input(&stream);
switch (ret) { switch (ret)
{
case XD3_INPUT: case XD3_INPUT:
continue; continue;
@ -182,10 +141,10 @@ process:
goto process; goto process;
case XD3_GETSRCBLK: case XD3_GETSRCBLK:
r = fseek(src, source.blksize * source.getblkno, SEEK_SET); r = fseek(in, source.blksize * source.getblkno, SEEK_SET);
if (r) if (r)
return r; return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, src); source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
source.curblkno = source.getblkno; source.curblkno = source.getblkno;
goto process; goto process;
@ -195,6 +154,7 @@ process:
goto process; goto process;
default: default:
__android_log_print(ANDROID_LOG_ERROR, "XDelta3", "Error %d: %s", ret, stream.msg);
if (stream.msg != NULL) { if (stream.msg != NULL) {
if (strcmp(stream.msg, "target window checksum mismatch") == 0) if (strcmp(stream.msg, "target window checksum mismatch") == 0)
return ERR_WRONG_CHECKSUM; return ERR_WRONG_CHECKSUM;

View file

@ -1,35 +0,0 @@
/*
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

@ -0,0 +1,51 @@
/*
Copyright (C) 2013-2016 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 final String KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA384jTCBEuJ8nCWaC4S6AFrnMQN4mBlmkOXHV3Xg5hlFOl8TkVwiCfqz8r20yJpEy0IJ1+3QRnlq59zadUxbkD+PacJlGB/r2b3mbKfu+m0K+e/0aL6eWupjMSIyPgpnbN3uswiBEGUb4ytzYF53ZKTbLARnruQdMnjV6+VyfwMgpor/48anVQawDARBj/AIAj6VGtRHLmg6DmKDyOGQ7uCgXSv+ysnBKJjtIX/L/5nQgL8Q+9jsr2knuWY7j9BmrtpUXaDH3Kb50M1TOCKiqxPGa8lInOOIndABWxcpqmSMXP06SPYOanUlEH7lT0jjqpHpFNx8hRTT9xf652rgMJwIDAQAB";
private static String cmdArgument = null;
private static boolean isFullVersion = false;
public static String getCmdArgument() {
return cmdArgument;
}
public static void setCmdArgument(String cmdArgument) {
Globals.cmdArgument = cmdArgument;
}
public static boolean isFullVersion() {
return isFullVersion;
}
public static void setFullVersion() {
isFullVersion = true;
}
public static String getKey() {
return KEY;
}
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,33 +69,4 @@ 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);
}
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

@ -1,57 +0,0 @@
/*
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

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2013-2017 Boris Timofeev Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher. This file is part of UniPatcher.
@ -22,9 +22,10 @@ package org.emunix.unipatcher;
import android.Manifest; import android.Manifest;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build; import android.os.Build;
import android.os.StatFs; import android.os.StatFs;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@ -41,6 +42,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
@ -49,14 +51,6 @@ public class Utils {
private static final int BUFFER_SIZE = 10240; // 10 Kb 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) { public static String getAppVersion(Context context) {
String versionName = "N/A"; String versionName = "N/A";
try { try {
@ -73,6 +67,12 @@ public class Utils {
== PackageManager.PERMISSION_GRANTED; == PackageManager.PERMISSION_GRANTED;
} }
public static boolean isOnline(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnectedOrConnecting();
}
public static int dpToPx(Context context, int dp) { public static int dpToPx(Context context, int dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
@ -82,20 +82,17 @@ public class Utils {
public static long getFreeSpace(File file) { public static long getFreeSpace(File file) {
StatFs stat = new StatFs(file.getPath()); StatFs stat = new StatFs(file.getPath());
long bytesAvailable; long bytesAvailable;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
bytesAvailable = stat.getAvailableBytes(); bytesAvailable = stat.getAvailableBytes();
} else else
//noinspection deprecation //noinspection deprecation
{
bytesAvailable = (long) stat.getBlockSize() * (long) stat.getAvailableBlocks(); bytesAvailable = (long) stat.getBlockSize() * (long) stat.getAvailableBlocks();
}
return bytesAvailable; return bytesAvailable;
} }
public static void copyFile(Context context, File from, File to) throws IOException { public static void copyFile(Context context, File from, File to) throws IOException {
if (Utils.getFreeSpace(to.getParentFile()) < from.length()) { if (Utils.getFreeSpace(to.getParentFile()) < from.length())
throw new IOException(context.getString(R.string.notify_error_not_enough_space)); throw new IOException(context.getString(R.string.notify_error_not_enough_space));
}
try { try {
FileUtils.copyFile(from, to); FileUtils.copyFile(from, to);
@ -160,21 +157,17 @@ public class Utils {
} }
public static boolean isPatch(File file) { public static boolean isPatch(File file) {
String[] patches = String[] patches = {"ips", "ups", "bps", "ppf", "dps", "xdelta", "xdelta3", "vcdiff"};
{"ips", "ups", "bps", "aps", "ppf", "dps", "ebp", "xdelta", "xdelta3", "xd", "vcdiff"};
String ext = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.getDefault()); String ext = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.getDefault());
for (String patch : patches) { for (String patch : patches) {
if (ext.equals(patch)) return true; if (ext.equals(patch))
return true;
} }
return false; return false;
} }
public static boolean isArchive(String path) { public static boolean isArchive(String path) {
String ext = FilenameUtils.getExtension(path).toLowerCase(Locale.getDefault()); String ext = FilenameUtils.getExtension(path).toLowerCase(Locale.getDefault());
return ext.equals("zip") return ext.equals("zip") || ext.equals("rar") || ext.equals("7z") || ext.equals("gz") || ext.equals("tgz");
|| ext.equals("rar")
|| ext.equals("7z")
|| ext.equals("gz")
|| ext.equals("tgz");
} }
} }

View file

@ -24,27 +24,25 @@ import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.patcher.APS; import org.emunix.unipatcher.patch.APS;
import org.emunix.unipatcher.patcher.BPS; import org.emunix.unipatcher.patch.BPS;
import org.emunix.unipatcher.patcher.DPS; import org.emunix.unipatcher.patch.DPS;
import org.emunix.unipatcher.patcher.EBP; import org.emunix.unipatcher.patch.EBP;
import org.emunix.unipatcher.patcher.IPS; import org.emunix.unipatcher.patch.IPS;
import org.emunix.unipatcher.patcher.PPF; import org.emunix.unipatcher.patch.PPF;
import org.emunix.unipatcher.patcher.Patcher; import org.emunix.unipatcher.patch.Patch;
import org.emunix.unipatcher.patcher.PatchException; import org.emunix.unipatcher.patch.PatchException;
import org.emunix.unipatcher.patcher.UPS; import org.emunix.unipatcher.patch.UPS;
import org.emunix.unipatcher.patcher.XDelta; import org.emunix.unipatcher.patch.XDelta;
import org.emunix.unipatcher.tools.RomException; import org.emunix.unipatcher.tools.RomException;
import org.emunix.unipatcher.tools.SmdFixChecksum; import org.emunix.unipatcher.tools.SmdFixChecksum;
import org.emunix.unipatcher.tools.SnesSmcHeader; import org.emunix.unipatcher.tools.SnesSmcHeader;
import org.emunix.unipatcher.ui.activity.MainActivity; 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.Notify;
import org.emunix.unipatcher.ui.notify.PatchingNotify; import org.emunix.unipatcher.ui.notify.PatchingNotify;
import org.emunix.unipatcher.ui.notify.SmdFixChecksumNotify; import org.emunix.unipatcher.ui.notify.SmdFixChecksumNotify;
@ -76,19 +74,16 @@ public class WorkerService extends IntentService {
try { try {
int action = intent.getIntExtra("action", 0); int action = intent.getIntExtra("action", 0);
switch (action) { switch (action) {
case Action.APPLY_PATCH: case Globals.ACTION_PATCHING:
actionPatching(intent); actionPatching(intent);
break; break;
case Action.CREATE_PATCH: case Globals.ACTION_SMD_FIX_CHECKSUM:
actionCreatePatch(intent);
break;
case Action.SMD_FIX_CHECKSUM:
actionSmdFixChecksum(intent); actionSmdFixChecksum(intent);
break; break;
case Action.SNES_ADD_SMC_HEADER: case Globals.ACTION_SNES_ADD_SMC_HEADER:
actionSnesAddSmcHeader(intent); actionSnesAddSmcHeader(intent);
break; break;
case Action.SNES_DELETE_SMC_HEADER: case Globals.ACTION_SNES_DELETE_SMC_HEADER:
actionSnesDeleteSmcHeader(intent); actionSnesDeleteSmcHeader(intent);
break; break;
} }
@ -102,7 +97,7 @@ public class WorkerService extends IntentService {
File romFile = new File(intent.getStringExtra("romPath")); File romFile = new File(intent.getStringExtra("romPath"));
File patchFile = new File(intent.getStringExtra("patchPath")); File patchFile = new File(intent.getStringExtra("patchPath"));
File outputFile = new File(intent.getStringExtra("outputPath")); File outputFile = new File(intent.getStringExtra("outputPath"));
Patcher patcher = null; Patch patcher = null;
if(!fileExists(patchFile) || !fileExists(romFile)) if(!fileExists(patchFile) || !fileExists(romFile))
return; return;
@ -146,88 +141,31 @@ public class WorkerService extends IntentService {
patcher = new EBP(this, patchFile, romFile, outputFile); patcher = new EBP(this, patchFile, romFile, outputFile);
else if ("dps".equals(ext)) else if ("dps".equals(ext))
patcher = new DPS(this, patchFile, romFile, outputFile); patcher = new DPS(this, patchFile, romFile, outputFile);
else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "xd".equals(ext) || "vcdiff".equals(ext)) else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "vcdiff".equals(ext))
patcher = new XDelta(this, patchFile, romFile, outputFile); patcher = new XDelta(this, patchFile, romFile, outputFile);
else else
errorMsg = getString(R.string.notify_error_unknown_patch_format); errorMsg = getString(R.string.notify_error_unknown_patch_format);
Notify notify = new PatchingNotify(this, outputFile.getName());
if (errorMsg != null) { if (errorMsg != null) {
showErrorNotification(errorMsg); notify.showResult(errorMsg);
return; return;
} }
Notify notify = new PatchingNotify(this, outputFile.getName());
startForeground(notify.getID(), notify.getNotifyBuilder().build()); startForeground(notify.getID(), notify.getNotifyBuilder().build());
try { try {
if ("ppf".equals(ext)) if ("ppf".equals(ext))
Utils.copyFile(this, romFile, outputFile); Utils.copyFile(this, romFile, outputFile);
patcher.apply(Settings.getIgnoreChecksum(this)); patcher.apply();
Settings.setPatchingSuccessful(this, true);
} 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);
} else { } else {
errorMsg = e.getMessage(); errorMsg = e.getMessage();
} }
if (outputFile.isFile()) {
FileUtils.deleteQuietly(outputFile); 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 { } finally {
stopForeground(true); stopForeground(true);
} }
@ -326,21 +264,15 @@ public class WorkerService extends IntentService {
private void showErrorNotification(String text) { private void showErrorNotification(String text) {
Intent notificationIntent = new Intent(this, MainActivity.class); Intent notificationIntent = new Intent(this, MainActivity.class);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notify = new NotificationCompat.Builder(this, UniPatcher.NOTIFICATION_CHANNEL_ID) Notification notify = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.notify_error)) .setContentTitle(getString(R.string.notify_error))
.setContentText(text) .setContentText(text)
.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp) .setSmallIcon(R.drawable.ic_stat_patching)
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT)) .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setAutoCancel(true) .setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle() .setStyle(new NotificationCompat.BigTextStyle()
.bigText(text)) .bigText(text))
.build(); .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

@ -0,0 +1,97 @@
/*
Copyright (C) 2014 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.ad;
import android.content.Context;
import android.view.View;
import android.widget.FrameLayout;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import org.emunix.unipatcher.Globals;
public class AdMobController implements AdsController {
private static final String ADMOB_ID = "ca-app-pub-2445378722408015/8831379284";
private AdView adView;
private final Context context;
public AdMobController(Context c, FrameLayout layout) {
context = c;
createView(layout);
}
public void createView(FrameLayout layout) {
adView = new AdView(context);
adView.setAdSize(com.google.android.gms.ads.AdSize.SMART_BANNER);
adView.setAdUnitId(ADMOB_ID);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.addTestDevice("018B7216B142ABE97F9BFCD880C02EBC") // htc one v
.addTestDevice("64F8D0EE579BA448E833172DB2D91CBB") // lg nexus 5
.build();
adView.setAdListener(new AdListener() {
@Override
public void onAdLoaded() {
super.onAdLoaded();
if (Globals.isFullVersion()) {
adView.setVisibility(View.GONE);
}
}
@Override
public void onAdOpened() {
}
});
adView.loadAd(adRequest);
layout.addView(adView);
}
public void show(boolean show) {
if (adView != null) {
if (!show) {
adView.setVisibility(View.GONE);
} else if (!Globals.isFullVersion()) {
adView.setVisibility(View.VISIBLE);
}
}
}
public void pause() {
if (adView != null) {
adView.pause();
}
}
public void resume() {
if (adView != null) {
adView.resume();
}
}
public void start() {}
public void destroy() {
if (adView != null) {
adView.destroy();
}
}
}

View file

@ -0,0 +1,31 @@
/*
Copyright (C) 2014 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.ad;
import android.widget.FrameLayout;
interface AdsController {
void createView(FrameLayout layout);
void show(boolean show);
void start();
void destroy();
void resume();
void pause();
}

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -29,7 +29,7 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
public class APS extends Patcher { public class APS extends Patch {
public static final int NOT_APS_PATCH = 0; public static final int NOT_APS_PATCH = 0;
public static final int APS_N64_PATCH = 1; public static final int APS_N64_PATCH = 1;
@ -43,20 +43,18 @@ public class APS extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() throws PatchException, IOException {
Patcher aps = null; Patch aps = null;
switch (checkAPS(patchFile)) { switch (checkAPS(patchFile)) {
case APS_N64_PATCH: case APS_N64_PATCH:
aps = new APS_N64(context, patchFile, romFile, outputFile); aps = new APS_N64(context, patchFile, romFile, outputFile); break;
break;
case APS_GBA_PATCH: case APS_GBA_PATCH:
aps = new APS_GBA(context, patchFile, romFile, outputFile); aps = new APS_GBA(context, patchFile, romFile, outputFile); break;
break;
case NOT_APS_PATCH: case NOT_APS_PATCH:
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(ignoreChecksum); aps.apply();
} }
public int checkAPS(File file) throws PatchException, IOException { public int checkAPS(File file) throws PatchException, IOException {

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -33,7 +33,7 @@ import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.Arrays; import java.util.Arrays;
public class APS_GBA extends Patcher { public class APS_GBA extends Patch {
private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31}; // APS1 private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31}; // APS1
private static final int CHUNK_SIZE = 65536; private static final int CHUNK_SIZE = 65536;
@ -77,7 +77,7 @@ public class APS_GBA extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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,6 @@ public class APS_GBA extends Patcher {
} else if (crc == patchCrc2) { } else if (crc == patchCrc2) {
isModified = true; isModified = true;
} else { } else {
if (!ignoreChecksum)
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
} }
if (isOriginal && isModified) if (isOriginal && isModified)
@ -154,7 +153,7 @@ public class APS_GBA extends Patcher {
if(isOriginal) { if(isOriginal) {
Utils.truncateFile(outputFile, fileSize2); Utils.truncateFile(outputFile, fileSize2);
} else if (isModified) { } else {
Utils.truncateFile(outputFile, fileSize1); Utils.truncateFile(outputFile, fileSize1);
} }
} }

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -35,7 +35,7 @@ import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.Arrays; import java.util.Arrays;
public class APS_N64 extends Patcher { public class APS_N64 extends Patch {
private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31, 0x30}; // APS10 private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31, 0x30}; // APS10
private static final int TYPE_SIMPLE_PATCH = 0; private static final int TYPE_SIMPLE_PATCH = 0;
@ -47,7 +47,7 @@ public class APS_N64 extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() throws PatchException, IOException {
BufferedInputStream romStream = null; BufferedInputStream romStream = null;
BufferedInputStream patchStream = null; BufferedInputStream patchStream = null;
BufferedOutputStream outputStream = null; BufferedOutputStream outputStream = null;
@ -96,10 +96,8 @@ 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 (!ignoreChecksum) {
if (!validateROM(endianness, cardID, country, crc)) if (!validateROM(endianness, cardID, country, crc))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -35,7 +35,7 @@ import java.nio.channels.FileChannel;
import java.util.Arrays; import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
public class BPS extends Patcher { public class BPS extends Patch {
private static final byte[] MAGIC_NUMBER = {0x42, 0x50, 0x53, 0x31}; // "BPS1" private static final byte[] MAGIC_NUMBER = {0x42, 0x50, 0x53, 0x31}; // "BPS1"
private static final byte SOURCE_READ = 0b00; private static final byte SOURCE_READ = 0b00;
@ -50,7 +50,7 @@ public class BPS extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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,12 +68,10 @@ 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));
if (!ignoreChecksum) {
long realRomCrc = FileUtils.checksumCRC32(romFile); long realRomCrc = FileUtils.checksumCRC32(romFile);
if (realRomCrc != bpsCrc.getInputFileCRC()) { if (realRomCrc != bpsCrc.getInputFileCRC()) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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();
patch.position(4); // skip magic patch.position(4); // skip magic
@ -135,12 +133,10 @@ public class BPS extends Patcher {
IOUtils.closeQuietly(output); IOUtils.closeQuietly(output);
} }
if(!ignoreChecksum) {
long realOutCrc = FileUtils.checksumCRC32(outputFile); long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != bpsCrc.getOutputFileCRC()) if (realOutCrc != bpsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching)); throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
} }
}
// decode pointer // decode pointer
private long decode(FileChannel fc) throws IOException { private long decode(FileChannel fc) throws IOException {

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -30,7 +30,7 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
public class DPS extends Patcher { public class DPS extends Patch {
private static final int MIN_SIZE_PATCH = 136; private static final int MIN_SIZE_PATCH = 136;
private static final int BUFFER_SIZE = 32768; private static final int BUFFER_SIZE = 32768;
@ -42,7 +42,7 @@ public class DPS extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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,11 +63,9 @@ 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
if (!ignoreChecksum) {
long romSize = getUInt(buffer, 194); long romSize = getUInt(buffer, 194);
if (romSize != romFile.length()) if (romSize != romFile.length())
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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

@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -45,7 +45,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
public class EBP extends Patcher { public class EBP extends Patch {
private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH" private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
private static final byte[] EARTH_BOUND = {0x45, 0x41, 0x52, 0x54, 0x48, 0x20, 0x42, 0x4f, 0x55, 0x4e, 0x44}; private static final byte[] EARTH_BOUND = {0x45, 0x41, 0x52, 0x54, 0x48, 0x20, 0x42, 0x4f, 0x55, 0x4e, 0x44};
@ -68,12 +68,12 @@ public class EBP extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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, ignoreChecksum); prepareCleanRom(cleanRom);
EBPtoIPS(patchFile, ipsPatch); EBPtoIPS(patchFile, ipsPatch);
@ -85,7 +85,7 @@ public class EBP extends Patcher {
} }
} }
private void prepareCleanRom(File file, boolean ignoreChecksum) throws IOException, PatchException { private void prepareCleanRom(File file) throws IOException, PatchException {
// delete smc header // delete smc header
SnesSmcHeader smc = new SnesSmcHeader(); SnesSmcHeader smc = new SnesSmcHeader();
try { try {
@ -95,10 +95,8 @@ public class EBP extends Patcher {
} }
// check rom size and remove unused expanded space // check rom size and remove unused expanded space
if (!ignoreChecksum) {
if (file.length() < EB_CLEAN_ROM_SIZE) if (file.length() < EB_CLEAN_ROM_SIZE)
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2013, 2016, 2017 Boris Timofeev Copyright (C) 2013, 2016 Boris Timofeev
This file is part of UniPatcher. This file is part of UniPatcher.
@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -34,27 +34,17 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
public class IPS extends Patcher { public class IPS extends Patch {
public static final int NOT_IPS_PATCH = 0; private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
public static final int IPS_PATCH = 1;
public static final int IPS32_PATCH = 2;
private static final byte[] MAGIC_NUMBER_IPS = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
private static final byte[] MAGIC_NUMBER_IPS32 = {0x49, 0x50, 0x53, 0x33, 0x32}; // "IPS32"
private int mPatchType = NOT_IPS_PATCH;
public IPS(Context context, File patch, File rom, File output) { public IPS(Context context, File patch, File rom, File output) {
super(context, patch, rom, output); super(context, patch, rom, output);
} }
@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;
BufferedOutputStream outputStream = null; BufferedOutputStream outputStream = null;
@ -65,37 +55,33 @@ public class IPS extends Patcher {
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile)); outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
long romSize = romFile.length(); long romSize = romFile.length();
long romPos = 0; int romPos = 0;
long outPos = 0; int outPos = 0;
long offset; int offset;
long size; int size;
if (patchFile.length() < 14) { if (patchFile.length() < 14) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
} }
// check magic string
byte[] magic = new byte[5]; byte[] magic = new byte[5];
size = patchStream.read(magic); size = patchStream.read(magic);
if (Arrays.equals(magic, MAGIC_NUMBER_IPS)) { if (size != 5 || !Arrays.equals(magic, MAGIC_NUMBER))
mPatchType = IPS_PATCH;
} else if (Arrays.equals(magic, MAGIC_NUMBER_IPS32)) {
mPatchType = IPS32_PATCH;
} else {
throw new PatchException(context.getString(R.string.notify_error_not_ips_patch)); throw new PatchException(context.getString(R.string.notify_error_not_ips_patch));
}
while (true) { while (true) {
offset = readOffset(patchStream); offset = readOffset(patchStream, 3);
if (offset < 0) if (offset < 0)
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted)); throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
if (checkEOF(offset)) { if (offset == 0x454f46) { // EOF
// truncate file or copy tail // truncate file or copy tail
if (romPos < romSize) { if (romPos < romSize) {
offset = readOffset(patchStream); offset = readOffset(patchStream, 3);
if (offset != -1 && offset >= romPos) { if (offset != -1 && offset >= romPos) {
size = offset - romPos; size = offset - romPos;
} else { } else {
size = romSize - romPos; size = (int) romSize - romPos;
} }
Utils.copy(romStream, outputStream, size); Utils.copy(romStream, outputStream, size);
} }
@ -111,7 +97,7 @@ public class IPS extends Patcher {
} }
} else { } else {
if (outPos < romSize) { if (outPos < romSize) {
size = romSize - outPos; size = (int) romSize - outPos;
Utils.copy(romStream, outputStream, size); Utils.copy(romStream, outputStream, size);
romPos += size; romPos += size;
outPos += size; outPos += size;
@ -125,14 +111,14 @@ public class IPS extends Patcher {
size = (patchStream.read() << 8) + patchStream.read(); size = (patchStream.read() << 8) + patchStream.read();
if (size != 0) { if (size != 0) {
byte[] data = new byte[(int)size]; byte[] data = new byte[size];
patchStream.read(data); patchStream.read(data);
outputStream.write(data); outputStream.write(data);
outPos += size; outPos += size;
} else { // RLE } else { // RLE
size = (patchStream.read() << 8) + patchStream.read(); size = (patchStream.read() << 8) + patchStream.read();
byte val = (byte) patchStream.read(); byte val = (byte) patchStream.read();
byte[] data = new byte[(int)size]; byte[] data = new byte[size];
Arrays.fill(data, val); Arrays.fill(data, val);
outputStream.write(data); outputStream.write(data);
outPos += size; outPos += size;
@ -140,9 +126,10 @@ public class IPS extends Patcher {
if (offset <= romSize) { if (offset <= romSize) {
if (romPos + size > romSize) { if (romPos + size > romSize) {
romPos = romSize; romPos = (int) romSize;
} else { } else {
byte[] buf = new byte[(int)size]; // Не используй romStream.skip(size), оно по какой-то причине не всегда пропускает байты.
byte[] buf = new byte[size];
romStream.read(buf); romStream.read(buf);
romPos += size; romPos += size;
} }
@ -155,30 +142,8 @@ public class IPS extends Patcher {
} }
} }
private boolean checkEOF(long value) { private int readOffset(InputStream stream, int numBytes) throws IOException {
switch (mPatchType) { int offset = 0;
case IPS_PATCH:
return value == 0x454f46; // "EOF"
case IPS32_PATCH:
return value == 0x45454f46; // "EEOF"
}
return false;
}
private long readOffset(InputStream stream) throws IOException {
long offset = 0;
int numBytes;
switch (mPatchType) {
case IPS_PATCH:
numBytes = 3;
break;
case IPS32_PATCH:
numBytes = 4;
break;
default:
throw new IOException("Internal IPS error");
}
while (numBytes-- != 0) { while (numBytes-- != 0) {
int b = stream.read(); int b = stream.read();
if (b == -1) if (b == -1)

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -30,8 +30,9 @@ import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.util.Arrays; import java.util.Arrays;
public class PPF extends Patcher { public class PPF extends Patch {
// private static final String LOG_TAG = "PPF";
private static final byte[] MAGIC_NUMBER = {0x50, 0x50, 0x46}; // "PPF" without version private static final byte[] MAGIC_NUMBER = {0x50, 0x50, 0x46}; // "PPF" without version
private RandomAccessFile patchStream; private RandomAccessFile patchStream;
@ -68,23 +69,16 @@ public class PPF extends Patcher {
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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));
} }
switch (getPPFVersion(patchFile)) { switch (getPPFVersion(patchFile)) {
case 1: case 1: applyPPF1(); break;
applyPPF1(); case 2: applyPPF2(); break;
break; case 3: applyPPF3(); break;
case 2: default: throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch));
applyPPF2(ignoreChecksum);
break;
case 3:
applyPPF3(ignoreChecksum);
break;
default:
throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch));
} }
} }
@ -112,18 +106,16 @@ public class PPF extends Patcher {
} }
} }
private void applyPPF2(boolean ignoreChecksum) throws IOException, PatchException { private void applyPPF2() 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 (!ignoreChecksum) {
if (romSize != romFile.length()) { if (romSize != romFile.length()) {
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
} }
}
outputStream = new RandomAccessFile(outputFile, "rw"); outputStream = new RandomAccessFile(outputFile, "rw");
@ -133,10 +125,8 @@ 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 (!ignoreChecksum) {
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock)) if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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();
@ -164,7 +154,7 @@ public class PPF extends Patcher {
} }
} }
private void applyPPF3(boolean ignoreChecksum) throws IOException, PatchException { private void applyPPF3() 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");
@ -186,11 +176,9 @@ 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 (!ignoreChecksum) {
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock)) if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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();
@ -229,10 +217,11 @@ public class PPF extends Patcher {
private long readLittleEndianLong(RandomAccessFile stream) throws IOException { private long readLittleEndianLong(RandomAccessFile stream) throws IOException {
byte[] b = new byte[8]; byte[] b = new byte[8];
stream.read(b); stream.read(b);
return ((long) (b[7] & 0xff) << 56) + ((long) (b[6] & 0xff) << 48) + long result = ((long)(b[7] & 0xff) << 56) + ((long)(b[6] & 0xff) << 48 ) +
((long)(b[5] & 0xff) << 40) + ((long)(b[4] & 0xff) << 32 ) + ((long)(b[5] & 0xff) << 40) + ((long)(b[4] & 0xff) << 32 ) +
((long)(b[3] & 0xff) << 24) + ((long)(b[2] & 0xff) << 16 ) + ((long)(b[3] & 0xff) << 24) + ((long)(b[2] & 0xff) << 16 ) +
((long)(b[1] & 0xff) << 8) + ((long)b[0] & 0xff); ((long)(b[1] & 0xff) << 8) + ((long)b[0] & 0xff);
return result;
} }
private int readLittleEndianInt(RandomAccessFile stream) throws IOException { private int readLittleEndianInt(RandomAccessFile stream) throws IOException {

View file

@ -17,25 +17,25 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
public abstract class Patcher { public abstract class Patch {
protected File patchFile = null; protected File patchFile = null;
protected File romFile = null; protected File romFile = null;
protected File outputFile = null; protected File outputFile = null;
protected Context context = null; protected Context context = null;
public Patcher(Context c, File patch, File rom, File output) { public Patch(Context c, File patch, File rom, File output) {
context = c; context = c;
patchFile = patch; patchFile = patch;
romFile = rom; romFile = rom;
outputFile = output; outputFile = output;
} }
public abstract void apply(boolean ignoreChecksum) throws PatchException, IOException; public abstract void apply() throws PatchException, IOException;
} }

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
public class PatchException extends Exception { public class PatchException extends Exception {

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -35,16 +35,15 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
public class UPS extends Patcher { public class UPS extends Patch {
private static final byte[] MAGIC_NUMBER = {0x55, 0x50, 0x53, 0x31}; // "UPS1" private static final byte[] MAGIC_NUMBER = {0x55, 0x50, 0x53, 0x31}; // "UPS1"
public UPS(Context context, File patch, File rom, File output) { public UPS(Context context, File patch, File rom, File output) {
super(context, patch, rom, output); super(context, patch, rom, output);
} }
@Override @Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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,10 +89,8 @@ public class UPS extends Patcher {
ySize = tmp; ySize = tmp;
upsCrc.swapInOut(); upsCrc.swapInOut();
} else { } else {
if (!ignoreChecksum) {
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch)); 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));
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile)); outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
@ -127,12 +124,10 @@ public class UPS extends Patcher {
IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(outputStream);
} }
if (!ignoreChecksum) {
long realOutCrc = FileUtils.checksumCRC32(outputFile); long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != upsCrc.getOutputFileCRC()) if (realOutCrc != upsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching)); throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
} }
}
// decode pointer // decode pointer
private Pair decode(BufferedInputStream stream) throws PatchException, IOException { private Pair decode(BufferedInputStream stream) throws PatchException, IOException {

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2016-2017 Boris Timofeev Copyright (C) 2016 Boris Timofeev
This file is part of UniPatcher. This file is part of UniPatcher.
@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>. along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.emunix.unipatcher.patcher; package org.emunix.unipatcher.patch;
import android.content.Context; import android.content.Context;
@ -29,26 +29,23 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
public class XDelta extends Patcher { public class XDelta extends Patch {
private static final int NO_ERROR = 0; private static final int NO_ERROR = 0;
private static final int ERR_UNABLE_OPEN_PATCH = -5001; 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_ROM = -5002;
private static final int ERR_UNABLE_OPEN_OUTPUT = -5003; 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_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, boolean ignoreChecksum); public static native int xdelta3apply(String patchPath, String romPath, String outputPath);
public static native int xdelta3create(String patchPath, String sourcePath, String modifiedPath);
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(boolean ignoreChecksum) throws PatchException, IOException { public void apply() 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));
@ -58,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(), ignoreChecksum); int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
switch (ret) { switch (ret) {
case NO_ERROR: case NO_ERROR:
@ -81,32 +78,6 @@ 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 { public boolean checkXDelta1(File file) throws IOException {
String[] MAGIC_XDELTA1 = {"%XDELTA%", "%XDZ000%", "%XDZ001%", String[] MAGIC_XDELTA1 = {"%XDELTA%", "%XDZ000%", "%XDZ001%",
"%XDZ002%", "%XDZ003%", "%XDZ004%"}; "%XDZ002%", "%XDZ003%", "%XDZ004%"};

View file

@ -29,12 +29,12 @@ import android.os.Environment;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -42,8 +42,10 @@ import com.afollestad.materialdialogs.MaterialDialog;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R; import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Utils; import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.ad.AdMobController;
import org.emunix.unipatcher.ui.adapter.FilePickerAdapter; import org.emunix.unipatcher.ui.adapter.FilePickerAdapter;
import java.io.File; import java.io.File;
@ -61,10 +63,10 @@ import java.util.zip.CRC32;
public class FilePickerActivity extends AppCompatActivity implements FilePickerAdapter.OnItemClickListener { public class FilePickerActivity extends AppCompatActivity implements FilePickerAdapter.OnItemClickListener {
private AdMobController ad;
private RecyclerView list; private RecyclerView list;
private FilePickerAdapter listAdapter; private FilePickerAdapter listAdapter;
private TextView permissionErrorText; private TextView permissionErrorText;
private CardView card;
private TextView crc32; private TextView crc32;
private TextView md5; private TextView md5;
@ -106,7 +108,6 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
permissionErrorText = (TextView) findViewById(R.id.empty_view); permissionErrorText = (TextView) findViewById(R.id.empty_view);
list = (RecyclerView) findViewById(R.id.list); list = (RecyclerView) findViewById(R.id.list);
card = (CardView) findViewById(R.id.card);
try { try {
list.setHasFixedSize(true); list.setHasFixedSize(true);
} catch (NullPointerException e) {/* TODO log */} } catch (NullPointerException e) {/* TODO log */}
@ -117,6 +118,14 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
listAdapter.setOnItemClickListener(this); listAdapter.setOnItemClickListener(this);
requestStoragePermission(); requestStoragePermission();
// Load ads
if (!Globals.isFullVersion()) {
FrameLayout adView = (FrameLayout) findViewById(R.id.adView);
ad = new AdMobController(this, adView);
if (!Utils.isOnline(this))
ad.show(false);
}
} }
@Override @Override
@ -204,7 +213,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
if (currentDir.getParent() != null && currentDir.getParentFile().canRead()) { if (currentDir.getParent() != null && currentDir.getParentFile().canRead()) {
entry = new FileEntry(); entry = new FileEntry();
entry.setIcon(R.drawable.folder_upload); entry.setIcon(R.drawable.ic_folder_upload_grey600_24dp);
entry.setName(".."); entry.setName("..");
fileList.add(entry); fileList.add(entry);
} }
@ -214,15 +223,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
continue; continue;
if (file.isDirectory()) { if (file.isDirectory()) {
entry = new FileEntry(); entry = new FileEntry();
entry.setIcon(R.drawable.folder); entry.setIcon(R.drawable.ic_folder_grey600_24dp);
entry.setName(file.getName()); entry.setName(file.getName());
fileList.add(entry); fileList.add(entry);
} else { } else {
entry = new FileEntry(); entry = new FileEntry();
if (Utils.isPatch(file)){ if (Utils.isPatch(file)){
entry.setIcon(R.drawable.healing); entry.setIcon(R.drawable.ic_healing_grey600_24dp);
} else { } else {
entry.setIcon(R.drawable.file); entry.setIcon(R.drawable.ic_insert_drive_file_grey600_24dp);
} }
entry.setName(file.getName()); entry.setName(file.getName());
fileList.add(entry); fileList.add(entry);
@ -250,6 +259,30 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
savedInstanceState.putString("currentDirectory", currentDir.getAbsolutePath()); savedInstanceState.putString("currentDirectory", currentDir.getAbsolutePath());
} }
@Override
public void onPause() {
if (ad != null) {
ad.pause();
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (ad != null) {
ad.resume();
}
}
@Override
public void onDestroy() {
if (ad != null) {
ad.destroy();
}
super.onDestroy();
}
private void requestStoragePermission() { private void requestStoragePermission() {
if (!Utils.hasStoragePermission(this)) { if (!Utils.hasStoragePermission(this)) {
ActivityCompat.requestPermissions(this, ActivityCompat.requestPermissions(this,
@ -277,11 +310,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
private void showPermissionError(boolean on) { private void showPermissionError(boolean on) {
if (on) { if (on) {
card.setVisibility(View.GONE); list.setVisibility(View.GONE);
if (ad != null)
ad.show(false);
permissionErrorText.setVisibility(View.VISIBLE); permissionErrorText.setVisibility(View.VISIBLE);
} else { } else {
permissionErrorText.setVisibility(View.GONE); permissionErrorText.setVisibility(View.GONE);
card.setVisibility(View.VISIBLE); if (ad != null)
ad.show(true);
list.setVisibility(View.VISIBLE);
} }
} }
@ -362,7 +399,8 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
} }
} }
private HashMap<String, String> getFileChecksums(File file) throws IOException, NoSuchAlgorithmException, IllegalArgumentException { private HashMap<String, String> getFileChecksums(File file) throws IOException, NoSuchAlgorithmException, IllegalArgumentException
{
if (file.isDirectory()) if (file.isDirectory())
throw new IllegalArgumentException("Unable calculate checksum for directory"); throw new IllegalArgumentException("Unable calculate checksum for directory");

View file

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2013-2017 Boris Timofeev Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher. This file is part of UniPatcher.
@ -19,13 +19,13 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
package org.emunix.unipatcher.ui.activity; package org.emunix.unipatcher.ui.activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView; import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
@ -36,34 +36,54 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.anjlab.android.iab.v3.BillingProcessor;
import com.anjlab.android.iab.v3.TransactionDetails;
import com.google.firebase.analytics.FirebaseAnalytics;
import org.emunix.unipatcher.BuildConfig; import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R; import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings; import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.UniPatcher; import org.emunix.unipatcher.ad.AdMobController;
import org.emunix.unipatcher.ui.dialog.RateThisApp;
import org.emunix.unipatcher.ui.fragment.ActionFragment; 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.PatchingFragment;
import org.emunix.unipatcher.ui.fragment.SmdFixChecksumFragment; import org.emunix.unipatcher.ui.fragment.SmdFixChecksumFragment;
import org.emunix.unipatcher.ui.fragment.SnesSmcHeaderFragment; 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_AUTO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
public class MainActivity extends AppCompatActivity public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener { implements NavigationView.OnNavigationItemSelectedListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final String SKU_FULL = "full";
private static final String SKU_REMOVE_ADS = "ad";
private boolean readyToPurchase = false;
private BillingProcessor bp;
private AdMobController ad;
private FirebaseAnalytics firebaseAnalytics;
private Context context;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
setTheme(); setTheme();
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
context = this;
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
if (BuildConfig.DEBUG)
firebaseAnalytics.setAnalyticsCollectionEnabled(false);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
@ -82,6 +102,8 @@ public class MainActivity extends AppCompatActivity
if (fragment != null){ if (fragment != null){
boolean ret = fragment.runAction(); boolean ret = fragment.runAction();
} }
//Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
// .setAction("Action", null).show();
} }
}); });
@ -93,7 +115,50 @@ public class MainActivity extends AppCompatActivity
} }
parseArgument(); parseArgument();
showDonateSnackbar(); bp = new BillingProcessor(this, Globals.getKey(), new BillingProcessor.IBillingHandler() {
@Override
public void onBillingInitialized() {
Log.d(LOG_TAG, "Billing initialized");
readyToPurchase = true;
if (bp.isPurchased(SKU_FULL) || bp.isPurchased(SKU_REMOVE_ADS)) {
setFullVersion();
}
}
@Override
public void onProductPurchased(String productId, TransactionDetails details) {
Log.d(LOG_TAG, "Item purchased: " + productId);
complain(getString(R.string.purchase_successful));
setFullVersion();
}
@Override
public void onBillingError(int errorCode, Throwable error) {
if (errorCode != 110) // cancel purchase
complain("Billing error: " + Integer.toString(errorCode));
}
@Override
public void onPurchaseHistoryRestored() {
for(String sku : bp.listOwnedProducts())
Log.d(LOG_TAG, "Owned Managed Product: " + sku);
if (bp.isPurchased(SKU_FULL) || bp.isPurchased(SKU_REMOVE_ADS)) {
setFullVersion();
}
}
});
RateThisApp.launch(this);
// Load ads
if (!Globals.isFullVersion()) {
Handler adHandler = new Handler();
adHandler.postDelayed(new Runnable() {
@Override
public void run() {
FrameLayout adView = (FrameLayout) findViewById(R.id.adView);
ad = new AdMobController(context, adView);
if (!Utils.isOnline(context))
ad.show(false);
}
}, 1000);
}
} }
private void setTheme() { private void setTheme() {
@ -118,12 +183,10 @@ public class MainActivity extends AppCompatActivity
if (id == R.id.nav_apply_patch) { if (id == R.id.nav_apply_patch) {
selectDrawerItem(0); selectDrawerItem(0);
} else if (id == R.id.nav_create_patch) {
selectDrawerItem(1);
} else if (id == R.id.nav_smd_fix_checksum) { } else if (id == R.id.nav_smd_fix_checksum) {
selectDrawerItem(2); selectDrawerItem(1);
} else if (id == R.id.nav_snes_add_del_smc_header) { } else if (id == R.id.nav_snes_add_del_smc_header) {
selectDrawerItem(3); selectDrawerItem(2);
} }
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
@ -133,9 +196,9 @@ public class MainActivity extends AppCompatActivity
Intent settingsIntent = new Intent(this, SettingsActivity.class); Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent); startActivity(settingsIntent);
} else if (id == R.id.nav_rate) { } else if (id == R.id.nav_rate) {
rateApp(); RateThisApp.rate(this);
} else if (id == R.id.nav_donate) { } else if (id == R.id.nav_buy) {
showDonateActivity(); buyFullVersion();
} else if (id == R.id.nav_share) { } else if (id == R.id.nav_share) {
shareApp(); shareApp();
} else if (id == R.id.nav_help) { } else if (id == R.id.nav_help) {
@ -150,17 +213,9 @@ public class MainActivity extends AppCompatActivity
// update the main content by replacing fragments // update the main content by replacing fragments
Fragment fragment; Fragment fragment;
switch (position) { switch (position) {
case 1: case 1: fragment = new SmdFixChecksumFragment(); break;
fragment = new CreatePatchFragment(); case 2: fragment = new SnesSmcHeaderFragment(); break;
break; default: fragment = new PatchingFragment();
case 2:
fragment = new SmdFixChecksumFragment();
break;
case 3:
fragment = new SnesSmcHeaderFragment();
break;
default:
fragment = new PatchingFragment();
} }
FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
@ -171,66 +226,70 @@ public class MainActivity extends AppCompatActivity
private void parseArgument() { private void parseArgument() {
try { try {
String arg = getIntent().getData().getPath(); String arg = getIntent().getData().getPath();
UniPatcher.setAppArgument(arg); Globals.setCmdArgument(arg);
Log.d(LOG_TAG, "Cmd argument: " + arg);
} catch (NullPointerException e) { } catch (NullPointerException e) {
// The application is not opened from the file manager Log.e(LOG_TAG, "NullPointerException in argument fetching");
} }
} }
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 @Override
public void onClick(View v) { protected void onActivityResult(int requestCode, int resultCode, Intent data){
showDonateActivity(); Log.d(LOG_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
} if (!bp.handleActivityResult(requestCode, resultCode, data))
}) super.onActivityResult(requestCode, resultCode, data);
.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() { private void shareApp() {
Intent shareIntent = new Intent(Intent.ACTION_SEND); Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain"); shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.app_name)); shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.app_name));
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_text) + BuildConfig.SHARE_URL); shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_text) + "https://play.google.com/store/apps/details?id=org.eminix.unipatcher");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title))); startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title)));
} }
public void rateApp() { private void buyFullVersion() {
Intent rateAppIntent = new Intent(Intent.ACTION_VIEW); if (readyToPurchase)
rateAppIntent.setData(Uri.parse(BuildConfig.RATE_URL)); bp.purchase(this, SKU_REMOVE_ADS);
if (getPackageManager().queryIntentActivities(rateAppIntent, 0).size() == 0) { else
// Market app is not installed. Open web browser complain("Billing not initialized.");
rateAppIntent.setData(Uri.parse(BuildConfig.SHARE_URL));
} }
startActivity(rateAppIntent);
private void setFullVersion() {
Globals.setFullVersion();
if (ad != null)
ad.show(false);
}
private void complain(String message) {
Log.d(LOG_TAG, message);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
@Override
public void onPause() {
if (ad != null) {
ad.pause();
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (ad != null) {
ad.resume();
}
}
@Override
public void onDestroy() {
if (bp != null) {
bp.release();
}
if (ad != null) {
ad.destroy();
}
super.onDestroy();
} }
} }

View file

@ -38,7 +38,6 @@ public class FilePickerAdapter extends RecyclerView.Adapter<FilePickerAdapter.Vi
public interface OnItemClickListener { public interface OnItemClickListener {
void onItemClick(View v, int position); void onItemClick(View v, int position);
void onItemLongClick(View v, int position); void onItemLongClick(View v, int position);
} }

View file

@ -39,11 +39,14 @@ public class HelpPagerAdapter extends FragmentStatePagerAdapter {
public Fragment getItem(int position) { public Fragment getItem(int position) {
switch (position) { switch (position) {
case 0: case 0:
return new FaqFragment(); FaqFragment tab1 = new FaqFragment();
return tab1;
case 1: case 1:
return new ChangelogFragment(); ChangelogFragment tab2 = new ChangelogFragment();
return tab2;
case 2: case 2:
return new AboutFragment(); AboutFragment tab3 = new AboutFragment();
return tab3;
default: default:
return null; return null;
} }

View file

@ -0,0 +1,89 @@
/*
Copyright (C) 2013 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.dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
import org.emunix.unipatcher.R;
public class RateThisApp {
private final static int LAUNCHES_UNTIL_PROMPT = 7;
public static void launch(Context context) {
SharedPreferences preferences = context.getSharedPreferences("RateThisApp", 0);
if (preferences.getBoolean("dont_show_again", false))
return;
SharedPreferences.Editor editor = preferences.edit();
long launchCount = preferences.getLong("launch_count", 0) + 1;
editor.putLong("launch_count", launchCount);
if (launchCount % LAUNCHES_UNTIL_PROMPT == 0)
showDialog(context);
editor.apply();
}
public static void rate(Context context) {
String packageName = context.getApplicationContext().getPackageName();
Intent rateAppIntent = new Intent(Intent.ACTION_VIEW);
rateAppIntent.setData(Uri.parse("market://details?id=" + packageName));
if (context.getPackageManager().queryIntentActivities(rateAppIntent, 0).size() == 0) {
// Market app is not installed. Open web browser
rateAppIntent.setData(Uri.parse("https://play.google.com/store/apps/details?id=" + packageName));
}
context.startActivity(rateAppIntent);
dontShowDialogAgain(context);
}
private static void showDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.rate_dialog_title);
builder.setMessage(R.string.rate_dialog_message);
builder.setPositiveButton(R.string.rate_dialog_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
rate(context);
}
});
builder.setNegativeButton(R.string.rate_dialog_later, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private static void dontShowDialogAgain(Context context) {
SharedPreferences preferences = context.getSharedPreferences("RateThisApp", 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("dont_show_again", true);
editor.apply();
}
}

View file

@ -1,259 +0,0 @@
/*
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,10 +36,9 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Action; import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R; import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings; import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.Utils; import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.WorkerService; import org.emunix.unipatcher.WorkerService;
import org.emunix.unipatcher.ui.activity.FilePickerActivity; import org.emunix.unipatcher.ui.activity.FilePickerActivity;
@ -50,6 +49,8 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
private static final String LOG_TAG = "org.emunix.unipatcher"; 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 romNameTextView;
private TextView patchNameTextView; private TextView patchNameTextView;
private TextView outputNameTextView; private TextView outputNameTextView;
@ -57,8 +58,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
private String patchPath = null; private String patchPath = null;
private String outputPath = null; private String outputPath = null;
public PatchingFragment() { public PatchingFragment() {}
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -96,7 +96,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
} }
private void parseArgument() { private void parseArgument() {
patchPath = UniPatcher.getAppArgument(); patchPath = Globals.getCmdArgument();
if (patchPath != null) { if (patchPath != null) {
patchNameTextView.setText(new File(patchPath).getName()); patchNameTextView.setText(new File(patchPath).getName());
} }
@ -146,12 +146,12 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
case R.id.patchCardView: case R.id.patchCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_patch)); intent.putExtra("title", getString(R.string.file_picker_activity_title_select_patch));
intent.putExtra("directory", Settings.getPatchDir(getActivity())); intent.putExtra("directory", Settings.getPatchDir(getActivity()));
startActivityForResult(intent, Action.SELECT_PATCH_FILE); startActivityForResult(intent, SELECT_PATCH_FILE);
break; break;
case R.id.romCardView: case R.id.romCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom)); intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
intent.putExtra("directory", Settings.getRomDir(getActivity())); intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_ROM_FILE); startActivityForResult(intent, SELECT_ROM_FILE);
break; break;
case R.id.outputCardView: case R.id.outputCardView:
renameOutputRom(); renameOutputRom();
@ -171,7 +171,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
} }
switch (requestCode) { switch (requestCode) {
case Action.SELECT_ROM_FILE: case SELECT_ROM_FILE:
romPath = path; romPath = path;
romNameTextView.setVisibility(View.VISIBLE); romNameTextView.setVisibility(View.VISIBLE);
romNameTextView.setText(fpath.getName()); romNameTextView.setText(fpath.getName());
@ -179,7 +179,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
outputPath = makeOutputPath(path); outputPath = makeOutputPath(path);
outputNameTextView.setText(new File(outputPath).getName()); outputNameTextView.setText(new File(outputPath).getName());
break; break;
case Action.SELECT_PATCH_FILE: case SELECT_PATCH_FILE:
patchPath = path; patchPath = path;
patchNameTextView.setVisibility(View.VISIBLE); patchNameTextView.setVisibility(View.VISIBLE);
patchNameTextView.setText(fpath.getName()); patchNameTextView.setText(fpath.getName());
@ -213,11 +213,11 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
} }
Intent intent = new Intent(getActivity(), WorkerService.class); Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Action.APPLY_PATCH); intent.putExtra("action", Globals.ACTION_PATCHING);
intent.putExtra("romPath", romPath); intent.putExtra("romPath", romPath);
intent.putExtra("patchPath", patchPath); intent.putExtra("patchPath", patchPath);
intent.putExtra("outputPath", outputPath); intent.putExtra("outputPath", outputPath);
Utils.startForegroundService(getActivity(), intent); getActivity().startService(intent);
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify,Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify,Toast.LENGTH_SHORT).show();
return true; return true;
@ -247,10 +247,6 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String newName = input.getText().toString(); 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("/")) { if (newName.contains("/")) {
newName = newName.replaceAll("/", "_"); newName = newName.replaceAll("/", "_");
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show(); 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.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.emunix.unipatcher.Action; import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R; import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings; import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils; import org.emunix.unipatcher.Utils;
@ -43,13 +43,13 @@ import java.io.File;
public class SmdFixChecksumFragment extends ActionFragment implements View.OnClickListener { public class SmdFixChecksumFragment extends ActionFragment implements View.OnClickListener {
private static final String LOG_TAG = "org.emunix.unipatcher"; private static final String LOG_TAG = "org.emunix.unipatcher";
private static final int SELECT_ROM_FILE = 1;
private TextView romNameTextView; private TextView romNameTextView;
private TextView fixChecksumInfoTextview; private TextView fixChecksumInfoTextview;
private String romPath = null; private String romPath = null;
public SmdFixChecksumFragment() { public SmdFixChecksumFragment() {}
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -107,7 +107,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
case R.id.romCardView: case R.id.romCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom)); intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
intent.putExtra("directory", Settings.getRomDir(getActivity())); intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_ROM_FILE); startActivityForResult(intent, SELECT_ROM_FILE);
break; break;
} }
} }
@ -123,7 +123,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
} }
switch (requestCode) { switch (requestCode) {
case Action.SELECT_ROM_FILE: case SELECT_ROM_FILE:
romPath = path; romPath = path;
romNameTextView.setVisibility(View.VISIBLE); romNameTextView.setVisibility(View.VISIBLE);
romNameTextView.setText(new File(path).getName()); romNameTextView.setText(new File(path).getName());
@ -141,9 +141,9 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
} }
Intent intent = new Intent(getActivity(), WorkerService.class); Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Globals.ACTION_SMD_FIX_CHECKSUM);
intent.putExtra("romPath", romPath); intent.putExtra("romPath", romPath);
intent.putExtra("action", Action.SMD_FIX_CHECKSUM); getActivity().startService(intent);
Utils.startForegroundService(getActivity(), intent);
Toast.makeText(getActivity(), R.string.notify_smd_fix_checksum_started_check_notify,Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), R.string.notify_smd_fix_checksum_started_check_notify,Toast.LENGTH_SHORT).show();
return true; return true;

View file

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

View file

@ -1,57 +0,0 @@
/*
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,7 +24,6 @@ import android.content.Intent;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat; import android.support.v4.app.NotificationManagerCompat;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.ui.activity.MainActivity; import org.emunix.unipatcher.ui.activity.MainActivity;
public abstract class Notify { public abstract class Notify {
@ -41,8 +40,9 @@ public abstract class Notify {
Intent notificationIntent = new Intent(context, MainActivity.class); Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notifyBuilder = new NotificationCompat.Builder(context, UniPatcher.NOTIFICATION_CHANNEL_ID); notifyBuilder = new NotificationCompat.Builder(context);
notifyBuilder.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT)); notifyBuilder.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT));
//notifyMng = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notifyMng = NotificationManagerCompat.from(context); notifyMng = NotificationManagerCompat.from(context);
} }
@ -68,7 +68,6 @@ public abstract class Notify {
} }
public abstract void setCompleted(); public abstract void setCompleted();
public abstract void setFailed(String message); public abstract void setFailed(String message);
public void setProgress(boolean isEnabled) { public void setProgress(boolean isEnabled) {

View file

@ -28,7 +28,7 @@ public class PatchingNotify extends Notify {
public PatchingNotify(Context c, String text) { public PatchingNotify(Context c, String text) {
super(c); super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp); notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setContentTitle(context.getString(R.string.notify_applying_patch)); notifyBuilder.setContentTitle(context.getString(R.string.notify_applying_patch));
notifyBuilder.setContentText(text); notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(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) { public SmdFixChecksumNotify(Context c, String text) {
super(c); super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp); notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setContentTitle(context.getString(R.string.notify_smd_fix_checksum_in_progress)); notifyBuilder.setContentTitle(context.getString(R.string.notify_smd_fix_checksum_in_progress));
notifyBuilder.setContentText(text); notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(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 class SnesAddSmcHeaderNotify extends Notify {
public SnesAddSmcHeaderNotify(Context c, String text) { public SnesAddSmcHeaderNotify(Context c, String text) {
super(c); super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp); notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_add_smc_header_in_progress)); notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_add_smc_header_in_progress));
notifyBuilder.setContentText(text); notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(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 class SnesDeleteSmcHeaderNotify extends Notify {
public SnesDeleteSmcHeaderNotify(Context c, String text) { public SnesDeleteSmcHeaderNotify(Context c, String text) {
super(c); super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp); notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_delete_smc_header_in_progress)); notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_delete_smc_header_in_progress));
notifyBuilder.setContentText(text); notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text)); notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));

View file

@ -3,8 +3,8 @@
android:interpolator="@android:anim/decelerate_interpolator"> android:interpolator="@android:anim/decelerate_interpolator">
<translate <translate
android:duration="500"
android:fromYDelta="100%" android:fromYDelta="100%"
android:toYDelta="0"/> android:toYDelta="0"
android:duration="500" />
</set> </set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

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