From 3b4ef682fe74957a7abb39d4cf09fae5784c834f Mon Sep 17 00:00:00 2001 From: piepie62 Date: Mon, 15 Apr 2019 15:14:53 -0700 Subject: [PATCH 1/5] Debuffer cheat reading and strip whitespace Effectively doubles the amount of cheats possible, while also getting rid of that annoying line limit --- sysmodules/rosalina/source/menus/cheats.c | 181 +++++++++++++++------- 1 file changed, 127 insertions(+), 54 deletions(-) diff --git a/sysmodules/rosalina/source/menus/cheats.c b/sysmodules/rosalina/source/menus/cheats.c index 8667f79..809ae71 100644 --- a/sysmodules/rosalina/source/menus/cheats.c +++ b/sysmodules/rosalina/source/menus/cheats.c @@ -55,10 +55,16 @@ typedef struct CheatDescription u64 codes[0]; } CheatDescription; +typedef struct BufferedFile +{ + IFile file; + u64 curPos; + u64 maxPos; + char buffer[512]; +} BufferedFile; + CheatDescription* cheats[1024] = { 0 }; -u8 cheatFileBuffer[16384] = { 0 }; -u32 cheatFilePos = 0; -u8 cheatBuffer[16384] = { 0 }; +u8 cheatBuffer[32768] = { 0 }; static CheatProcessInfo cheatinfo[0x40] = { 0 }; @@ -883,35 +889,95 @@ static void Cheat_AddCode(CheatDescription* cheat, u64 code) } } -static Result Cheat_ReadLine(char* line) +static Result BufferedFile_Open(BufferedFile* file, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 flags) +{ + Result res = 0; + memset(file->buffer, '\0', sizeof(file->buffer)); + res = IFile_Open(&file->file, archiveId, archivePath, filePath, flags); + if (R_SUCCEEDED(res)) + { + file->curPos = 0; + res = IFile_Read(&file->file, &file->maxPos, file->buffer, sizeof(file->buffer)); + } + return res; +} + +static Result BufferedFile_Read(BufferedFile* file, u64* totalRead, void* buffer, u32 len) +{ + Result res = 0; + if (len == 0) + { + *totalRead = 0; + return 0; + } + else if (file->curPos + len < file->maxPos) + { + memcpy(buffer, file->buffer + file->curPos, len); + file->curPos += len; + *totalRead = len; + } + else + { + *totalRead = 0; + while(R_SUCCEEDED(res) && file->maxPos != 0 && *totalRead < len) + { + u32 toRead = file->maxPos - file->curPos < len - *totalRead ? file->maxPos - file->curPos : len - *totalRead; + memcpy(buffer + *totalRead, file->buffer + file->curPos, toRead); + *totalRead += toRead; + file->curPos += toRead; + if (file->curPos >= file->maxPos) + { + res = IFile_Read(&file->file, &file->maxPos, file->buffer, sizeof(file->buffer)); + file->curPos = 0; + } + } + } + return res; +} + +static Result Cheat_ReadLine(BufferedFile* file, char* line, u32 lineSize) { Result res = 0; - char c = '\0'; u32 idx = 0; - while (R_SUCCEEDED(res)) + u64 total = 0; + bool lastWasCarriageReturn = false; + while (R_SUCCEEDED(res) && idx < lineSize) { - c = cheatFileBuffer[cheatFilePos++]; - res = c ? 0 : -1; - if (R_SUCCEEDED(res) && c != '\0') + res = BufferedFile_Read(file, &total, line + idx, 1); + if (total == 0) { - if (c == '\n' || c == '\r' || idx >= 1023) + line[idx] = '\0'; + return -1; + } + if (R_SUCCEEDED(res)) + { + if (line[idx] == '\r') { - line[idx++] = '\0'; - return idx; + lastWasCarriageReturn = true; + } + else if (line[idx] == '\n') + { + if (lastWasCarriageReturn) + { + line[--idx] = '\0'; + return idx; + } + else + { + line[idx] = '\0'; + return idx; + } + } + else if (line[idx] == '\0') + { + return -1; } else { - line[idx++] = c; - } - } - else - { - if (idx > 0) - { - line[idx++] = '\0'; - return idx; + lastWasCarriageReturn = false; } + idx++; } } return res; @@ -998,6 +1064,22 @@ static u64 Cheat_GetCode(const char *line) return tmp; } +static char* stripWhitespace(char* in) +{ + char* ret = in; + while (*ret == ' ' || *ret == '\t') + { + ret++; + } + int back = strlen(ret) - 1; + while (back > 0 && (ret[back] == ' ' || ret[back] == '\t')) + { + back--; + } + ret[back+1] = '\0'; + return ret; +} + static void Cheat_LoadCheatsIntoMemory(u64 titleId) { cheatCount = 0; @@ -1007,54 +1089,46 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) char path[64] = { 0 }; sprintf(path, "/luma/titles/%016llX/cheats.txt", titleId); - IFile file; + BufferedFile file; - if (R_FAILED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) + if (R_FAILED(BufferedFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) { // OK, let's try another source sprintf(path, "/cheats/%016llX.txt", titleId); - if (R_FAILED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) return; + if (R_FAILED(BufferedFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) return; }; - u64 fileLen = 0; - IFile_GetSize(&file, &fileLen); - if (fileLen > 16383) - { - fileLen = 16383; - } - - u64 total; - IFile_Read(&file, &total, cheatFileBuffer, fileLen); - IFile_Close(&file); - for (int i = fileLen; i < 16384; i++) - { - cheatFileBuffer[i] = 0; - } - char line[1024] = { 0 }; Result res = 0; CheatDescription* cheat = 0; - cheatFilePos = 0; + u32 cheatSize = 0; do { - res = Cheat_ReadLine(line); + res = Cheat_ReadLine(&file, line, 1024); if (R_SUCCEEDED(res)) { - s32 lineLen = strnlen(line, 1023); + char* strippedLine = stripWhitespace(line); + s32 lineLen = strnlen(strippedLine, 1023); if (!lineLen) { continue; } - if (line[0] == '#') + if (strippedLine[0] == '#') { continue; } - if (Cheat_IsCodeLine(line)) + if (Cheat_IsCodeLine(strippedLine)) { + if (cheatSize + sizeof(u64) >= sizeof(cheatBuffer)) + { + cheatCount--; + break; + } if (cheat) { - u64 tmp = Cheat_GetCode(line); + u64 tmp = Cheat_GetCode(strippedLine); Cheat_AddCode(cheat, tmp); + cheatSize += sizeof(u64); if (((tmp >> 32) & 0xFFFFFFFF) == 0xDD000000) { if (tmp & 0xFFFFFFFF) @@ -1068,23 +1142,22 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) } else { - if (!cheat) + if (!cheat || cheat->codesCount > 0) { - cheat = Cheat_AllocCheat(); - } - else - { - if (cheat->codesCount > 0) + if (cheatSize + sizeof(CheatDescription) >= sizeof(cheatBuffer)) { - // Add new cheat only if previous has body. In other case just rewrite it's name - cheat = Cheat_AllocCheat(); + break; } + cheat = Cheat_AllocCheat(); + cheatSize += sizeof(CheatDescription); } strncpy(cheat->name, line, 38); cheat->name[38] = '\0'; } } - } while (R_SUCCEEDED(res)); + } while (R_SUCCEEDED(res) && cheatSize < sizeof(cheatBuffer)); + + IFile_Close(&file.file); if ((cheatCount > 0) && (cheats[cheatCount - 1]->codesCount == 0)) { From f5f3764f99aceb7989c47592cd209f32bde5a75f Mon Sep 17 00:00:00 2001 From: piepie62 Date: Sun, 14 Apr 2019 23:12:14 -0700 Subject: [PATCH 2/5] Improve cheat execution parameters and handle CRLF properly --- sysmodules/rosalina/source/menus/cheats.c | 50 ++++++++--------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/sysmodules/rosalina/source/menus/cheats.c b/sysmodules/rosalina/source/menus/cheats.c index 8667f79..e32d321 100644 --- a/sysmodules/rosalina/source/menus/cheats.c +++ b/sysmodules/rosalina/source/menus/cheats.c @@ -48,9 +48,8 @@ typedef struct CheatDescription { u32 active; u32 valid; - u32 keyActivated; - u32 keyCombo; - char name[40]; + char hasKeyCode; + char name[39]; u32 codesCount; u64 codes[0]; } CheatDescription; @@ -110,7 +109,6 @@ typedef struct CheatState CheatState cheat_state = { 0 }; u8 cheatCount = 0; -u8 hasKeyActivated = 0; u64 cheatTitleInfo = -1ULL; char failureReason[64]; @@ -840,6 +838,7 @@ static Result Cheat_MapMemoryAndApplyCheat(u32 pid, CheatDescription* const chea } else { + sprintf(failureReason, "Debug process failed"); svcCloseHandle(processHandle); } } @@ -865,8 +864,7 @@ static CheatDescription* Cheat_AllocCheat() cheat->active = 0; cheat->valid = 1; cheat->codesCount = 0; - cheat->keyActivated = 0; - cheat->keyCombo = 0; + cheat->hasKeyCode = 0; cheat->name[0] = '\0'; cheats[cheatCount] = cheat; @@ -883,7 +881,7 @@ static void Cheat_AddCode(CheatDescription* cheat, u64 code) } } -static Result Cheat_ReadLine(char* line) +static Result Cheat_ReadLine(char* line, u32 lineSize) { Result res = 0; @@ -895,7 +893,13 @@ static Result Cheat_ReadLine(char* line) res = c ? 0 : -1; if (R_SUCCEEDED(res) && c != '\0') { - if (c == '\n' || c == '\r' || idx >= 1023) + if (c == '\r' && cheatFileBuffer[cheatFilePos] == '\n') + { + cheatFilePos++; + line[idx++] = '\0'; + return idx; + } + else if (c == '\n' || idx >= lineSize - 1) { line[idx++] = '\0'; return idx; @@ -1002,7 +1006,6 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) { cheatCount = 0; cheatTitleInfo = titleId; - hasKeyActivated = 0; char path[64] = { 0 }; sprintf(path, "/luma/titles/%016llX/cheats.txt", titleId); @@ -1037,7 +1040,7 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) cheatFilePos = 0; do { - res = Cheat_ReadLine(line); + res = Cheat_ReadLine(line, 1024); if (R_SUCCEEDED(res)) { s32 lineLen = strnlen(line, 1023); @@ -1057,12 +1060,7 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) Cheat_AddCode(cheat, tmp); if (((tmp >> 32) & 0xFFFFFFFF) == 0xDD000000) { - if (tmp & 0xFFFFFFFF) - { - // Not empty key code - cheat->keyCombo |= (tmp & 0xFFF); - cheat->keyActivated = 1; - } + cheat->hasKeyCode = 1; } } } @@ -1134,28 +1132,21 @@ void Cheat_ApplyCheats(void) if (!titleId) { cheatCount = 0; - hasKeyActivated = 0; return; } if (titleId != cheatTitleInfo) { cheatCount = 0; - hasKeyActivated = 0; return; } - u32 keys = HID_PAD & 0xFFF; for (int i = 0; i < cheatCount; i++) { - if (cheats[i]->active && !(cheats[i]->keyActivated)) + if (cheats[i]->active) { Cheat_MapMemoryAndApplyCheat(pid, cheats[i]); } - else if (cheats[i]->active && cheats[i]->keyActivated && (cheats[i]->keyCombo & keys) == keys) - { - Cheat_MapMemoryAndApplyCheat(pid, cheats[i]); - } } } @@ -1217,7 +1208,7 @@ void RosalinaMenu_Cheats(void) char buf[65] = { 0 }; s32 j = page * CHEATS_PER_MENU_PAGE + i; const char * checkbox = (cheats[j]->active ? "(x) " : "( ) "); - const char * keyAct = (cheats[j]->keyActivated ? "*" : " "); + const char * keyAct = (cheats[j]->hasKeyCode ? "*" : " "); sprintf(buf, "%s%s%s", checkbox, keyAct, cheats[j]->name); Draw_DrawString(30, 30 + i * SPACING_Y, cheats[j]->valid ? COLOR_WHITE : COLOR_RED, buf); @@ -1253,15 +1244,6 @@ void RosalinaMenu_Cheats(void) { r = Cheat_MapMemoryAndApplyCheat(pid, cheats[selected]); } - hasKeyActivated = 0; - for (int i = 0; i < cheatCount; i++) - { - if (cheats[i]->active && cheats[i]->keyActivated) - { - hasKeyActivated = 1; - break; - } - } } else if (pressed & BUTTON_DOWN) selected++; From 353f7d1705679bfb1ba0a5b5d9d77509dddaa583 Mon Sep 17 00:00:00 2001 From: piepie62 Date: Mon, 15 Apr 2019 03:41:25 -0700 Subject: [PATCH 3/5] Add bounds checking --- sysmodules/rosalina/source/menus/cheats.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysmodules/rosalina/source/menus/cheats.c b/sysmodules/rosalina/source/menus/cheats.c index e32d321..5adfe4b 100644 --- a/sysmodules/rosalina/source/menus/cheats.c +++ b/sysmodules/rosalina/source/menus/cheats.c @@ -887,13 +887,13 @@ static Result Cheat_ReadLine(char* line, u32 lineSize) char c = '\0'; u32 idx = 0; - while (R_SUCCEEDED(res)) + while (R_SUCCEEDED(res) && cheatFilePos < sizeof(cheatFileBuffer)) { c = cheatFileBuffer[cheatFilePos++]; res = c ? 0 : -1; if (R_SUCCEEDED(res) && c != '\0') { - if (c == '\r' && cheatFileBuffer[cheatFilePos] == '\n') + if (c == '\r' && cheatFilePos < sizeof(cheatFileBuffer) && cheatFileBuffer[cheatFilePos] == '\n') { cheatFilePos++; line[idx++] = '\0'; From 1e0baeb30e64255e116b1df87953cd2195e85e05 Mon Sep 17 00:00:00 2001 From: piepie62 Date: Mon, 15 Apr 2019 13:08:53 -0700 Subject: [PATCH 4/5] Fix reading past the end of a file --- sysmodules/rosalina/source/ifile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysmodules/rosalina/source/ifile.c b/sysmodules/rosalina/source/ifile.c index 18d8c00..430620a 100644 --- a/sysmodules/rosalina/source/ifile.c +++ b/sysmodules/rosalina/source/ifile.c @@ -71,7 +71,7 @@ Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len) while (1) { res = FSFILE_Read(file->handle, &read, file->pos, buf, left); - if (R_FAILED(res)) + if (R_FAILED(res) || read == 0) { break; } From 90a43089ba9b9f72ecadca747d7acc0132ccc88f Mon Sep 17 00:00:00 2001 From: piepie62 Date: Mon, 15 Apr 2019 15:14:53 -0700 Subject: [PATCH 5/5] Debuffer cheat reading and strip whitespace Effectively doubles the amount of cheats possible, while also getting rid of that annoying line limit --- sysmodules/rosalina/source/menus/cheats.c | 183 +++++++++++++++------- 1 file changed, 125 insertions(+), 58 deletions(-) diff --git a/sysmodules/rosalina/source/menus/cheats.c b/sysmodules/rosalina/source/menus/cheats.c index 5adfe4b..ce268b9 100644 --- a/sysmodules/rosalina/source/menus/cheats.c +++ b/sysmodules/rosalina/source/menus/cheats.c @@ -54,10 +54,16 @@ typedef struct CheatDescription u64 codes[0]; } CheatDescription; +typedef struct BufferedFile +{ + IFile file; + u64 curPos; + u64 maxPos; + char buffer[512]; +} BufferedFile; + CheatDescription* cheats[1024] = { 0 }; -u8 cheatFileBuffer[16384] = { 0 }; -u32 cheatFilePos = 0; -u8 cheatBuffer[16384] = { 0 }; +u8 cheatBuffer[32768] = { 0 }; static CheatProcessInfo cheatinfo[0x40] = { 0 }; @@ -881,41 +887,95 @@ static void Cheat_AddCode(CheatDescription* cheat, u64 code) } } -static Result Cheat_ReadLine(char* line, u32 lineSize) +static Result BufferedFile_Open(BufferedFile* file, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 flags) +{ + Result res = 0; + memset(file->buffer, '\0', sizeof(file->buffer)); + res = IFile_Open(&file->file, archiveId, archivePath, filePath, flags); + if (R_SUCCEEDED(res)) + { + file->curPos = 0; + res = IFile_Read(&file->file, &file->maxPos, file->buffer, sizeof(file->buffer)); + } + return res; +} + +static Result BufferedFile_Read(BufferedFile* file, u64* totalRead, void* buffer, u32 len) +{ + Result res = 0; + if (len == 0) + { + *totalRead = 0; + return 0; + } + else if (file->curPos + len < file->maxPos) + { + memcpy(buffer, file->buffer + file->curPos, len); + file->curPos += len; + *totalRead = len; + } + else + { + *totalRead = 0; + while(R_SUCCEEDED(res) && file->maxPos != 0 && *totalRead < len) + { + u32 toRead = file->maxPos - file->curPos < len - *totalRead ? file->maxPos - file->curPos : len - *totalRead; + memcpy(buffer + *totalRead, file->buffer + file->curPos, toRead); + *totalRead += toRead; + file->curPos += toRead; + if (file->curPos >= file->maxPos) + { + res = IFile_Read(&file->file, &file->maxPos, file->buffer, sizeof(file->buffer)); + file->curPos = 0; + } + } + } + return res; +} + +static Result Cheat_ReadLine(BufferedFile* file, char* line, u32 lineSize) { Result res = 0; - char c = '\0'; u32 idx = 0; - while (R_SUCCEEDED(res) && cheatFilePos < sizeof(cheatFileBuffer)) + u64 total = 0; + bool lastWasCarriageReturn = false; + while (R_SUCCEEDED(res) && idx < lineSize) { - c = cheatFileBuffer[cheatFilePos++]; - res = c ? 0 : -1; - if (R_SUCCEEDED(res) && c != '\0') + res = BufferedFile_Read(file, &total, line + idx, 1); + if (total == 0) { - if (c == '\r' && cheatFilePos < sizeof(cheatFileBuffer) && cheatFileBuffer[cheatFilePos] == '\n') + line[idx] = '\0'; + return -1; + } + if (R_SUCCEEDED(res)) + { + if (line[idx] == '\r') { - cheatFilePos++; - line[idx++] = '\0'; - return idx; + lastWasCarriageReturn = true; } - else if (c == '\n' || idx >= lineSize - 1) + else if (line[idx] == '\n') { - line[idx++] = '\0'; - return idx; + if (lastWasCarriageReturn) + { + line[--idx] = '\0'; + return idx; + } + else + { + line[idx] = '\0'; + return idx; + } + } + else if (line[idx] == '\0') + { + return -1; } else { - line[idx++] = c; - } - } - else - { - if (idx > 0) - { - line[idx++] = '\0'; - return idx; + lastWasCarriageReturn = false; } + idx++; } } return res; @@ -1002,6 +1062,22 @@ static u64 Cheat_GetCode(const char *line) return tmp; } +static char* stripWhitespace(char* in) +{ + char* ret = in; + while (*ret == ' ' || *ret == '\t') + { + ret++; + } + int back = strlen(ret) - 1; + while (back > 0 && (ret[back] == ' ' || ret[back] == '\t')) + { + back--; + } + ret[back+1] = '\0'; + return ret; +} + static void Cheat_LoadCheatsIntoMemory(u64 titleId) { cheatCount = 0; @@ -1010,54 +1086,46 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) char path[64] = { 0 }; sprintf(path, "/luma/titles/%016llX/cheats.txt", titleId); - IFile file; + BufferedFile file; - if (R_FAILED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) + if (R_FAILED(BufferedFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) { // OK, let's try another source sprintf(path, "/cheats/%016llX.txt", titleId); - if (R_FAILED(IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) return; + if (R_FAILED(BufferedFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ))) return; }; - u64 fileLen = 0; - IFile_GetSize(&file, &fileLen); - if (fileLen > 16383) - { - fileLen = 16383; - } - - u64 total; - IFile_Read(&file, &total, cheatFileBuffer, fileLen); - IFile_Close(&file); - for (int i = fileLen; i < 16384; i++) - { - cheatFileBuffer[i] = 0; - } - char line[1024] = { 0 }; Result res = 0; CheatDescription* cheat = 0; - cheatFilePos = 0; + u32 cheatSize = 0; do { - res = Cheat_ReadLine(line, 1024); + res = Cheat_ReadLine(&file, line, 1024); if (R_SUCCEEDED(res)) { - s32 lineLen = strnlen(line, 1023); + char* strippedLine = stripWhitespace(line); + s32 lineLen = strnlen(strippedLine, 1023); if (!lineLen) { continue; } - if (line[0] == '#') + if (strippedLine[0] == '#') { continue; } - if (Cheat_IsCodeLine(line)) + if (Cheat_IsCodeLine(strippedLine)) { + if (cheatSize + sizeof(u64) >= sizeof(cheatBuffer)) + { + cheatCount--; + break; + } if (cheat) { - u64 tmp = Cheat_GetCode(line); + u64 tmp = Cheat_GetCode(strippedLine); Cheat_AddCode(cheat, tmp); + cheatSize += sizeof(u64); if (((tmp >> 32) & 0xFFFFFFFF) == 0xDD000000) { cheat->hasKeyCode = 1; @@ -1066,23 +1134,22 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId) } else { - if (!cheat) + if (!cheat || cheat->codesCount > 0) { - cheat = Cheat_AllocCheat(); - } - else - { - if (cheat->codesCount > 0) + if (cheatSize + sizeof(CheatDescription) >= sizeof(cheatBuffer)) { - // Add new cheat only if previous has body. In other case just rewrite it's name - cheat = Cheat_AllocCheat(); + break; } + cheat = Cheat_AllocCheat(); + cheatSize += sizeof(CheatDescription); } strncpy(cheat->name, line, 38); cheat->name[38] = '\0'; } } - } while (R_SUCCEEDED(res)); + } while (R_SUCCEEDED(res) && cheatSize < sizeof(cheatBuffer)); + + IFile_Close(&file.file); if ((cheatCount > 0) && (cheats[cheatCount - 1]->codesCount == 0)) {