Add scripting commands extrcode, cmprcode, cp -p

* extrcode already existed, but now has a progress bar and an entry in HelloScript.
* cmprcode recompresses a code binary into its original format. It takes 2-3 minutes.
* cp -p appends file 2 to the end of file 1 rather than overwriting it.
This commit is contained in:
Hyarion Sanyënóna 2018-04-22 12:28:37 -05:00 committed by d0k3
parent 2d807915e6
commit 5c138e1219
8 changed files with 370 additions and 22 deletions

View File

@ -11,8 +11,8 @@
#include "ff.h" #include "ff.h"
#include "ui.h" #include "ui.h"
#define SKIP_CUR (1UL<< 9) #define SKIP_CUR (1UL<<10)
#define OVERWRITE_CUR (1UL<<10) #define OVERWRITE_CUR (1UL<<11)
#define _MAX_FS_OPT 8 // max file selector options #define _MAX_FS_OPT 8 // max file selector options
@ -439,6 +439,8 @@ bool PathExist(const char* path) {
bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer, u32 bufsiz) { bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer, u32 bufsiz) {
bool to_virtual = GetVirtualSource(dest); bool to_virtual = GetVirtualSource(dest);
bool silent = (flags && (*flags & SILENT)); bool silent = (flags && (*flags & SILENT));
bool append = (flags && (*flags & APPEND_ALL));
bool calcsha = (flags && (*flags & CALC_SHA) && !append);
bool ret = false; bool ret = false;
// check destination write permission (special paths only) // check destination write permission (special paths only)
@ -465,6 +467,11 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
DIR pdir; DIR pdir;
char* fname = orig + strnlen(orig, 256); char* fname = orig + strnlen(orig, 256);
if (append) {
if (!silent) ShowPrompt(false, "%s\nError: Cannot append a folder", deststr);
return false;
}
// create the destination folder if it does not already exist // create the destination folder if it does not already exist
if (fvx_opendir(&pdir, dest) != FR_OK) { if (fvx_opendir(&pdir, dest) != FR_OK) {
if (fvx_mkdir(dest) != FR_OK) { if (fvx_mkdir(dest) != FR_OK) {
@ -509,7 +516,8 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
} else { // copying files } else { // copying files
FIL ofile; FIL ofile;
FIL dfile; FIL dfile;
u64 fsize; u64 osize;
u64 dsize;
if (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { if (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
if (!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)) if (!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK))
@ -517,48 +525,54 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
ShowProgress(0, 0, orig); // reinit progress bar ShowProgress(0, 0, orig); // reinit progress bar
} }
if (fvx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { if ((!append || (fvx_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK)) &&
(fvx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)) {
if (!silent) ShowPrompt(false, "%s\nError: Cannot open destination file", deststr); if (!silent) ShowPrompt(false, "%s\nError: Cannot open destination file", deststr);
fvx_close(&ofile); fvx_close(&ofile);
return false; return false;
} }
ret = true; // destination file exists by now, so we need to handle deletion ret = true; // destination file exists by now, so we need to handle deletion
fsize = fvx_size(&ofile); // check space via cluster preallocation osize = fvx_size(&ofile);
if ((fvx_lseek(&dfile, fsize) != FR_OK) || (fvx_sync(&dfile) != FR_OK) || (fvx_tell(&dfile) != fsize)) { dsize = fvx_size(&dfile); // always 0 if not appending to file
if ((fvx_lseek(&dfile, (osize + dsize)) != FR_OK) || (fvx_sync(&dfile) != FR_OK) || (fvx_tell(&dfile) != (osize + dsize))) { // check space via cluster preallocation
if (!silent) ShowPrompt(false, "%s\nError: Not enough space available", deststr); if (!silent) ShowPrompt(false, "%s\nError: Not enough space available", deststr);
ret = false; ret = false;
} }
fvx_lseek(&dfile, 0); fvx_lseek(&dfile, dsize);
fvx_sync(&dfile); fvx_sync(&dfile);
fvx_lseek(&ofile, 0); fvx_lseek(&ofile, 0);
fvx_sync(&ofile); fvx_sync(&ofile);
if (flags && (*flags & CALC_SHA)) sha_init(SHA256_MODE); if (calcsha) sha_init(SHA256_MODE);
for (u64 pos = 0; (pos < fsize) && ret; pos += bufsiz) { for (u64 pos = 0; (pos < osize) && ret; pos += bufsiz) {
UINT bytes_read = 0; UINT bytes_read = 0;
UINT bytes_written = 0; UINT bytes_written = 0;
if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) || if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) ||
(fvx_write(&dfile, buffer, bytes_read, &bytes_written) != FR_OK) || (fvx_write(&dfile, buffer, bytes_read, &bytes_written) != FR_OK) ||
(bytes_read != bytes_written)) (bytes_read != bytes_written))
ret = false; ret = false;
if (ret && !ShowProgress(pos + bytes_read, fsize, orig)) {
u64 current = pos + bytes_read;
u64 total = osize;
if (ret && !ShowProgress(current, total, orig)) {
if (flags && (*flags & NO_CANCEL)) { if (flags && (*flags & NO_CANCEL)) {
ShowPrompt(false, "%s\nCancel is not allowed here", deststr); ShowPrompt(false, "%s\nCancel is not allowed here", deststr);
} else ret = !ShowPrompt(true, "%s\nB button detected. Cancel?", deststr); } else ret = !ShowPrompt(true, "%s\nB button detected. Cancel?", deststr);
ShowProgress(0, 0, orig); ShowProgress(0, 0, orig);
ShowProgress(pos + bytes_read, fsize, orig); ShowProgress(current, total, orig);
} }
if (flags && (*flags & CALC_SHA)) if (calcsha)
sha_update(buffer, bytes_read); sha_update(buffer, bytes_read);
} }
ShowProgress(1, 1, orig); ShowProgress(1, 1, orig);
fvx_close(&ofile); fvx_close(&ofile);
fvx_close(&dfile); fvx_close(&dfile);
if (!ret) fvx_unlink(dest); if (!ret && ((dsize == 0) || (fvx_lseek(&dfile, dsize) != FR_OK) || (f_truncate(&dfile) != FR_OK))) {
else if (!to_virtual && flags && (*flags & CALC_SHA)) { fvx_unlink(dest);
} else if (!to_virtual && calcsha) {
u8 sha256[0x20]; u8 sha256[0x20];
char* ext_sha = dest + strnlen(dest, 256); char* ext_sha = dest + strnlen(dest, 256);
strncpy(ext_sha, ".sha", 256 - (ext_sha - dest)); strncpy(ext_sha, ".sha", 256 - (ext_sha - dest));
@ -616,7 +630,7 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
} }
// check if destination exists // check if destination exists
if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL)) && (fa_stat(ldest, NULL) == FR_OK)) { if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL|APPEND_ALL)) && (fa_stat(ldest, NULL) == FR_OK)) {
if (*flags & SKIP_ALL) { if (*flags & SKIP_ALL) {
*flags |= SKIP_CUR; *flags |= SKIP_CUR;
return true; return true;

View File

@ -12,6 +12,7 @@
#define ASK_ALL (1UL<<6) #define ASK_ALL (1UL<<6)
#define SKIP_ALL (1UL<<7) #define SKIP_ALL (1UL<<7)
#define OVERWRITE_ALL (1UL<<8) #define OVERWRITE_ALL (1UL<<8)
#define APPEND_ALL (1UL<<9)
// file selector flags // file selector flags
#define NO_DIRS (1UL<<0) #define NO_DIRS (1UL<<0)

View File

@ -1,4 +1,5 @@
#include "codelzss.h" #include "codelzss.h"
#include "ui.h"
#define CODE_COMP_SIZE(f) ((f)->off_size_comp & 0xFFFFFF) #define CODE_COMP_SIZE(f) ((f)->off_size_comp & 0xFFFFFF)
#define CODE_COMP_END(f) ((int) CODE_COMP_SIZE(f) - (int) (((f)->off_size_comp >> 24) % 0xFF)) #define CODE_COMP_END(f) ((int) CODE_COMP_SIZE(f) - (int) (((f)->off_size_comp >> 24) % 0xFF))
@ -44,6 +45,12 @@ u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
// main decompression loop // main decompression loop
while ((ptr_in > comp_start) && (ptr_out > comp_start)) { while ((ptr_in > comp_start) && (ptr_out > comp_start)) {
if (!ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code...")) {
if (ShowPrompt(true, "Decompressing .code...\nB button detected. Cancel?")) return 1;
ShowProgress(0, data_end - data_start, "Decompressing .code...");
ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code...");
}
// sanity check // sanity check
if (ptr_out < ptr_in) return 1; if (ptr_out < ptr_in) return 1;
@ -91,3 +98,266 @@ u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
*code_size = data_end - data_start; *code_size = data_end - data_start;
return 0; return 0;
} }
// see https://github.com/dnasdw/3dstool/blob/master/src/backwardlz77.cpp (GPLv3)
typedef struct {
u16 WindowPos;
u16 WindowLen;
s16* OffsetTable;
s16* ReversedOffsetTable;
s16* ByteTable;
s16* EndTable;
} sCompressInfo;
void initTable(sCompressInfo* a_pInfo, void* a_pWork) {
a_pInfo->WindowPos = 0;
a_pInfo->WindowLen = 0;
a_pInfo->OffsetTable = (s16*)(a_pWork);
a_pInfo->ReversedOffsetTable = (s16*)(a_pWork) + 4098;
a_pInfo->ByteTable = (s16*)(a_pWork) + 4098 + 4098;
a_pInfo->EndTable = (s16*)(a_pWork) + 4098 + 4098 + 256;
for (int i = 0; i < 256; i++) {
a_pInfo->ByteTable[i] = -1;
a_pInfo->EndTable[i] = -1;
}
}
int search(sCompressInfo* a_pInfo, const u8* a_pSrc, int* a_nOffset, int a_nMaxSize) {
if (a_nMaxSize < 3) {
return 0;
}
const u8* pSearch = NULL;
int nSize = 2;
const u16 uWindowPos = a_pInfo->WindowPos;
const u16 uWindowLen = a_pInfo->WindowLen;
s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable;
for (s16 nOffset = a_pInfo->EndTable[*(a_pSrc - 1)]; nOffset != -1; nOffset = pReversedOffsetTable[nOffset]) {
if (nOffset < uWindowPos) {
pSearch = a_pSrc + uWindowPos - nOffset;
} else {
pSearch = a_pSrc + uWindowLen + uWindowPos - nOffset;
}
if (pSearch - a_pSrc < 3) {
continue;
}
if (*(pSearch - 2) != *(a_pSrc - 2) || *(pSearch - 3) != *(a_pSrc - 3)) {
continue;
}
int nMaxSize = (int)((s64)min(a_nMaxSize, pSearch - a_pSrc));
int nCurrentSize = 3;
while (nCurrentSize < nMaxSize && *(pSearch - nCurrentSize - 1) == *(a_pSrc - nCurrentSize - 1)) {
nCurrentSize++;
}
if (nCurrentSize > nSize) {
nSize = nCurrentSize;
*a_nOffset = (int)(pSearch - a_pSrc);
if (nSize == a_nMaxSize) {
break;
}
}
}
if (nSize < 3) {
return 0;
}
return nSize;
}
void slideByte(sCompressInfo* a_pInfo, const u8* a_pSrc) {
u8 uInData = *(a_pSrc - 1);
u16 uInsertOffset = 0;
const u16 uWindowPos = a_pInfo->WindowPos;
const u16 uWindowLen = a_pInfo->WindowLen;
s16* pOffsetTable = a_pInfo->OffsetTable;
s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable;
s16* pByteTable = a_pInfo->ByteTable;
s16* pEndTable = a_pInfo->EndTable;
if (uWindowLen == 4098) {
u8 uOutData = *(a_pSrc + 4097);
if ((pByteTable[uOutData] = pOffsetTable[pByteTable[uOutData]]) == -1) {
pEndTable[uOutData] = -1;
} else {
pReversedOffsetTable[pByteTable[uOutData]] = -1;
}
uInsertOffset = uWindowPos;
} else {
uInsertOffset = uWindowLen;
}
s16 nOffset = pEndTable[uInData];
if (nOffset == -1) {
pByteTable[uInData] = uInsertOffset;
} else {
pOffsetTable[nOffset] = uInsertOffset;
}
pEndTable[uInData] = uInsertOffset;
pOffsetTable[uInsertOffset] = -1;
pReversedOffsetTable[uInsertOffset] = nOffset;
if (uWindowLen == 4098) {
a_pInfo->WindowPos = (uWindowPos + 1) % 4098;
} else {
a_pInfo->WindowLen++;
}
}
inline void slide(sCompressInfo* a_pInfo, const u8* a_pSrc, int a_nSize) {
for (int i = 0; i < a_nSize; i++) {
slideByte(a_pInfo, a_pSrc--);
}
}
s64 alignBytes(s64 a_nData, s64 a_nAlignment) {
return (a_nData + a_nAlignment - 1) / a_nAlignment * a_nAlignment;
}
bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_pCompressed, u32* a_uCompressedSize) {
const int s_nCompressWorkSize = (4098 + 4098 + 256 + 256) * sizeof(s16);
bool bResult = true;
if (a_uUncompressedSize > sizeof(CodeLzssFooter) && *a_uCompressedSize >= a_uUncompressedSize) {
u8* pWork = malloc(s_nCompressWorkSize * sizeof(u8));
if (!pWork) return false;
do {
sCompressInfo info;
initTable(&info, pWork);
const int nMaxSize = 0xF + 3;
const u8* pSrc = a_pUncompressed + a_uUncompressedSize;
u8* pDest = a_pCompressed + a_uUncompressedSize;
while (pSrc - a_pUncompressed > 0 && pDest - a_pCompressed > 0) {
if (!ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code...")) {
if (ShowPrompt(true, "Compressing .code...\nB button detected. Cancel?")) {
bResult = false;
break;
}
ShowProgress(0, a_uUncompressedSize, "Compressing .code...");
ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code...");
}
u8* pFlag = --pDest;
*pFlag = 0;
for (int i = 0; i < 8; i++) {
int nOffset = 0;
int nSize = search(&info, pSrc, &nOffset, (int)((s64)min((s64)min(nMaxSize, pSrc - a_pUncompressed), a_pUncompressed + a_uUncompressedSize - pSrc)));
if (nSize < 3) {
if (pDest - a_pCompressed < 1) {
bResult = false;
break;
}
slide(&info, pSrc, 1);
*--pDest = *--pSrc;
} else {
if (pDest - a_pCompressed < 2) {
bResult = false;
break;
}
*pFlag |= 0x80 >> i;
slide(&info, pSrc, nSize);
pSrc -= nSize;
nSize -= 3;
*--pDest = (nSize << 4 & 0xF0) | ((nOffset - 3) >> 8 & 0x0F);
*--pDest = (nOffset - 3) & 0xFF;
}
if (pSrc - a_pUncompressed <= 0) {
break;
}
}
if (!bResult) {
break;
}
}
if (!bResult) {
break;
}
*a_uCompressedSize = (u32)(a_pCompressed + a_uUncompressedSize - pDest);
} while (false);
free(pWork);
} else {
bResult = false;
}
if (bResult) {
u32 uOrigSize = a_uUncompressedSize;
u8* pCompressBuffer = a_pCompressed + a_uUncompressedSize - *a_uCompressedSize;
u32 uCompressBufferSize = *a_uCompressedSize;
u32 uOrigSafe = 0;
u32 uCompressSafe = 0;
bool bOver = false;
while (uOrigSize > 0) {
u8 uFlag = pCompressBuffer[--uCompressBufferSize];
for (int i = 0; i < 8; i++) {
if ((uFlag << i & 0x80) == 0) {
uCompressBufferSize--;
uOrigSize--;
} else {
int nSize = (pCompressBuffer[--uCompressBufferSize] >> 4 & 0x0F) + 3;
uCompressBufferSize--;
uOrigSize -= nSize;
if (uOrigSize < uCompressBufferSize) {
uOrigSafe = uOrigSize;
uCompressSafe = uCompressBufferSize;
bOver = true;
break;
}
}
if (uOrigSize <= 0) {
break;
}
}
if (bOver) {
break;
}
}
u32 uCompressedSize = *a_uCompressedSize - uCompressSafe;
u32 uPadOffset = uOrigSafe + uCompressedSize;
u32 uCompFooterOffset = (u32)(alignBytes(uPadOffset, 4));
*a_uCompressedSize = uCompFooterOffset + sizeof(CodeLzssFooter);
u32 uTop = *a_uCompressedSize - uOrigSafe;
u32 uBottom = *a_uCompressedSize - uPadOffset;
if (*a_uCompressedSize >= a_uUncompressedSize || uTop > 0xFFFFFF) {
bResult = false;
} else {
memcpy(a_pCompressed, a_pUncompressed, uOrigSafe);
memmove(a_pCompressed + uOrigSafe, pCompressBuffer + uCompressSafe, uCompressedSize);
memset(a_pCompressed + uPadOffset, 0xFF, uCompFooterOffset - uPadOffset);
CodeLzssFooter* pCompFooter = (CodeLzssFooter*)(a_pCompressed + uCompFooterOffset);
pCompFooter->off_size_comp = uTop | (uBottom << 24);
pCompFooter->addsize_dec = a_uUncompressedSize - *a_uCompressedSize;
}
}
return bResult;
}

View File

@ -6,3 +6,4 @@
u32 GetCodeLzssUncompressedSize(void* footer, u32 comp_size); u32 GetCodeLzssUncompressedSize(void* footer, u32 comp_size);
u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size); u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size);
bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_pCompressed, u32* a_uCompressedSize);

View File

@ -1615,6 +1615,46 @@ u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr)
return 0; return 0;
} }
u32 CompressCode(const char* path, const char* path_out) {
char dest[256];
strncpy(dest, path_out ? path_out : OUTPUT_PATH, 255);
if (!CheckWritePermissions(dest)) return 1;
if (!path_out && (fvx_rmkdir(OUTPUT_PATH) != FR_OK)) return 1;
// allocate memory
u32 code_dec_size = fvx_qsize(path);
u8* code_dec = (u8*) malloc(code_dec_size);
u32 code_cmp_size = code_dec_size;
u8* code_cmp = (u8*) malloc(code_cmp_size);
if (!code_dec || !code_cmp) {
if (code_dec != NULL) free(code_dec);
if (code_cmp != NULL) free(code_cmp);
ShowPrompt(false, "Out of memory.");
return 1;
}
// load code.bin and compress code
if ((fvx_qread(path, code_dec, 0, code_dec_size, NULL) != FR_OK) ||
(!CompressCodeLzss(code_dec, code_dec_size, code_cmp, &code_cmp_size))) {
free(code_dec);
free(code_cmp);
return 1;
}
// write output file
fvx_unlink(dest);
free(code_dec);
if (fvx_qwrite(dest, code_cmp, 0, code_cmp_size, NULL) != FR_OK) {
fvx_unlink(dest);
free(code_cmp);
return 1;
}
free(code_cmp);
return 0;
}
u32 ExtractDataFromDisaDiff(const char* path) { u32 ExtractDataFromDisaDiff(const char* path) {
char dest[256]; char dest[256];
u32 ret = 0; u32 ret = 0;

View File

@ -8,6 +8,7 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 DumpCxiSrlFromTmdFile(const char* path); u32 DumpCxiSrlFromTmdFile(const char* path);
u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr); u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr);
u32 CompressCode(const char* path, const char* path_out);
u32 ExtractDataFromDisaDiff(const char* path); u32 ExtractDataFromDisaDiff(const char* path);
u64 GetGameFileTrimmedSize(const char* path); u64 GetGameFileTrimmedSize(const char* path);
u32 TrimGameFile(const char* path); u32 TrimGameFile(const char* path);

View File

@ -108,6 +108,7 @@ typedef enum {
CMD_ID_ENCRYPT, CMD_ID_ENCRYPT,
CMD_ID_BUILDCIA, CMD_ID_BUILDCIA,
CMD_ID_EXTRCODE, CMD_ID_EXTRCODE,
CMD_ID_CMPRCODE,
CMD_ID_SDUMP, CMD_ID_SDUMP,
CMD_ID_APPLYIPS, CMD_ID_APPLYIPS,
CMD_ID_APPLYBPS, CMD_ID_APPLYBPS,
@ -157,7 +158,7 @@ Gm9ScriptCmd cmd_list[] = {
{ CMD_ID_STRREP , "strrep" , 3, 0 }, { CMD_ID_STRREP , "strrep" , 3, 0 },
{ CMD_ID_CHK , "chk" , 2, _FLG('u') }, { CMD_ID_CHK , "chk" , 2, _FLG('u') },
{ CMD_ID_ALLOW , "allow" , 1, _FLG('a') }, { CMD_ID_ALLOW , "allow" , 1, _FLG('a') },
{ CMD_ID_CP , "cp" , 2, _FLG('h') | _FLG('w') | _FLG('k') | _FLG('s') | _FLG('n')}, { CMD_ID_CP , "cp" , 2, _FLG('h') | _FLG('w') | _FLG('k') | _FLG('s') | _FLG('n') | _FLG('p')},
{ CMD_ID_MV , "mv" , 2, _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_INJECT , "inject" , 2, _FLG('n') },
{ CMD_ID_FILL , "fill" , 2, 0 }, { CMD_ID_FILL , "fill" , 2, 0 },
@ -178,6 +179,7 @@ Gm9ScriptCmd cmd_list[] = {
{ CMD_ID_ENCRYPT , "encrypt" , 1, 0 }, { CMD_ID_ENCRYPT , "encrypt" , 1, 0 },
{ CMD_ID_BUILDCIA, "buildcia", 1, _FLG('l') }, { CMD_ID_BUILDCIA, "buildcia", 1, _FLG('l') },
{ CMD_ID_EXTRCODE, "extrcode", 2, 0 }, { CMD_ID_EXTRCODE, "extrcode", 2, 0 },
{ CMD_ID_CMPRCODE, "cmprcode", 2, 0 },
{ CMD_ID_SDUMP , "sdump", 1, _FLG('w') }, { CMD_ID_SDUMP , "sdump", 1, _FLG('w') },
{ CMD_ID_APPLYIPS, "applyips", 3, 0 }, { CMD_ID_APPLYIPS, "applyips", 3, 0 },
{ CMD_ID_APPLYBPS, "applybps", 3, 0 }, { CMD_ID_APPLYBPS, "applybps", 3, 0 },
@ -551,6 +553,7 @@ u32 get_flag(char* str, u32 len, char* err_str) {
else if (strncmp(str, "--legit", len) == 0) flag_char = 'l'; else if (strncmp(str, "--legit", len) == 0) flag_char = 'l';
else if (strncmp(str, "--no_cancel", len) == 0) flag_char = 'n'; else if (strncmp(str, "--no_cancel", len) == 0) flag_char = 'n';
else if (strncmp(str, "--optional", len) == 0) flag_char = 'o'; else if (strncmp(str, "--optional", len) == 0) flag_char = 'o';
else if (strncmp(str, "--append", len) == 0) flag_char = 'p';
else if (strncmp(str, "--recursive", len) == 0) flag_char = 'r'; else if (strncmp(str, "--recursive", len) == 0) flag_char = 'r';
else if (strncmp(str, "--silent", len) == 0) flag_char = 's'; else if (strncmp(str, "--silent", len) == 0) flag_char = 's';
else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u'; else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u';
@ -1107,6 +1110,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
if (flags & _FLG('s')) flags_ext |= SILENT; if (flags & _FLG('s')) flags_ext |= SILENT;
if (flags & _FLG('w')) flags_ext |= OVERWRITE_ALL; if (flags & _FLG('w')) flags_ext |= OVERWRITE_ALL;
else if (flags & _FLG('k')) flags_ext |= SKIP_ALL; else if (flags & _FLG('k')) flags_ext |= SKIP_ALL;
else if (flags & _FLG('p')) flags_ext |= APPEND_ALL;
ret = PathMoveCopy(argv[1], argv[0], &flags_ext, false); ret = PathMoveCopy(argv[1], argv[0], &flags_ext, false);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "copy fail"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "copy fail");
} }
@ -1298,15 +1302,20 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
} }
else if (id == CMD_ID_EXTRCODE) { else if (id == CMD_ID_EXTRCODE) {
u64 filetype = IdentifyFileType(argv[0]); u64 filetype = IdentifyFileType(argv[0]);
if ((filetype&(GAME_NCCH|FLAG_CXI)) != (GAME_NCCH|FLAG_CXI)) { if (!FTYPE_HASCODE(filetype)) {
ret = false; ret = false;
if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a CXI file"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "does not contain .code");
} else { } else {
ShowString("Extracting .code, please wait..."); ShowString("Extracting .code, please wait...");
ret = (ExtractCodeFromCxiFile(argv[0], argv[1], NULL) == 0); ret = (ExtractCodeFromCxiFile(argv[0], argv[1], NULL) == 0);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "extract .code failed"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "extract .code failed");
} }
} }
else if (id == CMD_ID_CMPRCODE) {
ShowString("Compressing .code, please wait...");
ret = (CompressCode(argv[0], argv[1]) == 0);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "compress .code failed");
}
else if (id == CMD_ID_SDUMP) { else if (id == CMD_ID_SDUMP) {
ret = false; ret = false;
if (err_str) snprintf(err_str, _ERR_STR_LEN, "build failed"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "build failed");

View File

@ -141,8 +141,9 @@ imgmount S:/ctrnand_full.bin
# 'cp' COMMAND # 'cp' COMMAND
# Use 'cp' to copy a file or directory (recursively) # Use 'cp' to copy a file or directory (recursively)
# -h / --hash also adds a .sha file containing the files SHA256 # -h / --hash also adds a .sha file containing the files SHA256
# -w / --overwite forces overwrite on existing files # -w / --overwrite forces overwrite on existing files (disables -k and -p)
# -k / --skip forces skip on existing files # -k / --skip forces skip on existing files (disables -p)
# -p / --append will append copied files to the end of existing files (disables -h)
# -n / --no_cancel prevents user cancels (useful on critical operations) # -n / --no_cancel prevents user cancels (useful on critical operations)
cp -h -w -n 7:/dbs/ticket.db $[TESTPATH] cp -h -w -n 7:/dbs/ticket.db $[TESTPATH]
@ -155,8 +156,8 @@ imgumount
findnot $[TESTPATH]_???.rn RENPATH findnot $[TESTPATH]_???.rn RENPATH
# 'mv' COMMAND # 'mv' COMMAND
# The 'mv command renames or moves a file or directory # The 'mv' command renames or moves a file or directory
# -w / --overwite forces overwrite on existing files # -w / --overwrite forces overwrite on existing files (disables -k)
# -k / --skip forces skip on existing files # -k / --skip forces skip on existing files
# -n / --no_cancel prevents user cancels (useful on critical operations) # -n / --no_cancel prevents user cancels (useful on critical operations)
mv -w -k $[TESTPATH] $[RENPATH] mv -w -k $[TESTPATH] $[RENPATH]
@ -268,6 +269,17 @@ verify S:/firm1.bin
# -l / --legit force CIA to be legit (only works for legit system installed titles) # -l / --legit force CIA to be legit (only works for legit system installed titles)
# buildcia 0:/x.ncch # buildcia 0:/x.ncch
# 'extrcode' COMMAND
# You can extract the binary code from any file that contains it (NCSD, NCCH, CXI).
# Specify the containing file and the file to write to.
# If the code is compressed, it will be decompressed.
# C:/titleid.3ds 0:/gm9/out/titleid.dec.code
# 'cmprcode' COMMAND
# Attempt to open a file as uncompressed binary code and compress it into the 3DS's reverse LZSS format.
# Specify the source file and the file to write to.
# 0:/gm9/out/titleid.dec.code 0:/gm9/out/titleid.code
# 'sdump' COMMAND # 'sdump' COMMAND
# This command dumps a supported file to the standard output directory (0:/gm9/out) # This command dumps a supported file to the standard output directory (0:/gm9/out)
# Supported files: encTitleKeys.bin, decTitleKeys.bin, seeddb.bin # Supported files: encTitleKeys.bin, decTitleKeys.bin, seeddb.bin