mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
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:
parent
2d807915e6
commit
5c138e1219
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user