Compare commits
106 commits
Author | SHA1 | Date | |
---|---|---|---|
|
5531a55fea | ||
|
0369c6e85a | ||
|
faaee235a2 | ||
|
af8216dd05 | ||
|
5cd83abcfc | ||
|
c0c26e59ad | ||
|
beb6ed9404 | ||
|
2cc0e0585d | ||
|
512f23e680 | ||
|
655e0954fa | ||
|
a959211176 | ||
|
f61cfb5121 | ||
|
0cab3fa8b5 | ||
|
550b14896e | ||
|
3f56868998 | ||
|
70db06eebc | ||
|
9ec29e3936 | ||
|
6fea1b7b8e | ||
|
3e2f57dbd7 | ||
|
b267f87a99 | ||
|
e6b3dc1331 | ||
|
8852b80907 | ||
|
a22d786f93 | ||
|
2c4512e775 | ||
|
b9f9c30009 | ||
|
b460e3b0a6 | ||
|
c0bdd147cd | ||
|
b1705b3e7d | ||
|
f137b4950f | ||
|
e055a65d28 | ||
|
c1192dd20d | ||
|
c6869d01ba | ||
|
6b27827d4f | ||
|
e6174550ab | ||
|
a36c4ac1c7 | ||
|
c3b0d595d1 | ||
|
7d9fd3017d | ||
|
1cb040b216 | ||
|
18fe8fe8b1 | ||
|
9192fb975a | ||
|
72264b4ffb | ||
|
6c01a85f86 | ||
|
77b4916e0b | ||
|
e55ed589ad | ||
|
15604c6163 | ||
|
c25ddc247a | ||
|
815a64a7ed | ||
|
23b1414549 | ||
|
415e2cb2aa | ||
|
bb41a70bb0 | ||
|
312634cf84 | ||
|
069939444d | ||
|
b5ddf27443 | ||
|
9051da11d3 | ||
|
039fbb85a8 | ||
|
4538bcea11 | ||
|
cd86ae9188 | ||
|
a2e6052538 | ||
|
f8e39c52f5 | ||
|
7dad097c6f | ||
|
a7228702fe | ||
|
540a49979c | ||
|
61c37de75f | ||
|
a987b5326a | ||
|
b14dd6b852 | ||
|
4c1e0d844e | ||
|
b8af6b1da1 | ||
|
ff44a72f80 | ||
|
f5d8d8d2ec | ||
|
4a36743845 | ||
|
ec2c1fc7ed | ||
|
7cf64ca059 | ||
|
815978d9aa | ||
|
77d2656db8 | ||
|
6035ca2eb0 | ||
|
623bd22045 | ||
|
84f46ba9a1 | ||
|
279cbf7dc5 | ||
|
7765404b7b | ||
|
c6cac2481f | ||
|
7c648665dc | ||
|
397bb2b107 | ||
|
c856257dfe | ||
|
3f7333020d | ||
|
9265bb5c54 | ||
|
dfe171f21b | ||
|
fe57b7ce8c | ||
|
4f4e2e5dda | ||
|
22543f0710 | ||
|
c47334e71b | ||
|
2141253cd9 | ||
|
71a863cbbe | ||
|
6634a63642 | ||
|
5eb50fde18 | ||
|
1698ea4e85 | ||
|
0c6d0c973a | ||
|
c7754a562f | ||
|
1e634ec86e | ||
|
220583aaf0 | ||
|
efb563e14a | ||
|
d14dcd0870 | ||
|
cb512e0a47 | ||
|
4866144707 | ||
|
cecfb9460e | ||
|
46e84b607c | ||
|
0c89a35060 |
|
@ -1,5 +1,6 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = he: iw, zh_CN: zh-rCN, zh_TW: zh-rTW, es_MX: es-rMX, pt_BR: pt-rBR, nb_NO: nb-rNO
|
||||
|
||||
[unipatcher.strings_xml]
|
||||
file_filter = app/src/main/res/values-<lang>/strings.xml
|
||||
|
|
26
README.md
|
@ -1,29 +1,37 @@
|
|||
[](https://opensource.org/licenses/GPL-3.0/)
|
||||
[](https://android-arsenal.com/api?level=14)
|
||||
|
||||
UniPatcher
|
||||
----------
|
||||
|
||||
UniPatcher is a ROM patcher for Android that supports IPS, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP and XDelta3 patch types.
|
||||
UniPatcher is a ROM patcher for Android that supports IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP and XDelta3 patch types.
|
||||
|
||||
### Additional features:
|
||||
|
||||
* Creating XDelta3 patches
|
||||
* Fix checksum in Sega Mega Drive ROMs
|
||||
* Add/Delete SMC header in Super Nintendo ROMs
|
||||
|
||||
### Screenshots:
|
||||
|
||||
<img src="/google-play/screenshot_1.png" width="350">
|
||||
<img src="/google-play/screenshot_2.png" width="350">
|
||||
<img src="/google-play/screenshot_1.png" width="350"> <img src="/google-play/screenshot_2.png" width="350">
|
||||
|
||||
### Install UniPatcher:
|
||||
|
||||
[<img src="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/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)
|
||||
|
||||
### How to contribute to 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)
|
||||
|
||||
#### Report a bug or suggest a new feature
|
||||
[... or get a Release APK here on GitHub](https://github.com/btimofeev/UniPatcher/releases)
|
||||
|
||||
Bugs and new features are being discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
|
||||
### Contribute:
|
||||
|
||||
#### Help with translations
|
||||
The translations are managed on [Transifex](https://www.transifex.com/unipatcher/unipatcher/).
|
||||
#### Report a bug or suggest features
|
||||
|
||||
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.
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
cmake_minimum_required(VERSION 3.4.1)
|
||||
add_library( xdelta3 SHARED src/main/cpp/xdelta3.c )
|
||||
find_library( log-lib log )
|
||||
target_link_libraries( xdelta3 ${log-lib} )
|
||||
add_library( xdelta3 SHARED src/main/cpp/xdelta3.c )
|
|
@ -1,13 +1,8 @@
|
|||
def versionMajor = 0
|
||||
def versionMinor = 12
|
||||
def versionPatch = 0
|
||||
def versionBuild = 0
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.2'
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.1'
|
||||
|
||||
signingConfigs {
|
||||
release
|
||||
|
@ -16,13 +11,17 @@ android {
|
|||
defaultConfig {
|
||||
applicationId "org.emunix.unipatcher"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 25
|
||||
versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild
|
||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
targetSdkVersion 27
|
||||
versionCode 150000
|
||||
versionName "0.15"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
arguments "-DANDROID_PLATFORM=android-14"
|
||||
arguments "-DANDROID_PLATFORM=android-14", "-DCMAKE_BUILD_TYPE=Release"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,19 +35,60 @@ 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 {
|
||||
cmake {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def Properties props = new Properties()
|
||||
def propFile = file('../../signing.properties')
|
||||
if (propFile.canRead()){
|
||||
if (propFile.canRead()) {
|
||||
props.load(new FileInputStream(propFile))
|
||||
|
||||
if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
|
||||
if (props != null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
|
||||
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
|
||||
|
||||
println 'RELEASE BUILD SIGNING'
|
||||
|
@ -69,22 +109,18 @@ if (propFile.canRead()){
|
|||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:2.2.28'
|
||||
testCompile 'org.mockito:mockito-core:2.12.0'
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:support-v4:25.1.0'
|
||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
||||
compile 'com.android.support:cardview-v7:25.1.0'
|
||||
compile 'com.android.support:preference-v14:25.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:25.1.0'
|
||||
compile 'com.android.support:design:25.1.0'
|
||||
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 'com.android.support:support-v4:27.0.2'
|
||||
compile 'com.android.support:appcompat-v7:27.0.2'
|
||||
compile 'com.android.support:cardview-v7:27.0.2'
|
||||
compile 'com.android.support:preference-v14:27.0.2'
|
||||
compile 'com.android.support:recyclerview-v7:27.0.2'
|
||||
compile 'com.android.support:design:27.0.2'
|
||||
compile 'com.android.support:support-v13:27.0.2' // used in material-dialogs
|
||||
compile 'commons-io:commons-io:2.5'
|
||||
compile 'org.sufficientlysecure:html-textview:3.0'
|
||||
compile 'org.sufficientlysecure:donations:2.5'
|
||||
compile 'org.sufficientlysecure:html-textview:3.5'
|
||||
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.1'
|
||||
compile 'com.afollestad.material-dialogs:core:0.9.2.3'
|
||||
compile 'com.afollestad.material-dialogs:core:0.9.6.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "32713218397",
|
||||
"firebase_url": "https://unipatcher-897b3.firebaseio.com",
|
||||
"project_id": "unipatcher-897b3",
|
||||
"storage_bucket": "unipatcher-897b3.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:32713218397:android:4b0afdca1aa174e1",
|
||||
"android_client_info": {
|
||||
"package_name": "org.emunix.unipatcher"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "32713218397-h8ptc6lelg9l871v10pgq11ni8ejaavg.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "org.emunix.unipatcher",
|
||||
"certificate_hash": "C31CB23D974F426B38D7AF0848A6F1066FCF1FD9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "32713218397-bhgevevp6i5coukhnkti1g9cecnujuav.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyD_8JprF21wL7wpRV7JymLzlbHR-jUBZKI"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 2,
|
||||
"analytics_property": {
|
||||
"tracking_id": "UA-77394676-1"
|
||||
}
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
9
app/src/google/AndroidManifest.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?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>
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.emunix.unipatcher"
|
||||
android:installLocation="auto">
|
||||
package="org.emunix.unipatcher"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
||||
<application
|
||||
android:name=".UniPatcher"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="false"
|
||||
android:extractNativeLibs="false"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern="/.*\\.aps" />
|
||||
<data android:pathPattern="/.*\\.ips" />
|
||||
<data android:pathPattern="/.*\\.ups" />
|
||||
<data android:pathPattern="/.*\\.bps" />
|
||||
<data android:pathPattern="/.*\\.ppf" />
|
||||
<data android:pathPattern="/.*\\.ebp" />
|
||||
<data android:pathPattern="/.*\\.dps" />
|
||||
<data android:pathPattern="/.*\\.xdelta" />
|
||||
<data android:pathPattern="/.*\\.xdelta3" />
|
||||
<data android:pathPattern="/.*\\.vcdiff" />
|
||||
<data android:host="*" />
|
||||
<data android:scheme="file"/>
|
||||
<data android:mimeType="*/*"/>
|
||||
<data android:pathPattern="/.*\\.aps"/>
|
||||
<data android:pathPattern="/.*\\.ips"/>
|
||||
<data android:pathPattern="/.*\\.ups"/>
|
||||
<data android:pathPattern="/.*\\.bps"/>
|
||||
<data android:pathPattern="/.*\\.ppf"/>
|
||||
<data android:pathPattern="/.*\\.ebp"/>
|
||||
<data android:pathPattern="/.*\\.dps"/>
|
||||
<data android:pathPattern="/.*\\.xdelta"/>
|
||||
<data android:pathPattern="/.*\\.xdelta3"/>
|
||||
<data android:pathPattern="/.*\\.xd"/>
|
||||
<data android:pathPattern="/.*\\.vcdiff"/>
|
||||
<data android:host="*"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.FilePickerActivity"
|
||||
android:label="@string/file_picker_activity_title" />
|
||||
android:label="@string/file_picker_activity_title"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.SettingsActivity"
|
||||
android:label="@string/settings_activity_title"
|
||||
android:theme="@style/PreferenceTheme" />
|
||||
android:theme="@style/PreferenceTheme"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.HelpActivity"
|
||||
android:label="@string/help_activity_title" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
android:label="@string/help_activity_title"/>
|
||||
|
||||
<activity
|
||||
android:name="com.google.android.gms.ads.AdActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
|
||||
android:theme="@android:style/Theme.Translucent" />
|
||||
android:name=".ui.activity.DonateActivity"
|
||||
android:label="@string/donate_activity_title"/>
|
||||
|
||||
<service
|
||||
android:name=".WorkerService"
|
||||
android:exported="false" />
|
||||
android:exported="false"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
This file based on encode_decode_test.c from XDelta3 sources.
|
||||
|
||||
Copyright (C) 2007 Ralf Junker
|
||||
Copyright (C) 2016 Boris Timofeev
|
||||
Copyright (C) 2016-2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
|
@ -23,7 +23,6 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#define SIZEOF_SIZE_T 4
|
||||
#define SIZEOF_UNSIGNED_LONG_LONG 8
|
||||
|
@ -31,19 +30,21 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "xdelta3/xdelta3/xdelta3.h"
|
||||
#include "xdelta3/xdelta3/xdelta3.c"
|
||||
|
||||
int apply(FILE *patch, FILE *in, FILE *out);
|
||||
int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum);
|
||||
|
||||
const int ERR_UNABLE_OPEN_PATCH = -5001;
|
||||
const int ERR_UNABLE_OPEN_ROM = -5002;
|
||||
const int ERR_UNABLE_OPEN_OUTPUT = -5003;
|
||||
const int ERR_UNABLE_OPEN_SOURCE = -5004;
|
||||
const int ERR_UNABLE_OPEN_MODIFIED = -5005;
|
||||
const int ERR_WRONG_CHECKSUM = -5010;
|
||||
|
||||
int Java_org_emunix_unipatcher_patch_XDelta_xdelta3apply(JNIEnv *env,
|
||||
jobject this,
|
||||
jstring patchPath,
|
||||
jstring romPath,
|
||||
jstring outputPath)
|
||||
{
|
||||
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
|
||||
jobject this,
|
||||
jstring patchPath,
|
||||
jstring romPath,
|
||||
jstring outputPath,
|
||||
jboolean ignoreChecksum) {
|
||||
int ret = 0;
|
||||
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
|
||||
const char *romName = (*env)->GetStringUTFChars(env, romPath, NULL);
|
||||
|
@ -57,25 +58,22 @@ int Java_org_emunix_unipatcher_patch_XDelta_xdelta3apply(JNIEnv *env,
|
|||
(*env)->ReleaseStringUTFChars(env, romPath, romName);
|
||||
(*env)->ReleaseStringUTFChars(env, outputPath, outputName);
|
||||
|
||||
if (!patchFile)
|
||||
{
|
||||
if (!patchFile) {
|
||||
return ERR_UNABLE_OPEN_PATCH;
|
||||
}
|
||||
|
||||
if (!romFile)
|
||||
{
|
||||
if (!romFile) {
|
||||
fclose(patchFile);
|
||||
return ERR_UNABLE_OPEN_ROM;
|
||||
}
|
||||
|
||||
if (!outputFile)
|
||||
{
|
||||
if (!outputFile) {
|
||||
fclose(patchFile);
|
||||
fclose(romFile);
|
||||
return ERR_UNABLE_OPEN_OUTPUT;
|
||||
}
|
||||
|
||||
ret = apply(patchFile, romFile, outputFile);
|
||||
ret = code(0, patchFile, romFile, outputFile, (int)ignoreChecksum);
|
||||
|
||||
fclose(patchFile);
|
||||
fclose(romFile);
|
||||
|
@ -83,68 +81,111 @@ int Java_org_emunix_unipatcher_patch_XDelta_xdelta3apply(JNIEnv *env,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int apply(FILE *patch, FILE *in, FILE *out)
|
||||
{
|
||||
int BUFFER_SIZE = 32768;
|
||||
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3create(JNIEnv *env,
|
||||
jobject this,
|
||||
jstring patchPath,
|
||||
jstring sourcePath,
|
||||
jstring modifiedPath) {
|
||||
int ret = 0;
|
||||
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
|
||||
const char *sourceName = (*env)->GetStringUTFChars(env, sourcePath, NULL);
|
||||
const char *modifiedName = (*env)->GetStringUTFChars(env, modifiedPath, NULL);
|
||||
|
||||
FILE *patchFile = fopen(patchName, "wb");
|
||||
FILE *sourceFile = fopen(sourceName, "rb");
|
||||
FILE *modifiedFile = fopen(modifiedName, "rb");
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, patchPath, patchName);
|
||||
(*env)->ReleaseStringUTFChars(env, sourcePath, sourceName);
|
||||
(*env)->ReleaseStringUTFChars(env, modifiedPath, modifiedName);
|
||||
|
||||
if (!patchFile) {
|
||||
return ERR_UNABLE_OPEN_PATCH;
|
||||
}
|
||||
|
||||
if (!sourceFile) {
|
||||
fclose(patchFile);
|
||||
return ERR_UNABLE_OPEN_SOURCE;
|
||||
}
|
||||
|
||||
if (!modifiedFile) {
|
||||
fclose(patchFile);
|
||||
fclose(sourceFile);
|
||||
return ERR_UNABLE_OPEN_MODIFIED;
|
||||
}
|
||||
|
||||
ret = code(1, modifiedFile, sourceFile, patchFile, 0);
|
||||
|
||||
fclose(patchFile);
|
||||
fclose(sourceFile);
|
||||
fclose(modifiedFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum) {
|
||||
int BUFFER_SIZE = 0x1000;
|
||||
|
||||
int r, ret;
|
||||
xd3_stream stream;
|
||||
xd3_config config;
|
||||
xd3_source source;
|
||||
void* Input_Buf;
|
||||
void *Input_Buf;
|
||||
int Input_Buf_Read;
|
||||
|
||||
memset (&stream, 0, sizeof (stream));
|
||||
memset (&source, 0, sizeof (source));
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
memset(&source, 0, sizeof(source));
|
||||
|
||||
xd3_init_config(&config, 0);
|
||||
config.winsize = BUFFER_SIZE;
|
||||
if (ignoreChecksum) {
|
||||
config.flags |= XD3_ADLER32_NOVER;
|
||||
}
|
||||
xd3_config_stream(&stream, &config);
|
||||
|
||||
source.blksize = BUFFER_SIZE;
|
||||
source.curblk = malloc(source.blksize);
|
||||
|
||||
/* Load 1st block of stream. */
|
||||
r = fseek(in, 0, SEEK_SET);
|
||||
r = fseek(src, 0, SEEK_SET);
|
||||
if (r)
|
||||
return r;
|
||||
source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
|
||||
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
|
||||
source.curblkno = 0;
|
||||
xd3_set_source(&stream, &source);
|
||||
|
||||
Input_Buf = malloc(BUFFER_SIZE);
|
||||
|
||||
fseek(patch, 0, SEEK_SET);
|
||||
do
|
||||
{
|
||||
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, patch);
|
||||
if (Input_Buf_Read < BUFFER_SIZE)
|
||||
{
|
||||
fseek(in, 0, SEEK_SET);
|
||||
do {
|
||||
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, in);
|
||||
if (Input_Buf_Read < BUFFER_SIZE) {
|
||||
xd3_set_flags(&stream, XD3_FLUSH | stream.flags);
|
||||
}
|
||||
xd3_avail_input(&stream, Input_Buf, Input_Buf_Read);
|
||||
|
||||
process:
|
||||
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:
|
||||
continue;
|
||||
|
||||
case XD3_OUTPUT:
|
||||
r = fwrite(stream.next_out, 1, stream.avail_out, out);
|
||||
if (r != (int)stream.avail_out)
|
||||
if (r != (int) stream.avail_out)
|
||||
return r;
|
||||
xd3_consume_output(&stream);
|
||||
goto process;
|
||||
|
||||
case XD3_GETSRCBLK:
|
||||
r = fseek(in, source.blksize * source.getblkno, SEEK_SET);
|
||||
r = fseek(src, source.blksize * source.getblkno, SEEK_SET);
|
||||
if (r)
|
||||
return r;
|
||||
source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
|
||||
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
|
||||
source.curblkno = source.getblkno;
|
||||
goto process;
|
||||
|
||||
|
@ -154,7 +195,6 @@ int apply(FILE *patch, FILE *in, FILE *out)
|
|||
goto process;
|
||||
|
||||
default:
|
||||
__android_log_print(ANDROID_LOG_ERROR, "XDelta3", "Error %d: %s", ret, stream.msg);
|
||||
if (stream.msg != NULL) {
|
||||
if (strcmp(stream.msg, "target window checksum mismatch") == 0)
|
||||
return ERR_WRONG_CHECKSUM;
|
||||
|
@ -166,7 +206,7 @@ int apply(FILE *patch, FILE *in, FILE *out)
|
|||
|
||||
free(Input_Buf);
|
||||
|
||||
free((void*)source.curblk);
|
||||
free((void *) source.curblk);
|
||||
xd3_close_stream(&stream);
|
||||
xd3_free_stream(&stream);
|
||||
|
||||
|
|
35
app/src/main/java/org/emunix/unipatcher/Action.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright (c) 2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
UniPatcher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
UniPatcher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher;
|
||||
|
||||
public class Action {
|
||||
public static final int SELECT_ROM_FILE = 1;
|
||||
public static final int SELECT_PATCH_FILE = 2;
|
||||
public static final int SELECT_SOURCE_FILE = 3;
|
||||
public static final int SELECT_MODIFIED_FILE = 4;
|
||||
public static final int SELECT_HEADER_FILE = 5;
|
||||
|
||||
public static final int APPLY_PATCH = 101;
|
||||
public static final int CREATE_PATCH = 102;
|
||||
public static final int SMD_FIX_CHECKSUM = 103;
|
||||
public static final int SNES_ADD_SMC_HEADER = 104;
|
||||
public static final int SNES_DELETE_SMC_HEADER = 105;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
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;
|
||||
}
|
|
@ -69,4 +69,33 @@ public class Settings {
|
|||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getString("output_directory", "");
|
||||
}
|
||||
|
||||
public static boolean getIgnoreChecksum(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getBoolean("ignore_checksum", false);
|
||||
}
|
||||
|
||||
public static void setPatchingSuccessful(Context context, Boolean isSuccessful) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean("patching_successful", isSuccessful);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean getPatchingSuccessful(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getBoolean("patching_successful", false);
|
||||
}
|
||||
|
||||
public static void setDontShowDonateSnackbarCount(Context context, int count) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt("dont_show_donate_snackbar", count);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static int getDontShowDonateSnackbarCount(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getInt("dont_show_donate_snackbar", 0);
|
||||
}
|
||||
}
|
||||
|
|
57
app/src/main/java/org/emunix/unipatcher/UniPatcher.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright (c) 2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
UniPatcher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
UniPatcher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
public class UniPatcher extends Application {
|
||||
private static String appArgument = null;
|
||||
public static final String NOTIFICATION_CHANNEL_ID = "notifications";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
initNotificationChannel();
|
||||
}
|
||||
|
||||
public static String getAppArgument() {
|
||||
return appArgument;
|
||||
}
|
||||
|
||||
public static void setAppArgument(String appArgument) {
|
||||
UniPatcher.appArgument = appArgument;
|
||||
}
|
||||
|
||||
public void initNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
|
||||
getString(R.string.notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2013-2016 Boris Timofeev
|
||||
Copyright (C) 2013-2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
|
@ -22,10 +22,9 @@ package org.emunix.unipatcher;
|
|||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.StatFs;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
@ -42,7 +41,6 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -51,6 +49,14 @@ public class Utils {
|
|||
|
||||
private static final int BUFFER_SIZE = 10240; // 10 Kb
|
||||
|
||||
public static void startForegroundService(Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
context.startService(intent);
|
||||
} else {
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAppVersion(Context context) {
|
||||
String versionName = "N/A";
|
||||
try {
|
||||
|
@ -67,12 +73,6 @@ public class Utils {
|
|||
== 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) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
|
@ -82,17 +82,20 @@ public class Utils {
|
|||
public static long getFreeSpace(File file) {
|
||||
StatFs stat = new StatFs(file.getPath());
|
||||
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();
|
||||
else
|
||||
//noinspection deprecation
|
||||
} else
|
||||
//noinspection deprecation
|
||||
{
|
||||
bytesAvailable = (long) stat.getBlockSize() * (long) stat.getAvailableBlocks();
|
||||
}
|
||||
return bytesAvailable;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.copyFile(from, to);
|
||||
|
@ -103,7 +106,7 @@ public class Utils {
|
|||
|
||||
public static void moveFile(Context context, File from, File to) throws IOException {
|
||||
FileUtils.deleteQuietly(to);
|
||||
if(!from.renameTo(to)) {
|
||||
if (!from.renameTo(to)) {
|
||||
copyFile(context, from, to);
|
||||
FileUtils.deleteQuietly(from);
|
||||
}
|
||||
|
@ -150,24 +153,28 @@ public class Utils {
|
|||
|
||||
public static String bytesToHexString(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = 0; i < bytes.length ;i++) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isPatch(File file) {
|
||||
String[] patches = {"ips", "ups", "bps", "ppf", "dps", "xdelta", "xdelta3", "vcdiff"};
|
||||
String[] patches =
|
||||
{"ips", "ups", "bps", "aps", "ppf", "dps", "ebp", "xdelta", "xdelta3", "xd", "vcdiff"};
|
||||
String ext = FilenameUtils.getExtension(file.getName()).toLowerCase(Locale.getDefault());
|
||||
for (String patch : patches) {
|
||||
if (ext.equals(patch))
|
||||
return true;
|
||||
if (ext.equals(patch)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isArchive(String path) {
|
||||
String ext = FilenameUtils.getExtension(path).toLowerCase(Locale.getDefault());
|
||||
return ext.equals("zip") || ext.equals("rar") || ext.equals("7z") || ext.equals("gz") || ext.equals("tgz");
|
||||
return ext.equals("zip")
|
||||
|| ext.equals("rar")
|
||||
|| ext.equals("7z")
|
||||
|| ext.equals("gz")
|
||||
|| ext.equals("tgz");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,25 +24,27 @@ import android.app.Notification;
|
|||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.emunix.unipatcher.patch.APS;
|
||||
import org.emunix.unipatcher.patch.BPS;
|
||||
import org.emunix.unipatcher.patch.DPS;
|
||||
import org.emunix.unipatcher.patch.EBP;
|
||||
import org.emunix.unipatcher.patch.IPS;
|
||||
import org.emunix.unipatcher.patch.PPF;
|
||||
import org.emunix.unipatcher.patch.Patch;
|
||||
import org.emunix.unipatcher.patch.PatchException;
|
||||
import org.emunix.unipatcher.patch.UPS;
|
||||
import org.emunix.unipatcher.patch.XDelta;
|
||||
import org.emunix.unipatcher.patcher.APS;
|
||||
import org.emunix.unipatcher.patcher.BPS;
|
||||
import org.emunix.unipatcher.patcher.DPS;
|
||||
import org.emunix.unipatcher.patcher.EBP;
|
||||
import org.emunix.unipatcher.patcher.IPS;
|
||||
import org.emunix.unipatcher.patcher.PPF;
|
||||
import org.emunix.unipatcher.patcher.Patcher;
|
||||
import org.emunix.unipatcher.patcher.PatchException;
|
||||
import org.emunix.unipatcher.patcher.UPS;
|
||||
import org.emunix.unipatcher.patcher.XDelta;
|
||||
import org.emunix.unipatcher.tools.RomException;
|
||||
import org.emunix.unipatcher.tools.SmdFixChecksum;
|
||||
import org.emunix.unipatcher.tools.SnesSmcHeader;
|
||||
import org.emunix.unipatcher.ui.activity.MainActivity;
|
||||
import org.emunix.unipatcher.ui.notify.CreatePatchNotify;
|
||||
import org.emunix.unipatcher.ui.notify.Notify;
|
||||
import org.emunix.unipatcher.ui.notify.PatchingNotify;
|
||||
import org.emunix.unipatcher.ui.notify.SmdFixChecksumNotify;
|
||||
|
@ -74,16 +76,19 @@ public class WorkerService extends IntentService {
|
|||
try {
|
||||
int action = intent.getIntExtra("action", 0);
|
||||
switch (action) {
|
||||
case Globals.ACTION_PATCHING:
|
||||
case Action.APPLY_PATCH:
|
||||
actionPatching(intent);
|
||||
break;
|
||||
case Globals.ACTION_SMD_FIX_CHECKSUM:
|
||||
case Action.CREATE_PATCH:
|
||||
actionCreatePatch(intent);
|
||||
break;
|
||||
case Action.SMD_FIX_CHECKSUM:
|
||||
actionSmdFixChecksum(intent);
|
||||
break;
|
||||
case Globals.ACTION_SNES_ADD_SMC_HEADER:
|
||||
case Action.SNES_ADD_SMC_HEADER:
|
||||
actionSnesAddSmcHeader(intent);
|
||||
break;
|
||||
case Globals.ACTION_SNES_DELETE_SMC_HEADER:
|
||||
case Action.SNES_DELETE_SMC_HEADER:
|
||||
actionSnesDeleteSmcHeader(intent);
|
||||
break;
|
||||
}
|
||||
|
@ -97,9 +102,9 @@ public class WorkerService extends IntentService {
|
|||
File romFile = new File(intent.getStringExtra("romPath"));
|
||||
File patchFile = new File(intent.getStringExtra("patchPath"));
|
||||
File outputFile = new File(intent.getStringExtra("outputPath"));
|
||||
Patch patcher = null;
|
||||
Patcher patcher = null;
|
||||
|
||||
if(!fileExists(patchFile) || !fileExists(romFile))
|
||||
if (!fileExists(patchFile) || !fileExists(romFile))
|
||||
return;
|
||||
|
||||
// create output dir
|
||||
|
@ -115,7 +120,7 @@ public class WorkerService extends IntentService {
|
|||
|
||||
// check access to output dir
|
||||
try {
|
||||
if (!outputFile.getParentFile().canWrite()){
|
||||
if (!outputFile.getParentFile().canWrite()) {
|
||||
String text = getString(R.string.notify_error_unable_to_write_to_directory, outputFile.getParent());
|
||||
showErrorNotification(text);
|
||||
return;
|
||||
|
@ -141,31 +146,88 @@ public class WorkerService extends IntentService {
|
|||
patcher = new EBP(this, patchFile, romFile, outputFile);
|
||||
else if ("dps".equals(ext))
|
||||
patcher = new DPS(this, patchFile, romFile, outputFile);
|
||||
else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "vcdiff".equals(ext))
|
||||
else if ("xdelta".equals(ext) || "xdelta3".equals(ext) || "xd".equals(ext) || "vcdiff".equals(ext))
|
||||
patcher = new XDelta(this, patchFile, romFile, outputFile);
|
||||
else
|
||||
errorMsg = getString(R.string.notify_error_unknown_patch_format);
|
||||
|
||||
Notify notify = new PatchingNotify(this, outputFile.getName());
|
||||
|
||||
if (errorMsg != null) {
|
||||
notify.showResult(errorMsg);
|
||||
showErrorNotification(errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
Notify notify = new PatchingNotify(this, outputFile.getName());
|
||||
startForeground(notify.getID(), notify.getNotifyBuilder().build());
|
||||
|
||||
try {
|
||||
if ("ppf".equals(ext))
|
||||
Utils.copyFile(this, romFile, outputFile);
|
||||
patcher.apply();
|
||||
patcher.apply(Settings.getIgnoreChecksum(this));
|
||||
Settings.setPatchingSuccessful(this, true);
|
||||
} catch (PatchException | IOException e) {
|
||||
if (Utils.getFreeSpace(outputFile.getParentFile()) == 0) {
|
||||
errorMsg = getString(R.string.notify_error_not_enough_space);
|
||||
} else {
|
||||
errorMsg = e.getMessage();
|
||||
}
|
||||
FileUtils.deleteQuietly(outputFile);
|
||||
if (outputFile.isFile()) {
|
||||
FileUtils.deleteQuietly(outputFile);
|
||||
}
|
||||
} finally {
|
||||
stopForeground(true);
|
||||
}
|
||||
notify.showResult(errorMsg);
|
||||
}
|
||||
|
||||
private void actionCreatePatch(Intent intent) {
|
||||
String errorMsg = null;
|
||||
File sourceFile = new File(intent.getStringExtra("sourcePath"));
|
||||
File modifiedFile = new File(intent.getStringExtra("modifiedPath"));
|
||||
File patchFile = new File(intent.getStringExtra("patchPath"));
|
||||
|
||||
if (!fileExists(sourceFile) || !fileExists(modifiedFile))
|
||||
return;
|
||||
|
||||
// create output dir
|
||||
try {
|
||||
if (!patchFile.getParentFile().exists()) {
|
||||
FileUtils.forceMkdirParent(patchFile);
|
||||
}
|
||||
} catch (IOException | SecurityException e) {
|
||||
String text = getString(R.string.notify_error_unable_to_create_directory, patchFile.getParent());
|
||||
showErrorNotification(text);
|
||||
return;
|
||||
}
|
||||
|
||||
// check access to output dir
|
||||
try {
|
||||
if (!patchFile.getParentFile().canWrite()) {
|
||||
String text = getString(R.string.notify_error_unable_to_write_to_directory, patchFile.getParent());
|
||||
showErrorNotification(text);
|
||||
return;
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
String text = getString(R.string.notify_error_unable_to_write_to_directory, patchFile.getParent());
|
||||
showErrorNotification(text);
|
||||
return;
|
||||
}
|
||||
|
||||
XDelta patcher = new XDelta(this, patchFile, sourceFile, modifiedFile);
|
||||
|
||||
Notify notify = new CreatePatchNotify(this, patchFile.getName());
|
||||
|
||||
startForeground(notify.getID(), notify.getNotifyBuilder().build());
|
||||
|
||||
try {
|
||||
patcher.create();
|
||||
Settings.setPatchingSuccessful(this, true);
|
||||
} catch (PatchException | IOException e) {
|
||||
if (Utils.getFreeSpace(patchFile.getParentFile()) == 0) {
|
||||
errorMsg = getString(R.string.notify_error_not_enough_space);
|
||||
} else {
|
||||
errorMsg = e.getMessage();
|
||||
}
|
||||
FileUtils.deleteQuietly(patchFile);
|
||||
} finally {
|
||||
stopForeground(true);
|
||||
}
|
||||
|
@ -176,7 +238,7 @@ public class WorkerService extends IntentService {
|
|||
String errorMsg = null;
|
||||
File romFile = new File(intent.getStringExtra("romPath"));
|
||||
|
||||
if(!fileExists(romFile))
|
||||
if (!fileExists(romFile))
|
||||
return;
|
||||
|
||||
SmdFixChecksum fixer = new SmdFixChecksum(this, romFile);
|
||||
|
@ -200,7 +262,7 @@ public class WorkerService extends IntentService {
|
|||
File romFile = new File(intent.getStringExtra("romPath"));
|
||||
String headerPath = intent.getStringExtra("headerPath");
|
||||
|
||||
if(!fileExists(romFile))
|
||||
if (!fileExists(romFile))
|
||||
return;
|
||||
|
||||
SnesSmcHeader worker = new SnesSmcHeader();
|
||||
|
@ -230,7 +292,7 @@ public class WorkerService extends IntentService {
|
|||
|
||||
File romFile = new File(intent.getStringExtra("romPath"));
|
||||
|
||||
if(!fileExists(romFile))
|
||||
if (!fileExists(romFile))
|
||||
return;
|
||||
|
||||
SnesSmcHeader worker = new SnesSmcHeader();
|
||||
|
@ -264,15 +326,21 @@ public class WorkerService extends IntentService {
|
|||
private void showErrorNotification(String text) {
|
||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
Notification notify = new NotificationCompat.Builder(this)
|
||||
Notification notify = new NotificationCompat.Builder(this, UniPatcher.NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.notify_error))
|
||||
.setContentText(text)
|
||||
.setSmallIcon(R.drawable.ic_stat_patching)
|
||||
.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp)
|
||||
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
|
||||
.setAutoCancel(true)
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(text))
|
||||
.bigText(text))
|
||||
.build();
|
||||
nm.notify(0, notify);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
nm.notify(32768, notify);
|
||||
} else {
|
||||
startForeground(32768, notify);
|
||||
stopForeground(STOP_FOREGROUND_DETACH);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
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();
|
||||
}
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -29,7 +29,7 @@ import java.io.FileInputStream;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class APS extends Patch {
|
||||
public class APS extends Patcher {
|
||||
|
||||
public static final int NOT_APS_PATCH = 0;
|
||||
public static final int APS_N64_PATCH = 1;
|
||||
|
@ -43,18 +43,20 @@ public class APS extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
Patch aps = null;
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
Patcher aps = null;
|
||||
switch (checkAPS(patchFile)) {
|
||||
case APS_N64_PATCH:
|
||||
aps = new APS_N64(context, patchFile, romFile, outputFile); break;
|
||||
aps = new APS_N64(context, patchFile, romFile, outputFile);
|
||||
break;
|
||||
case APS_GBA_PATCH:
|
||||
aps = new APS_GBA(context, patchFile, romFile, outputFile); break;
|
||||
aps = new APS_GBA(context, patchFile, romFile, outputFile);
|
||||
break;
|
||||
case NOT_APS_PATCH:
|
||||
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
|
||||
}
|
||||
|
||||
aps.apply();
|
||||
aps.apply(ignoreChecksum);
|
||||
}
|
||||
|
||||
public int checkAPS(File file) throws PatchException, IOException {
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -33,7 +33,7 @@ import java.io.InputStream;
|
|||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class APS_GBA extends Patch {
|
||||
public class APS_GBA extends Patcher {
|
||||
private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31}; // APS1
|
||||
private static final int CHUNK_SIZE = 65536;
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class APS_GBA extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
long fileSize1, fileSize2, bytesLeft, offset;
|
||||
int crc, patchCrc1, patchCrc2, pCount, oCount;
|
||||
boolean isOriginal = false;
|
||||
|
@ -130,7 +130,7 @@ public class APS_GBA extends Patch {
|
|||
|
||||
crc = crc16(romBuf);
|
||||
|
||||
for(int i = 0; i < CHUNK_SIZE; i++)
|
||||
for (int i = 0; i < CHUNK_SIZE; i++)
|
||||
romBuf[i] ^= patchBuf[i];
|
||||
|
||||
if (crc == patchCrc1) {
|
||||
|
@ -138,7 +138,8 @@ public class APS_GBA extends Patch {
|
|||
} else if (crc == patchCrc2) {
|
||||
isModified = true;
|
||||
} else {
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum)
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
if (isOriginal && isModified)
|
||||
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
|
||||
|
@ -151,9 +152,9 @@ public class APS_GBA extends Patch {
|
|||
IOUtils.closeQuietly(output);
|
||||
}
|
||||
|
||||
if(isOriginal) {
|
||||
if (isOriginal) {
|
||||
Utils.truncateFile(outputFile, fileSize2);
|
||||
} else {
|
||||
} else if (isModified) {
|
||||
Utils.truncateFile(outputFile, fileSize1);
|
||||
}
|
||||
}
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -35,7 +35,7 @@ import java.io.InputStream;
|
|||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class APS_N64 extends Patch {
|
||||
public class APS_N64 extends Patcher {
|
||||
|
||||
private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31, 0x30}; // APS10
|
||||
private static final int TYPE_SIMPLE_PATCH = 0;
|
||||
|
@ -47,7 +47,7 @@ public class APS_N64 extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
BufferedInputStream romStream = null;
|
||||
BufferedInputStream patchStream = null;
|
||||
BufferedOutputStream outputStream = null;
|
||||
|
@ -96,8 +96,10 @@ public class APS_N64 extends Patch {
|
|||
int country = patchStream.read();
|
||||
byte[] crc = new byte[8];
|
||||
patchStream.read(crc);
|
||||
if (!validateROM(endianness, cardID, country, crc))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
if (!validateROM(endianness, cardID, country, crc))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
// skip bytes for future expansion
|
||||
byte[] skip = new byte[5];
|
||||
patchStream.read(skip);
|
||||
|
@ -144,7 +146,7 @@ public class APS_N64 extends Patch {
|
|||
size = patchStream.read();
|
||||
patchPos++;
|
||||
if (size != 0) {
|
||||
byte[] data = new byte[(int)size];
|
||||
byte[] data = new byte[(int) size];
|
||||
patchStream.read(data);
|
||||
patchPos += size;
|
||||
outputStream.write(data);
|
||||
|
@ -153,7 +155,7 @@ public class APS_N64 extends Patch {
|
|||
byte val = (byte) patchStream.read();
|
||||
size = patchStream.read();
|
||||
patchPos += 2;
|
||||
byte[] data = new byte[(int)size];
|
||||
byte[] data = new byte[(int) size];
|
||||
Arrays.fill(data, val);
|
||||
outputStream.write(data);
|
||||
outPos += size;
|
||||
|
@ -164,7 +166,7 @@ public class APS_N64 extends Patch {
|
|||
if (romPos + size > romSize) {
|
||||
romPos = (int) romSize;
|
||||
} else {
|
||||
byte[] buf = new byte[(int)size];
|
||||
byte[] buf = new byte[(int) size];
|
||||
romStream.read(buf);
|
||||
romPos += size;
|
||||
}
|
||||
|
@ -226,7 +228,7 @@ public class APS_N64 extends Patch {
|
|||
}
|
||||
|
||||
private long readLELong(InputStream stream) throws IOException {
|
||||
return (stream.read() & 0xff) + ((stream.read() & 0xff) << 8)
|
||||
return (stream.read() & 0xff) + ((stream.read() & 0xff) << 8)
|
||||
+ ((stream.read() & 0xff) << 16) + ((stream.read() & 0xff) << 24);
|
||||
}
|
||||
}
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -35,7 +35,7 @@ import java.nio.channels.FileChannel;
|
|||
import java.util.Arrays;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public class BPS extends Patch {
|
||||
public class BPS extends Patcher {
|
||||
|
||||
private static final byte[] MAGIC_NUMBER = {0x42, 0x50, 0x53, 0x31}; // "BPS1"
|
||||
private static final byte SOURCE_READ = 0b00;
|
||||
|
@ -50,7 +50,7 @@ public class BPS extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
|
||||
if (patchFile.length() < 19) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
|
@ -68,9 +68,11 @@ public class BPS extends Patch {
|
|||
if (bpsCrc.getPatchFileCRC() != bpsCrc.getRealPatchCRC())
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
|
||||
long realRomCrc = FileUtils.checksumCRC32(romFile);
|
||||
if (realRomCrc != bpsCrc.getInputFileCRC()) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
long realRomCrc = FileUtils.checksumCRC32(romFile);
|
||||
if (realRomCrc != bpsCrc.getInputFileCRC()) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
}
|
||||
|
||||
patch = new RandomAccessFile(patchFile, "r").getChannel();
|
||||
|
@ -133,9 +135,11 @@ public class BPS extends Patch {
|
|||
IOUtils.closeQuietly(output);
|
||||
}
|
||||
|
||||
long realOutCrc = FileUtils.checksumCRC32(outputFile);
|
||||
if (realOutCrc != bpsCrc.getOutputFileCRC())
|
||||
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
|
||||
if(!ignoreChecksum) {
|
||||
long realOutCrc = FileUtils.checksumCRC32(outputFile);
|
||||
if (realOutCrc != bpsCrc.getOutputFileCRC())
|
||||
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
|
||||
}
|
||||
}
|
||||
|
||||
// decode pointer
|
||||
|
@ -250,7 +254,7 @@ public class BPS extends Patch {
|
|||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
return new BpsCrc(inputCrc, outputCrc, patchCrc, realPatchCrc);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(stream);
|
||||
IOUtils.closeQuietly(stream);
|
||||
}
|
||||
}
|
||||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -30,7 +30,7 @@ import java.io.FileInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class DPS extends Patch {
|
||||
public class DPS extends Patcher {
|
||||
|
||||
private static final int MIN_SIZE_PATCH = 136;
|
||||
private static final int BUFFER_SIZE = 32768;
|
||||
|
@ -42,13 +42,13 @@ public class DPS extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
|
||||
if (patchFile.length() < MIN_SIZE_PATCH) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
}
|
||||
if (patchFile.length() < MIN_SIZE_PATCH) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
}
|
||||
|
||||
BufferedInputStream patchStream = null;
|
||||
BufferedInputStream patchStream = null;
|
||||
RandomAccessFile romStream = null;
|
||||
RandomAccessFile outputStream = null;
|
||||
|
||||
|
@ -63,9 +63,11 @@ public class DPS extends Patch {
|
|||
throw new PatchException(context.getString(R.string.notify_error_not_dps_patch));
|
||||
|
||||
// verify rom
|
||||
long romSize = getUInt(buffer, 194);
|
||||
if (romSize != romFile.length())
|
||||
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
long romSize = getUInt(buffer, 194);
|
||||
if (romSize != romFile.length())
|
||||
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
|
||||
romStream = new RandomAccessFile(romFile, "r");
|
||||
outputStream = new RandomAccessFile(outputFile, "rw");
|
||||
|
@ -121,7 +123,7 @@ public class DPS extends Patch {
|
|||
}
|
||||
|
||||
private long getUInt(byte[] a, int offset) {
|
||||
return ((long)(a[offset] & 0xff)) + ((long)(a[offset + 1] & 0xff) << 8) +
|
||||
((long)(a[offset + 2] & 0xff) << 16) + ((long)(a[offset + 3] & 0xff) << 24);
|
||||
return ((long) (a[offset] & 0xff)) + ((long) (a[offset + 1] & 0xff) << 8) +
|
||||
((long) (a[offset + 2] & 0xff) << 16) + ((long) (a[offset + 3] & 0xff) << 24);
|
||||
}
|
||||
}
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
@ -45,7 +45,7 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class EBP extends Patch {
|
||||
public class EBP extends Patcher {
|
||||
|
||||
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};
|
||||
|
@ -68,12 +68,12 @@ public class EBP extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
File cleanRom = File.createTempFile("rom", null, context.getCacheDir());
|
||||
File ipsPatch = File.createTempFile("patch", null, context.getCacheDir());
|
||||
try {
|
||||
Utils.copyFile(context, romFile, cleanRom);
|
||||
prepareCleanRom(cleanRom);
|
||||
prepareCleanRom(cleanRom, ignoreChecksum);
|
||||
|
||||
EBPtoIPS(patchFile, ipsPatch);
|
||||
|
||||
|
@ -85,7 +85,7 @@ public class EBP extends Patch {
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareCleanRom(File file) throws IOException, PatchException {
|
||||
private void prepareCleanRom(File file, boolean ignoreChecksum) throws IOException, PatchException {
|
||||
// delete smc header
|
||||
SnesSmcHeader smc = new SnesSmcHeader();
|
||||
try {
|
||||
|
@ -95,8 +95,10 @@ public class EBP extends Patch {
|
|||
}
|
||||
|
||||
// check rom size and remove unused expanded space
|
||||
if (file.length() < EB_CLEAN_ROM_SIZE)
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
if (file.length() < EB_CLEAN_ROM_SIZE)
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
if (file.length() > EB_CLEAN_ROM_SIZE && checkExpanded(file))
|
||||
removeExpanded(file);
|
||||
|
||||
|
@ -106,7 +108,7 @@ public class EBP extends Patch {
|
|||
|
||||
// if we couldn't fix the ROM, try to remove a 0xff byte at the end.
|
||||
if (!checkMD5(file)) {
|
||||
int length = (int)file.length();
|
||||
int length = (int) file.length();
|
||||
byte[] buffer = new byte[length];
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
int count = in.read(buffer);
|
||||
|
@ -254,7 +256,7 @@ public class EBP extends Patch {
|
|||
if (size < 2)
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
ips.write(buffer, 0, 2);
|
||||
size = (((int)buffer[0] & 0xff) << 8) + ((int)buffer[1] & 0xff);
|
||||
size = (((int) buffer[0] & 0xff) << 8) + ((int) buffer[1] & 0xff);
|
||||
if (size != 0) {
|
||||
int c = ebp.read(buffer, 0, size);
|
||||
if (c < size)
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2013, 2016 Boris Timofeev
|
||||
Copyright (C) 2013, 2016, 2017 Boris Timofeev
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -34,17 +34,27 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IPS extends Patch {
|
||||
public class IPS extends Patcher {
|
||||
|
||||
private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
|
||||
public static final int NOT_IPS_PATCH = 0;
|
||||
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) {
|
||||
super(context, patch, rom, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
apply();
|
||||
}
|
||||
|
||||
public void apply() throws PatchException, IOException {
|
||||
BufferedInputStream romStream = null;
|
||||
BufferedInputStream patchStream = null;
|
||||
BufferedOutputStream outputStream = null;
|
||||
|
@ -55,33 +65,37 @@ public class IPS extends Patch {
|
|||
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
|
||||
|
||||
long romSize = romFile.length();
|
||||
int romPos = 0;
|
||||
int outPos = 0;
|
||||
int offset;
|
||||
int size;
|
||||
long romPos = 0;
|
||||
long outPos = 0;
|
||||
long offset;
|
||||
long size;
|
||||
|
||||
if (patchFile.length() < 14) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
}
|
||||
|
||||
// check magic string
|
||||
byte[] magic = new byte[5];
|
||||
size = patchStream.read(magic);
|
||||
if (size != 5 || !Arrays.equals(magic, MAGIC_NUMBER))
|
||||
if (Arrays.equals(magic, MAGIC_NUMBER_IPS)) {
|
||||
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));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
offset = readOffset(patchStream, 3);
|
||||
offset = readOffset(patchStream);
|
||||
if (offset < 0)
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
if (offset == 0x454f46) { // EOF
|
||||
if (checkEOF(offset)) {
|
||||
// truncate file or copy tail
|
||||
if (romPos < romSize) {
|
||||
offset = readOffset(patchStream, 3);
|
||||
offset = readOffset(patchStream);
|
||||
if (offset != -1 && offset >= romPos) {
|
||||
size = offset - romPos;
|
||||
} else {
|
||||
size = (int) romSize - romPos;
|
||||
size = romSize - romPos;
|
||||
}
|
||||
Utils.copy(romStream, outputStream, size);
|
||||
}
|
||||
|
@ -97,7 +111,7 @@ public class IPS extends Patch {
|
|||
}
|
||||
} else {
|
||||
if (outPos < romSize) {
|
||||
size = (int) romSize - outPos;
|
||||
size = romSize - outPos;
|
||||
Utils.copy(romStream, outputStream, size);
|
||||
romPos += size;
|
||||
outPos += size;
|
||||
|
@ -111,14 +125,14 @@ public class IPS extends Patch {
|
|||
|
||||
size = (patchStream.read() << 8) + patchStream.read();
|
||||
if (size != 0) {
|
||||
byte[] data = new byte[size];
|
||||
byte[] data = new byte[(int)size];
|
||||
patchStream.read(data);
|
||||
outputStream.write(data);
|
||||
outPos += size;
|
||||
} else { // RLE
|
||||
size = (patchStream.read() << 8) + patchStream.read();
|
||||
byte val = (byte) patchStream.read();
|
||||
byte[] data = new byte[size];
|
||||
byte[] data = new byte[(int)size];
|
||||
Arrays.fill(data, val);
|
||||
outputStream.write(data);
|
||||
outPos += size;
|
||||
|
@ -126,10 +140,9 @@ public class IPS extends Patch {
|
|||
|
||||
if (offset <= romSize) {
|
||||
if (romPos + size > romSize) {
|
||||
romPos = (int) romSize;
|
||||
romPos = romSize;
|
||||
} else {
|
||||
// Не используй romStream.skip(size), оно по какой-то причине не всегда пропускает байты.
|
||||
byte[] buf = new byte[size];
|
||||
byte[] buf = new byte[(int)size];
|
||||
romStream.read(buf);
|
||||
romPos += size;
|
||||
}
|
||||
|
@ -142,8 +155,30 @@ public class IPS extends Patch {
|
|||
}
|
||||
}
|
||||
|
||||
private int readOffset(InputStream stream, int numBytes) throws IOException {
|
||||
int offset = 0;
|
||||
private boolean checkEOF(long value) {
|
||||
switch (mPatchType) {
|
||||
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) {
|
||||
int b = stream.read();
|
||||
if (b == -1)
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -30,9 +30,8 @@ import java.io.IOException;
|
|||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PPF extends Patch {
|
||||
public class PPF extends Patcher {
|
||||
|
||||
// private static final String LOG_TAG = "PPF";
|
||||
private static final byte[] MAGIC_NUMBER = {0x50, 0x50, 0x46}; // "PPF" without version
|
||||
|
||||
private RandomAccessFile patchStream;
|
||||
|
@ -69,16 +68,23 @@ public class PPF extends Patch {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
if (patchFile.length() < 61) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
}
|
||||
|
||||
switch (getPPFVersion(patchFile)) {
|
||||
case 1: applyPPF1(); break;
|
||||
case 2: applyPPF2(); break;
|
||||
case 3: applyPPF3(); break;
|
||||
default: throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch));
|
||||
case 1:
|
||||
applyPPF1();
|
||||
break;
|
||||
case 2:
|
||||
applyPPF2(ignoreChecksum);
|
||||
break;
|
||||
case 3:
|
||||
applyPPF3(ignoreChecksum);
|
||||
break;
|
||||
default:
|
||||
throw new PatchException(context.getString(R.string.notify_error_not_ppf_patch));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,15 +112,17 @@ public class PPF extends Patch {
|
|||
}
|
||||
}
|
||||
|
||||
private void applyPPF2() throws IOException, PatchException {
|
||||
private void applyPPF2(boolean ignoreChecksum) throws IOException, PatchException {
|
||||
try {
|
||||
patchStream = new RandomAccessFile(patchFile, "r");
|
||||
|
||||
// Check size of ROM
|
||||
patchStream.seek(56);
|
||||
long romSize = readLittleEndianInt(patchStream);
|
||||
if (romSize != romFile.length()) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
if (romSize != romFile.length()) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
}
|
||||
|
||||
outputStream = new RandomAccessFile(outputFile, "rw");
|
||||
|
@ -125,8 +133,10 @@ public class PPF extends Patch {
|
|||
outputStream.seek(0x9320);
|
||||
patchStream.read(patchBinaryBlock, 0, 1024);
|
||||
outputStream.read(romBinaryBlock, 0, 1024);
|
||||
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
|
||||
// Calculate end of patch data
|
||||
long dataEnd = patchFile.length();
|
||||
|
@ -154,7 +164,7 @@ public class PPF extends Patch {
|
|||
}
|
||||
}
|
||||
|
||||
private void applyPPF3() throws IOException, PatchException {
|
||||
private void applyPPF3(boolean ignoreChecksum) throws IOException, PatchException {
|
||||
try {
|
||||
patchStream = new RandomAccessFile(patchFile, "r");
|
||||
outputStream = new RandomAccessFile(outputFile, "rw");
|
||||
|
@ -176,8 +186,10 @@ public class PPF extends Patch {
|
|||
}
|
||||
patchStream.read(patchBinaryBlock, 0, 1024);
|
||||
outputStream.read(romBinaryBlock, 0, 1024);
|
||||
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
if (!Arrays.equals(patchBinaryBlock, romBinaryBlock))
|
||||
throw new PatchException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate end of patch data
|
||||
|
@ -217,24 +229,23 @@ public class PPF extends Patch {
|
|||
private long readLittleEndianLong(RandomAccessFile stream) throws IOException {
|
||||
byte[] b = new byte[8];
|
||||
stream.read(b);
|
||||
long result = ((long)(b[7] & 0xff) << 56) + ((long)(b[6] & 0xff) << 48 ) +
|
||||
((long)(b[5] & 0xff) << 40) + ((long)(b[4] & 0xff) << 32 ) +
|
||||
((long)(b[3] & 0xff) << 24) + ((long)(b[2] & 0xff) << 16 ) +
|
||||
((long)(b[1] & 0xff) << 8) + ((long)b[0] & 0xff);
|
||||
return result;
|
||||
return ((long) (b[7] & 0xff) << 56) + ((long) (b[6] & 0xff) << 48) +
|
||||
((long) (b[5] & 0xff) << 40) + ((long) (b[4] & 0xff) << 32) +
|
||||
((long) (b[3] & 0xff) << 24) + ((long) (b[2] & 0xff) << 16) +
|
||||
((long) (b[1] & 0xff) << 8) + ((long) b[0] & 0xff);
|
||||
}
|
||||
|
||||
private int readLittleEndianInt(RandomAccessFile stream) throws IOException {
|
||||
byte[] b = new byte[4];
|
||||
stream.read(b);
|
||||
return ((b[3] & 0xff) << 24) + ((b[2] & 0xff) <<16 ) +
|
||||
((b[1] & 0xff) << 8) + (b[0]&0xff);
|
||||
return ((b[3] & 0xff) << 24) + ((b[2] & 0xff) << 16) +
|
||||
((b[1] & 0xff) << 8) + (b[0] & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns size of FileID
|
||||
*
|
||||
* @param stream stream of PPF file
|
||||
* @param stream stream of PPF file
|
||||
* @param ppfVersion version of PPF patch
|
||||
* @return size of FileID or 0
|
||||
*/
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
public class PatchException extends Exception {
|
||||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class Patch {
|
||||
public abstract class Patcher {
|
||||
protected File patchFile = null;
|
||||
protected File romFile = null;
|
||||
protected File outputFile = null;
|
||||
protected Context context = null;
|
||||
|
||||
public Patch(Context c, File patch, File rom, File output) {
|
||||
public Patcher(Context c, File patch, File rom, File output) {
|
||||
context = c;
|
||||
patchFile = patch;
|
||||
romFile = rom;
|
||||
outputFile = output;
|
||||
}
|
||||
|
||||
public abstract void apply() throws PatchException, IOException;
|
||||
public abstract void apply(boolean ignoreChecksum) throws PatchException, IOException;
|
||||
}
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -35,15 +35,16 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public class UPS extends Patch {
|
||||
public class UPS extends Patcher {
|
||||
|
||||
private static final byte[] MAGIC_NUMBER = {0x55, 0x50, 0x53, 0x31}; // "UPS1"
|
||||
|
||||
public UPS(Context context, File patch, File rom, File output) {
|
||||
super(context, patch, rom, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
|
||||
if (patchFile.length() < 18) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
|
@ -89,7 +90,9 @@ public class UPS extends Patch {
|
|||
ySize = tmp;
|
||||
upsCrc.swapInOut();
|
||||
} else {
|
||||
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
if (!ignoreChecksum) {
|
||||
throw new IOException(context.getString(R.string.notify_error_rom_not_compatible_with_patch));
|
||||
}
|
||||
}
|
||||
|
||||
romStream = new BufferedInputStream(new FileInputStream(romFile));
|
||||
|
@ -124,9 +127,11 @@ public class UPS extends Patch {
|
|||
IOUtils.closeQuietly(outputStream);
|
||||
}
|
||||
|
||||
long realOutCrc = FileUtils.checksumCRC32(outputFile);
|
||||
if (realOutCrc != upsCrc.getOutputFileCRC())
|
||||
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
|
||||
if (!ignoreChecksum) {
|
||||
long realOutCrc = FileUtils.checksumCRC32(outputFile);
|
||||
if (realOutCrc != upsCrc.getOutputFileCRC())
|
||||
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
|
||||
}
|
||||
}
|
||||
|
||||
// decode pointer
|
||||
|
@ -197,7 +202,7 @@ public class UPS extends Patch {
|
|||
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
|
||||
return new UpsCrc(inputCrc, outputCrc, patchCrc, realPatchCrc);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(stream);
|
||||
IOUtils.closeQuietly(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +248,7 @@ public class UPS extends Patch {
|
|||
return realPatchCRC;
|
||||
}
|
||||
|
||||
public void swapInOut(){
|
||||
public void swapInOut() {
|
||||
long tmp;
|
||||
tmp = inputFileCRC;
|
||||
inputFileCRC = outputFileCRC;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2016 Boris Timofeev
|
||||
Copyright (C) 2016-2017 Boris Timofeev
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.patch;
|
||||
package org.emunix.unipatcher.patcher;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -29,23 +29,26 @@ import java.io.FileInputStream;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class XDelta extends Patch {
|
||||
public class XDelta extends Patcher {
|
||||
|
||||
private static final int NO_ERROR = 0;
|
||||
private static final int ERR_UNABLE_OPEN_PATCH = -5001;
|
||||
private static final int ERR_UNABLE_OPEN_ROM = -5002;
|
||||
private static final int ERR_UNABLE_OPEN_OUTPUT = -5003;
|
||||
private static final int ERR_UNABLE_OPEN_SOURCE = -5004;
|
||||
private static final int ERR_UNABLE_OPEN_MODIFIED = -5005;
|
||||
private static final int ERR_WRONG_CHECKSUM = -5010;
|
||||
private static final int ERR_INVALID_INPUT = -17712;
|
||||
|
||||
public static native int xdelta3apply(String patchPath, String romPath, String outputPath);
|
||||
public static native int xdelta3apply(String patchPath, String romPath, String outputPath, boolean ignoreChecksum);
|
||||
public static native int xdelta3create(String patchPath, String sourcePath, String modifiedPath);
|
||||
|
||||
public XDelta(Context context, File patch, File rom, File output) {
|
||||
super(context, patch, rom, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws PatchException, IOException {
|
||||
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
|
||||
if (checkXDelta1(patchFile))
|
||||
throw new PatchException(context.getString(R.string.notify_error_xdelta1_unsupported));
|
||||
|
||||
|
@ -55,7 +58,7 @@ public class XDelta extends Patch {
|
|||
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
|
||||
}
|
||||
|
||||
int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
|
||||
int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath(), ignoreChecksum);
|
||||
|
||||
switch (ret) {
|
||||
case NO_ERROR:
|
||||
|
@ -78,6 +81,32 @@ public class XDelta extends Patch {
|
|||
}
|
||||
}
|
||||
|
||||
public void create() throws PatchException, IOException {
|
||||
try {
|
||||
System.loadLibrary("xdelta3");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
|
||||
}
|
||||
|
||||
int ret = xdelta3create(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
|
||||
|
||||
switch (ret) {
|
||||
case NO_ERROR:
|
||||
return;
|
||||
case ERR_UNABLE_OPEN_PATCH:
|
||||
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
|
||||
.concat(" ").concat(patchFile.getName()));
|
||||
case ERR_UNABLE_OPEN_SOURCE:
|
||||
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
|
||||
.concat(" ").concat(romFile.getName()));
|
||||
case ERR_UNABLE_OPEN_MODIFIED:
|
||||
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
|
||||
.concat(" ").concat(outputFile.getName()));
|
||||
default:
|
||||
throw new PatchException(context.getString(R.string.notify_error_unknown));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkXDelta1(File file) throws IOException {
|
||||
String[] MAGIC_XDELTA1 = {"%XDELTA%", "%XDZ000%", "%XDZ001%",
|
||||
"%XDZ002%", "%XDZ003%", "%XDZ004%"};
|
|
@ -51,7 +51,7 @@ public class SmdFixChecksum {
|
|||
try {
|
||||
smdStream = new BufferedInputStream(new FileInputStream(smdFile));
|
||||
|
||||
long c = IOUtils.skip(smdStream, 512);
|
||||
long c = IOUtils.skip(smdStream, 512);
|
||||
if (c != 512)
|
||||
throw new IOException("Skip failed");
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class SmdFixChecksum {
|
|||
smdStream.writeByte((sum >> 8) & 0xff);
|
||||
smdStream.writeByte(sum & 0xff);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(smdStream);
|
||||
IOUtils.closeQuietly(smdStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class SnesSmcHeader {
|
|||
int length;
|
||||
length = inputRom.read(header);
|
||||
if (saveHeader) {
|
||||
File headerfile = new File(romfile.getPath()+".smc_header");
|
||||
File headerfile = new File(romfile.getPath() + ".smc_header");
|
||||
outputHeader = new FileOutputStream(headerfile);
|
||||
outputHeader.write(header, 0, length);
|
||||
}
|
||||
|
|
|
@ -29,12 +29,12 @@ import android.os.Environment;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
@ -42,10 +42,8 @@ import com.afollestad.materialdialogs.MaterialDialog;
|
|||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.emunix.unipatcher.Globals;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
import org.emunix.unipatcher.ad.AdMobController;
|
||||
import org.emunix.unipatcher.ui.adapter.FilePickerAdapter;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -63,10 +61,10 @@ import java.util.zip.CRC32;
|
|||
|
||||
public class FilePickerActivity extends AppCompatActivity implements FilePickerAdapter.OnItemClickListener {
|
||||
|
||||
private AdMobController ad;
|
||||
private RecyclerView list;
|
||||
private FilePickerAdapter listAdapter;
|
||||
private TextView permissionErrorText;
|
||||
private CardView card;
|
||||
|
||||
private TextView crc32;
|
||||
private TextView md5;
|
||||
|
@ -108,6 +106,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
permissionErrorText = (TextView) findViewById(R.id.empty_view);
|
||||
|
||||
list = (RecyclerView) findViewById(R.id.list);
|
||||
card = (CardView) findViewById(R.id.card);
|
||||
try {
|
||||
list.setHasFixedSize(true);
|
||||
} catch (NullPointerException e) {/* TODO log */}
|
||||
|
@ -118,14 +117,6 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
listAdapter.setOnItemClickListener(this);
|
||||
|
||||
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
|
||||
|
@ -213,7 +204,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
|
||||
if (currentDir.getParent() != null && currentDir.getParentFile().canRead()) {
|
||||
entry = new FileEntry();
|
||||
entry.setIcon(R.drawable.ic_folder_upload_grey600_24dp);
|
||||
entry.setIcon(R.drawable.folder_upload);
|
||||
entry.setName("..");
|
||||
fileList.add(entry);
|
||||
}
|
||||
|
@ -223,15 +214,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
continue;
|
||||
if (file.isDirectory()) {
|
||||
entry = new FileEntry();
|
||||
entry.setIcon(R.drawable.ic_folder_grey600_24dp);
|
||||
entry.setIcon(R.drawable.folder);
|
||||
entry.setName(file.getName());
|
||||
fileList.add(entry);
|
||||
} else {
|
||||
entry = new FileEntry();
|
||||
if (Utils.isPatch(file)){
|
||||
entry.setIcon(R.drawable.ic_healing_grey600_24dp);
|
||||
if (Utils.isPatch(file)) {
|
||||
entry.setIcon(R.drawable.healing);
|
||||
} else {
|
||||
entry.setIcon(R.drawable.ic_insert_drive_file_grey600_24dp);
|
||||
entry.setIcon(R.drawable.file);
|
||||
}
|
||||
entry.setName(file.getName());
|
||||
fileList.add(entry);
|
||||
|
@ -259,30 +250,6 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
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() {
|
||||
if (!Utils.hasStoragePermission(this)) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
|
@ -310,15 +277,11 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
|
|||
|
||||
private void showPermissionError(boolean on) {
|
||||
if (on) {
|
||||
list.setVisibility(View.GONE);
|
||||
if (ad != null)
|
||||
ad.show(false);
|
||||
card.setVisibility(View.GONE);
|
||||
permissionErrorText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
permissionErrorText.setVisibility(View.GONE);
|
||||
if (ad != null)
|
||||
ad.show(true);
|
||||
list.setVisibility(View.VISIBLE);
|
||||
card.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,8 +362,7 @@ 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())
|
||||
throw new IllegalArgumentException("Unable calculate checksum for directory");
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2013-2016 Boris Timofeev
|
||||
Copyright (C) 2013-2017 Boris Timofeev
|
||||
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
@ -36,54 +36,34 @@ import android.support.v7.app.AppCompatActivity;
|
|||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import 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.Globals;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
import org.emunix.unipatcher.ad.AdMobController;
|
||||
import org.emunix.unipatcher.ui.dialog.RateThisApp;
|
||||
import org.emunix.unipatcher.Settings;
|
||||
import org.emunix.unipatcher.UniPatcher;
|
||||
import org.emunix.unipatcher.ui.fragment.ActionFragment;
|
||||
import org.emunix.unipatcher.ui.fragment.CreatePatchFragment;
|
||||
import org.emunix.unipatcher.ui.fragment.PatchingFragment;
|
||||
import org.emunix.unipatcher.ui.fragment.SmdFixChecksumFragment;
|
||||
import org.emunix.unipatcher.ui.fragment.SnesSmcHeaderFragment;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO;
|
||||
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO;
|
||||
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
|
||||
|
||||
public class MainActivity extends AppCompatActivity
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
private static final String LOG_TAG = "org.emunix.unipatcher";
|
||||
|
||||
private 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;
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme();
|
||||
super.onCreate(savedInstanceState);
|
||||
context = this;
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
|
||||
if (BuildConfig.DEBUG)
|
||||
firebaseAnalytics.setAnalyticsCollectionEnabled(false);
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
|
@ -99,11 +79,9 @@ public class MainActivity extends AppCompatActivity
|
|||
public void onClick(View view) {
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
ActionFragment fragment = (ActionFragment) fragmentManager.findFragmentById(R.id.content_frame);
|
||||
if (fragment != null){
|
||||
if (fragment != null) {
|
||||
boolean ret = fragment.runAction();
|
||||
}
|
||||
//Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// .setAction("Action", null).show();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -115,55 +93,12 @@ public class MainActivity extends AppCompatActivity
|
|||
}
|
||||
|
||||
parseArgument();
|
||||
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);
|
||||
}
|
||||
showDonateSnackbar();
|
||||
}
|
||||
|
||||
private void setTheme() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String theme = sp.getString("theme","light");
|
||||
String theme = sp.getString("theme", "light");
|
||||
switch (theme) {
|
||||
case "light":
|
||||
AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO);
|
||||
|
@ -183,10 +118,12 @@ public class MainActivity extends AppCompatActivity
|
|||
|
||||
if (id == R.id.nav_apply_patch) {
|
||||
selectDrawerItem(0);
|
||||
} else if (id == R.id.nav_smd_fix_checksum) {
|
||||
} else if (id == R.id.nav_create_patch) {
|
||||
selectDrawerItem(1);
|
||||
} else if (id == R.id.nav_snes_add_del_smc_header) {
|
||||
} else if (id == R.id.nav_smd_fix_checksum) {
|
||||
selectDrawerItem(2);
|
||||
} else if (id == R.id.nav_snes_add_del_smc_header) {
|
||||
selectDrawerItem(3);
|
||||
}
|
||||
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
|
@ -196,9 +133,9 @@ public class MainActivity extends AppCompatActivity
|
|||
Intent settingsIntent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(settingsIntent);
|
||||
} else if (id == R.id.nav_rate) {
|
||||
RateThisApp.rate(this);
|
||||
} else if (id == R.id.nav_buy) {
|
||||
buyFullVersion();
|
||||
rateApp();
|
||||
} else if (id == R.id.nav_donate) {
|
||||
showDonateActivity();
|
||||
} else if (id == R.id.nav_share) {
|
||||
shareApp();
|
||||
} else if (id == R.id.nav_help) {
|
||||
|
@ -213,9 +150,17 @@ public class MainActivity extends AppCompatActivity
|
|||
// update the main content by replacing fragments
|
||||
Fragment fragment;
|
||||
switch (position) {
|
||||
case 1: fragment = new SmdFixChecksumFragment(); break;
|
||||
case 2: fragment = new SnesSmcHeaderFragment(); break;
|
||||
default: fragment = new PatchingFragment();
|
||||
case 1:
|
||||
fragment = new CreatePatchFragment();
|
||||
break;
|
||||
case 2:
|
||||
fragment = new SmdFixChecksumFragment();
|
||||
break;
|
||||
case 3:
|
||||
fragment = new SnesSmcHeaderFragment();
|
||||
break;
|
||||
default:
|
||||
fragment = new PatchingFragment();
|
||||
}
|
||||
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
|
@ -226,70 +171,66 @@ public class MainActivity extends AppCompatActivity
|
|||
private void parseArgument() {
|
||||
try {
|
||||
String arg = getIntent().getData().getPath();
|
||||
Globals.setCmdArgument(arg);
|
||||
Log.d(LOG_TAG, "Cmd argument: " + arg);
|
||||
UniPatcher.setAppArgument(arg);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(LOG_TAG, "NullPointerException in argument fetching");
|
||||
// The application is not opened from the file manager
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
Log.d(LOG_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
|
||||
if (!bp.handleActivityResult(requestCode, resultCode, data))
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
private void showDonateSnackbar() {
|
||||
// don't show snackbar if the user did not patch the file successfully
|
||||
if (!Settings.getPatchingSuccessful(this))
|
||||
return;
|
||||
|
||||
// don't show snackbar some time if the user swiped off it before
|
||||
int count = Settings.getDontShowDonateSnackbarCount(this);
|
||||
if (count != 0) {
|
||||
Settings.setDontShowDonateSnackbarCount(this, --count);
|
||||
return;
|
||||
}
|
||||
|
||||
// don't show snackbar each time you open the application
|
||||
if (new Random().nextInt(6) != 0)
|
||||
return;
|
||||
|
||||
Snackbar.make(findViewById(R.id.content_frame), R.string.main_activity_donate_snackbar_text, Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(R.string.main_activity_donate_snackbar_button, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showDonateActivity();
|
||||
}
|
||||
})
|
||||
.addCallback(new Snackbar.Callback() {
|
||||
@Override
|
||||
public void onDismissed(Snackbar snackbar, int event) {
|
||||
if (event == Snackbar.Callback.DISMISS_EVENT_SWIPE) {
|
||||
Settings.setDontShowDonateSnackbarCount(getApplicationContext(), 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
).show();
|
||||
}
|
||||
|
||||
private void showDonateActivity() {
|
||||
Intent donateIntent = new Intent(this, DonateActivity.class);
|
||||
startActivity(donateIntent);
|
||||
}
|
||||
|
||||
private void shareApp() {
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.app_name));
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_text) + "https://play.google.com/store/apps/details?id=org.eminix.unipatcher");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_text) + BuildConfig.SHARE_URL);
|
||||
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title)));
|
||||
}
|
||||
|
||||
private void buyFullVersion() {
|
||||
if (readyToPurchase)
|
||||
bp.purchase(this, SKU_REMOVE_ADS);
|
||||
else
|
||||
complain("Billing not initialized.");
|
||||
}
|
||||
|
||||
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();
|
||||
public void rateApp() {
|
||||
Intent rateAppIntent = new Intent(Intent.ACTION_VIEW);
|
||||
rateAppIntent.setData(Uri.parse(BuildConfig.RATE_URL));
|
||||
if (getPackageManager().queryIntentActivities(rateAppIntent, 0).size() == 0) {
|
||||
// Market app is not installed. Open web browser
|
||||
rateAppIntent.setData(Uri.parse(BuildConfig.SHARE_URL));
|
||||
}
|
||||
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();
|
||||
startActivity(rateAppIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class FilePickerAdapter extends RecyclerView.Adapter<FilePickerAdapter.Vi
|
|||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(View v, int position);
|
||||
|
||||
void onItemLongClick(View v, int position);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,14 +39,11 @@ public class HelpPagerAdapter extends FragmentStatePagerAdapter {
|
|||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
FaqFragment tab1 = new FaqFragment();
|
||||
return tab1;
|
||||
return new FaqFragment();
|
||||
case 1:
|
||||
ChangelogFragment tab2 = new ChangelogFragment();
|
||||
return tab2;
|
||||
return new ChangelogFragment();
|
||||
case 2:
|
||||
AboutFragment tab3 = new AboutFragment();
|
||||
return tab3;
|
||||
return new AboutFragment();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
Copyright (c) 2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
UniPatcher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
UniPatcher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.ui.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.emunix.unipatcher.Action;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Settings;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
import org.emunix.unipatcher.WorkerService;
|
||||
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class CreatePatchFragment extends ActionFragment implements View.OnClickListener {
|
||||
|
||||
private static final String LOG_TAG = "org.emunix.unipatcher";
|
||||
|
||||
private TextView sourceNameTextView;
|
||||
private TextView modifiedNameTextView;
|
||||
private TextView patchNameTextView;
|
||||
private String sourcePath = null;
|
||||
private String modifiedPath = null;
|
||||
private String patchPath = null;
|
||||
|
||||
public CreatePatchFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.create_patch_fragment, container, false);
|
||||
|
||||
sourceNameTextView = (TextView) view.findViewById(R.id.sourceFileNameTextView);
|
||||
modifiedNameTextView = (TextView) view.findViewById(R.id.modifiedFileNameTextView);
|
||||
patchNameTextView = (TextView) view.findViewById(R.id.patchFileNameTextView);
|
||||
|
||||
CardView sourceCardView = (CardView) view.findViewById(R.id.sourceFileCardView);
|
||||
sourceCardView.setOnClickListener(this);
|
||||
CardView modifiedCardView = (CardView) view.findViewById(R.id.modifiedFileCardView);
|
||||
modifiedCardView.setOnClickListener(this);
|
||||
CardView patchCardView = (CardView) view.findViewById(R.id.patchFileCardView);
|
||||
patchCardView.setOnClickListener(this);
|
||||
|
||||
restoreState(savedInstanceState);
|
||||
|
||||
setFonts(view);
|
||||
|
||||
// Set action bar title
|
||||
getActivity().setTitle(R.string.nav_create_patch);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setFonts(View view) {
|
||||
TextView sourceLabel = (TextView) view.findViewById(R.id.sourceFileLabel);
|
||||
TextView modifiedLabel = (TextView) view.findViewById(R.id.modifiedFileLabel);
|
||||
TextView patchLabel = (TextView) view.findViewById(R.id.patchFileLabel);
|
||||
|
||||
Typeface roboto_light = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Light.ttf");
|
||||
|
||||
sourceLabel.setTypeface(roboto_light);
|
||||
modifiedLabel.setTypeface(roboto_light);
|
||||
patchLabel.setTypeface(roboto_light);
|
||||
sourceNameTextView.setTypeface(roboto_light);
|
||||
modifiedNameTextView.setTypeface(roboto_light);
|
||||
patchNameTextView.setTypeface(roboto_light);
|
||||
}
|
||||
|
||||
private void restoreState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null) {
|
||||
sourcePath = savedInstanceState.getString("sourcePath");
|
||||
modifiedPath = savedInstanceState.getString("modifiedPath");
|
||||
patchPath = savedInstanceState.getString("patchPath");
|
||||
if (sourcePath != null)
|
||||
sourceNameTextView.setText(new File(sourcePath).getName());
|
||||
if (modifiedPath != null)
|
||||
modifiedNameTextView.setText(new File(modifiedPath).getName());
|
||||
if (patchPath != null)
|
||||
patchNameTextView.setText(new File(patchPath).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
savedInstanceState.putString("sourcePath", sourcePath);
|
||||
savedInstanceState.putString("modifiedPath", modifiedPath);
|
||||
savedInstanceState.putString("patchPath", patchPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
|
||||
switch (view.getId()) {
|
||||
case R.id.sourceFileCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_source_file));
|
||||
intent.putExtra("directory", Settings.getRomDir(getActivity()));
|
||||
startActivityForResult(intent, Action.SELECT_SOURCE_FILE);
|
||||
break;
|
||||
case R.id.modifiedFileCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_modified_file));
|
||||
intent.putExtra("directory", Settings.getRomDir(getActivity()));
|
||||
startActivityForResult(intent, Action.SELECT_MODIFIED_FILE);
|
||||
break;
|
||||
case R.id.patchFileCardView:
|
||||
renamePatchFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Log.d(LOG_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
String path = data.getStringExtra("path");
|
||||
File fpath = new File(path);
|
||||
|
||||
switch (requestCode) {
|
||||
case Action.SELECT_SOURCE_FILE:
|
||||
sourcePath = path;
|
||||
sourceNameTextView.setVisibility(View.VISIBLE);
|
||||
sourceNameTextView.setText(fpath.getName());
|
||||
Settings.setLastRomDir(getActivity(), fpath.getParent());
|
||||
break;
|
||||
case Action.SELECT_MODIFIED_FILE:
|
||||
modifiedPath = path;
|
||||
modifiedNameTextView.setVisibility(View.VISIBLE);
|
||||
modifiedNameTextView.setText(fpath.getName());
|
||||
Settings.setLastRomDir(getActivity(), fpath.getParent());
|
||||
patchPath = makeOutputPath(path);
|
||||
patchNameTextView.setText(new File(patchPath).getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private String makeOutputPath(String fullname) {
|
||||
String dir = Settings.getOutputDir(getActivity());
|
||||
if (dir.equals("")) { // get ROM directory
|
||||
dir = FilenameUtils.getFullPath(fullname);
|
||||
}
|
||||
String baseName = FilenameUtils.getBaseName(fullname);
|
||||
return FilenameUtils.concat(dir, baseName.concat(".xdelta"));
|
||||
}
|
||||
|
||||
public boolean runAction() {
|
||||
if (sourcePath == null & modifiedPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_and_modified_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
} else if (sourcePath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
} else if (modifiedPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), WorkerService.class);
|
||||
intent.putExtra("action", Action.CREATE_PATCH);
|
||||
intent.putExtra("sourcePath", sourcePath);
|
||||
intent.putExtra("modifiedPath", modifiedPath);
|
||||
intent.putExtra("patchPath", patchPath);
|
||||
Utils.startForegroundService(getActivity(), intent);
|
||||
|
||||
Toast.makeText(getActivity(), R.string.toast_create_patch_started_check_notify, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void renamePatchFile() {
|
||||
if (modifiedPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
|
||||
dialog.setTitle(R.string.dialog_rename_title);
|
||||
final EditText input = new EditText(getActivity());
|
||||
input.setText(patchNameTextView.getText());
|
||||
|
||||
// add left and right margins to EditText.
|
||||
FrameLayout container = new FrameLayout(getActivity());
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
int dp_24 = Utils.dpToPx(getActivity(), 24);
|
||||
params.setMargins(dp_24, 0, dp_24, 0);
|
||||
input.setLayoutParams(params);
|
||||
container.addView(input);
|
||||
dialog.setView(container);
|
||||
|
||||
dialog.setPositiveButton(R.string.dialog_rename_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String newName = input.getText().toString();
|
||||
if (newName.equals("")) {
|
||||
Toast.makeText(getActivity(), R.string.dialog_rename_error_empty_name, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (newName.contains("/")) {
|
||||
newName = newName.replaceAll("/", "_");
|
||||
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
String newPath = new File(patchPath).getParent().concat(File.separator).concat(newName);
|
||||
if (FilenameUtils.equals(newPath, sourcePath) || FilenameUtils.equals(newPath, modifiedPath)) {
|
||||
Toast.makeText(getActivity(), R.string.dialog_rename_error_same_name, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
patchNameTextView.setText(newName);
|
||||
patchPath = newPath;
|
||||
}
|
||||
});
|
||||
dialog.setNegativeButton(R.string.dialog_rename_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
}
|
|
@ -36,9 +36,10 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.emunix.unipatcher.Globals;
|
||||
import org.emunix.unipatcher.Action;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Settings;
|
||||
import org.emunix.unipatcher.UniPatcher;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
import org.emunix.unipatcher.WorkerService;
|
||||
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
|
||||
|
@ -49,8 +50,6 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
|
||||
private static final String LOG_TAG = "org.emunix.unipatcher";
|
||||
|
||||
private static final int SELECT_ROM_FILE = 1;
|
||||
private static final int SELECT_PATCH_FILE = 2;
|
||||
private TextView romNameTextView;
|
||||
private TextView patchNameTextView;
|
||||
private TextView outputNameTextView;
|
||||
|
@ -58,7 +57,8 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
private String patchPath = null;
|
||||
private String outputPath = null;
|
||||
|
||||
public PatchingFragment() {}
|
||||
public PatchingFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -96,7 +96,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
}
|
||||
|
||||
private void parseArgument() {
|
||||
patchPath = Globals.getCmdArgument();
|
||||
patchPath = UniPatcher.getAppArgument();
|
||||
if (patchPath != null) {
|
||||
patchNameTextView.setText(new File(patchPath).getName());
|
||||
}
|
||||
|
@ -118,14 +118,14 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
}
|
||||
|
||||
private void restoreState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null){
|
||||
if (savedInstanceState != null) {
|
||||
romPath = savedInstanceState.getString("romPath");
|
||||
patchPath = savedInstanceState.getString("patchPath");
|
||||
outputPath = savedInstanceState.getString("outputPath");
|
||||
if (romPath != null)
|
||||
romNameTextView.setText(new File(romPath).getName());
|
||||
if (patchPath != null)
|
||||
patchNameTextView.setText(new File (patchPath).getName());
|
||||
patchNameTextView.setText(new File(patchPath).getName());
|
||||
if (outputPath != null)
|
||||
outputNameTextView.setText(new File(outputPath).getName());
|
||||
}
|
||||
|
@ -140,18 +140,18 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view){
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
|
||||
switch (view.getId()) {
|
||||
case R.id.patchCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_patch));
|
||||
intent.putExtra("directory", Settings.getPatchDir(getActivity()));
|
||||
startActivityForResult(intent, SELECT_PATCH_FILE);
|
||||
startActivityForResult(intent, Action.SELECT_PATCH_FILE);
|
||||
break;
|
||||
case R.id.romCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
|
||||
intent.putExtra("directory", Settings.getRomDir(getActivity()));
|
||||
startActivityForResult(intent, SELECT_ROM_FILE);
|
||||
startActivityForResult(intent, Action.SELECT_ROM_FILE);
|
||||
break;
|
||||
case R.id.outputCardView:
|
||||
renameOutputRom();
|
||||
|
@ -160,7 +160,7 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
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");
|
||||
|
@ -171,20 +171,20 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case SELECT_ROM_FILE:
|
||||
romPath = path;
|
||||
romNameTextView.setVisibility(View.VISIBLE);
|
||||
romNameTextView.setText(fpath.getName());
|
||||
Settings.setLastRomDir(getActivity(), fpath.getParent());
|
||||
outputPath = makeOutputPath(path);
|
||||
outputNameTextView.setText(new File(outputPath).getName());
|
||||
break;
|
||||
case SELECT_PATCH_FILE:
|
||||
patchPath = path;
|
||||
patchNameTextView.setVisibility(View.VISIBLE);
|
||||
patchNameTextView.setText(fpath.getName());
|
||||
Settings.setLastPatchDir(getActivity(), fpath.getParent());
|
||||
break;
|
||||
case Action.SELECT_ROM_FILE:
|
||||
romPath = path;
|
||||
romNameTextView.setVisibility(View.VISIBLE);
|
||||
romNameTextView.setText(fpath.getName());
|
||||
Settings.setLastRomDir(getActivity(), fpath.getParent());
|
||||
outputPath = makeOutputPath(path);
|
||||
outputNameTextView.setText(new File(outputPath).getName());
|
||||
break;
|
||||
case Action.SELECT_PATCH_FILE:
|
||||
patchPath = path;
|
||||
patchNameTextView.setVisibility(View.VISIBLE);
|
||||
patchNameTextView.setText(fpath.getName());
|
||||
Settings.setLastPatchDir(getActivity(), fpath.getParent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
@ -200,30 +200,30 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
return FilenameUtils.concat(dir, baseName.concat(" [patched].").concat(ext));
|
||||
}
|
||||
|
||||
public boolean runAction(){
|
||||
if (romPath == null & patchPath == null){
|
||||
public boolean runAction() {
|
||||
if (romPath == null & patchPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_rom_and_patch_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
} else if (romPath == null){
|
||||
} else if (romPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_rom_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
} else if (patchPath == null){
|
||||
} else if (patchPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_patch_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), WorkerService.class);
|
||||
intent.putExtra("action", Globals.ACTION_PATCHING);
|
||||
intent.putExtra("action", Action.APPLY_PATCH);
|
||||
intent.putExtra("romPath", romPath);
|
||||
intent.putExtra("patchPath", patchPath);
|
||||
intent.putExtra("outputPath", outputPath);
|
||||
getActivity().startService(intent);
|
||||
Utils.startForegroundService(getActivity(), intent);
|
||||
|
||||
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify,Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void renameOutputRom(){
|
||||
private void renameOutputRom() {
|
||||
if (romPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_rom_not_selected), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
|
@ -247,6 +247,10 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
|
|||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String newName = input.getText().toString();
|
||||
if (newName.equals("")) {
|
||||
Toast.makeText(getActivity(), R.string.dialog_rename_error_empty_name, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (newName.contains("/")) {
|
||||
newName = newName.replaceAll("/", "_");
|
||||
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show();
|
||||
|
|
|
@ -31,7 +31,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.emunix.unipatcher.Globals;
|
||||
import org.emunix.unipatcher.Action;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Settings;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
|
@ -43,13 +43,13 @@ import java.io.File;
|
|||
public class SmdFixChecksumFragment extends ActionFragment implements View.OnClickListener {
|
||||
private static final String LOG_TAG = "org.emunix.unipatcher";
|
||||
|
||||
private static final int SELECT_ROM_FILE = 1;
|
||||
|
||||
private TextView romNameTextView;
|
||||
private TextView fixChecksumInfoTextview;
|
||||
private String romPath = null;
|
||||
|
||||
public SmdFixChecksumFragment() {}
|
||||
public SmdFixChecksumFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -87,7 +87,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
|
|||
}
|
||||
|
||||
private void restoreState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null){
|
||||
if (savedInstanceState != null) {
|
||||
romPath = savedInstanceState.getString("romPath");
|
||||
if (romPath != null)
|
||||
romNameTextView.setText(new File(romPath).getName());
|
||||
|
@ -101,19 +101,19 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view){
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
|
||||
switch (view.getId()) {
|
||||
case R.id.romCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
|
||||
intent.putExtra("directory", Settings.getRomDir(getActivity()));
|
||||
startActivityForResult(intent, SELECT_ROM_FILE);
|
||||
startActivityForResult(intent, Action.SELECT_ROM_FILE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
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");
|
||||
|
@ -123,7 +123,7 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
|
|||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case SELECT_ROM_FILE:
|
||||
case Action.SELECT_ROM_FILE:
|
||||
romPath = path;
|
||||
romNameTextView.setVisibility(View.VISIBLE);
|
||||
romNameTextView.setText(new File(path).getName());
|
||||
|
@ -134,18 +134,18 @@ public class SmdFixChecksumFragment extends ActionFragment implements View.OnCli
|
|||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
public boolean runAction(){
|
||||
if (romPath == null){
|
||||
public boolean runAction() {
|
||||
if (romPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_rom_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), WorkerService.class);
|
||||
intent.putExtra("action", Globals.ACTION_SMD_FIX_CHECKSUM);
|
||||
intent.putExtra("romPath", romPath);
|
||||
getActivity().startService(intent);
|
||||
intent.putExtra("action", Action.SMD_FIX_CHECKSUM);
|
||||
Utils.startForegroundService(getActivity(), intent);
|
||||
|
||||
Toast.makeText(getActivity(), R.string.notify_smd_fix_checksum_started_check_notify,Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.notify_smd_fix_checksum_started_check_notify, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.emunix.unipatcher.Globals;
|
||||
import org.emunix.unipatcher.Action;
|
||||
import org.emunix.unipatcher.R;
|
||||
import org.emunix.unipatcher.Settings;
|
||||
import org.emunix.unipatcher.Utils;
|
||||
|
@ -43,8 +43,6 @@ import java.io.File;
|
|||
|
||||
public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClickListener {
|
||||
private static final String LOG_TAG = "org.emunix.unipatcher";
|
||||
private static final int SELECT_ROM_FILE = 1;
|
||||
private static final int SELECT_HEADER_FILE = 2;
|
||||
|
||||
private TextView romNameTextView;
|
||||
private TextView headerNameTextView;
|
||||
|
@ -55,7 +53,8 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
|
||||
private int action = 0;
|
||||
|
||||
public SnesSmcHeaderFragment() {}
|
||||
public SnesSmcHeaderFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -69,7 +68,7 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
romNameTextView = (TextView) view.findViewById(R.id.romNameTextView);
|
||||
headerNameTextView = (TextView) view.findViewById(R.id.headerNameTextView);
|
||||
headerInfoTextView = (TextView) view.findViewById(R.id.headerInfoTextView);
|
||||
|
||||
|
||||
CardView romCardView = (CardView) view.findViewById(R.id.romCardView);
|
||||
romCardView.setOnClickListener(this);
|
||||
headerCardView = (CardView) view.findViewById(R.id.headerCardView);
|
||||
|
@ -99,14 +98,14 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
}
|
||||
|
||||
private void restoreState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null){
|
||||
if (savedInstanceState != null) {
|
||||
romPath = savedInstanceState.getString("romPath");
|
||||
headerPath = savedInstanceState.getString("headerPath");
|
||||
action = savedInstanceState.getInt("action");
|
||||
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
|
||||
if (action == Action.SNES_ADD_SMC_HEADER) {
|
||||
headerInfoTextView.setText(R.string.snes_smc_header_will_be_added);
|
||||
headerCardView.setVisibility(View.VISIBLE);
|
||||
} else if (action == Globals.ACTION_SNES_DELETE_SMC_HEADER) {
|
||||
} else if (action == Action.SNES_DELETE_SMC_HEADER) {
|
||||
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
|
||||
headerCardView.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -126,23 +125,23 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view){
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
|
||||
switch (view.getId()) {
|
||||
case R.id.romCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_rom));
|
||||
intent.putExtra("directory", Settings.getRomDir(getActivity()));
|
||||
startActivityForResult(intent, SELECT_ROM_FILE);
|
||||
startActivityForResult(intent, Action.SELECT_ROM_FILE);
|
||||
break;
|
||||
case R.id.headerCardView:
|
||||
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_header));
|
||||
startActivityForResult(intent, SELECT_HEADER_FILE);
|
||||
startActivityForResult(intent, Action.SELECT_HEADER_FILE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||
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");
|
||||
|
@ -152,25 +151,25 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case SELECT_ROM_FILE:
|
||||
case Action.SELECT_ROM_FILE:
|
||||
romPath = path;
|
||||
romNameTextView.setVisibility(View.VISIBLE);
|
||||
romNameTextView.setText(new File(path).getName());
|
||||
Settings.setLastRomDir(getActivity(), new File(path).getParent());
|
||||
SnesSmcHeader checker = new SnesSmcHeader();
|
||||
if (checker.isHasSmcHeader(new File(path))) {
|
||||
action = Globals.ACTION_SNES_DELETE_SMC_HEADER;
|
||||
action = Action.SNES_DELETE_SMC_HEADER;
|
||||
headerCardView.setVisibility(View.GONE);
|
||||
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
|
||||
} else {
|
||||
action = Globals.ACTION_SNES_ADD_SMC_HEADER;
|
||||
action = Action.SNES_ADD_SMC_HEADER;
|
||||
headerCardView.setVisibility(View.VISIBLE);
|
||||
headerInfoTextView.setText(R.string.snes_smc_header_will_be_added);
|
||||
}
|
||||
headerPath = null;
|
||||
headerNameTextView.setText(R.string.main_activity_tap_to_select);
|
||||
break;
|
||||
case SELECT_HEADER_FILE:
|
||||
case Action.SELECT_HEADER_FILE:
|
||||
headerPath = path;
|
||||
headerNameTextView.setText(new File(path).getName());
|
||||
break;
|
||||
|
@ -179,8 +178,8 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
public boolean runAction(){
|
||||
if (romPath == null){
|
||||
public boolean runAction() {
|
||||
if (romPath == null) {
|
||||
Toast.makeText(getActivity(), getString(R.string.main_activity_toast_rom_not_selected), Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
@ -189,9 +188,9 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
|
|||
intent.putExtra("action", action);
|
||||
intent.putExtra("romPath", romPath);
|
||||
intent.putExtra("headerPath", headerPath);
|
||||
getActivity().startService(intent);
|
||||
Utils.startForegroundService(getActivity(), intent);
|
||||
|
||||
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
|
||||
if (action == Action.SNES_ADD_SMC_HEADER) {
|
||||
Toast.makeText(getActivity(), R.string.notify_snes_add_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.notify_snes_delete_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright (c) 2017 Boris Timofeev
|
||||
|
||||
This file is part of UniPatcher.
|
||||
|
||||
UniPatcher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
UniPatcher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
package org.emunix.unipatcher.ui.notify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import org.emunix.unipatcher.R;
|
||||
|
||||
public class CreatePatchNotify extends Notify {
|
||||
|
||||
public CreatePatchNotify(Context c, String text) {
|
||||
super(c);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_creating_patch));
|
||||
notifyBuilder.setContentText(text);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
setSticked(true);
|
||||
setProgress(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompleted() {
|
||||
setProgress(false);
|
||||
setSticked(false);
|
||||
notifyBuilder.setTicker(context.getText(R.string.notify_create_patch_complete));
|
||||
notifyBuilder.setContentTitle(context.getText(R.string.notify_create_patch_complete));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFailed(String message) {
|
||||
setProgress(false);
|
||||
setSticked(false);
|
||||
notifyBuilder.setTicker(context.getText(R.string.notify_error));
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_error));
|
||||
notifyBuilder.setContentText(message);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import android.content.Intent;
|
|||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
|
||||
import org.emunix.unipatcher.UniPatcher;
|
||||
import org.emunix.unipatcher.ui.activity.MainActivity;
|
||||
|
||||
public abstract class Notify {
|
||||
|
@ -40,9 +41,8 @@ public abstract class Notify {
|
|||
|
||||
Intent notificationIntent = new Intent(context, MainActivity.class);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
notifyBuilder = new NotificationCompat.Builder(context);
|
||||
notifyBuilder = new NotificationCompat.Builder(context, UniPatcher.NOTIFICATION_CHANNEL_ID);
|
||||
notifyBuilder.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT));
|
||||
//notifyMng = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
|
||||
notifyMng = NotificationManagerCompat.from(context);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ public abstract class Notify {
|
|||
}
|
||||
|
||||
public abstract void setCompleted();
|
||||
|
||||
public abstract void setFailed(String message);
|
||||
|
||||
public void setProgress(boolean isEnabled) {
|
||||
|
|
|
@ -28,7 +28,7 @@ public class PatchingNotify extends Notify {
|
|||
|
||||
public PatchingNotify(Context c, String text) {
|
||||
super(c);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_applying_patch));
|
||||
notifyBuilder.setContentText(text);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
|
|
|
@ -28,7 +28,7 @@ public class SmdFixChecksumNotify extends Notify {
|
|||
|
||||
public SmdFixChecksumNotify(Context c, String text) {
|
||||
super(c);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_smd_fix_checksum_in_progress));
|
||||
notifyBuilder.setContentText(text);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.emunix.unipatcher.R;
|
|||
public class SnesAddSmcHeaderNotify extends Notify {
|
||||
public SnesAddSmcHeaderNotify(Context c, String text) {
|
||||
super(c);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_add_smc_header_in_progress));
|
||||
notifyBuilder.setContentText(text);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.emunix.unipatcher.R;
|
|||
public class SnesDeleteSmcHeaderNotify extends Notify {
|
||||
public SnesDeleteSmcHeaderNotify(Context c, String text) {
|
||||
super(c);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_stat_patching);
|
||||
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
|
||||
notifyBuilder.setContentTitle(context.getString(R.string.notify_snes_delete_smc_header_in_progress));
|
||||
notifyBuilder.setContentText(text);
|
||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
|
||||
<translate
|
||||
android:duration="500"
|
||||
android:fromYDelta="100%"
|
||||
android:toYDelta="0"
|
||||
android:duration="500" />
|
||||
android:toYDelta="0"/>
|
||||
|
||||
</set>
|
Before Width: | Height: | Size: 514 B |
Before Width: | Height: | Size: 341 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 447 B |
Before Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 588 B |
Before Width: | Height: | Size: 250 B |
Before Width: | Height: | Size: 341 B |
Before Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 513 B |
Before Width: | Height: | Size: 428 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 387 B |
Before Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 276 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 440 B |
Before Width: | Height: | Size: 406 B |
Before Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 257 B |
Before Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 498 B |
After Width: | Height: | Size: 855 B |
Before Width: | Height: | Size: 676 B |
Before Width: | Height: | Size: 716 B |
Before Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 359 B |
Before Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 629 B |
Before Width: | Height: | Size: 511 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 415 B |
Before Width: | Height: | Size: 841 B |
Before Width: | Height: | Size: 493 B |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 725 B |
Before Width: | Height: | Size: 943 B |
Before Width: | Height: | Size: 1,018 B |
Before Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 489 B |