Support creating XDelta3 patches

This commit is contained in:
Boris Timofeev 2017-03-21 16:04:49 +03:00
parent a7228702fe
commit 7dad097c6f
35 changed files with 701 additions and 26 deletions

View file

@ -8,6 +8,7 @@ UniPatcher is a ROM patcher for Android that supports IPS, IPS32, UPS, BPS, APS
### Additional features:
* Creating XDelta3 patches
* Fix checksum in Sega Mega Drive ROMs
* Add/Delete SMC header in Super Nintendo ROMs

View file

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

View file

@ -31,7 +31,8 @@ public class Globals {
}
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;
public static final int ACTION_CREATE_PATCH = 2;
public static final int ACTION_SMD_FIX_CHECKSUM = 3;
public static final int ACTION_SNES_ADD_SMC_HEADER = 4;
public static final int ACTION_SNES_DELETE_SMC_HEADER = 5;
}

View file

@ -43,6 +43,7 @@ import org.emunix.unipatcher.tools.RomException;
import org.emunix.unipatcher.tools.SmdFixChecksum;
import org.emunix.unipatcher.tools.SnesSmcHeader;
import org.emunix.unipatcher.ui.activity.MainActivity;
import org.emunix.unipatcher.ui.notify.CreatePatchNotify;
import org.emunix.unipatcher.ui.notify.Notify;
import org.emunix.unipatcher.ui.notify.PatchingNotify;
import org.emunix.unipatcher.ui.notify.SmdFixChecksumNotify;
@ -77,6 +78,9 @@ public class WorkerService extends IntentService {
case Globals.ACTION_PATCHING:
actionPatching(intent);
break;
case Globals.ACTION_CREATE_PATCH:
actionCreatePatch(intent);
break;
case Globals.ACTION_SMD_FIX_CHECKSUM:
actionSmdFixChecksum(intent);
break;
@ -172,6 +176,60 @@ public class WorkerService extends IntentService {
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();
} catch (PatchException | IOException e) {
if (Utils.getFreeSpace(patchFile.getParentFile()) == 0) {
errorMsg = getString(R.string.notify_error_not_enough_space);
} else {
errorMsg = e.getMessage();
}
FileUtils.deleteQuietly(patchFile);
} finally {
stopForeground(true);
}
notify.showResult(errorMsg);
}
private void actionSmdFixChecksum(Intent intent) {
String errorMsg = null;
File romFile = new File(intent.getStringExtra("romPath"));

View file

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

View file

@ -43,6 +43,7 @@ import org.emunix.unipatcher.BuildConfig;
import org.emunix.unipatcher.Globals;
import org.emunix.unipatcher.R;
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;
@ -116,10 +117,12 @@ public class MainActivity extends AppCompatActivity
if (id == R.id.nav_apply_patch) {
selectDrawerItem(0);
} else if (id == R.id.nav_smd_fix_checksum) {
} else if (id == R.id.nav_create_patch) {
selectDrawerItem(1);
} else if (id == R.id.nav_snes_add_del_smc_header) {
} else if (id == R.id.nav_smd_fix_checksum) {
selectDrawerItem(2);
} else if (id == R.id.nav_snes_add_del_smc_header) {
selectDrawerItem(3);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
@ -148,9 +151,12 @@ public class MainActivity extends AppCompatActivity
Fragment fragment;
switch (position) {
case 1:
fragment = new SmdFixChecksumFragment();
fragment = new CreatePatchFragment();
break;
case 2:
fragment = new SmdFixChecksumFragment();
break;
case 3:
fragment = new SnesSmcHeaderFragment();
break;
default:

View file

@ -0,0 +1,257 @@
/*
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.Globals;
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 static final int SELECT_SOURCE_FILE = 1;
private static final int SELECT_MODIFIED_FILE = 2;
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, 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, 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 SELECT_SOURCE_FILE:
sourcePath = path;
sourceNameTextView.setVisibility(View.VISIBLE);
sourceNameTextView.setText(fpath.getName());
Settings.setLastRomDir(getActivity(), fpath.getParent());
break;
case 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", Globals.ACTION_CREATE_PATCH);
intent.putExtra("sourcePath", sourcePath);
intent.putExtra("modifiedPath", modifiedPath);
intent.putExtra("patchPath", patchPath);
getActivity().startService(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.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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true">
<LinearLayout
style="@style/CardsHolder">
<android.support.v7.widget.CardView
android:id="@+id/sourceFileCardView"
style="@style/Card.Clickable">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/card_padding">
<TextView
android:id="@+id/sourceFileLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/create_patch_fragment_source_file"
android:textColor="@color/colorCardHeaderText"
android:textSize="24sp"/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/card_line_height"
android:layout_marginBottom="@dimen/card_line_margin"
android:layout_marginTop="@dimen/card_line_margin"
android:background="@color/colorCardLine"/>
<TextView
android:id="@+id/sourceFileNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_grey600_24dp"
android:drawablePadding="8dp"
android:padding="8dp"
android:text="@string/main_activity_tap_to_select"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/modifiedFileCardView"
style="@style/Card.Clickable">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/card_padding">
<TextView
android:id="@+id/modifiedFileLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/create_patch_fragment_modified_file"
android:textColor="@color/colorCardHeaderText"
android:textSize="24sp"/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/card_line_height"
android:layout_marginBottom="@dimen/card_line_margin"
android:layout_marginTop="@dimen/card_line_margin"
android:background="@color/colorCardLine"/>
<TextView
android:id="@+id/modifiedFileNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_grey600_24dp"
android:drawablePadding="8dp"
android:padding="8dp"
android:text="@string/main_activity_tap_to_select"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/patchFileCardView"
style="@style/Card.Clickable">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/card_padding">
<TextView
android:id="@+id/patchFileLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/create_patch_fragment_patch_file"
android:textColor="@color/colorCardHeaderText"
android:textSize="24sp"/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/card_line_height"
android:layout_marginBottom="@dimen/card_line_margin"
android:layout_marginTop="@dimen/card_line_margin"
android:background="@color/colorCardLine"/>
<TextView
android:id="@+id/patchFileNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_edit_grey600_24dp"
android:drawablePadding="8dp"
android:padding="8dp"
android:text="@string/main_activity_tap_to_rename"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>

View file

@ -8,6 +8,10 @@
android:id="@+id/nav_apply_patch"
android:icon="@drawable/ic_healing_grey600_24dp"
android:title="@string/nav_apply_patch"/>
<item
android:id="@+id/nav_create_patch"
android:icon="@drawable/ic_plus_box_grey600_24dp"
android:title="@string/nav_create_patch"/>
<item
android:id="@+id/nav_smd_fix_checksum"
android:icon="@drawable/ic_fingerprint_grey600_24dp"

View file

@ -2,6 +2,7 @@ UniPatcher è un patcher che supporta i tipi di patch IPS, IPS32, UPS, BPS, APS
Funzioni aggiuntive:
- Creating XDelta3 patches
- Risolvi checksum nei ROM del Sega Mega Drive
- Aggiungi/Elimina intestazione SMC nei ROM del Super Nintendo

View file

@ -75,6 +75,7 @@ Forse c'è un bug nel mio programma. Ti prego di contattarmi alla [e-mail](mailt
Si. UniPatcher può:
- create XDelta3 patches.
- Risolvere checksum per un gioco Sega Mega Drive / Sega Genesis.
- aggiungere o rimuovere l'intestazione SMC per un gioco Super Nintendo.

View file

@ -2,6 +2,7 @@ UniPatcher jest programem do łatkowania ROM-ów. Wsparcie dla typów łatek: IP
Dodatkowe funkcje:
- Creating XDelta3 patches
- Napraw sumę kontrolną dla ROM-ów Sega Mega Drive
- Dodaj/Usuń nagłówek dla Super Nintendo

View file

@ -75,6 +75,7 @@ Może jest to bug w moim programie. Proszę skontaktuj się ze mną przez [e-mai
Tak. UniPatcher może:
- create XDelta3 patches.
- naprawiać sumę kontrolną w grach z konsol Sega Mega Drive/Sega Genesis
- Dodawać bądź usuwać nagłówek SMC dla gier z konsoli Super Nintendo Entertaiment System (SNES).

View file

@ -2,6 +2,7 @@ UniPatcher это ROM патчер поддерживающий патчи в ф
Дополнительные функции:
- Создание патчей в формате XDelta3
- Исправление контрольной суммы для игр Sega Mega Drive
- Добавление или удалениие SMC заголовка для игр Super Nintendo

View file

@ -75,6 +75,7 @@ ECM это формат сжатия данных созданный специ
Да. Приложение может:
- создавать патчи в формате XDelta3.
- исправлять контрольную сумму для игр Sega Mega Drive.
- добавлять или удалять SMC заголовки для игр Super Nintendo.

View file

@ -2,6 +2,7 @@ UniPatcher це ROM патчер який підтримує патчі у фо
Додаткові функції:
- Creating XDelta3 patches
- Виправлення контрольної суми для ігор Sega Mega Drive
- Додавання або видаляння SMC заголовку для ігор Super Nintendo

View file

@ -75,6 +75,7 @@ ECM це формат стискання даних створений спец
Так. Додаток може:
- create XDelta3 patches.
- виправляти контрольну суму для ігор Sega Mega Drive.
- додавати або видаляти SMC назви для ігор Super Nintendo.

View file

@ -2,6 +2,7 @@ UniPatcher is a ROM patcher that supports IPS, IPS32, UPS, BPS, APS (GBA), APS (
Additional features:
- Creating XDelta3 patches
- Fix checksum in Sega Mega Drive ROMs
- Add/Delete SMC header in Super Nintendo ROMs

View file

@ -75,6 +75,7 @@ Maybe it's a bug in my program. Please contact me at [e-mail](mailto:mashin87@gm
Yes. UniPatcher can:
- create XDelta3 patches.
- fix checksum for a Sega Mega Drive / Sega Genesis games.
- add or remove SMC header for a Super Nintendo games.

View file

@ -21,9 +21,17 @@
<string name="dialog_rename_title">Rinomina</string>
<string name="dialog_rename_ok">OK</string>
<string name="dialog_rename_cancel">Cancella</string>
<string name="dialog_rename_error_same_name">I nomi di input e output della ROM dovrebbero essere differenti</string>
<string name="dialog_rename_error_same_name">The input and output file names must be different</string>
<string name="dialog_rename_error_invalid_chars">Simbolo non valido /</string>
<!-- Create patch fragment -->
<string name="create_patch_fragment_source_file">Source file</string>
<string name="create_patch_fragment_modified_file">Modified file</string>
<string name="create_patch_fragment_patch_file">Patch file</string>
<string name="create_patch_fragment_toast_source_and_modified_not_selected">The source and modified files is not selected</string>
<string name="create_patch_fragment_toast_source_not_selected">The source file is not selected</string>
<string name="create_patch_fragment_toast_modified_not_selected">The modified file is not selected</string>
<!-- Info for SNES SMC header -->
<string name="snes_smc_header_help">Questa caratteristica è solo per le ROM Super Nintendo.\\n\\nlf la ROM contiene l\'intestazione SMC - che verrà rimossa. Altrimenti, verrà aggiunta.\\n\\nAttenzione: questa funzione non crea un backup.</string>
<string name="snes_smc_header_will_be_removed">Questa ROM ha l\'intestazione SMC. Verrà rimossa.</string>
@ -36,6 +44,8 @@
<string name="file_picker_activity_title">Seleziona file</string>
<string name="file_picker_activity_title_select_rom">Seleziona il file ROM</string>
<string name="file_picker_activity_title_select_patch">Seleziona il file di patch</string>
<string name="file_picker_activity_title_select_source_file">Select the source file</string>
<string name="file_picker_activity_title_select_modified_file">Select the modified file</string>
<string name="file_picker_activity_title_select_header">Scegli il file di intestazione</string>
<string name="file_picker_activity_error_unable_read_dir">Impossibile leggere la cartella %1$s</string>
@ -54,7 +64,10 @@
<!-- Notifications -->
<string name="notify_applying_patch">Applicando la patch</string>
<string name="notify_patching_complete">Patch completato</string>
<string name="notify_creating_patch">Creating patch</string>
<string name="notify_create_patch_complete">Patch creation completed</string>
<string name="toast_patching_started_check_notify">Patching iniziato. Controlla l\'area di notifica</string>
<string name="toast_create_patch_started_check_notify">Creating patch started. Check the notification area</string>
<string name="notify_error">Errore</string>
<string name="notify_error_could_not_copy_file">Non si può copiare il file</string>
@ -138,6 +151,7 @@
<!-- Navigation list -->
<string name="nav_apply_patch">Applica patch</string>
<string name="nav_create_patch">Create patch</string>
<string name="nav_smd_fix_checksum">Risolvi checksum (SMD)</string>
<string name="nav_snes_add_del_smc_header">Aggiungi/Cancella intestazione SMC (SNES)</string>
<string name="nav_settings">Impostazioni</string>

View file

@ -21,9 +21,17 @@
<string name="dialog_rename_title">Zmień Nazwę</string>
<string name="dialog_rename_ok">OK</string>
<string name="dialog_rename_cancel">Anuluj</string>
<string name="dialog_rename_error_same_name">Wejściowe i wyjściowe nazwy ROM-ów muszą być różne</string>
<string name="dialog_rename_error_same_name">The input and output file names must be different</string>
<string name="dialog_rename_error_invalid_chars">Niewłaściwy symbol /</string>
<!-- Create patch fragment -->
<string name="create_patch_fragment_source_file">Source file</string>
<string name="create_patch_fragment_modified_file">Modified file</string>
<string name="create_patch_fragment_patch_file">Patch file</string>
<string name="create_patch_fragment_toast_source_and_modified_not_selected">The source and modified files is not selected</string>
<string name="create_patch_fragment_toast_source_not_selected">The source file is not selected</string>
<string name="create_patch_fragment_toast_modified_not_selected">The modified file is not selected</string>
<!-- Info for SNES SMC header -->
<string name="snes_smc_header_help">Ta opcja jest tylko dla ROM-ów Super Nintendo.\n\nJeżeli ROM zawiera nagłówek SMC - zostanie on usunięty. Inaczej zostanie on dodany.\n\nUwaga: ta funkcja nie tworzy kopii zapasowej.</string>
<string name="snes_smc_header_will_be_removed">Ten ROM zawiera nagłówek SMC. Zostanie on usunięty.</string>
@ -36,6 +44,8 @@
<string name="file_picker_activity_title">Wybierz plik</string>
<string name="file_picker_activity_title_select_rom">Wybierz plik ROM</string>
<string name="file_picker_activity_title_select_patch">Wybier plik Łatki</string>
<string name="file_picker_activity_title_select_source_file">Select the source file</string>
<string name="file_picker_activity_title_select_modified_file">Select the modified file</string>
<string name="file_picker_activity_title_select_header">Wybierz plik nagłówka</string>
<string name="file_picker_activity_error_unable_read_dir">Błędna ścieżka %1$s</string>
@ -54,7 +64,10 @@
<!-- Notifications -->
<string name="notify_applying_patch">Stosowanie łatki</string>
<string name="notify_patching_complete">Łatkowanie ukończone</string>
<string name="notify_creating_patch">Creating patch</string>
<string name="notify_create_patch_complete">Patch creation completed</string>
<string name="toast_patching_started_check_notify">Łatkowanie rozpoczęte. Sprawdzaj pasek powiadomień.</string>
<string name="toast_create_patch_started_check_notify">Creating patch started. Check the notification area</string>
<string name="notify_error">Błąd</string>
<string name="notify_error_could_not_copy_file">Nie można skopiować pliku</string>
@ -138,6 +151,7 @@
<!-- Navigation list -->
<string name="nav_apply_patch">Zastosuj łatkę</string>
<string name="nav_create_patch">Create patch</string>
<string name="nav_smd_fix_checksum">Napraw sumę kontrolną (SMD)</string>
<string name="nav_snes_add_del_smc_header">Dodaj/Usuń nagłówek SMC (SNES)</string>
<string name="nav_settings">Ustawienia</string>

View file

@ -21,9 +21,17 @@
<string name="dialog_rename_title">Переименовать</string>
<string name="dialog_rename_ok">Переименовать</string>
<string name="dialog_rename_cancel">Отмена</string>
<string name="dialog_rename_error_same_name">Имена ROM файлов должны отличаться</string>
<string name="dialog_rename_error_same_name">Имена файлов должны отличаться</string>
<string name="dialog_rename_error_invalid_chars">Некорректный символ /</string>
<!-- Create patch fragment -->
<string name="create_patch_fragment_source_file">Исходный файл</string>
<string name="create_patch_fragment_modified_file">Изменённый файл</string>
<string name="create_patch_fragment_patch_file">Патч</string>
<string name="create_patch_fragment_toast_source_and_modified_not_selected">Сначала выберите исходный и изменённый файлы</string>
<string name="create_patch_fragment_toast_source_not_selected">Сначала выберите исходный файл</string>
<string name="create_patch_fragment_toast_modified_not_selected">Сначала выберите изменённый файл</string>
<!-- Info for SNES SMC header -->
<string name="snes_smc_header_help">Эта функция только для ROM\'ов Super Nintendo.\n\nЕсли ROM содержит SMC заголовок - он будет удалён. Иначе он будет добавлен.\n\nПредупреждение: эта функция не создаёт резервную копию.</string>
<string name="snes_smc_header_will_be_removed">У этого ROM\'а есть SMC заголовок. Он будет удалён.</string>
@ -36,6 +44,8 @@
<string name="file_picker_activity_title">Выберите файл</string>
<string name="file_picker_activity_title_select_rom">Выберите ROM файл</string>
<string name="file_picker_activity_title_select_patch">Выберите патч</string>
<string name="file_picker_activity_title_select_source_file">Выберите исходный файл</string>
<string name="file_picker_activity_title_select_modified_file">Выберите изменённый файл</string>
<string name="file_picker_activity_title_select_header">Выберите файл заголовка</string>
<string name="file_picker_activity_error_unable_read_dir">Не удалось открыть директорию %1$s</string>
@ -54,7 +64,10 @@
<!-- Notifications -->
<string name="notify_applying_patch">Применяю патч</string>
<string name="notify_patching_complete">Применение патча завершено</string>
<string name="notify_creating_patch">Создаю патч</string>
<string name="notify_create_patch_complete">Создание патча завершено</string>
<string name="toast_patching_started_check_notify">Применяю патч. Проверьте область уведомлений</string>
<string name="toast_create_patch_started_check_notify">Создаю патч. Проверьте область уведомлений</string>
<string name="notify_error">Ошибка</string>
<string name="notify_error_could_not_copy_file">Не удалось скопировать файл</string>
@ -138,6 +151,7 @@
<!-- Navigation list -->
<string name="nav_apply_patch">Применить патч</string>
<string name="nav_create_patch">Создать патч</string>
<string name="nav_smd_fix_checksum">Испр. контр. сумму (SMD)</string>
<string name="nav_snes_add_del_smc_header">SMC заголовок (SNES)</string>
<string name="nav_settings">Настройки</string>

View file

@ -21,9 +21,17 @@
<string name="dialog_rename_title">Перейменувати</string>
<string name="dialog_rename_ok">OK</string>
<string name="dialog_rename_cancel">Скасувати</string>
<string name="dialog_rename_error_same_name">Назви вхідного та вихідного ROM\'у не повинні відрізнятись</string>
<string name="dialog_rename_error_same_name">The input and output file names must be different</string>
<string name="dialog_rename_error_invalid_chars">Недійсний символ /</string>
<!-- Create patch fragment -->
<string name="create_patch_fragment_source_file">Source file</string>
<string name="create_patch_fragment_modified_file">Modified file</string>
<string name="create_patch_fragment_patch_file">Patch file</string>
<string name="create_patch_fragment_toast_source_and_modified_not_selected">The source and modified files is not selected</string>
<string name="create_patch_fragment_toast_source_not_selected">The source file is not selected</string>
<string name="create_patch_fragment_toast_modified_not_selected">The modified file is not selected</string>
<!-- Info for SNES SMC header -->
<string name="snes_smc_header_help">Ця функція доступна тільки для Super Nintendo ROM\'ів.\n\nIf ROM містить SMC заголовок - його буде перезаписано. При його відсутності, він буде доданий.\n\nУвага: ця функція не створює резервної копії.</string>
<string name="snes_smc_header_will_be_removed">Цей ROM має SMC заголовок. Його буде перезаписано.</string>
@ -36,6 +44,8 @@
<string name="file_picker_activity_title">Обрати файл</string>
<string name="file_picker_activity_title_select_rom">Обрати ROM файл</string>
<string name="file_picker_activity_title_select_patch">Обрати Патч файл</string>
<string name="file_picker_activity_title_select_source_file">Select the source file</string>
<string name="file_picker_activity_title_select_modified_file">Select the modified file</string>
<string name="file_picker_activity_title_select_header">Обрати файл заголовок</string>
<string name="file_picker_activity_error_unable_read_dir">Неможливо прочитати папку %1$s</string>
@ -54,7 +64,10 @@
<!-- Notifications -->
<string name="notify_applying_patch">Застосування патчу</string>
<string name="notify_patching_complete">Патчинг завершено</string>
<string name="notify_creating_patch">Creating patch</string>
<string name="notify_create_patch_complete">Patch creation completed</string>
<string name="toast_patching_started_check_notify">Патчинг почався. Перевірте область повідомлень</string>
<string name="toast_create_patch_started_check_notify">Creating patch started. Check the notification area</string>
<string name="notify_error">Помилка</string>
<string name="notify_error_could_not_copy_file">Не вдалося скопіювати файл</string>
@ -138,6 +151,7 @@
<!-- Navigation list -->
<string name="nav_apply_patch">Застосувати Патч</string>
<string name="nav_create_patch">Create patch</string>
<string name="nav_smd_fix_checksum">Виправити контрольну суму (SMD)</string>
<string name="nav_snes_add_del_smc_header">Додати/Видалити SMC заголовок (SNES)</string>
<string name="nav_settings">Налаштування</string>

View file

@ -21,9 +21,17 @@
<string name="dialog_rename_title">Rename</string>
<string name="dialog_rename_ok">OK</string>
<string name="dialog_rename_cancel">Cancel</string>
<string name="dialog_rename_error_same_name">The input and output ROM names must be different</string>
<string name="dialog_rename_error_same_name">The input and output file names must be different</string>
<string name="dialog_rename_error_invalid_chars">Invalid symbol /</string>
<!-- Create patch fragment -->
<string name="create_patch_fragment_source_file">Source file</string>
<string name="create_patch_fragment_modified_file">Modified file</string>
<string name="create_patch_fragment_patch_file">Patch file</string>
<string name="create_patch_fragment_toast_source_and_modified_not_selected">The source and modified files is not selected</string>
<string name="create_patch_fragment_toast_source_not_selected">The source file is not selected</string>
<string name="create_patch_fragment_toast_modified_not_selected">The modified file is not selected</string>
<!-- Info for SNES SMC header -->
<string name="snes_smc_header_help">This feature is only for Super Nintendo ROMs.\n\nIf the ROM contains the SMC header - it will be removed. Otherwise, it will be added.\n\nWarning: this function does not create a backup.</string>
<string name="snes_smc_header_will_be_removed">This ROM has the SMC header. It will be removed.</string>
@ -36,6 +44,8 @@
<string name="file_picker_activity_title">Select file</string>
<string name="file_picker_activity_title_select_rom">Select the ROM file</string>
<string name="file_picker_activity_title_select_patch">Select the patch file</string>
<string name="file_picker_activity_title_select_source_file">Select the source file</string>
<string name="file_picker_activity_title_select_modified_file">Select the modified file</string>
<string name="file_picker_activity_title_select_header">Select the header file</string>
<string name="file_picker_activity_error_unable_read_dir">Unable to read directory %1$s</string>
@ -54,7 +64,10 @@
<!-- Notifications -->
<string name="notify_applying_patch">Applying patch</string>
<string name="notify_patching_complete">Patching complete</string>
<string name="notify_creating_patch">Creating patch</string>
<string name="notify_create_patch_complete">Patch creation completed</string>
<string name="toast_patching_started_check_notify">Patching started. Check the notification area</string>
<string name="toast_create_patch_started_check_notify">Creating patch started. Check the notification area</string>
<string name="notify_error">Error</string>
<string name="notify_error_could_not_copy_file">Could not copy file</string>
@ -138,6 +151,7 @@
<!-- Navigation list -->
<string name="nav_apply_patch">Apply patch</string>
<string name="nav_create_patch">Create patch</string>
<string name="nav_smd_fix_checksum">Fix checksum (SMD)</string>
<string name="nav_snes_add_del_smc_header">Add/Del SMC header (SNES)</string>
<string name="nav_settings">Settings</string>

View file

@ -8,6 +8,7 @@ Utility to apply patches to game rom.
<b>Features:</b>
&bull; Supported formats of patches: IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP, XDelta 3
&bull; Creating XDelta patches
&bull; Fix checksum in Sega Mega Drive ROMs
&bull; Add/Delete SMC header in Super Nintendo ROMs
&bull; No ads

View file

@ -8,6 +8,7 @@ Utilità per applicare le patch alle ROM dei giochi.
<b>Features:</b>
&bull; Supported formats of patches: IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP, XDelta 3
&bull; Creating XDelta patches
&bull; Risolvi checksum nei ROM del Sega Mega Drive
&bull; Aggiungi/Elimina intestazione SMC nei ROM del Super Nintendo
&bull; No ads

View file

@ -8,6 +8,7 @@ Narzędzie do aplikowania łatek do ROM-ów
<b>Features:</b>
&bull; Supported formats of patches: IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP, XDelta 3
&bull; Creating XDelta patches
&bull; Napraw sumę kontrolną dla ROM-ów Segs Mega Drive
&bull; Dodaj/Usuń nagłówek SMC dla ROM-ów Super Nintendo
&bull; No ads

View file

@ -8,6 +8,7 @@
<b>Особенности:</b>
&bull; Поддержка множества форматов патчей: IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP, XDelta 3
&bull; Создание патчей в формате XDelta
&bull; Исправление контрольной суммы для игр Sega Mega Drive
&bull; Удаление/добавление SMC заголовка для игр Super Nintendo
&bull; Не имеет рекламы

View file

@ -8,6 +8,7 @@
<b>Features:</b>
&bull; Supported formats of patches: IPS, IPS32, UPS, BPS, APS (GBA), APS (N64), PPF, DPS, EBP, XDelta 3
&bull; Creating XDelta patches
&bull; Виправлення контрольної суми для ігор Sega Mega Drive
&bull; Додавання або Видаляння SMC заголовку для ігор Super Nintendo
&bull; No ads