2020-06-19 12:06:10 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020, Kiyoshi Aman.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of the <organization> nor the
|
|
|
|
* names of its contributors may be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
2020-06-15 06:11:12 +00:00
|
|
|
|
|
|
|
#include "exdeath.hh"
|
|
|
|
#include <cstring>
|
2020-06-16 02:31:16 +00:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstdio>
|
2020-06-15 06:11:12 +00:00
|
|
|
|
|
|
|
Exdeath::Exdeath(QWidget *parent) : QWidget(parent) {
|
|
|
|
error = new QErrorMessage();
|
|
|
|
filename = nullptr;
|
|
|
|
|
|
|
|
layMain = new QGridLayout(this);
|
|
|
|
layMode = new QVBoxLayout(this);
|
|
|
|
|
|
|
|
txtROM = new QLabel("ROM:");
|
|
|
|
txtMode = new QLabel("Mode:");
|
|
|
|
txtPortraits = new QLabel("FFT-style Portraits:");
|
|
|
|
txtAP = new QLabel("Double AP:");
|
|
|
|
txtSound = new QLabel("Sound Restoration:");
|
|
|
|
txtSound->setToolTip("Requires GBA BIOS if using VisualBoyAdvance");
|
|
|
|
|
|
|
|
btnROM = new QPushButton("Select ROM");
|
|
|
|
btnApply = new QPushButton("Apply");
|
|
|
|
|
|
|
|
radBase = new QRadioButton("Base");
|
|
|
|
radFiesta = new QRadioButton("Fiesta");
|
|
|
|
radBalance = new QRadioButton("Balance");
|
|
|
|
radBase->setChecked(true);
|
|
|
|
layMode->addWidget(radBase);
|
|
|
|
layMode->addWidget(radFiesta);
|
|
|
|
layMode->addWidget(radBalance);
|
|
|
|
|
|
|
|
chkPortraits = new QCheckBox("Yes");
|
|
|
|
chkAP = new QCheckBox("Yes");
|
|
|
|
chkSound = new QCheckBox("Yes");
|
|
|
|
|
|
|
|
layMain->addWidget(txtROM, 0, 0);
|
|
|
|
layMain->addWidget(btnROM, 0, 1);
|
|
|
|
layMain->addWidget(txtMode, 1, 0);
|
|
|
|
layMain->addLayout(layMode, 1, 1);
|
|
|
|
layMain->addWidget(txtPortraits, 2, 0);
|
|
|
|
layMain->addWidget(chkPortraits, 2, 1);
|
|
|
|
layMain->addWidget(txtAP, 3, 0);
|
|
|
|
layMain->addWidget(chkAP, 3, 1);
|
|
|
|
layMain->addWidget(txtSound, 4, 0);
|
|
|
|
layMain->addWidget(chkSound, 4, 1);
|
|
|
|
layMain->addWidget(btnApply, 5, 1);
|
|
|
|
|
|
|
|
connect(btnROM, &QPushButton::clicked, this, &Exdeath::btnROM_clicked);
|
|
|
|
connect(btnApply, &QPushButton::clicked, this, &Exdeath::btnApply_clicked);
|
|
|
|
}
|
|
|
|
|
|
|
|
Exdeath::~Exdeath() {}
|
|
|
|
|
|
|
|
void Exdeath::btnROM_clicked(bool trigger) {
|
|
|
|
filename = QFileDialog::getOpenFileName(
|
|
|
|
this,
|
|
|
|
"Select ROM image",
|
|
|
|
QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)[0],
|
|
|
|
"GBA ROM images (*.gba)"
|
|
|
|
);
|
|
|
|
QFile *target = new QFile(filename);
|
|
|
|
QCryptographicHash *md5 = new QCryptographicHash(QCryptographicHash::Md5);
|
|
|
|
target->open(QIODevice::ReadOnly);
|
|
|
|
if (md5->addData(target)) {
|
|
|
|
if (md5->result().toHex().compare("9ed82843cc54876362be56faccb15d75") != 0) {
|
|
|
|
error->showMessage("You must provide a Final Fantasy V Advance US image.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
target->close();
|
|
|
|
}
|
|
|
|
void Exdeath::btnApply_clicked(bool trigger) {
|
|
|
|
if (filename == nullptr) {
|
|
|
|
error->showMessage("You can't apply patches without a ROM.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QFile *target;
|
|
|
|
QStringList patches;
|
|
|
|
QString output = QFileDialog::getSaveFileName(
|
|
|
|
this,
|
|
|
|
"Select target ROM image",
|
|
|
|
QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)[0],
|
|
|
|
"GBA ROM images (*.gba)"
|
|
|
|
);
|
|
|
|
QFile::copy(filename, output);
|
|
|
|
|
|
|
|
if (radFiesta->isChecked()) {
|
|
|
|
patches << ":/patches/fiesta.ips";
|
|
|
|
} else if (radBalance->isChecked()) {
|
|
|
|
patches << ":/patches/balance.ips";
|
|
|
|
}
|
|
|
|
if (chkPortraits->isChecked()) {
|
|
|
|
patches << ":/patches/portraits.ips";
|
|
|
|
}
|
|
|
|
if (chkAP->isChecked()) {
|
|
|
|
patches << ":/patches/double_ap.ips";
|
|
|
|
}
|
|
|
|
if (chkSound->isChecked()) {
|
|
|
|
patches << ":/patches/sound_restoration.ips";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (patches.size() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
target = new QFile(output);
|
2020-06-18 05:46:54 +00:00
|
|
|
target->open(QIODevice::ReadWrite);
|
2020-06-15 06:11:12 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < patches.size(); i++) {
|
|
|
|
applyPatch(target, patches[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
target->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Exdeath::applyPatch(QFile *file, QString patch) {
|
|
|
|
QFile *data = new QFile(patch);
|
|
|
|
data->open(QIODevice::ReadOnly);
|
|
|
|
|
2020-06-18 05:46:54 +00:00
|
|
|
// skipping header
|
|
|
|
data->seek(5);
|
2020-06-15 06:11:12 +00:00
|
|
|
|
|
|
|
while (!data->atEnd()) {
|
2020-06-18 05:46:54 +00:00
|
|
|
unsigned int seek = 0;
|
|
|
|
unsigned short length = 0;
|
|
|
|
char *temp = reinterpret_cast<char *>(malloc(65536));
|
|
|
|
|
|
|
|
data->read(temp, 3);
|
2020-06-15 06:11:12 +00:00
|
|
|
if (!strncmp(temp, "EOF", 3)) {
|
|
|
|
break;
|
|
|
|
}
|
2020-06-18 05:46:54 +00:00
|
|
|
|
2020-06-16 04:23:48 +00:00
|
|
|
seek = ((temp[0] & 0xff) << 16) + ((temp[1] & 0xff) << 8) + (temp[2] & 0xff);
|
2020-06-18 05:46:54 +00:00
|
|
|
file->seek(seek);
|
2020-06-15 06:11:12 +00:00
|
|
|
data->read(temp, 2);
|
2020-06-16 04:23:48 +00:00
|
|
|
length = ((temp[0] & 0xff) << 8) + (temp[1] & 0xff);
|
2020-06-15 06:11:12 +00:00
|
|
|
|
|
|
|
if (length == 0) {
|
|
|
|
// RLE hunk
|
|
|
|
data->read(temp, 2);
|
2020-06-16 04:23:48 +00:00
|
|
|
length = ((temp[0] & 0xff) << 8) + (temp[1] & 0xff);
|
2020-06-15 06:11:12 +00:00
|
|
|
data->read(temp, 1);
|
|
|
|
|
2020-06-16 02:31:16 +00:00
|
|
|
for (unsigned int i = 0; i < length; i++) {
|
2020-06-15 06:11:12 +00:00
|
|
|
file->write(temp, 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
data->read(temp, length);
|
|
|
|
file->write(temp, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-16 02:31:16 +00:00
|
|
|
file->flush();
|
2020-06-15 06:11:12 +00:00
|
|
|
data->close();
|
|
|
|
}
|