add BEAT interactivity, fix typos and other small bugs

This commit is contained in:
Wolfvak 2019-12-28 18:28:58 -03:00 committed by d0k3
parent e4b98e0932
commit ce9f0a25ef

View File

@ -16,18 +16,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <libgen.h>
#include "common.h" #include "common.h"
#include "timer.h"
#include "crc32.h" #include "crc32.h"
#include "bps.h"
#include "fs.h" #include "fs.h"
#include "ui.h" #include "ui.h"
#define BEAT_VLIBUFSZ (16) #include "hid.h"
#include "bps.h"
#define BEAT_VLIBUFSZ (8)
#define BEAT_MAXPATH (256) #define BEAT_MAXPATH (256)
#define BEAT_RANGESZ(c, i) ((c)->ranges[1][i] - (c)->ranges[0][i]) #define BEAT_FILEBUFSZ (256 * 1024)
#define BEAT_RANGE(c, i) ((c)->ranges[1][i] - (c)->ranges[0][i])
#define BEAT_UPDATEDELAYMS (1000 / 4)
#define BEAT_ABSPOS(c, i) ((c)->foff[i] + (c)->ranges[0][i])
#define BEAT_READONLY (FA_READ | FA_OPEN_EXISTING) #define BEAT_READONLY (FA_READ | FA_OPEN_EXISTING)
#define BEAT_RWCREATE (FA_READ | FA_WRITE | FA_CREATE_NEW) #define BEAT_RWCREATE (FA_READ | FA_WRITE | FA_CREATE_ALWAYS)
static u32 progress_refcnt = 0;
static u64 progress_timer = 0;
static size_t fs_size(const char *path) static size_t fs_size(const char *path)
{ {
@ -37,6 +50,15 @@ static size_t fs_size(const char *path)
return fno.fsize; return fno.fsize;
} }
static const char *basepath(const char *path)
{
const char *ret = path + strlen(path);
while((--ret) > path) {
if (*ret == '/') break;
}
return ret;
}
/* Possible error codes */ /* Possible error codes */
enum { enum {
BEAT_OK = 0, BEAT_OK = 0,
@ -69,23 +91,23 @@ enum {
/* File handles used within the Beat state */ /* File handles used within the Beat state */
enum { enum {
BEAT_PATCHFILE = 0, BEAT_PF = 0, // patch file
BEAT_SRCFILE, BEAT_IF, // input file
BEAT_DSTFILE, BEAT_OF, // output file
BEAT_FILECOUNT, BEAT_FILENUM,
}; };
static const u8 bps_signature[] = { 'B', 'P', 'S', '1' }; static const u8 bps_signature[] = { 'B', 'P', 'S', '1' };
static const u8 bps_chksumoffs[BEAT_FILECOUNT] = { static const u8 bps_chksumoffs[BEAT_FILENUM] = {
[BEAT_PATCHFILE] = 4, [BEAT_DSTFILE] = 8, [BEAT_SRCFILE] = 12, [BEAT_PF] = 4, [BEAT_OF] = 8, [BEAT_IF] = 12,
}; };
static const u8 bpm_signature[] = { 'B', 'P', 'M', '1' }; static const u8 bpm_signature[] = { 'B', 'P', 'M', '1' };
/** BEAT STATE STORAGE */ /** BEAT STATE STORAGE */
typedef struct { typedef struct {
u8 *copybuf; u8 *copybuf;
ssize_t foffset[BEAT_FILECOUNT], eoal_offset; size_t foff[BEAT_FILENUM], eoal_offset;
size_t ranges[2][BEAT_FILECOUNT]; size_t ranges[2][BEAT_FILENUM];
u32 ocrc; // Output crc u32 ocrc; // Output crc
union { union {
@ -97,10 +119,26 @@ typedef struct {
const char *bpm_path, *source_dir, *target_dir; const char *bpm_path, *source_dir, *target_dir;
}; };
}; };
FIL file[BEAT_FILECOUNT]; char processing[BEAT_MAXPATH];
FIL file[BEAT_FILENUM];
} BEAT_Context; } BEAT_Context;
typedef int (*BEAT_Action)(BEAT_Context*, u64); typedef int (*BEAT_Action)(BEAT_Context*, u32);
static bool BEAT_UpdateProgress(const BEAT_Context *ctx)
{ // only updates progress for the parent patch, so the embedded BPS wont be displayed
u64 tmr;
if (progress_refcnt > 2) bkpt; // nope, bug out
tmr = timer_msec(progress_timer);
if (CheckButton(BUTTON_B)) return false; // check for an abort situation
if (progress_refcnt != 1) return true; // only show the first level progress
if (tmr < BEAT_UPDATEDELAYMS) return true; // give it some time
progress_timer = timer_start();
ShowProgress(ctx->foff[BEAT_PF], BEAT_RANGE(ctx, BEAT_PF), ctx->processing);
return true;
}
static const char *BEAT_ErrString(int error) static const char *BEAT_ErrString(int error)
{ // Get an error description string { // Get an error description string
@ -120,103 +158,94 @@ static const char *BEAT_ErrString(int error)
} }
} }
static int BEAT_Read(BEAT_Context *ctx, int id, void *out, size_t len, bool advance) static int BEAT_Read(BEAT_Context *ctx, int id, void *out, size_t len, int fwd)
{ // Read up to `len` bytes from the context file `id` to the `out` buffer { // Read up to `len` bytes from the context file `id` to the `out` buffer
FRESULT res;
UINT br; UINT br;
len = min(len, BEAT_RANGESZ(ctx, id) - ctx->foffset[id]); FRESULT res;
fvx_lseek(&ctx->file[id], ctx->ranges[0][id] + ctx->foffset[id]); // ALWAYS use the state offset + start range if ((len + ctx->foff[id]) > BEAT_RANGE(ctx, id))
if (advance) ctx->foffset[id] += len; return BEAT_OVERFLOW;
fvx_lseek(&ctx->file[id], BEAT_ABSPOS(ctx, id)); // ALWAYS use the state offset + start range
ctx->foff[id] += len * fwd;
res = fvx_read(&ctx->file[id], out, len, &br); res = fvx_read(&ctx->file[id], out, len, &br);
return (res == FR_OK && br == len) ? BEAT_OK : BEAT_IO_ERROR; return (res == FR_OK && br == len) ? BEAT_OK : BEAT_IO_ERROR;
} }
static int BEAT_WriteOut(BEAT_Context *ctx, const u8 *in, size_t len, bool advance) static int BEAT_WriteOut(BEAT_Context *ctx, const u8 *in, size_t len, int fwd)
{ // Write `len` bytes from `in` to BEAT_DSTFILE, updates the output CRC { // Write `len` bytes from `in` to BEAT_OF, updates the output CRC
FRESULT res;
UINT bw; UINT bw;
if ((len + ctx->foffset[BEAT_DSTFILE]) > BEAT_RANGESZ(ctx, BEAT_DSTFILE)) FRESULT res;
if ((len + ctx->foff[BEAT_OF]) > BEAT_RANGE(ctx, BEAT_OF))
return BEAT_OVERFLOW; return BEAT_OVERFLOW;
// Blindly assume all writes will be done linearly // Blindly assume all writes will be done linearly
ctx->ocrc = ~crc32_calculate(~ctx->ocrc, in, len); ctx->ocrc = ~crc32_calculate(~ctx->ocrc, in, len);
fvx_lseek(&ctx->file[BEAT_DSTFILE], ctx->ranges[0][BEAT_DSTFILE] + ctx->foffset[BEAT_DSTFILE]); fvx_lseek(&ctx->file[BEAT_OF], BEAT_ABSPOS(ctx, BEAT_OF));
if (advance) ctx->foffset[BEAT_DSTFILE] += len; ctx->foff[BEAT_OF] += len * fwd;
res = fvx_write(&ctx->file[BEAT_DSTFILE], in, len, &bw); res = fvx_write(&ctx->file[BEAT_OF], in, len, &bw);
return (res == FR_OK && bw == len) ? BEAT_OK : BEAT_IO_ERROR; return (res == FR_OK && bw == len) ? BEAT_OK : BEAT_IO_ERROR;
} }
static void BEAT_SeekOff(BEAT_Context *ctx, int id, ssize_t offset) static void BEAT_SeekOff(BEAT_Context *ctx, int id, ssize_t offset)
{ ctx->foffset[id] += offset; } // Seek `offset` bytes forward { ctx->foff[id] += offset; } // Seek `offset` bytes forward
static void BEAT_SeekAbs(BEAT_Context *ctx, int id, size_t pos) static void BEAT_SeekAbs(BEAT_Context *ctx, int id, size_t pos)
{ ctx->foffset[id] = pos; } // Seek to absolute position `pos` { ctx->foff[id] = pos; } // Seek to absolute position `pos`
static int BEAT_NextVLI(BEAT_Context *ctx, u64 *vli) static int BEAT_NextVLI(BEAT_Context *ctx, u32 *vli)
{ // Read the next VLI in the file, update the seek position { // Read the next VLI in the file, update the seek position
u8 vli_rdbuf[BEAT_VLIBUFSZ], *scan = vli_rdbuf;
u32 iter = 0;
u64 ret = 0;
int res; int res;
u32 ret = 0;
u32 iter = 0;
u8 vli_rdbuf[BEAT_VLIBUFSZ], *scan = vli_rdbuf;
res = BEAT_Read(ctx, BEAT_PATCHFILE, vli_rdbuf, sizeof(vli_rdbuf), false); res = BEAT_Read(ctx, BEAT_PF, vli_rdbuf, sizeof(vli_rdbuf), 0);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
while(scan < &vli_rdbuf[sizeof(vli_rdbuf)]) { while(scan < &vli_rdbuf[sizeof(vli_rdbuf)]) {
u64 val = *(scan++); u32 val = *(scan++);
ret += (val & 0x7F) << iter; ret += (val & 0x7F) << iter;
if (val & 0x80) break; if (val & 0x80) break;
iter += 7; iter += 7;
ret += (u64)(1ULL << iter); ret += (u32)(1ULL << iter);
} }
// Seek forward only by the amount of used bytes // Seek forward only by the amount of used bytes
BEAT_SeekOff(ctx, BEAT_PATCHFILE, scan - vli_rdbuf); BEAT_SeekOff(ctx, BEAT_PF, scan - vli_rdbuf);
*vli = ret; *vli = ret;
return res; return res;
} }
static s64 BEAT_DecodeSigned(u64 val) // Extract the signed number static s32 BEAT_DecodeSigned(u32 val) // Extract the signed number
{ if (val&1) return -(val>>1); else return (val>>1); } { if (val&1) return -(val>>1); else return (val>>1); }
static int BEAT_NextAction(int *act, u64 *len, BEAT_Context *ctx)
{ // Decode next action word, retrieves state and length parameters
int res;
ssize_t end;
u64 val;
end = BEAT_RANGESZ(ctx, BEAT_PATCHFILE) - ctx->foffset[BEAT_PATCHFILE];
if (end == ctx->eoal_offset) return BEAT_EOAL;
if (end < ctx->eoal_offset) return BEAT_PATCH_EXPECT;
res = BEAT_NextVLI(ctx, &val);
*act = val & 3;
*len = (val >> 2) + 1;
return res;
}
static int BEAT_RunActions(BEAT_Context *ctx, const BEAT_Action *acts) static int BEAT_RunActions(BEAT_Context *ctx, const BEAT_Action *acts)
{ // Parses an action list and runs commands specified in `acts` { // Parses an action list and runs commands specified in `acts`
int cmd; u32 vli, len;
u64 len; int cmd, res;
while(1) { while((res == BEAT_OK) &&
int res = BEAT_NextAction(&cmd, &len, ctx); (ctx->foff[BEAT_PF] < (BEAT_RANGE(ctx, BEAT_PF) - ctx->eoal_offset))) {
if (res == BEAT_EOAL) return BEAT_EOAL; // End of patch res = BEAT_NextVLI(ctx, &vli); // get next action
if (res != BEAT_OK) return res; // Failed to get next action cmd = vli & 3;
len = (vli >> 2) + 1;
if (res != BEAT_OK) return res;
if (!BEAT_UpdateProgress(ctx)) return BEAT_ABORTED;
res = (acts[cmd])(ctx, len); // Execute next action res = (acts[cmd])(ctx, len); // Execute next action
if (res == BEAT_ABORTED) return BEAT_ABORTED; // Return on user abort if (res != BEAT_OK) return res; // Break on error or user abort
if (res != BEAT_OK) return res; // Break on error
} }
return res;
} }
static void BEAT_ReleaseCTX(BEAT_Context *ctx) static void BEAT_ReleaseCTX(BEAT_Context *ctx)
{ // Release any resources associated to the context { // Release any resources associated to the context
free(ctx->copybuf); free(ctx->copybuf);
for (int i = 0; i < BEAT_FILECOUNT; i++) { for (int i = 0; i < BEAT_FILENUM; i++) {
if (fvx_opened(&ctx->file[i])) fvx_close(&ctx->file[i]); if (fvx_opened(&ctx->file[i])) fvx_close(&ctx->file[i]);
} }
progress_refcnt--; // lol what even are atomics
} }
// BPS Specific functions // BPS Specific functions
@ -227,12 +256,12 @@ static void BEAT_ReleaseCTX(BEAT_Context *ctx)
- extracts initial info - extracts initial info
- leaves the file ready to begin state machine execution - leaves the file ready to begin state machine execution
*/ */
static int BPS_InitCTX_Advanced(BEAT_Context *ctx, const char *bps_path, const char *in_path, const char *out_path, size_t start, size_t end) static int BPS_InitCTX_Advanced(BEAT_Context *ctx, const char *bps_path, const char *in_path, const char *out_path, size_t start, size_t end, bool do_chksum)
{ {
int res; int res;
u8 read_magic[4]; u8 read_magic[4];
u64 vli, in_sz, metaend_off; u32 vli, in_sz, metaend_off;
u32 chksum[BEAT_FILECOUNT], expected_chksum[BEAT_FILECOUNT]; u32 chksum[BEAT_FILENUM], expected_chksum[BEAT_FILENUM];
// Clear stackbuf // Clear stackbuf
memset(ctx, 0, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
@ -243,24 +272,25 @@ static int BPS_InitCTX_Advanced(BEAT_Context *ctx, const char *bps_path, const c
end = fs_size(bps_path); end = fs_size(bps_path);
} }
// Get checksums of BPS and input files if (do_chksum) // get BPS checksum
chksum[BEAT_PATCHFILE] = crc32_calculate_from_file(bps_path, start, end - start - 4); chksum[BEAT_PF] = crc32_calculate_from_file(bps_path, start, end - start - 4);
chksum[BEAT_SRCFILE] = crc32_calculate_from_file(in_path, 0, fs_size(in_path));
strcpy(ctx->processing, basepath(bps_path));
// open all files // open all files
fvx_open(&ctx->file[BEAT_PATCHFILE], bps_path, BEAT_READONLY); fvx_open(&ctx->file[BEAT_PF], bps_path, BEAT_READONLY);
ctx->ranges[0][BEAT_PATCHFILE] = start; ctx->ranges[0][BEAT_PF] = start;
ctx->ranges[1][BEAT_PATCHFILE] = end; ctx->ranges[1][BEAT_PF] = end;
fvx_open(&ctx->file[BEAT_SRCFILE], in_path, BEAT_READONLY); fvx_open(&ctx->file[BEAT_IF], in_path, BEAT_READONLY);
ctx->ranges[0][BEAT_SRCFILE] = 0; ctx->ranges[0][BEAT_IF] = 0;
ctx->ranges[1][BEAT_SRCFILE] = fs_size(in_path); ctx->ranges[1][BEAT_IF] = fs_size(in_path);
res = fvx_open(&ctx->file[BEAT_DSTFILE], out_path, BEAT_RWCREATE); res = fvx_open(&ctx->file[BEAT_OF], out_path, BEAT_RWCREATE);
if (res != FR_OK) return BEAT_IO_ERROR; if (res != FR_OK) return BEAT_IO_ERROR;
// Verify BPS1 header magic // Verify BPS1 header magic
res = BEAT_Read(ctx, BEAT_PATCHFILE, read_magic, sizeof(read_magic), true); res = BEAT_Read(ctx, BEAT_PF, read_magic, sizeof(read_magic), 1);
if (res != BEAT_OK) return BEAT_IO_ERROR; if (res != BEAT_OK) return BEAT_IO_ERROR;
res = memcmp(read_magic, bps_signature, sizeof(bps_signature)); res = memcmp(read_magic, bps_signature, sizeof(bps_signature));
if (res != 0) return BEAT_BADPATCH; if (res != 0) return BEAT_BADPATCH;
@ -268,113 +298,114 @@ static int BPS_InitCTX_Advanced(BEAT_Context *ctx, const char *bps_path, const c
// Check input size // Check input size
res = BEAT_NextVLI(ctx, &in_sz); res = BEAT_NextVLI(ctx, &in_sz);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (ctx->ranges[1][BEAT_SRCFILE] != in_sz) return BEAT_BADINPUT; if (ctx->ranges[1][BEAT_IF] != in_sz) return BEAT_BADINPUT;
// Get expected output size // Get expected output size
res = BEAT_NextVLI(ctx, &vli); res = BEAT_NextVLI(ctx, &vli);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
ctx->ranges[0][BEAT_DSTFILE] = 0; ctx->ranges[0][BEAT_OF] = 0;
ctx->ranges[1][BEAT_DSTFILE] = vli; ctx->ranges[1][BEAT_OF] = vli;
// Get end of metadata offset // Get end of metadata offset
res = BEAT_NextVLI(ctx, &metaend_off); res = BEAT_NextVLI(ctx, &metaend_off);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
metaend_off += ctx->foffset[BEAT_PATCHFILE]; metaend_off += ctx->foff[BEAT_PF];
// Read checksums from BPS file // Read checksums from BPS file
for (int i = 0; i < BEAT_FILECOUNT; i++) { for (int i = 0; i < BEAT_FILENUM; i++) {
BEAT_SeekAbs(ctx, BEAT_PATCHFILE, ctx->ranges[1][BEAT_PATCHFILE] - ctx->ranges[0][BEAT_PATCHFILE] - bps_chksumoffs[i]); BEAT_SeekAbs(ctx, BEAT_PF, BEAT_RANGE(ctx, BEAT_PF) - bps_chksumoffs[i]);
BEAT_Read(ctx, BEAT_PATCHFILE, &expected_chksum[i], sizeof(u32), false); BEAT_Read(ctx, BEAT_PF, &expected_chksum[i], sizeof(u32), 0);
} }
// Verify patch and input checksums if (do_chksum) { // Verify patch checksum
if (chksum[BEAT_PATCHFILE] != expected_chksum[BEAT_PATCHFILE]) return BEAT_BADCHKSUM; if (chksum[BEAT_PF] != expected_chksum[BEAT_PF]) return BEAT_BADCHKSUM;
if (chksum[BEAT_SRCFILE] != expected_chksum[BEAT_SRCFILE]) return BEAT_BADCHKSUM; }
// Initialize output checksums // Initialize output checksums
ctx->ocrc = 0; ctx->ocrc = 0;
ctx->xocrc = expected_chksum[BEAT_DSTFILE]; ctx->xocrc = expected_chksum[BEAT_OF];
// Allocate temporary block copy buffer // Allocate temporary block copy buffer
ctx->copybuf = malloc(STD_BUFFER_SIZE); ctx->copybuf = malloc(BEAT_FILEBUFSZ);
if (ctx->copybuf == NULL) return BEAT_OUT_OF_MEMORY; if (ctx->copybuf == NULL) return BEAT_OUT_OF_MEMORY;
// Seek back to the start of action stream / end of metadata // Seek back to the start of action stream / end of metadata
BEAT_SeekAbs(ctx, BEAT_PATCHFILE, metaend_off); BEAT_SeekAbs(ctx, BEAT_PF, metaend_off);
progress_refcnt++;
return BEAT_OK; return BEAT_OK;
} }
static int BPS_InitCTX(BEAT_Context *ctx, const char *bps_path, const char *in_path, const char *out_path) static int BPS_InitCTX(BEAT_Context *ctx, const char *bps, const char *in, const char *out)
{ { return BPS_InitCTX_Advanced(ctx, bps, in, out, 0, 0, true); }
return BPS_InitCTX_Advanced(ctx, bps_path, in_path, out_path, 0, 0);
}
/* /*
Generic helper function to copy from `src_id` to BEAT_DSTFILE Generic helper function to copy from `src_id` to BEAT_OF
Used by SourceRead, TargetRead and CreateFile Used by SourceRead, TargetRead and CreateFile
*/ */
static int BEAT_BlkCopy(BEAT_Context *ctx, int src_id, u64 len) static int BEAT_BlkCopy(BEAT_Context *ctx, int src_id, u32 len)
{ {
while(len > 0) { while(len > 0) {
ssize_t blksz = min(len, STD_BUFFER_SIZE); ssize_t blksz = min(len, BEAT_FILEBUFSZ);
int res = BEAT_Read(ctx, src_id, ctx->copybuf, blksz, true); int res = BEAT_Read(ctx, src_id, ctx->copybuf, blksz, 1);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BEAT_WriteOut(ctx, ctx->copybuf, blksz, true); res = BEAT_WriteOut(ctx, ctx->copybuf, blksz, 1);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (!BEAT_UpdateProgress(ctx)) return BEAT_ABORTED;
len -= blksz; len -= blksz;
} }
return BEAT_OK; return BEAT_OK;
} }
static int BPS_SourceRead(BEAT_Context *ctx, u64 len) static int BPS_SourceRead(BEAT_Context *ctx, u32 len)
{ // This command copies bytes from the source file to the target file { // This command copies bytes from the source file to the target file
BEAT_SeekAbs(ctx, BEAT_SRCFILE, ctx->foffset[BEAT_DSTFILE]); BEAT_SeekAbs(ctx, BEAT_IF, ctx->foff[BEAT_OF]);
return BEAT_BlkCopy(ctx, BEAT_SRCFILE, len); return BEAT_BlkCopy(ctx, BEAT_IF, len);
} }
/* /*
[...] the actual data is not available to the patch applier, [...] the actual data is not available to the patch applier,
so it is stored directly inside the patch. so it is stored directly inside the patch.
*/ */
static int BPS_TargetRead(BEAT_Context *ctx, u64 len) static int BPS_TargetRead(BEAT_Context *ctx, u32 len)
{ {
return BEAT_BlkCopy(ctx, BEAT_PATCHFILE, len); return BEAT_BlkCopy(ctx, BEAT_PF, len);
} }
/* /*
An offset is supplied to seek the sourceRelativeOffset to the desired An offset is supplied to seek the sourceRelativeOffset to the desired
location, and then data is copied from said offset to the target file location, and then data is copied from said offset to the target file
*/ */
static int BPS_SourceCopy(BEAT_Context *ctx, u64 len) static int BPS_SourceCopy(BEAT_Context *ctx, u32 len)
{ {
int res; int res;
u64 vli; u32 vli;
s64 offset; s32 offset;
res = BEAT_NextVLI(ctx, &vli); res = BEAT_NextVLI(ctx, &vli);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
offset = BEAT_DecodeSigned(vli); offset = BEAT_DecodeSigned(vli);
BEAT_SeekAbs(ctx, BEAT_SRCFILE, ctx->source_relative + offset); BEAT_SeekAbs(ctx, BEAT_IF, ctx->source_relative + offset);
ctx->source_relative += offset + len; ctx->source_relative += offset + len;
return BEAT_BlkCopy(ctx, BEAT_SRCFILE, len); return BEAT_BlkCopy(ctx, BEAT_IF, len);
} }
/* This command treats all of the data that has already been written to the target file as a dictionary */ /* This command treats all of the data that has already been written to the target file as a dictionary */
static int BPS_TargetCopy(BEAT_Context *ctx, u64 len) static int BPS_TargetCopy(BEAT_Context *ctx, u32 len)
{ // the black sheep of the family, needs special care { // the black sheep of the family, needs special care
int res; int res;
s64 offset; s32 offset;
u64 out_off, rel_off, vli; u32 out_off, rel_off, vli;
res = BEAT_NextVLI(ctx, &vli); res = BEAT_NextVLI(ctx, &vli);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
offset = BEAT_DecodeSigned(vli); offset = BEAT_DecodeSigned(vli);
out_off = ctx->foffset[BEAT_DSTFILE]; out_off = ctx->foff[BEAT_OF];
rel_off = ctx->target_relative + offset; rel_off = ctx->target_relative + offset;
if (rel_off > out_off) return BEAT_BADPATCH; // Illegal if (rel_off > out_off) return BEAT_BADPATCH; // Illegal
@ -382,11 +413,11 @@ static int BPS_TargetCopy(BEAT_Context *ctx, u64 len)
u8 *remfill; u8 *remfill;
ssize_t blksz, distance, remainder; ssize_t blksz, distance, remainder;
blksz = min(len, STD_BUFFER_SIZE); blksz = min(len, BEAT_FILEBUFSZ);
distance = min((ssize_t)(out_off - rel_off), blksz); distance = min((ssize_t)(out_off - rel_off), blksz);
BEAT_SeekAbs(ctx, BEAT_DSTFILE, rel_off); BEAT_SeekAbs(ctx, BEAT_OF, rel_off);
res = BEAT_Read(ctx, BEAT_DSTFILE, ctx->copybuf, distance, false); res = BEAT_Read(ctx, BEAT_OF, ctx->copybuf, distance, 0);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
remfill = ctx->copybuf + distance; remfill = ctx->copybuf + distance;
@ -398,16 +429,17 @@ static int BPS_TargetCopy(BEAT_Context *ctx, u64 len)
remainder -= remblk; remainder -= remblk;
} }
BEAT_SeekAbs(ctx, BEAT_DSTFILE, out_off); BEAT_SeekAbs(ctx, BEAT_OF, out_off);
res = BEAT_WriteOut(ctx, ctx->copybuf, blksz, false); res = BEAT_WriteOut(ctx, ctx->copybuf, blksz, 0);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (!BEAT_UpdateProgress(ctx)) return BEAT_ABORTED;
rel_off += blksz; rel_off += blksz;
out_off += blksz; out_off += blksz;
len -= blksz; len -= blksz;
} }
BEAT_SeekAbs(ctx, BEAT_DSTFILE, out_off); BEAT_SeekAbs(ctx, BEAT_OF, out_off);
ctx->target_relative = rel_off; ctx->target_relative = rel_off;
return BEAT_OK; return BEAT_OK;
} }
@ -433,10 +465,10 @@ static int BPS_RunActions(BEAT_Context *ctx)
static int BPM_OpenFile(BEAT_Context *ctx, int id, const char *path, size_t max_sz) static int BPM_OpenFile(BEAT_Context *ctx, int id, const char *path, size_t max_sz)
{ {
FRESULT res; FRESULT res;
if (fvx_opened(&ctx->file[id])) fvx_close(&ctx->file[id]);
if (fvx_opened(&ctx->file[id])) fvx_close(&ctx->file[id]);
res = fvx_open(&ctx->file[id], path, max_sz ? BEAT_RWCREATE : BEAT_READONLY); res = fvx_open(&ctx->file[id], path, max_sz ? BEAT_RWCREATE : BEAT_READONLY);
if (res == FR_OK) return BEAT_IO_ERROR; if (res != FR_OK) return BEAT_IO_ERROR;
ctx->ranges[0][id] = 0; ctx->ranges[0][id] = 0;
if (max_sz > 0) { if (max_sz > 0) {
@ -448,14 +480,14 @@ static int BPM_OpenFile(BEAT_Context *ctx, int id, const char *path, size_t max_
// if a new file is opened it makes no sense to keep the old CRC // if a new file is opened it makes no sense to keep the old CRC
// a single outfile wont be created from more than one infile (& patch) // a single outfile wont be created from more than one infile (& patch)
ctx->ocrc = 0; ctx->ocrc = 0;
ctx->foffset[id] = 0; ctx->foff[id] = 0;
return BEAT_OK; return BEAT_OK;
} }
static int BPM_InitCTX(BEAT_Context *ctx, const char *bpm_path, const char *src_dir, const char *dst_dir) static int BPM_InitCTX(BEAT_Context *ctx, const char *bpm_path, const char *src_dir, const char *dst_dir)
{ {
int res; int res;
u64 metaend_off; u32 metaend_off;
u8 read_magic[4]; u8 read_magic[4];
u32 chksum, expected_chksum; u32 chksum, expected_chksum;
@ -467,37 +499,44 @@ static int BPM_InitCTX(BEAT_Context *ctx, const char *bpm_path, const char *src_
ctx->eoal_offset = 4; ctx->eoal_offset = 4;
chksum = crc32_calculate_from_file(bpm_path, 0, fs_size(bpm_path) - 4); chksum = crc32_calculate_from_file(bpm_path, 0, fs_size(bpm_path) - 4);
res = BPM_OpenFile(ctx, BEAT_PATCHFILE, bpm_path, 0); res = BPM_OpenFile(ctx, BEAT_PF, bpm_path, 0);
res = BEAT_Read(ctx, BEAT_PATCHFILE, read_magic, sizeof(read_magic), true); if (res != BEAT_OK) return res;
res = BEAT_Read(ctx, BEAT_PF, read_magic, sizeof(read_magic), 1);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = memcmp(read_magic, bpm_signature, sizeof(bpm_signature)); res = memcmp(read_magic, bpm_signature, sizeof(bpm_signature));
ShowPrompt(false, "%.4s %.4s", bpm_signature, read_magic);
if (res != 0) return BEAT_BADPATCH; if (res != 0) return BEAT_BADPATCH;
// Get end of metadata offset // Get end of metadata offset
res = BEAT_NextVLI(ctx, &metaend_off); res = BEAT_NextVLI(ctx, &metaend_off);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
metaend_off += ctx->foffset[BEAT_PATCHFILE]; metaend_off += ctx->foff[BEAT_PF];
// Read checksums from BPS file // Read checksums from BPS file
BEAT_SeekAbs(ctx, BEAT_PATCHFILE, BEAT_RANGESZ(ctx, BEAT_PATCHFILE) - 4); BEAT_SeekAbs(ctx, BEAT_PF, BEAT_RANGE(ctx, BEAT_PF) - 4);
res = BEAT_Read(ctx, BEAT_PATCHFILE, &expected_chksum, sizeof(u32), false); res = BEAT_Read(ctx, BEAT_PF, &expected_chksum, sizeof(u32), 0);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (expected_chksum != chksum) return BEAT_BADCHKSUM; if (expected_chksum != chksum) return BEAT_BADCHKSUM;
// Allocate temporary block copy buffer // Allocate temporary block copy buffer
ctx->copybuf = malloc(STD_BUFFER_SIZE); ctx->copybuf = malloc(BEAT_FILEBUFSZ);
if (ctx->copybuf == NULL) return BEAT_OUT_OF_MEMORY; if (ctx->copybuf == NULL) return BEAT_OUT_OF_MEMORY;
// Seek back to the start of action stream / end of metadata // Seek back to the start of action stream / end of metadata
BEAT_SeekAbs(ctx, BEAT_PATCHFILE, metaend_off); BEAT_SeekAbs(ctx, BEAT_PF, metaend_off);
progress_refcnt++;
return BEAT_OK; return BEAT_OK;
} }
static int BPM_NextPath(BEAT_Context *ctx, char *out, int name_len) static int BPM_NextPath(BEAT_Context *ctx, char *out, int name_len)
{ {
if (name_len >= BEAT_MAXPATH) return BEAT_BADPATCH; if (name_len >= BEAT_MAXPATH) return BEAT_BADPATCH;
int res = BEAT_Read(ctx, BEAT_PATCHFILE, out, name_len, true); int res = BEAT_Read(ctx, BEAT_PF, out, name_len, 1);
out[name_len] = '\0';
if (res == BEAT_OK) {
out[name_len] = '\0'; // make sure the buffer ends with a zero char out[name_len] = '\0'; // make sure the buffer ends with a zero char
strcpy(ctx->processing, out);
}
return res; return res;
} }
@ -513,7 +552,7 @@ static int BPM_MakeRelativePaths(BEAT_Context *ctx, char *src, char *dst, int na
return res; return res;
} }
static int BPM_CreatePath(BEAT_Context *ctx, u64 name_len) static int BPM_CreatePath(BEAT_Context *ctx, u32 name_len)
{ // Create a directory { // Create a directory
char path[BEAT_MAXPATH]; char path[BEAT_MAXPATH];
int res = BPM_MakeRelativePaths(ctx, NULL, path, name_len); int res = BPM_MakeRelativePaths(ctx, NULL, path, name_len);
@ -524,9 +563,9 @@ static int BPM_CreatePath(BEAT_Context *ctx, u64 name_len)
return BEAT_OK; return BEAT_OK;
} }
static int BPM_CreateFile(BEAT_Context *ctx, u64 name_len) static int BPM_CreateFile(BEAT_Context *ctx, u32 name_len)
{ // Create a file and fill it with data provided in the BPM { // Create a file and fill it with data provided in the BPM
u64 file_sz; u32 file_sz;
u32 checksum; u32 checksum;
char path[BEAT_MAXPATH]; char path[BEAT_MAXPATH];
@ -535,20 +574,20 @@ static int BPM_CreateFile(BEAT_Context *ctx, u64 name_len)
res = BEAT_NextVLI(ctx, &file_sz); // get new file size res = BEAT_NextVLI(ctx, &file_sz); // get new file size
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BPM_OpenFile(ctx, BEAT_DSTFILE, path, file_sz); // open file as RW res = BPM_OpenFile(ctx, BEAT_OF, path, file_sz); // open file as RW
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BEAT_BlkCopy(ctx, BEAT_PATCHFILE, file_sz); // copy data to new file res = BEAT_BlkCopy(ctx, BEAT_PF, file_sz); // copy data to new file
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BEAT_Read(ctx, BEAT_PATCHFILE, &checksum, sizeof(u32), true); res = BEAT_Read(ctx, BEAT_PF, &checksum, sizeof(u32), 1);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (ctx->ocrc != checksum) return BEAT_BADOUTPUT; // get and check CRC32 if (ctx->ocrc != checksum) return BEAT_BADOUTPUT; // get and check CRC32
return BEAT_OK; return BEAT_OK;
} }
static int BPM_ModifyFile(BEAT_Context *ctx, u64 name_len) static int BPM_ModifyFile(BEAT_Context *ctx, u32 name_len)
{ // Apply a BPS patch { // Apply a BPS patch
u64 origin, bps_sz; u32 origin, bps_sz;
BEAT_Context bps_context; BEAT_Context bps_context;
char src[BEAT_MAXPATH], dst[BEAT_MAXPATH]; char src[BEAT_MAXPATH], dst[BEAT_MAXPATH];
@ -562,19 +601,20 @@ static int BPM_ModifyFile(BEAT_Context *ctx, u64 name_len)
res = BPS_InitCTX_Advanced( res = BPS_InitCTX_Advanced(
&bps_context, ctx->bpm_path, src, dst, &bps_context, ctx->bpm_path, src, dst,
ctx->foffset[BEAT_PATCHFILE], ctx->foffset[BEAT_PATCHFILE] + bps_sz ctx->foff[BEAT_PF], ctx->foff[BEAT_PF] + bps_sz,
false
); // create a BPS context using the current ranges ); // create a BPS context using the current ranges
if (res == BEAT_OK) res = BPS_RunActions(&bps_context); // run if OK if (res == BEAT_OK) res = BPS_RunActions(&bps_context); // run if OK
BEAT_ReleaseCTX(&bps_context); BEAT_ReleaseCTX(&bps_context);
if (res != BEAT_OK) return res; // break off if there was an error if (res != BEAT_OK) return res; // break off if there was an error
BEAT_SeekOff(ctx, BEAT_PATCHFILE, bps_sz); // advance beyond the BPS BEAT_SeekOff(ctx, BEAT_PF, bps_sz); // advance beyond the BPS
return BEAT_OK; return BEAT_OK;
} }
static int BPM_MirrorFile(BEAT_Context *ctx, u64 name_len) static int BPM_MirrorFile(BEAT_Context *ctx, u32 name_len)
{ // Copy a file from source to target without any modifications { // Copy a file from source to target without any modifications
u64 origin; u32 origin;
u32 checksum; u32 checksum;
char src[BEAT_MAXPATH], dst[BEAT_MAXPATH]; char src[BEAT_MAXPATH], dst[BEAT_MAXPATH];
@ -582,18 +622,18 @@ static int BPM_MirrorFile(BEAT_Context *ctx, u64 name_len)
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
// open source and destination files, read the origin dummy // open source and destination files, read the origin dummy
res = BPM_OpenFile(ctx, BEAT_SRCFILE, src, 0); res = BPM_OpenFile(ctx, BEAT_IF, src, 0);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BPM_OpenFile(ctx, BEAT_DSTFILE, dst, ctx->ranges[1][BEAT_SRCFILE]); res = BPM_OpenFile(ctx, BEAT_OF, dst, ctx->ranges[1][BEAT_IF]);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BEAT_NextVLI(ctx, &origin); res = BEAT_NextVLI(ctx, &origin);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
// copy straight from source to destination // copy straight from source to destination
res = BEAT_BlkCopy(ctx, BEAT_SRCFILE, ctx->ranges[1][BEAT_SRCFILE]); res = BEAT_BlkCopy(ctx, BEAT_IF, ctx->ranges[1][BEAT_IF]);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
res = BEAT_Read(ctx, BEAT_PATCHFILE, &checksum, sizeof(u32), true); res = BEAT_Read(ctx, BEAT_PF, &checksum, sizeof(u32), 1);
if (res != BEAT_OK) return res; if (res != BEAT_OK) return res;
if (ctx->ocrc != checksum) return BEAT_BADOUTPUT; // verify checksum if (ctx->ocrc != checksum) return BEAT_BADOUTPUT; // verify checksum
return BEAT_OK; return BEAT_OK;
@ -617,6 +657,8 @@ static int BEAT_Run(const char *p, const char *s, const char *d, bool bpm)
{ {
int res; int res;
BEAT_Context ctx; BEAT_Context ctx;
progress_timer = timer_start();
res = (bpm ? BPM_InitCTX : BPS_InitCTX)(&ctx, p, s, d); res = (bpm ? BPM_InitCTX : BPS_InitCTX)(&ctx, p, s, d);
if (res != BEAT_OK) { if (res != BEAT_OK) {
ShowPrompt(false, "Failed to initialize %s file:\n%s", ShowPrompt(false, "Failed to initialize %s file:\n%s",
@ -625,7 +667,7 @@ static int BEAT_Run(const char *p, const char *s, const char *d, bool bpm)
res = (bpm ? BPM_RunActions : BPS_RunActions)(&ctx); res = (bpm ? BPM_RunActions : BPS_RunActions)(&ctx);
switch(res) { switch(res) {
case BEAT_OK: case BEAT_OK:
ShowPrompt(false, "Successfully patched"); ShowPrompt(false, "Patch successfully applied");
break; break;
case BEAT_ABORTED: case BEAT_ABORTED:
ShowPrompt(false, "Patching aborted by user"); ShowPrompt(false, "Patching aborted by user");
@ -641,6 +683,5 @@ static int BEAT_Run(const char *p, const char *s, const char *d, bool bpm)
int ApplyBPSPatch(const char* modifyName, const char* sourceName, const char* targetName) int ApplyBPSPatch(const char* modifyName, const char* sourceName, const char* targetName)
{ return BEAT_Run(modifyName, sourceName, targetName, false); } { return BEAT_Run(modifyName, sourceName, targetName, false); }
int ApplyBPMPatch(const char* patchName, const char* sourcePath, const char* targetPath) int ApplyBPMPatch(const char* patchName, const char* sourcePath, const char* targetPath)
{ return BEAT_Run(patchName, sourcePath, targetPath, true); } { return BEAT_Run(patchName, sourcePath, targetPath, true); }