mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 05:32:47 +00:00
- Removed trailing whitespace from all source code files (.c, .h. and .s) and the README.md
230 lines
7.4 KiB
C
230 lines
7.4 KiB
C
// C port of Alcaro's libips.cpp, which was released under GPLv3
|
|
// https://github.com/Alcaro/Flips/blob/master/libips.cpp
|
|
// Ported by Hyarion for use with VirtualFatFS
|
|
|
|
#include "ips.h"
|
|
#include "common.h"
|
|
#include "fsperm.h"
|
|
#include "ui.h"
|
|
#include "vff.h"
|
|
|
|
typedef enum {
|
|
IPS_OK,
|
|
IPS_NOTTHIS,
|
|
IPS_THISOUT,
|
|
IPS_SCRAMBLED,
|
|
IPS_INVALID,
|
|
IPS_16MB,
|
|
IPS_INVALID_FILE_PATH,
|
|
IPS_CANCELED,
|
|
IPS_MEMORY
|
|
} IPSERROR;
|
|
|
|
static FIL patchFile, inFile, outFile;
|
|
static size_t patchSize;
|
|
static u8 *patch;
|
|
static u32 patchOffset;
|
|
|
|
char errName[256];
|
|
|
|
int displayError(int errcode) {
|
|
switch(errcode) {
|
|
case IPS_NOTTHIS:
|
|
ShowPrompt(false, "%s\nThe patch is most likely not intended for this file.", errName); break;
|
|
case IPS_THISOUT:
|
|
ShowPrompt(false, "%s\nYou most likely applied the patch on the output file.", errName); break;
|
|
case IPS_SCRAMBLED:
|
|
ShowPrompt(false, "%s\nThe patch is technically valid,\nbut seems scrambled or malformed.", errName); break;
|
|
case IPS_INVALID:
|
|
ShowPrompt(false, "%s\nThe patch is invalid.", errName); break;
|
|
case IPS_16MB:
|
|
ShowPrompt(false, "%s\nOne or both files is bigger than 16MB.\nThe IPS format doesn't support that.", errName); break;
|
|
case IPS_INVALID_FILE_PATH:
|
|
ShowPrompt(false, "%s\nThe requested file path was invalid.", errName); break;
|
|
case IPS_CANCELED:
|
|
ShowPrompt(false, "%s\nPatching canceled.", errName); break;
|
|
case IPS_MEMORY:
|
|
ShowPrompt(false, "%s\nNot enough memory.", errName); break;
|
|
}
|
|
fvx_close(&patchFile);
|
|
fvx_close(&inFile);
|
|
fvx_close(&outFile);
|
|
return errcode;
|
|
}
|
|
|
|
typedef enum {
|
|
COPY_IN,
|
|
COPY_PATCH,
|
|
COPY_RLE
|
|
} COPYMODE;
|
|
|
|
bool IPScopy(u8 mode, u32 size, u8 rle) {
|
|
bool ret = true;
|
|
if (mode == COPY_PATCH) {
|
|
UINT bytes_written = size;
|
|
if ((fvx_write(&outFile, &patch[patchOffset], size, &bytes_written) != FR_OK) ||
|
|
(size != bytes_written))
|
|
ret = false;
|
|
patchOffset += size;
|
|
} else {
|
|
u32 bufsiz = min(STD_BUFFER_SIZE, size);
|
|
u8* buffer = malloc(bufsiz);
|
|
if (!buffer) return false;
|
|
if (mode == COPY_RLE) memset(buffer, rle, bufsiz);
|
|
|
|
for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) {
|
|
UINT read_bytes = min(bufsiz, size - pos);
|
|
UINT bytes_written = read_bytes;
|
|
if (((mode == COPY_IN) && (fvx_read(&inFile, buffer, read_bytes, &bytes_written) != FR_OK)) ||
|
|
((mode == COPY_PATCH) && (fvx_read(&patchFile, buffer, read_bytes, &bytes_written) != FR_OK)) ||
|
|
(read_bytes != bytes_written))
|
|
ret = false;
|
|
if ((ret && (fvx_write(&outFile, buffer, read_bytes, &bytes_written) != FR_OK)) ||
|
|
(read_bytes != bytes_written))
|
|
ret = false;
|
|
}
|
|
|
|
free(buffer);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u8 read8() {
|
|
if (patchOffset >= patchSize) return 0;
|
|
return patch[patchOffset++];
|
|
}
|
|
|
|
UINT read16() {
|
|
if (patchOffset+1 >= patchSize) return 0;
|
|
UINT buf = patch[patchOffset++] << 8;
|
|
buf |= patch[patchOffset++];
|
|
return buf;
|
|
}
|
|
|
|
UINT read24() {
|
|
if (patchOffset+2 >= patchSize) return 0;
|
|
UINT buf = patch[patchOffset++] << 16;
|
|
buf |= patch[patchOffset++] << 8;
|
|
buf |= patch[patchOffset++];
|
|
return buf;
|
|
}
|
|
|
|
int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName) {
|
|
int error = IPS_INVALID;
|
|
UINT outlen_min, outlen_max, outlen_min_mem;
|
|
snprintf(errName, 256, "%s", patchName);
|
|
|
|
if (fvx_open(&patchFile, patchName, FA_READ) != FR_OK) return displayError(IPS_INVALID_FILE_PATH);
|
|
patchSize = fvx_size(&patchFile);
|
|
ShowProgress(0, patchSize, patchName);
|
|
|
|
patch = malloc(patchSize);
|
|
if (!patch || fvx_read(&patchFile, patch, patchSize, NULL) != FR_OK) return displayError(IPS_MEMORY);
|
|
|
|
// Check validity of patch
|
|
if (patchSize < 8) return displayError(IPS_INVALID);
|
|
if (read8() != 'P' ||
|
|
read8() != 'A' ||
|
|
read8() != 'T' ||
|
|
read8() != 'C' ||
|
|
read8() != 'H')
|
|
{
|
|
return displayError(IPS_INVALID);
|
|
}
|
|
|
|
unsigned int offset = read24();
|
|
unsigned int outlen = 0;
|
|
unsigned int thisout = 0;
|
|
unsigned int lastoffset = 0;
|
|
bool w_scrambled = false;
|
|
while (offset != 0x454F46) // 454F46=EOF
|
|
{
|
|
if (!ShowProgress(patchOffset, patchSize, patchName)) {
|
|
if (ShowPrompt(true, "%s\nB button detected. Cancel?", patchName)) return displayError(IPS_CANCELED);
|
|
ShowProgress(0, patchSize, patchName);
|
|
ShowProgress(patchOffset, patchSize, patchName);
|
|
}
|
|
|
|
unsigned int size = read16();
|
|
if (size == 0)
|
|
{
|
|
size = read16();
|
|
if (!size) return displayError(IPS_INVALID);
|
|
thisout = offset + size;
|
|
read8();
|
|
}
|
|
else
|
|
{
|
|
thisout = offset + size;
|
|
patchOffset += size;
|
|
}
|
|
if (offset < lastoffset) w_scrambled = true;
|
|
lastoffset = offset;
|
|
if (thisout > outlen) outlen = thisout;
|
|
if (patchOffset >= patchSize) return displayError(IPS_INVALID);
|
|
offset = read24();
|
|
}
|
|
outlen_min_mem = outlen;
|
|
outlen_max = 0xFFFFFFFF;
|
|
if (patchOffset+3 == patchSize)
|
|
{
|
|
unsigned int truncate = read24();
|
|
outlen_max = truncate;
|
|
if (outlen > truncate)
|
|
{
|
|
outlen = truncate;
|
|
w_scrambled = true;
|
|
}
|
|
}
|
|
if (patchOffset != patchSize) return displayError(IPS_INVALID);
|
|
outlen_min = outlen;
|
|
error = IPS_OK;
|
|
if (w_scrambled) error = IPS_SCRAMBLED;
|
|
|
|
// start applying patch
|
|
bool inPlace = false;
|
|
if (!CheckWritePermissions(outName)) return displayError(IPS_INVALID_FILE_PATH);
|
|
if (strncasecmp(inName, outName, 256) == 0)
|
|
{
|
|
if (fvx_open(&outFile, outName, FA_WRITE | FA_READ) != FR_OK) return displayError(IPS_INVALID_FILE_PATH);
|
|
inFile = outFile;
|
|
inPlace = true;
|
|
}
|
|
else if ((fvx_open(&inFile, inName, FA_READ) != FR_OK) ||
|
|
(fvx_open(&outFile, outName, FA_CREATE_ALWAYS | FA_WRITE | FA_READ) != FR_OK))
|
|
return displayError(IPS_INVALID_FILE_PATH);
|
|
|
|
size_t inSize = fvx_size(&inFile);
|
|
outlen = max(outlen_min, min(inSize, outlen_max));
|
|
fvx_lseek(&outFile, max(outlen, outlen_min_mem));
|
|
fvx_lseek(&outFile, 0);
|
|
size_t outSize = outlen;
|
|
ShowProgress(0, outSize, outName);
|
|
|
|
fvx_lseek(&inFile, 0);
|
|
if (!inPlace && !IPScopy(COPY_IN, min(inSize, outlen), 0)) return displayError(IPS_MEMORY);
|
|
fvx_lseek(&outFile, inSize);
|
|
if (outSize > inSize && !IPScopy(COPY_RLE, outSize - inSize, 0)) return displayError(IPS_MEMORY);
|
|
|
|
fvx_lseek(&patchFile, 5);
|
|
offset = read24();
|
|
while (offset != 0x454F46)
|
|
{
|
|
if (!ShowProgress(offset, outSize, outName)) {
|
|
if (ShowPrompt(true, "%s\nB button detected. Cancel?", outName)) return displayError(IPS_CANCELED);
|
|
ShowProgress(0, outSize, outName);
|
|
ShowProgress(offset, outSize, outName);
|
|
}
|
|
|
|
fvx_lseek(&outFile, offset);
|
|
unsigned int size = read16();
|
|
if (size == 0 && !IPScopy(COPY_RLE, read16(), read8())) return displayError(IPS_MEMORY);
|
|
else if (size != 0 && !IPScopy(COPY_PATCH, size, 0)) return displayError(IPS_MEMORY);
|
|
offset = read24();
|
|
}
|
|
|
|
fvx_lseek(&outFile, outSize);
|
|
f_truncate(&outFile);
|
|
return displayError(error);
|
|
}
|