From 93f966e1da78429b7204f202a49772974252318a Mon Sep 17 00:00:00 2001 From: d0k3 Date: Tue, 2 Jan 2018 02:26:49 +0100 Subject: [PATCH] Scripting: 'fill' and 'fdummy' commands --- HelloScript.gm9 | 17 +++++++++++++ arm9/source/filesys/fsutil.c | 47 ++++++++++++++++++++++++++++++++++- arm9/source/filesys/fsutil.h | 3 +++ arm9/source/utils/scripting.c | 28 ++++++++++++++++++++- 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/HelloScript.gm9 b/HelloScript.gm9 index 837018f..091c996 100644 --- a/HelloScript.gm9 +++ b/HelloScript.gm9 @@ -198,6 +198,23 @@ set ERRORMSG "SHA check failed (this was expected)" sha -o $[RENPATH] $[TESTPATH].sha set ERRORMSG "" +# 'fdummy' COMMAND +# This command creates a dummy file of the specified size (size in hex) +# Contents of the dummy file are undefined and existing files won't be overwritten +set DUMMY 0:/testdir/dummy.bin +fdummy $[DUMMY] 400 + +# 'fill' COMMAND +# This command fills (a portition of) a file with the specified byte value +# The syntax is: fill destination@x:y fillbyte +# x: destination offset (in hex) +# y: destination size, starting at x (in hex) +# If x is not given, the full file size, starting from offset 0, is overwritten +# If y is not given, everything starting from offset x is overwritten +# -n / --no_cancel prevents user cancels (useful on critical operations) +fill $[DUMMY]@100:100 FF +fill $[DUMMY]@300 80 + # 'fixcmac' COMMAND # Use this to fix the CMACs for a file or a whole folder (recursively) # This will count as success if a file does not contain a CMAC diff --git a/arm9/source/filesys/fsutil.c b/arm9/source/filesys/fsutil.c index ee837a1..e6c868c 100644 --- a/arm9/source/filesys/fsutil.c +++ b/arm9/source/filesys/fsutil.c @@ -270,10 +270,55 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or return ret; } +bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags) { + FIL dfile; + bool allow_expand = (flags && (*flags & ALLOW_EXPAND)); + + if (!CheckWritePermissions(dest)) return false; + + // open destination + if (fvx_open(&dfile, dest, FA_WRITE | ((allow_expand) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING)) != FR_OK) + return false; + fvx_lseek(&dfile, offset); + if (!size && (offset < fvx_size(&dfile))) + size = fvx_size(&dfile) - offset; + + // check file limits + if (!allow_expand && (offset + size > fvx_size(&dfile))) { + ShowPrompt(false, "Operation would write beyond end of file"); + fvx_close(&dfile); + return false; + } + + bool ret = true; + memset(MAIN_BUFFER, fillbyte, size); + ShowProgress(0, 0, dest); + for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { + UINT write_bytes = min(MAIN_BUFFER_SIZE, size - pos); + UINT bytes_written = write_bytes; + if ((fvx_write(&dfile, MAIN_BUFFER, write_bytes, &bytes_written) != FR_OK) || + (write_bytes != bytes_written)) + ret = false; + if (ret && !ShowProgress(pos + bytes_written, size, dest)) { + if (flags && (*flags & NO_CANCEL)) { + ShowPrompt(false, "Cancel is not allowed here"); + } else ret = !ShowPrompt(true, "B button detected. Cancel?"); + ShowProgress(0, 0, dest); + ShowProgress(pos + bytes_written, size, dest); + } + } + ShowProgress(1, 1, dest); + + fvx_close(&dfile); + + return ret; +} + bool FileCreateDummy(const char* cpath, const char* filename, u64 size) { char npath[256]; // 256 is the maximum length of a full path if (!CheckWritePermissions(cpath)) return false; - snprintf(npath, 255, "%s/%s", cpath, filename); + if (filename) snprintf(npath, 255, "%s/%s", cpath, filename); + else snprintf(npath, 255, "%s", cpath); // create dummy file (fail if already existing) // then, expand the file size via cluster preallocation diff --git a/arm9/source/filesys/fsutil.h b/arm9/source/filesys/fsutil.h index e350ce3..f1d536b 100644 --- a/arm9/source/filesys/fsutil.h +++ b/arm9/source/filesys/fsutil.h @@ -43,6 +43,9 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file); /** Inject file into file @offset **/ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_orig, u64 size, u32* flags); +/** Fill (a portion of) a file with a fillbyte **/ +bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags); + /** Create a dummy file at dest **/ bool FileCreateDummy(const char* cpath, const char* filename, u64 size); diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 1d8ebe1..24d1273 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -78,6 +78,8 @@ typedef enum { CMD_ID_CP, CMD_ID_MV, CMD_ID_INJECT, + CMD_ID_FILL, + CMD_ID_FDUMMY, CMD_ID_RM, CMD_ID_MKDIR, CMD_ID_MOUNT, @@ -133,6 +135,8 @@ Gm9ScriptCmd cmd_list[] = { { CMD_ID_CP , "cp" , 2, _FLG('h') | _FLG('w') | _FLG('k') | _FLG('s') | _FLG('n')}, { CMD_ID_MV , "mv" , 2, _FLG('w') | _FLG('k') | _FLG('s') | _FLG('n') }, { CMD_ID_INJECT , "inject" , 2, _FLG('n') }, + { CMD_ID_FILL , "fill" , 2, 0 }, + { CMD_ID_FDUMMY , "fdummy" , 2, 0 }, { CMD_ID_RM , "rm" , 1, 0 }, { CMD_ID_MKDIR , "mkdir" , 1, 0 }, { CMD_ID_MOUNT , "imgmount", 1, 0 }, @@ -661,7 +665,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { // process arg0 @string u64 at_org = 0; u64 sz_org = 0; - if ((id == CMD_ID_SHA) || (id == CMD_ID_SHAGET) || (id == CMD_ID_INJECT)) { + if ((id == CMD_ID_SHA) || (id == CMD_ID_SHAGET) || (id == CMD_ID_INJECT) || (id == CMD_ID_FILL)) { char* atstr_org = strrchr(argv[0], '@'); if (atstr_org) { *(atstr_org++) = '\0'; @@ -910,6 +914,28 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { ret = FileInjectFile(argv[1], argv[0], at_dst, at_org, sz_org, &flags_ext); if (err_str) snprintf(err_str, _ERR_STR_LEN, "inject fail"); } + else if (id == CMD_ID_FILL) { + u32 flags_ext = ALLOW_EXPAND; + u8 fillbyte = 0; + if ((strnlen(argv[1], _ARG_MAX_LEN) != 2) || !strntohex(argv[1], &fillbyte, 1)) { + ret = false; + if (err_str) snprintf(err_str, _ERR_STR_LEN, "fillbyte fail"); + } else { + if (flags & _FLG('n')) flags_ext |= NO_CANCEL; + ret = FileSetByte(argv[0], at_org, sz_org, fillbyte, &flags_ext); + if (err_str) snprintf(err_str, _ERR_STR_LEN, "fill fail"); + } + } + else if (id == CMD_ID_FDUMMY) { + u32 fsize; + if (sscanf(argv[1], "%lX", &fsize) != 1) { + ret = false; + if (err_str) snprintf(err_str, _ERR_STR_LEN, "bad filesize"); + } else { + ret = FileCreateDummy(argv[0], NULL, fsize); + if (err_str) snprintf(err_str, _ERR_STR_LEN, "create dummy fail"); + } + } else if (id == CMD_ID_RM) { char pathstr[_ERR_STR_LEN]; TruncateString(pathstr, argv[0], 24, 8);