Compare commits

..

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

256 changed files with 2252 additions and 7901 deletions

View file

@ -1,33 +0,0 @@
[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
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID
[unipatcher.about_md]
file_filter = app/src/main/res/raw-<lang>/about.md
source_file = app/src/main/res/raw/about.md
source_lang = en
type = TXT
[unipatcher.faq_md]
file_filter = app/src/main/res/raw-<lang>/faq.md
source_file = app/src/main/res/raw/faq.md
source_lang = en
type = TXT
[unipatcher.changelog_md]
file_filter = app/src/main/res/raw-<lang>/changelog.md
source_file = app/src/main/res/raw/changelog.md
source_lang = en
type = TXT
[unipatcher.google-play_txt]
file_filter = google-play/<lang>/google-play.txt
source_file = google-play/en/google-play.txt
source_lang = en
type = TXT

View file

@ -1,37 +1,24 @@
[![GPL Licence](https://badges.frapsoft.com/os/gpl/gpl.png?v=103)](https://opensource.org/licenses/GPL-3.0/)
[![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14)
UniPatcher
----------
UniPatcher is a ROM patcher for Android that supports IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP and XDelta3 patch types.
UniPatcher is a ROM patcher for Android that supports IPS, UPS, BPS, 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">
### Install UniPatcher:
[<img src="/google-play/badges/google-play.png" alt="Get it on Google Play" width="220">](https://play.google.com/store/apps/details?id=org.emunix.unipatcher) [<img src="/google-play/badges/f-droid.png" alt="Get it on F-Droid" width="220">](https://f-droid.org/app/org.emunix.unipatcher)
[<img src="https://play.google.com/intl/de_de/badges/images/generic/en_badge_web_generic.png" width="192">](https://play.google.com/store/apps/details?id=org.emunix.unipatcher)
[<img src="/google-play/badges/amazon.png" alt="Get it on Amazon" width="220">](http://www.amazon.com/gp/mas/dl/android?p=org.emunix.unipatcher) [<img src="/google-play/badges/slideme.png" alt="Get it on SlideMe" width="220">](http://slideme.org/application/unipatcher)
### How to contribute to UniPatcher:
[... or get a Release APK here on GitHub](https://github.com/btimofeev/UniPatcher/releases)
#### Report a bug or suggest a new feature
### Contribute:
Bugs and new features are being discussed on the GitHub [Issue Tracker](https://github.com/btimofeev/UniPatcher/issues).
#### Report a bug or suggest features
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.
#### Help with translations
The translations are managed on [Transifex](https://www.transifex.com/unipatcher/unipatcher/).

View file

@ -1,2 +1,4 @@
cmake_minimum_required(VERSION 3.4.1)
add_library( xdelta3 SHARED src/main/cpp/xdelta3.c )
add_library( xdelta3 SHARED src/main/cpp/xdelta3.c )
find_library( log-lib log )
target_link_libraries( xdelta3 ${log-lib} )

View file

@ -1,8 +1,13 @@
def versionMajor = 0
def versionMinor = 11
def versionPatch = 0
def versionBuild = 0
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion '27.0.1'
compileSdkVersion 25
buildToolsVersion '25.0.2'
signingConfigs {
release
@ -11,17 +16,13 @@ android {
defaultConfig {
applicationId "org.emunix.unipatcher"
minSdkVersion 14
targetSdkVersion 27
versionCode 150000
versionName "0.15"
vectorDrawables.useSupportLibrary = true
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
targetSdkVersion 25
versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
externalNativeBuild {
cmake {
cppFlags ""
arguments "-DANDROID_PLATFORM=android-14", "-DCMAKE_BUILD_TYPE=Release"
arguments "-DANDROID_PLATFORM=android-14"
}
}
}
@ -29,66 +30,24 @@ android {
buildTypes {
release {
minifyEnabled true
shrinkResources false
proguardFile './proguard-android.txt'
signingConfig signingConfigs.release
}
}
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'
@ -109,18 +68,21 @@ if (propFile.canRead()) {
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.12.0'
testCompile 'org.mockito:mockito-core:2.2.28'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile '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 '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.36'
compile 'commons-io:commons-io:2.5'
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.6.0'
compile 'org.sufficientlysecure:html-textview:3.0'
compile 'com.afollestad.material-dialogs:core:0.9.1.0'
}
apply plugin: 'com.google.gms.google-services'

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

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

View file

@ -1,72 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.activity;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.R;
import org.sufficientlysecure.donations.DonationsFragment;
public class DonateActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (NullPointerException e) {
/* empty */
}
getSupportActionBar().setTitle(R.string.donate_activity_title);
DonationsFragment fragment = (DonationsFragment) getSupportFragmentManager().findFragmentByTag("donationsFragment");
if (fragment == null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
fragment = DonationsFragment.newInstance(BuildConfig.DEBUG,
false, null, null, null,
true, BuildConfig.PAYPAL_USER, BuildConfig.PAYPAL_CURRENCY_CODE, getString(R.string.donation),
false, null, null,
true, BuildConfig.BITCOIN_ADDRESS);
ft.replace(R.id.donate_fragment, fragment, "donationsFragment");
ft.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View file

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

View file

@ -1,91 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.R;
import org.sufficientlysecure.donations.DonationsFragment;
public class DonateActivity extends AppCompatActivity {
private static final String[] GOOGLE_PLAY_CATALOG = new String[]{"donate_1", "donate_3",
"donate_5", "donate_10", "donate_25", "donate_50", "donate_100"};
private static final String[] GOOGLE_PLAY_COST = new String[]{"$1", "$3", "$5", "$10",
"$25", "$50", "$100"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donate);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (NullPointerException e) {
/* empty */
}
getSupportActionBar().setTitle(R.string.donate_activity_title);
DonationsFragment fragment = (DonationsFragment) getSupportFragmentManager().findFragmentByTag("donationsFragment");
if (fragment == null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
fragment = DonationsFragment.newInstance(BuildConfig.DEBUG,
true, BuildConfig.GOOGLE_PLAY_PUBKEY, GOOGLE_PLAY_CATALOG, GOOGLE_PLAY_COST,
false, null, null, null,
false, null, null,
false, null);
ft.replace(R.id.donate_fragment, fragment, "donationsFragment");
ft.commit();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag("donationsFragment");
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View file

@ -1,16 +1,17 @@
<?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="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" />
<application
android:name=".UniPatcher"
android:allowBackup="false"
android:extractNativeLibs="true"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -21,54 +22,57 @@
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="/.*\\.xd"/>
<data android:pathPattern="/.*\\.vcdiff"/>
<data android:host="*"/>
<data android:scheme="file" />
<data android:mimeType="*/*" />
<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="*" />
</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"/>
android:label="@string/help_activity_title" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".ui.activity.DonateActivity"
android:label="@string/donate_activity_title"/>
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
<service
android:name=".WorkerService"
android:exported="false"/>
android:exported="false" />
</application>
</manifest>
</manifest>

View file

@ -2,7 +2,7 @@
This file based on encode_decode_test.c from XDelta3 sources.
Copyright (C) 2007 Ralf Junker
Copyright (C) 2016-2017 Boris Timofeev
Copyright (C) 2016 Boris Timofeev
This file is part of UniPatcher.
@ -23,6 +23,7 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <android/log.h>
#define SIZEOF_SIZE_T 4
#define SIZEOF_UNSIGNED_LONG_LONG 8
@ -30,21 +31,19 @@ along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
#include "xdelta3/xdelta3/xdelta3.h"
#include "xdelta3/xdelta3/xdelta3.c"
int code(int encode, FILE *in, FILE *src, FILE *out, int ignoreChecksum);
int apply(FILE *patch, FILE *in, FILE *out);
const int ERR_UNABLE_OPEN_PATCH = -5001;
const int ERR_UNABLE_OPEN_ROM = -5002;
const int ERR_UNABLE_OPEN_OUTPUT = -5003;
const int ERR_UNABLE_OPEN_SOURCE = -5004;
const int ERR_UNABLE_OPEN_MODIFIED = -5005;
const int ERR_WRONG_CHECKSUM = -5010;
int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
jobject this,
jstring patchPath,
jstring romPath,
jstring outputPath,
jboolean ignoreChecksum) {
int Java_org_emunix_unipatcher_patch_XDelta_xdelta3apply(JNIEnv *env,
jobject this,
jstring patchPath,
jstring romPath,
jstring outputPath)
{
int ret = 0;
const char *patchName = (*env)->GetStringUTFChars(env, patchPath, NULL);
const char *romName = (*env)->GetStringUTFChars(env, romPath, NULL);
@ -58,22 +57,25 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
(*env)->ReleaseStringUTFChars(env, romPath, romName);
(*env)->ReleaseStringUTFChars(env, 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 = code(0, patchFile, romFile, outputFile, (int)ignoreChecksum);
ret = apply(patchFile, romFile, outputFile);
fclose(patchFile);
fclose(romFile);
@ -81,111 +83,68 @@ int Java_org_emunix_unipatcher_patcher_XDelta_xdelta3apply(JNIEnv *env,
return ret;
}
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 apply(FILE *patch, FILE *in, FILE *out)
{
int BUFFER_SIZE = 32768;
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(src, 0, SEEK_SET);
r = fseek(in, 0, SEEK_SET);
if (r)
return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
source.curblkno = 0;
xd3_set_source(&stream, &source);
Input_Buf = malloc(BUFFER_SIZE);
fseek(in, 0, SEEK_SET);
do {
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, in);
if (Input_Buf_Read < BUFFER_SIZE) {
fseek(patch, 0, SEEK_SET);
do
{
Input_Buf_Read = fread(Input_Buf, 1, BUFFER_SIZE, patch);
if (Input_Buf_Read < BUFFER_SIZE)
{
xd3_set_flags(&stream, XD3_FLUSH | stream.flags);
}
xd3_avail_input(&stream, Input_Buf, Input_Buf_Read);
process:
if (encode)
ret = xd3_encode_input(&stream);
else
ret = xd3_decode_input(&stream);
process:
switch (ret) {
ret = xd3_decode_input(&stream);
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(src, source.blksize * source.getblkno, SEEK_SET);
r = fseek(in, source.blksize * source.getblkno, SEEK_SET);
if (r)
return r;
source.onblk = fread((void *) source.curblk, 1, source.blksize, src);
source.onblk = fread((void*)source.curblk, 1, source.blksize, in);
source.curblkno = source.getblkno;
goto process;
@ -195,6 +154,7 @@ process:
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;
@ -206,7 +166,7 @@ process:
free(Input_Buf);
free((void *) source.curblk);
free((void*)source.curblk);
xd3_close_stream(&stream);
xd3_free_stream(&stream);

View file

@ -1,35 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
public class Action {
public static final int SELECT_ROM_FILE = 1;
public static final int SELECT_PATCH_FILE = 2;
public static final int SELECT_SOURCE_FILE = 3;
public static final int SELECT_MODIFIED_FILE = 4;
public static final int SELECT_HEADER_FILE = 5;
public static final int APPLY_PATCH = 101;
public static final int CREATE_PATCH = 102;
public static final int SMD_FIX_CHECKSUM = 103;
public static final int SNES_ADD_SMC_HEADER = 104;
public static final int SNES_DELETE_SMC_HEADER = 105;
}

View file

@ -0,0 +1,51 @@
/*
Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
public class Globals {
private static final String KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA384jTCBEuJ8nCWaC4S6AFrnMQN4mBlmkOXHV3Xg5hlFOl8TkVwiCfqz8r20yJpEy0IJ1+3QRnlq59zadUxbkD+PacJlGB/r2b3mbKfu+m0K+e/0aL6eWupjMSIyPgpnbN3uswiBEGUb4ytzYF53ZKTbLARnruQdMnjV6+VyfwMgpor/48anVQawDARBj/AIAj6VGtRHLmg6DmKDyOGQ7uCgXSv+ysnBKJjtIX/L/5nQgL8Q+9jsr2knuWY7j9BmrtpUXaDH3Kb50M1TOCKiqxPGa8lInOOIndABWxcpqmSMXP06SPYOanUlEH7lT0jjqpHpFNx8hRTT9xf652rgMJwIDAQAB";
private static String cmdArgument = null;
private static boolean isFullVersion = false;
public static String getCmdArgument() {
return cmdArgument;
}
public static void setCmdArgument(String cmdArgument) {
Globals.cmdArgument = cmdArgument;
}
public static boolean isFullVersion() {
return isFullVersion;
}
public static void setFullVersion() {
isFullVersion = true;
}
public static String getKey() {
return KEY;
}
public static final int ACTION_PATCHING = 1;
public static final int ACTION_SMD_FIX_CHECKSUM = 2;
public static final int ACTION_SNES_ADD_SMC_HEADER = 3;
public static final int ACTION_SNES_DELETE_SMC_HEADER = 4;
}

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2016, 2017 Boris Timofeev
Copyright (C) 2016 Boris Timofeev
This file is part of UniPatcher.
@ -64,38 +64,4 @@ public class Settings {
} else
return prefs.getString("patch_directory", "/");
}
public static String getOutputDir(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString("output_directory", "");
}
public static boolean getIgnoreChecksum(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("ignore_checksum", false);
}
public static void setPatchingSuccessful(Context context, Boolean isSuccessful) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("patching_successful", isSuccessful);
editor.apply();
}
public static boolean getPatchingSuccessful(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("patching_successful", false);
}
public static void setDontShowDonateSnackbarCount(Context context, int count) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("dont_show_donate_snackbar", count);
editor.apply();
}
public static int getDontShowDonateSnackbarCount(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getInt("dont_show_donate_snackbar", 0);
}
}

View file

@ -1,57 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
public class UniPatcher extends Application {
private static String appArgument = null;
public static final String NOTIFICATION_CHANNEL_ID = "notifications";
@Override
public void onCreate() {
super.onCreate();
initNotificationChannel();
}
public static String getAppArgument() {
return appArgument;
}
public static void setAppArgument(String appArgument) {
UniPatcher.appArgument = appArgument;
}
public void initNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(channel);
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2017 Boris Timofeev
Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher.
@ -22,9 +22,10 @@ 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;
@ -33,14 +34,12 @@ import android.util.Log;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
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;
@ -49,14 +48,6 @@ 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 {
@ -73,6 +64,12 @@ 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,20 +79,17 @@ 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);
@ -106,18 +100,12 @@ 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);
}
}
public static void truncateFile(File f, long size) throws IOException {
FileChannel channel = new FileOutputStream(f, true).getChannel();
channel.truncate(size);
IOUtils.closeQuietly(channel);
}
public static void copy(InputStream from, OutputStream to, long size) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int c;
@ -153,28 +141,24 @@ 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", "aps", "ppf", "dps", "ebp", "xdelta", "xdelta3", "xd", "vcdiff"};
String[] patches = {"ips", "ups", "bps", "ppf", "dps", "xdelta", "xdelta3", "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");
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2017 Boris Timofeev
Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher.
@ -24,27 +24,24 @@ 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.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.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.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;
@ -65,7 +62,16 @@ public class WorkerService extends IntentService {
protected void onHandleIntent(Intent intent) {
// if user deny write storage permission
if (!Utils.hasStoragePermission(this)) {
showErrorNotification(getString(R.string.permissions_storage_error_notify_access_denied));
Intent notificationIntent = new Intent(this, MainActivity.class);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notify = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.notify_error))
.setContentText(getString(R.string.permissions_storage_error_notify_access_denied))
.setSmallIcon(R.drawable.ic_stat_patching)
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setAutoCancel(true)
.build();
nm.notify(0, notify);
return;
}
@ -76,19 +82,16 @@ public class WorkerService extends IntentService {
try {
int action = intent.getIntExtra("action", 0);
switch (action) {
case Action.APPLY_PATCH:
case Globals.ACTION_PATCHING:
actionPatching(intent);
break;
case Action.CREATE_PATCH:
actionCreatePatch(intent);
break;
case Action.SMD_FIX_CHECKSUM:
case Globals.ACTION_SMD_FIX_CHECKSUM:
actionSmdFixChecksum(intent);
break;
case Action.SNES_ADD_SMC_HEADER:
case Globals.ACTION_SNES_ADD_SMC_HEADER:
actionSnesAddSmcHeader(intent);
break;
case Action.SNES_DELETE_SMC_HEADER:
case Globals.ACTION_SNES_DELETE_SMC_HEADER:
actionSnesDeleteSmcHeader(intent);
break;
}
@ -102,35 +105,11 @@ 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"));
Patcher patcher = null;
Patch patcher = null;
if (!fileExists(patchFile) || !fileExists(romFile))
if(!fileExists(patchFile) || !fileExists(romFile))
return;
// create output dir
try {
if (!outputFile.getParentFile().exists()) {
FileUtils.forceMkdirParent(outputFile);
}
} catch (IOException | SecurityException e) {
String text = getString(R.string.notify_error_unable_to_create_directory, outputFile.getParent());
showErrorNotification(text);
return;
}
// check access to output dir
try {
if (!outputFile.getParentFile().canWrite()) {
String text = getString(R.string.notify_error_unable_to_write_to_directory, outputFile.getParent());
showErrorNotification(text);
return;
}
} catch (SecurityException e) {
String text = getString(R.string.notify_error_unable_to_write_to_directory, outputFile.getParent());
showErrorNotification(text);
return;
}
String ext = FilenameUtils.getExtension(patchFile.getName()).toLowerCase(Locale.getDefault());
if ("ips".equals(ext))
patcher = new IPS(this, patchFile, romFile, outputFile);
@ -140,94 +119,35 @@ public class WorkerService extends IntentService {
patcher = new BPS(this, patchFile, romFile, outputFile);
else if ("ppf".equals(ext))
patcher = new PPF(this, patchFile, romFile, outputFile);
else if ("aps".equals(ext))
patcher = new APS(this, patchFile, romFile, outputFile);
else if ("ebp".equals(ext))
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) || "xd".equals(ext) || "vcdiff".equals(ext))
else if ("xdelta".equals(ext) || "xdelta3".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) {
showErrorNotification(errorMsg);
notify.showResult(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(Settings.getIgnoreChecksum(this));
Settings.setPatchingSuccessful(this, true);
patcher.apply();
} catch (PatchException | IOException e) {
if (Utils.getFreeSpace(outputFile.getParentFile()) == 0) {
errorMsg = getString(R.string.notify_error_not_enough_space);
} else {
errorMsg = e.getMessage();
}
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);
FileUtils.deleteQuietly(outputFile);
} finally {
stopForeground(true);
}
@ -238,7 +158,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);
@ -262,7 +182,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();
@ -292,7 +212,7 @@ public class WorkerService extends IntentService {
File romFile = new File(intent.getStringExtra("romPath"));
if (!fileExists(romFile))
if(!fileExists(romFile))
return;
SnesSmcHeader worker = new SnesSmcHeader();
@ -316,31 +236,19 @@ public class WorkerService extends IntentService {
private boolean fileExists(File f) {
if (!f.exists() || f.isDirectory()) {
Intent notificationIntent = new Intent(this, MainActivity.class);
String text = getString(R.string.notify_error_file_not_found).concat(": ").concat(f.getName());
showErrorNotification(text);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notify = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.notify_error))
.setContentText(text)
.setSmallIcon(R.drawable.ic_stat_patching)
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setAutoCancel(true)
.build();
nm.notify(0, notify);
return false;
}
return true;
}
private void showErrorNotification(String text) {
Intent notificationIntent = new Intent(this, MainActivity.class);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notify = new NotificationCompat.Builder(this, UniPatcher.NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.notify_error))
.setContentText(text)
.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp)
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(text))
.build();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
nm.notify(32768, notify);
} else {
startForeground(32768, notify);
stopForeground(STOP_FOREGROUND_DETACH);
}
}
}

View file

@ -0,0 +1,97 @@
/*
Copyright (C) 2014 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ad;
import android.content.Context;
import android.view.View;
import android.widget.FrameLayout;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import org.emunix.unipatcher.Globals;
public class AdMobController implements AdsController {
private static final String ADMOB_ID = "ca-app-pub-2445378722408015/8831379284";
private AdView adView;
private final Context context;
public AdMobController(Context c, FrameLayout layout) {
context = c;
createView(layout);
}
public void createView(FrameLayout layout) {
adView = new AdView(context);
adView.setAdSize(com.google.android.gms.ads.AdSize.SMART_BANNER);
adView.setAdUnitId(ADMOB_ID);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.addTestDevice("018B7216B142ABE97F9BFCD880C02EBC") // htc one v
.addTestDevice("64F8D0EE579BA448E833172DB2D91CBB") // lg nexus 5
.build();
adView.setAdListener(new AdListener() {
@Override
public void onAdLoaded() {
super.onAdLoaded();
if (Globals.isFullVersion()) {
adView.setVisibility(View.GONE);
}
}
@Override
public void onAdOpened() {
}
});
adView.loadAd(adRequest);
layout.addView(adView);
}
public void show(boolean show) {
if (adView != null) {
if (!show) {
adView.setVisibility(View.GONE);
} else if (!Globals.isFullVersion()) {
adView.setVisibility(View.VISIBLE);
}
}
}
public void pause() {
if (adView != null) {
adView.pause();
}
}
public void resume() {
if (adView != null) {
adView.resume();
}
}
public void start() {}
public void destroy() {
if (adView != null) {
adView.destroy();
}
}
}

View file

@ -0,0 +1,31 @@
/*
Copyright (C) 2014 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ad;
import android.widget.FrameLayout;
interface AdsController {
void createView(FrameLayout layout);
void show(boolean show);
void start();
void destroy();
void resume();
void pause();
}

View file

@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.patcher;
package org.emunix.unipatcher.patch;
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 Patcher {
public class BPS extends Patch {
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 Patcher {
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
public void apply() throws PatchException, IOException {
if (patchFile.length() < 19) {
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
@ -68,11 +68,9 @@ public class BPS extends Patcher {
if (bpsCrc.getPatchFileCRC() != bpsCrc.getRealPatchCRC())
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
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));
}
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();
@ -135,11 +133,9 @@ public class BPS extends Patcher {
IOUtils.closeQuietly(output);
}
if(!ignoreChecksum) {
long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != bpsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
}
long realOutCrc = FileUtils.checksumCRC32(outputFile);
if (realOutCrc != bpsCrc.getOutputFileCRC())
throw new PatchException(context.getString(R.string.notify_error_wrong_checksum_after_patching));
}
// decode pointer
@ -254,7 +250,7 @@ public class BPS extends Patcher {
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);
}
}

View file

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

View file

@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.patcher;
package org.emunix.unipatcher.patch;
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 Patcher {
public class EBP extends Patch {
private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
private static final byte[] EARTH_BOUND = {0x45, 0x41, 0x52, 0x54, 0x48, 0x20, 0x42, 0x4f, 0x55, 0x4e, 0x44};
@ -68,12 +68,12 @@ public class EBP extends Patcher {
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
public void apply() throws PatchException, IOException {
File cleanRom = File.createTempFile("rom", null, context.getCacheDir());
File ipsPatch = File.createTempFile("patch", null, context.getCacheDir());
try {
Utils.copyFile(context, romFile, cleanRom);
prepareCleanRom(cleanRom, ignoreChecksum);
prepareCleanRom(cleanRom);
EBPtoIPS(patchFile, ipsPatch);
@ -85,7 +85,7 @@ public class EBP extends Patcher {
}
}
private void prepareCleanRom(File file, boolean ignoreChecksum) throws IOException, PatchException {
private void prepareCleanRom(File file) throws IOException, PatchException {
// delete smc header
SnesSmcHeader smc = new SnesSmcHeader();
try {
@ -95,10 +95,8 @@ public class EBP extends Patcher {
}
// check rom size and remove unused expanded space
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)
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);
@ -108,7 +106,7 @@ public class EBP extends Patcher {
// 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);
@ -256,7 +254,7 @@ public class EBP extends Patcher {
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)

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2013, 2016, 2017 Boris Timofeev
Copyright (C) 2013, 2016 Boris Timofeev
This file is part of UniPatcher.
@ -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.patcher;
package org.emunix.unipatcher.patch;
import android.content.Context;
@ -34,27 +34,17 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class IPS extends Patcher {
public class IPS extends 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;
private static final byte[] MAGIC_NUMBER = {0x50, 0x41, 0x54, 0x43, 0x48}; // "PATCH"
public IPS(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
apply();
}
public void apply() throws PatchException, IOException {
BufferedInputStream romStream = null;
BufferedInputStream patchStream = null;
BufferedOutputStream outputStream = null;
@ -65,37 +55,33 @@ public class IPS extends Patcher {
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
long romSize = romFile.length();
long romPos = 0;
long outPos = 0;
long offset;
long size;
int romPos = 0;
int outPos = 0;
int offset;
int 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 (Arrays.equals(magic, MAGIC_NUMBER_IPS)) {
mPatchType = IPS_PATCH;
} else if (Arrays.equals(magic, MAGIC_NUMBER_IPS32)) {
mPatchType = IPS32_PATCH;
} else {
if (size != 5 || !Arrays.equals(magic, MAGIC_NUMBER))
throw new PatchException(context.getString(R.string.notify_error_not_ips_patch));
}
while (true) {
offset = readOffset(patchStream);
offset = readOffset(patchStream, 3);
if (offset < 0)
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
if (checkEOF(offset)) {
if (offset == 0x454f46) { // EOF
// truncate file or copy tail
if (romPos < romSize) {
offset = readOffset(patchStream);
offset = readOffset(patchStream, 3);
if (offset != -1 && offset >= romPos) {
size = offset - romPos;
} else {
size = romSize - romPos;
size = (int) romSize - romPos;
}
Utils.copy(romStream, outputStream, size);
}
@ -111,7 +97,7 @@ public class IPS extends Patcher {
}
} else {
if (outPos < romSize) {
size = romSize - outPos;
size = (int) romSize - outPos;
Utils.copy(romStream, outputStream, size);
romPos += size;
outPos += size;
@ -125,14 +111,14 @@ public class IPS extends Patcher {
size = (patchStream.read() << 8) + patchStream.read();
if (size != 0) {
byte[] data = new byte[(int)size];
byte[] data = new byte[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[(int)size];
byte[] data = new byte[size];
Arrays.fill(data, val);
outputStream.write(data);
outPos += size;
@ -140,9 +126,10 @@ public class IPS extends Patcher {
if (offset <= romSize) {
if (romPos + size > romSize) {
romPos = romSize;
romPos = (int) romSize;
} else {
byte[] buf = new byte[(int)size];
// Не используй romStream.skip(size), оно по какой-то причине не всегда пропускает байты.
byte[] buf = new byte[size];
romStream.read(buf);
romPos += size;
}
@ -155,30 +142,8 @@ public class IPS extends Patcher {
}
}
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");
}
private int readOffset(InputStream stream, int numBytes) throws IOException {
int offset = 0;
while (numBytes-- != 0) {
int b = stream.read();
if (b == -1)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2016-2017 Boris Timofeev
Copyright (C) 2016 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.patcher;
package org.emunix.unipatcher.patch;
import android.content.Context;
@ -29,26 +29,23 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class XDelta extends Patcher {
public class XDelta extends Patch {
private static final int NO_ERROR = 0;
private static final int 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, boolean ignoreChecksum);
public static native int xdelta3create(String patchPath, String sourcePath, String modifiedPath);
public static native int xdelta3apply(String patchPath, String romPath, String outputPath);
public XDelta(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
public void apply() throws PatchException, IOException {
if (checkXDelta1(patchFile))
throw new PatchException(context.getString(R.string.notify_error_xdelta1_unsupported));
@ -58,7 +55,7 @@ public class XDelta extends Patcher {
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
}
int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath(), ignoreChecksum);
int ret = xdelta3apply(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
switch (ret) {
case NO_ERROR:
@ -81,32 +78,6 @@ public class XDelta extends Patcher {
}
}
public void create() throws PatchException, IOException {
try {
System.loadLibrary("xdelta3");
} catch (UnsatisfiedLinkError e) {
throw new PatchException(context.getString(R.string.notify_error_failed_load_lib_xdelta3));
}
int ret = xdelta3create(patchFile.getPath(), romFile.getPath(), outputFile.getPath());
switch (ret) {
case NO_ERROR:
return;
case ERR_UNABLE_OPEN_PATCH:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(patchFile.getName()));
case ERR_UNABLE_OPEN_SOURCE:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(romFile.getName()));
case ERR_UNABLE_OPEN_MODIFIED:
throw new PatchException(context.getString(R.string.notify_error_unable_open_file)
.concat(" ").concat(outputFile.getName()));
default:
throw new PatchException(context.getString(R.string.notify_error_unknown));
}
}
public boolean checkXDelta1(File file) throws IOException {
String[] MAGIC_XDELTA1 = {"%XDELTA%", "%XDZ000%", "%XDZ001%",
"%XDZ002%", "%XDZ003%", "%XDZ004%"};

View file

@ -1,84 +0,0 @@
/*
Copyright (C) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.patcher;
import android.content.Context;
import org.apache.commons.io.IOUtils;
import org.emunix.unipatcher.R;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class APS extends Patcher {
public static final int NOT_APS_PATCH = 0;
public static final int APS_N64_PATCH = 1;
public static final int APS_GBA_PATCH = 2;
private static final byte[] APS_N64_MAGIC = {0x41, 0x50, 0x53, 0x31, 0x30}; // APS10
private static final byte[] APS_GBA_MAGIC = {0x41, 0x50, 0x53, 0x31}; // APS1
public APS(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
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;
case APS_GBA_PATCH:
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(ignoreChecksum);
}
public int checkAPS(File file) throws PatchException, IOException {
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
byte[] magicN64 = new byte[5];
int count = stream.read(magicN64);
if (count < 5)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
if (Arrays.equals(magicN64, APS_N64_MAGIC)) {
return APS_N64_PATCH;
} else {
byte[] magicGBA = new byte[4];
System.arraycopy(magicN64, 0, magicGBA, 0, 4);
if (Arrays.equals(magicGBA, APS_GBA_MAGIC)) {
return APS_GBA_PATCH;
}
}
} finally {
IOUtils.closeQuietly(stream);
}
return NOT_APS_PATCH;
}
}

View file

@ -1,194 +0,0 @@
/*
Copyright (C) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.patcher;
import android.content.Context;
import org.apache.commons.io.IOUtils;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
public class APS_GBA extends Patcher {
private static final byte[] MAGIC_NUMBER = {0x41, 0x50, 0x53, 0x31}; // APS1
private static final int CHUNK_SIZE = 65536;
private static final int[] table = { // 16-bit CRC-CCITT table
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
public APS_GBA(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
long fileSize1, fileSize2, bytesLeft, offset;
int crc, patchCrc1, patchCrc2, pCount, oCount;
boolean isOriginal = false;
boolean isModified = false;
byte[] romBuf = new byte[CHUNK_SIZE];
byte[] patchBuf = new byte[CHUNK_SIZE];
BufferedInputStream patchStream = null;
RandomAccessFile output = null;
Utils.copyFile(context, romFile, outputFile);
try {
patchStream = new BufferedInputStream(new FileInputStream(patchFile));
output = new RandomAccessFile(outputFile, "rw");
byte[] magic = new byte[4];
pCount = patchStream.read(magic);
if (pCount < 4 || !Arrays.equals(magic, MAGIC_NUMBER))
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
fileSize1 = readLEInt(patchStream);
fileSize2 = readLEInt(patchStream);
if (fileSize1 < 0 || fileSize2 < 0)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
bytesLeft = patchFile.length() - 12;
while (bytesLeft > 0) {
offset = readLEInt(patchStream);
patchCrc1 = readLEChar(patchStream);
patchCrc2 = readLEChar(patchStream);
bytesLeft -= 8;
if (offset < 0 || patchCrc1 < 0 || patchCrc2 < 0)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
output.seek(offset);
oCount = output.read(romBuf);
pCount = patchStream.read(patchBuf);
bytesLeft -= CHUNK_SIZE;
if (pCount < CHUNK_SIZE)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
if (oCount < CHUNK_SIZE) {
if (oCount < 0) oCount = 0;
for (int i = oCount; i < CHUNK_SIZE; i++)
romBuf[i] = 0x0;
}
crc = crc16(romBuf);
for (int i = 0; i < CHUNK_SIZE; i++)
romBuf[i] ^= patchBuf[i];
if (crc == patchCrc1) {
isOriginal = true;
} else if (crc == patchCrc2) {
isModified = true;
} else {
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));
output.seek(offset);
output.write(romBuf);
}
} finally {
IOUtils.closeQuietly(patchStream);
IOUtils.closeQuietly(output);
}
if (isOriginal) {
Utils.truncateFile(outputFile, fileSize2);
} else if (isModified) {
Utils.truncateFile(outputFile, fileSize1);
}
}
private long readLEInt(InputStream stream) throws IOException {
long result = 0;
int x;
for (int i = 0; i < 4; i++) {
x = stream.read();
if (x == -1)
return -1;
result += ((long) x) << (i * 8);
}
return result;
}
private int readLEChar(InputStream stream) throws IOException {
int result = 0;
int x;
for (int i = 0; i < 2; i++) {
x = stream.read();
if (x == -1)
return -1;
result += x << (i * 8);
}
return result;
}
private int crc16(byte[] data) {
int crc = 0xffff;
for (byte b : data) {
crc = (crc << 8) ^ table[((crc >> 8) ^ b) & 0xff];
crc = (short) crc & 0xffff;
}
return crc;
}
}

View file

@ -1,234 +0,0 @@
/*
Copyright (C) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.patcher;
import android.content.Context;
import org.apache.commons.io.IOUtils;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
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;
private static final int TYPE_N64_PATCH = 1;
private static final int ENCODING_SIMPLE = 0;
public APS_N64(Context context, File patch, File rom, File output) {
super(context, patch, rom, output);
}
@Override
public void apply(boolean ignoreChecksum) throws PatchException, IOException {
BufferedInputStream romStream = null;
BufferedInputStream patchStream = null;
BufferedOutputStream outputStream = null;
try {
patchStream = new BufferedInputStream(new FileInputStream(patchFile));
long patchSize = patchFile.length();
long romSize = romFile.length();
long outSize;
int romPos = 0;
int outPos = 0;
int patchPos = 0;
long offset, size;
// check magic string
byte[] magic = new byte[5];
size = patchStream.read(magic);
if (size != 5 || !Arrays.equals(magic, MAGIC_NUMBER))
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
patchPos += 5;
// read and check type of the patch
int patchType = patchStream.read();
if ((patchType != TYPE_SIMPLE_PATCH) && (patchType != TYPE_N64_PATCH))
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
patchPos++;
// check encoding method
int encoding = patchStream.read();
if (encoding != ENCODING_SIMPLE)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
patchPos++;
// skip description
byte[] description = new byte[50];
size = patchStream.read(description);
if (size < 50)
throw new PatchException(context.getString(R.string.notify_error_not_aps_patch));
patchPos += 50;
// validate ROM
if (patchType == TYPE_N64_PATCH) {
int endianness = patchStream.read();
int cardID = ((patchStream.read() & 0xff) << 8) + (patchStream.read() & 0xff);
int country = patchStream.read();
byte[] crc = new byte[8];
patchStream.read(crc);
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);
patchPos += 17;
}
// read size of destination image.
outSize = readLELong(patchStream);
patchPos += 4;
romStream = new BufferedInputStream(new FileInputStream(romFile));
outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
// apply patch
while (patchPos < patchSize) {
offset = readLELong(patchStream);
if (offset < 0)
throw new PatchException(context.getString(R.string.notify_error_patch_corrupted));
patchPos += 4;
// copy data from rom to out
if (offset <= romSize) {
if (outPos < offset) {
size = offset - outPos;
Utils.copy(romStream, outputStream, size);
romPos += size;
outPos += size;
}
} else {
if (outPos < romSize) {
size = (int) romSize - outPos;
Utils.copy(romStream, outputStream, size);
romPos += size;
outPos += size;
}
if (outPos < offset) {
size = offset - outPos;
Utils.copy(size, (byte) 0x0, outputStream);
outPos += size;
}
}
// copy data from patch to out
size = patchStream.read();
patchPos++;
if (size != 0) {
byte[] data = new byte[(int) size];
patchStream.read(data);
patchPos += size;
outputStream.write(data);
outPos += size;
} else { // RLE
byte val = (byte) patchStream.read();
size = patchStream.read();
patchPos += 2;
byte[] data = new byte[(int) size];
Arrays.fill(data, val);
outputStream.write(data);
outPos += size;
}
// skip rom data
if (offset <= romSize) {
if (romPos + size > romSize) {
romPos = (int) romSize;
} else {
byte[] buf = new byte[(int) size];
romStream.read(buf);
romPos += size;
}
}
}
// write rom tail and trim
Utils.copy(romStream, outputStream, outSize - outPos);
} finally {
IOUtils.closeQuietly(romStream);
IOUtils.closeQuietly(patchStream);
IOUtils.closeQuietly(outputStream);
}
}
private boolean validateROM(int endianness, int cartID, int country, byte[] crc) throws IOException {
RandomAccessFile rom = new RandomAccessFile(romFile, "r");
int val;
try {
// check endianness
val = rom.read();
if ((endianness == 1 && val != 0x80) || (endianness == 0 && val != 0x37))
return false;
// check cartID
rom.seek(0x3c);
if (endianness == 1) {
val = ((rom.read() & 0xff) << 8) + (rom.read() & 0xff);
} else {
val = (rom.read() & 0xff) + ((rom.read() & 0xff) << 8);
}
if (cartID != val)
return false;
// check country
val = rom.read();
if (endianness == 0)
val = rom.read();
if (country != val)
return false;
// check crc
byte[] buf = new byte[8];
rom.seek(0x10);
rom.read(buf);
if (endianness == 0) {
byte tmp;
for (int i = 0; i < buf.length; i += 2) {
tmp = buf[i];
buf[i] = buf[i + 1];
buf[i + 1] = tmp;
}
}
if (!Arrays.equals(crc, buf))
return false;
} finally {
IOUtils.closeQuietly(rom);
}
return true;
}
private long readLELong(InputStream stream) throws IOException {
return (stream.read() & 0xff) + ((stream.read() & 0xff) << 8)
+ ((stream.read() & 0xff) << 16) + ((stream.read() & 0xff) << 24);
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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,8 +42,10 @@ 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;
@ -61,10 +63,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;
@ -106,7 +108,6 @@ 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 */}
@ -117,6 +118,14 @@ 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
@ -204,7 +213,7 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
if (currentDir.getParent() != null && currentDir.getParentFile().canRead()) {
entry = new FileEntry();
entry.setIcon(R.drawable.folder_upload);
entry.setIcon(R.drawable.ic_folder_upload_grey600_24dp);
entry.setName("..");
fileList.add(entry);
}
@ -214,15 +223,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
continue;
if (file.isDirectory()) {
entry = new FileEntry();
entry.setIcon(R.drawable.folder);
entry.setIcon(R.drawable.ic_folder_grey600_24dp);
entry.setName(file.getName());
fileList.add(entry);
} else {
entry = new FileEntry();
if (Utils.isPatch(file)) {
entry.setIcon(R.drawable.healing);
if (Utils.isPatch(file)){
entry.setIcon(R.drawable.ic_healing_grey600_24dp);
} else {
entry.setIcon(R.drawable.file);
entry.setIcon(R.drawable.ic_insert_drive_file_grey600_24dp);
}
entry.setName(file.getName());
fileList.add(entry);
@ -250,6 +259,30 @@ 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,
@ -277,11 +310,15 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
private void showPermissionError(boolean on) {
if (on) {
card.setVisibility(View.GONE);
list.setVisibility(View.GONE);
if (ad != null)
ad.show(false);
permissionErrorText.setVisibility(View.VISIBLE);
} else {
permissionErrorText.setVisibility(View.GONE);
card.setVisibility(View.VISIBLE);
if (ad != null)
ad.show(true);
list.setVisibility(View.VISIBLE);
}
}
@ -362,7 +399,8 @@ public class FilePickerActivity extends AppCompatActivity implements FilePickerA
}
}
private HashMap<String, String> getFileChecksums(File file) throws IOException, NoSuchAlgorithmException, IllegalArgumentException {
private HashMap<String, String> getFileChecksums(File file) throws IOException, NoSuchAlgorithmException, IllegalArgumentException
{
if (file.isDirectory())
throw new IllegalArgumentException("Unable calculate checksum for directory");

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2017 Boris Timofeev
Copyright (C) 2013-2016 Boris Timofeev
This file is part of UniPatcher.
@ -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,34 +36,54 @@ 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.Settings;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.ad.AdMobController;
import org.emunix.unipatcher.ui.dialog.RateThisApp;
import org.emunix.unipatcher.ui.fragment.ActionFragment;
import org.emunix.unipatcher.ui.fragment.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 {
implements NavigationView.OnNavigationItemSelectedListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final String SKU_FULL = "full";
private static final String SKU_REMOVE_ADS = "ad";
private boolean readyToPurchase = false;
private BillingProcessor bp;
private AdMobController ad;
private FirebaseAnalytics firebaseAnalytics;
private Context context;
@Override
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);
@ -79,9 +99,11 @@ 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();
}
});
@ -93,12 +115,55 @@ public class MainActivity extends AppCompatActivity
}
parseArgument();
showDonateSnackbar();
bp = new BillingProcessor(this, Globals.getKey(), new BillingProcessor.IBillingHandler() {
@Override
public void onBillingInitialized() {
Log.d(LOG_TAG, "Billing initialized");
readyToPurchase = true;
if (bp.isPurchased(SKU_FULL) || bp.isPurchased(SKU_REMOVE_ADS)) {
setFullVersion();
}
}
@Override
public void onProductPurchased(String productId, TransactionDetails details) {
Log.d(LOG_TAG, "Item purchased: " + productId);
complain(getString(R.string.purchase_successful));
setFullVersion();
}
@Override
public void onBillingError(int errorCode, Throwable error) {
if (errorCode != 110) // cancel purchase
complain("Billing error: " + Integer.toString(errorCode));
}
@Override
public void onPurchaseHistoryRestored() {
for(String sku : bp.listOwnedProducts())
Log.d(LOG_TAG, "Owned Managed Product: " + sku);
if (bp.isPurchased(SKU_FULL) || bp.isPurchased(SKU_REMOVE_ADS)) {
setFullVersion();
}
}
});
RateThisApp.launch(this);
// Load ads
if (!Globals.isFullVersion()) {
Handler adHandler = new Handler();
adHandler.postDelayed(new Runnable() {
@Override
public void run() {
FrameLayout adView = (FrameLayout) findViewById(R.id.adView);
ad = new AdMobController(context, adView);
if (!Utils.isOnline(context))
ad.show(false);
}
}, 1000);
}
}
private void setTheme() {
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);
@ -118,12 +183,10 @@ public class MainActivity extends AppCompatActivity
if (id == R.id.nav_apply_patch) {
selectDrawerItem(0);
} else if (id == R.id.nav_create_patch) {
selectDrawerItem(1);
} else if (id == R.id.nav_smd_fix_checksum) {
selectDrawerItem(2);
selectDrawerItem(1);
} else if (id == R.id.nav_snes_add_del_smc_header) {
selectDrawerItem(3);
selectDrawerItem(2);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
@ -133,9 +196,9 @@ public class MainActivity extends AppCompatActivity
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent);
} else if (id == R.id.nav_rate) {
rateApp();
} else if (id == R.id.nav_donate) {
showDonateActivity();
RateThisApp.rate(this);
} else if (id == R.id.nav_buy) {
buyFullVersion();
} else if (id == R.id.nav_share) {
shareApp();
} else if (id == R.id.nav_help) {
@ -150,17 +213,9 @@ public class MainActivity extends AppCompatActivity
// update the main content by replacing fragments
Fragment fragment;
switch (position) {
case 1:
fragment = new CreatePatchFragment();
break;
case 2:
fragment = new SmdFixChecksumFragment();
break;
case 3:
fragment = new SnesSmcHeaderFragment();
break;
default:
fragment = new PatchingFragment();
case 1: fragment = new SmdFixChecksumFragment(); break;
case 2: fragment = new SnesSmcHeaderFragment(); break;
default: fragment = new PatchingFragment();
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
@ -171,66 +226,70 @@ public class MainActivity extends AppCompatActivity
private void parseArgument() {
try {
String arg = getIntent().getData().getPath();
UniPatcher.setAppArgument(arg);
Globals.setCmdArgument(arg);
Log.d(LOG_TAG, "Cmd argument: " + arg);
} catch (NullPointerException e) {
// The application is not opened from the file manager
Log.e(LOG_TAG, "NullPointerException in argument fetching");
}
}
private void showDonateSnackbar() {
// don't show snackbar if the user did not patch the file successfully
if (!Settings.getPatchingSuccessful(this))
return;
// don't show snackbar some time if the user swiped off it before
int count = Settings.getDontShowDonateSnackbarCount(this);
if (count != 0) {
Settings.setDontShowDonateSnackbarCount(this, --count);
return;
}
// don't show snackbar each time you open the application
if (new Random().nextInt(6) != 0)
return;
Snackbar.make(findViewById(R.id.content_frame), R.string.main_activity_donate_snackbar_text, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.main_activity_donate_snackbar_button, new View.OnClickListener() {
@Override
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);
@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 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) + BuildConfig.SHARE_URL);
shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_text) + "https://play.google.com/store/apps/details?id=org.eminix.unipatcher");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title)));
}
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));
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();
}
startActivity(rateAppIntent);
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (ad != null) {
ad.resume();
}
}
@Override
public void onDestroy() {
if (bp != null) {
bp.release();
}
if (ad != null) {
ad.destroy();
}
super.onDestroy();
}
}

View file

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

View file

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

View file

@ -0,0 +1,89 @@
/*
Copyright (C) 2013 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
import org.emunix.unipatcher.R;
public class RateThisApp {
private final static int LAUNCHES_UNTIL_PROMPT = 7;
public static void launch(Context context) {
SharedPreferences preferences = context.getSharedPreferences("RateThisApp", 0);
if (preferences.getBoolean("dont_show_again", false))
return;
SharedPreferences.Editor editor = preferences.edit();
long launchCount = preferences.getLong("launch_count", 0) + 1;
editor.putLong("launch_count", launchCount);
if (launchCount % LAUNCHES_UNTIL_PROMPT == 0)
showDialog(context);
editor.apply();
}
public static void rate(Context context) {
String packageName = context.getApplicationContext().getPackageName();
Intent rateAppIntent = new Intent(Intent.ACTION_VIEW);
rateAppIntent.setData(Uri.parse("market://details?id=" + packageName));
if (context.getPackageManager().queryIntentActivities(rateAppIntent, 0).size() == 0) {
// Market app is not installed. Open web browser
rateAppIntent.setData(Uri.parse("https://play.google.com/store/apps/details?id=" + packageName));
}
context.startActivity(rateAppIntent);
dontShowDialogAgain(context);
}
private static void showDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.rate_dialog_title);
builder.setMessage(R.string.rate_dialog_message);
builder.setPositiveButton(R.string.rate_dialog_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
rate(context);
}
});
builder.setNegativeButton(R.string.rate_dialog_later, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private static void dontShowDialogAgain(Context context) {
SharedPreferences preferences = context.getSharedPreferences("RateThisApp", 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("dont_show_again", true);
editor.apply();
}
}

View file

@ -21,7 +21,6 @@ package org.emunix.unipatcher.ui.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -29,12 +28,9 @@ import android.widget.TextView;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Utils;
import org.markdown4j.Markdown4jProcessor;
import org.sufficientlysecure.htmltextview.HtmlResImageGetter;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import java.io.IOException;
public class AboutFragment extends Fragment {
@Override
@ -45,13 +41,7 @@ public class AboutFragment extends Fragment {
TextView versionText = (TextView) view.findViewById(R.id.versionText);
versionText.setText(getString(R.string.help_activity_about_tab_version, Utils.getAppVersion(getActivity())));
HtmlTextView aboutText = (HtmlTextView) view.findViewById(R.id.aboutText);
try {
String html = new Markdown4jProcessor().process(
getActivity().getResources().openRawResource(R.raw.about));
aboutText.setHtml(html, new HtmlResImageGetter(aboutText));
} catch (IOException e) {
Log.e("UniPatcher", "IOException", e);
}
aboutText.setHtml(R.raw.about, new HtmlResImageGetter(aboutText));
return view;
}

View file

@ -21,18 +21,14 @@ package org.emunix.unipatcher.ui.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.emunix.unipatcher.R;
import org.markdown4j.Markdown4jProcessor;
import org.sufficientlysecure.htmltextview.HtmlResImageGetter;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import java.io.IOException;
public class ChangelogFragment extends Fragment {
public ChangelogFragment() {
@ -45,13 +41,7 @@ public class ChangelogFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_changelog, container, false);
HtmlTextView changelogText = (HtmlTextView) view.findViewById(R.id.changelogText);
try {
String html = new Markdown4jProcessor().process(
getActivity().getResources().openRawResource(R.raw.changelog));
changelogText.setHtml(html, new HtmlResImageGetter(changelogText));
} catch (IOException e) {
Log.e("UniPatcher", "IOException", e);
}
changelogText.setHtml(R.raw.changelog, new HtmlResImageGetter(changelogText));
return view;
}

View file

@ -1,259 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.fragment;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.CardView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.WorkerService;
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
import java.io.File;
public class CreatePatchFragment extends ActionFragment implements View.OnClickListener {
private static final String LOG_TAG = "org.emunix.unipatcher";
private TextView sourceNameTextView;
private TextView modifiedNameTextView;
private TextView patchNameTextView;
private String sourcePath = null;
private String modifiedPath = null;
private String patchPath = null;
public CreatePatchFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_patch_fragment, container, false);
sourceNameTextView = (TextView) view.findViewById(R.id.sourceFileNameTextView);
modifiedNameTextView = (TextView) view.findViewById(R.id.modifiedFileNameTextView);
patchNameTextView = (TextView) view.findViewById(R.id.patchFileNameTextView);
CardView sourceCardView = (CardView) view.findViewById(R.id.sourceFileCardView);
sourceCardView.setOnClickListener(this);
CardView modifiedCardView = (CardView) view.findViewById(R.id.modifiedFileCardView);
modifiedCardView.setOnClickListener(this);
CardView patchCardView = (CardView) view.findViewById(R.id.patchFileCardView);
patchCardView.setOnClickListener(this);
restoreState(savedInstanceState);
setFonts(view);
// Set action bar title
getActivity().setTitle(R.string.nav_create_patch);
return view;
}
private void setFonts(View view) {
TextView sourceLabel = (TextView) view.findViewById(R.id.sourceFileLabel);
TextView modifiedLabel = (TextView) view.findViewById(R.id.modifiedFileLabel);
TextView patchLabel = (TextView) view.findViewById(R.id.patchFileLabel);
Typeface roboto_light = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Roboto-Light.ttf");
sourceLabel.setTypeface(roboto_light);
modifiedLabel.setTypeface(roboto_light);
patchLabel.setTypeface(roboto_light);
sourceNameTextView.setTypeface(roboto_light);
modifiedNameTextView.setTypeface(roboto_light);
patchNameTextView.setTypeface(roboto_light);
}
private void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
sourcePath = savedInstanceState.getString("sourcePath");
modifiedPath = savedInstanceState.getString("modifiedPath");
patchPath = savedInstanceState.getString("patchPath");
if (sourcePath != null)
sourceNameTextView.setText(new File(sourcePath).getName());
if (modifiedPath != null)
modifiedNameTextView.setText(new File(modifiedPath).getName());
if (patchPath != null)
patchNameTextView.setText(new File(patchPath).getName());
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("sourcePath", sourcePath);
savedInstanceState.putString("modifiedPath", modifiedPath);
savedInstanceState.putString("patchPath", patchPath);
}
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), FilePickerActivity.class);
switch (view.getId()) {
case R.id.sourceFileCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_source_file));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_SOURCE_FILE);
break;
case R.id.modifiedFileCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_modified_file));
intent.putExtra("directory", Settings.getRomDir(getActivity()));
startActivityForResult(intent, Action.SELECT_MODIFIED_FILE);
break;
case R.id.patchFileCardView:
renamePatchFile();
break;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(LOG_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (resultCode == Activity.RESULT_OK) {
String path = data.getStringExtra("path");
File fpath = new File(path);
switch (requestCode) {
case Action.SELECT_SOURCE_FILE:
sourcePath = path;
sourceNameTextView.setVisibility(View.VISIBLE);
sourceNameTextView.setText(fpath.getName());
Settings.setLastRomDir(getActivity(), fpath.getParent());
break;
case Action.SELECT_MODIFIED_FILE:
modifiedPath = path;
modifiedNameTextView.setVisibility(View.VISIBLE);
modifiedNameTextView.setText(fpath.getName());
Settings.setLastRomDir(getActivity(), fpath.getParent());
patchPath = makeOutputPath(path);
patchNameTextView.setText(new File(patchPath).getName());
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private String makeOutputPath(String fullname) {
String dir = Settings.getOutputDir(getActivity());
if (dir.equals("")) { // get ROM directory
dir = FilenameUtils.getFullPath(fullname);
}
String baseName = FilenameUtils.getBaseName(fullname);
return FilenameUtils.concat(dir, baseName.concat(".xdelta"));
}
public boolean runAction() {
if (sourcePath == null & modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_and_modified_not_selected), Toast.LENGTH_LONG).show();
return false;
} else if (sourcePath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_source_not_selected), Toast.LENGTH_LONG).show();
return false;
} else if (modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
return false;
}
Intent intent = new Intent(getActivity(), WorkerService.class);
intent.putExtra("action", Action.CREATE_PATCH);
intent.putExtra("sourcePath", sourcePath);
intent.putExtra("modifiedPath", modifiedPath);
intent.putExtra("patchPath", patchPath);
Utils.startForegroundService(getActivity(), intent);
Toast.makeText(getActivity(), R.string.toast_create_patch_started_check_notify, Toast.LENGTH_SHORT).show();
return true;
}
private void renamePatchFile() {
if (modifiedPath == null) {
Toast.makeText(getActivity(), getString(R.string.create_patch_fragment_toast_modified_not_selected), Toast.LENGTH_LONG).show();
return;
}
AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
dialog.setTitle(R.string.dialog_rename_title);
final EditText input = new EditText(getActivity());
input.setText(patchNameTextView.getText());
// add left and right margins to EditText.
FrameLayout container = new FrameLayout(getActivity());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int dp_24 = Utils.dpToPx(getActivity(), 24);
params.setMargins(dp_24, 0, dp_24, 0);
input.setLayoutParams(params);
container.addView(input);
dialog.setView(container);
dialog.setPositiveButton(R.string.dialog_rename_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String newName = input.getText().toString();
if (newName.equals("")) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_empty_name, Toast.LENGTH_LONG).show();
return;
}
if (newName.contains("/")) {
newName = newName.replaceAll("/", "_");
Toast.makeText(getActivity(), R.string.dialog_rename_error_invalid_chars, Toast.LENGTH_LONG).show();
}
String newPath = new File(patchPath).getParent().concat(File.separator).concat(newName);
if (FilenameUtils.equals(newPath, sourcePath) || FilenameUtils.equals(newPath, modifiedPath)) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_same_name, Toast.LENGTH_LONG).show();
return;
}
patchNameTextView.setText(newName);
patchPath = newPath;
}
});
dialog.setNegativeButton(R.string.dialog_rename_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
}
}

View file

@ -21,32 +21,23 @@ package org.emunix.unipatcher.ui.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.emunix.unipatcher.R;
import org.markdown4j.Markdown4jProcessor;
import org.sufficientlysecure.htmltextview.HtmlResImageGetter;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import java.io.IOException;
public class FaqFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_faq, container, false);
HtmlTextView faqText = (HtmlTextView) view.findViewById(R.id.faqText);
try {
String html = new Markdown4jProcessor().process(
getActivity().getResources().openRawResource(R.raw.faq));
faqText.setHtml(html, new HtmlResImageGetter(faqText));
} catch (IOException e) {
Log.e("UniPatcher", "IOException", e);
}
faqText.setHtml(R.raw.faq, new HtmlResImageGetter(faqText));
return view;
}

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2014, 2016, 2017 Boris Timofeev
Copyright (C) 2014, 2016 Boris Timofeev
This file is part of UniPatcher.
@ -36,10 +36,9 @@ import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.io.FilenameUtils;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.UniPatcher;
import org.emunix.unipatcher.Utils;
import org.emunix.unipatcher.WorkerService;
import org.emunix.unipatcher.ui.activity.FilePickerActivity;
@ -50,6 +49,8 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
private static final String LOG_TAG = "org.emunix.unipatcher";
private static final int SELECT_ROM_FILE = 1;
private static final int SELECT_PATCH_FILE = 2;
private TextView romNameTextView;
private TextView patchNameTextView;
private TextView outputNameTextView;
@ -57,8 +58,7 @@ 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 = UniPatcher.getAppArgument();
patchPath = Globals.getCmdArgument();
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, Action.SELECT_PATCH_FILE);
startActivityForResult(intent, 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, Action.SELECT_ROM_FILE);
startActivityForResult(intent, 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,59 +171,56 @@ public class PatchingFragment extends ActionFragment implements View.OnClickList
}
switch (requestCode) {
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;
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;
}
}
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 dir = FilenameUtils.getPath(fullname);
String baseName = FilenameUtils.getBaseName(fullname);
String ext = FilenameUtils.getExtension(fullname);
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", Action.APPLY_PATCH);
intent.putExtra("action", Globals.ACTION_PATCHING);
intent.putExtra("romPath", romPath);
intent.putExtra("patchPath", patchPath);
intent.putExtra("outputPath", outputPath);
Utils.startForegroundService(getActivity(), intent);
getActivity().startService(intent);
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify, Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity(), R.string.toast_patching_started_check_notify,Toast.LENGTH_SHORT).show();
return true;
}
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,21 +244,16 @@ 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();
if (newName.equals(romNameTextView.getText())) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_same_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(outputPath).getParent().concat(File.separator).concat(newName);
if (FilenameUtils.equals(newPath, romPath)) {
Toast.makeText(getActivity(), R.string.dialog_rename_error_same_name, Toast.LENGTH_LONG).show();
return;
}
outputNameTextView.setText(newName);
outputPath = newPath;
outputPath = new File(romPath).getParent().concat(File.separator).concat(newName);
}
});
dialog.setNegativeButton(R.string.dialog_rename_cancel, new DialogInterface.OnClickListener() {

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2016-2017 Boris Timofeev
Copyright (C) 2016 Boris Timofeev
This file is part of UniPatcher.
@ -36,7 +36,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals("theme") || key.equals("output_directory")) {
if (key.equals("theme")) {
Toast.makeText(getActivity(), R.string.settings_theme_message_restart_app, Toast.LENGTH_SHORT).show();
}
}

View file

@ -31,7 +31,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.Globals;
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, Action.SELECT_ROM_FILE);
startActivityForResult(intent, 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 Action.SELECT_ROM_FILE:
case 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);
intent.putExtra("action", Action.SMD_FIX_CHECKSUM);
Utils.startForegroundService(getActivity(), intent);
getActivity().startService(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;
}
}

View file

@ -31,7 +31,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import org.emunix.unipatcher.Action;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R;
import org.emunix.unipatcher.Settings;
import org.emunix.unipatcher.Utils;
@ -43,6 +43,8 @@ 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;
@ -53,8 +55,7 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
private int action = 0;
public SnesSmcHeaderFragment() {
}
public SnesSmcHeaderFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
@ -68,7 +69,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);
@ -98,14 +99,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 == Action.SNES_ADD_SMC_HEADER) {
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
headerInfoTextView.setText(R.string.snes_smc_header_will_be_added);
headerCardView.setVisibility(View.VISIBLE);
} else if (action == Action.SNES_DELETE_SMC_HEADER) {
} else if (action == Globals.ACTION_SNES_DELETE_SMC_HEADER) {
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
headerCardView.setVisibility(View.GONE);
}
@ -125,23 +126,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, Action.SELECT_ROM_FILE);
startActivityForResult(intent, SELECT_ROM_FILE);
break;
case R.id.headerCardView:
intent.putExtra("title", getString(R.string.file_picker_activity_title_select_header));
startActivityForResult(intent, Action.SELECT_HEADER_FILE);
startActivityForResult(intent, 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");
@ -151,25 +152,25 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
}
switch (requestCode) {
case Action.SELECT_ROM_FILE:
case 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 = Action.SNES_DELETE_SMC_HEADER;
action = Globals.ACTION_SNES_DELETE_SMC_HEADER;
headerCardView.setVisibility(View.GONE);
headerInfoTextView.setText(R.string.snes_smc_header_will_be_removed);
} else {
action = Action.SNES_ADD_SMC_HEADER;
action = Globals.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 Action.SELECT_HEADER_FILE:
case SELECT_HEADER_FILE:
headerPath = path;
headerNameTextView.setText(new File(path).getName());
break;
@ -178,8 +179,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;
}
@ -188,9 +189,9 @@ public class SnesSmcHeaderFragment extends ActionFragment implements View.OnClic
intent.putExtra("action", action);
intent.putExtra("romPath", romPath);
intent.putExtra("headerPath", headerPath);
Utils.startForegroundService(getActivity(), intent);
getActivity().startService(intent);
if (action == Action.SNES_ADD_SMC_HEADER) {
if (action == Globals.ACTION_SNES_ADD_SMC_HEADER) {
Toast.makeText(getActivity(), R.string.notify_snes_add_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.notify_snes_delete_smc_header_stared_check_noify, Toast.LENGTH_SHORT).show();

View file

@ -1,57 +0,0 @@
/*
Copyright (c) 2017 Boris Timofeev
This file is part of UniPatcher.
UniPatcher is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
UniPatcher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UniPatcher. If not, see <http://www.gnu.org/licenses/>.
*/
package org.emunix.unipatcher.ui.notify;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import org.emunix.unipatcher.R;
public class CreatePatchNotify extends Notify {
public CreatePatchNotify(Context c, String text) {
super(c);
notifyBuilder.setSmallIcon(R.drawable.ic_gamepad_variant_white_24dp);
notifyBuilder.setContentTitle(context.getString(R.string.notify_creating_patch));
notifyBuilder.setContentText(text);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
setSticked(true);
setProgress(true);
}
@Override
public void setCompleted() {
setProgress(false);
setSticked(false);
notifyBuilder.setTicker(context.getText(R.string.notify_create_patch_complete));
notifyBuilder.setContentTitle(context.getText(R.string.notify_create_patch_complete));
}
@Override
public void setFailed(String message) {
setProgress(false);
setSticked(false);
notifyBuilder.setTicker(context.getText(R.string.notify_error));
notifyBuilder.setContentTitle(context.getString(R.string.notify_error));
notifyBuilder.setContentText(message);
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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:toYDelta="0"
android:duration="500" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

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