mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-13 00:26:35 +00:00
dmnt-cheat: Add support for saving/restoring cheat toggle state
This commit is contained in:
parent
20ba6432b9
commit
f4950ff26e
3 changed files with 333 additions and 157 deletions
|
@ -12,3 +12,7 @@ power_menu_reboot_function = str!payload
|
|||
; Controls whether dmnt cheats should be toggled on or off by
|
||||
; default. 1 = toggled on by default, 0 = toggled off by default.
|
||||
dmnt_cheats_enabled_by_default = u8!0x1
|
||||
; Controls whether dmnt should always save cheat toggle state
|
||||
; for restoration on new game launch. 1 = always save toggles,
|
||||
; 0 = only save toggles if toggle file exists.
|
||||
dmnt_always_save_cheat_toggles = u8!0x0
|
|
@ -33,6 +33,8 @@ static Handle g_cheat_process_debug_hnd = 0;
|
|||
|
||||
/* Should we enable cheats by default? */
|
||||
static bool g_enable_cheats_by_default = true;
|
||||
static bool g_always_save_cheat_toggles = false;
|
||||
static bool g_should_save_cheat_toggles = false;
|
||||
|
||||
/* For debug event thread management. */
|
||||
static HosMutex g_debug_event_thread_lock, g_attach_lock;
|
||||
|
@ -83,6 +85,14 @@ void DmntCheatManager::CloseActiveCheatProcess() {
|
|||
/* Close process resources. */
|
||||
svcCloseHandle(g_cheat_process_debug_hnd);
|
||||
g_cheat_process_debug_hnd = 0;
|
||||
|
||||
/* Save cheat toggles. */
|
||||
if (g_always_save_cheat_toggles || g_should_save_cheat_toggles) {
|
||||
SaveCheatToggles(g_cheat_process_metadata.title_id);
|
||||
g_should_save_cheat_toggles = false;
|
||||
}
|
||||
|
||||
/* Clear metadata. */
|
||||
g_cheat_process_metadata = (CheatProcessMetadata){0};
|
||||
|
||||
/* Clear cheat list. */
|
||||
|
@ -278,6 +288,17 @@ CheatEntry *DmntCheatManager::GetCheatEntryById(size_t i) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CheatEntry *DmntCheatManager::GetCheatEntryByReadableName(const char *readable_name) {
|
||||
/* Check all non-master cheats for match. */
|
||||
for (size_t i = 1; i < DmntCheatManager::MaxCheatCount; i++) {
|
||||
if (strcmp(g_cheat_entries[i].definition.readable_name, readable_name) == 0) {
|
||||
return &g_cheat_entries[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DmntCheatManager::ParseCheats(const char *s, size_t len) {
|
||||
size_t i = 0;
|
||||
CheatEntry *cur_entry = NULL;
|
||||
|
@ -427,6 +448,145 @@ bool DmntCheatManager::LoadCheats(u64 title_id, const u8 *build_id) {
|
|||
return ParseCheats(cht_txt, strlen(cht_txt));
|
||||
}
|
||||
|
||||
bool DmntCheatManager::ParseCheatToggles(const char *s, size_t len) {
|
||||
size_t i = 0;
|
||||
char cur_cheat_name[sizeof(CheatEntry::definition.readable_name)];
|
||||
char toggle[8];
|
||||
|
||||
while (i < len) {
|
||||
if (isspace(s[i])) {
|
||||
/* Just ignore space. */
|
||||
i++;
|
||||
} else if (s[i] == '[') {
|
||||
|
||||
/* Extract name bounds. */
|
||||
size_t j = i + 1;
|
||||
while (s[j] != ']') {
|
||||
j++;
|
||||
if (j >= len || (j - i - 1) >= sizeof(cur_cheat_name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* s[i+1:j] is cheat name. */
|
||||
const size_t cheat_name_len = (j - i - 1);
|
||||
memcpy(cur_cheat_name, &s[i+1], cheat_name_len);
|
||||
cur_cheat_name[cheat_name_len] = 0;
|
||||
|
||||
/* Skip onwards. */
|
||||
i = j + 1;
|
||||
|
||||
/* Skip whitespace. */
|
||||
while (isspace(s[i])) {
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Parse whether to toggle. */
|
||||
j = i + 1;
|
||||
while (!isspace(s[j])) {
|
||||
j++;
|
||||
if (j >= len || (j - i) >= sizeof(toggle)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* s[i:j] is toggle. */
|
||||
const size_t toggle_len = (j - i);
|
||||
memcpy(toggle, &s[i], toggle_len);
|
||||
toggle[toggle_len] = 0;
|
||||
|
||||
/* Allow specifying toggle for not present cheat. */
|
||||
CheatEntry *entry = GetCheatEntryByReadableName(cur_cheat_name);
|
||||
if (entry != nullptr) {
|
||||
if (strcasecmp(toggle, "1") == 0 || strcasecmp(toggle, "true") == 0 || strcasecmp(toggle, "on") == 0) {
|
||||
entry->enabled = true;
|
||||
} else if (strcasecmp(toggle, "0") == 0 || strcasecmp(toggle, "false") == 0 || strcasecmp(toggle, "off") == 0) {
|
||||
entry->enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip onwards. */
|
||||
i = j + 1;
|
||||
} else {
|
||||
/* Unexpected character encountered. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DmntCheatManager::LoadCheatToggles(u64 title_id) {
|
||||
FILE *f_tg = NULL;
|
||||
/* Open the file for title id. */
|
||||
{
|
||||
char path[FS_MAX_PATH+1] = {0};
|
||||
snprintf(path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/cheats/toggles.txt", title_id);
|
||||
|
||||
f_tg = fopen(path, "rb");
|
||||
}
|
||||
|
||||
/* Unless we successfully parse, don't save toggles on close. */
|
||||
g_should_save_cheat_toggles = false;
|
||||
|
||||
/* Check for NULL, which is allowed. */
|
||||
if (f_tg == NULL) {
|
||||
return true;
|
||||
}
|
||||
ON_SCOPE_EXIT { fclose(f_tg); };
|
||||
|
||||
/* Get file size. */
|
||||
fseek(f_tg, 0L, SEEK_END);
|
||||
const size_t tg_sz = ftell(f_tg);
|
||||
fseek(f_tg, 0L, SEEK_SET);
|
||||
|
||||
/* Allocate toggle txt buffer. */
|
||||
char *tg_txt = (char *)malloc(tg_sz + 1);
|
||||
if (tg_txt == NULL) {
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT { free(tg_txt); };
|
||||
|
||||
/* Read toggles into buffer. */
|
||||
if (fread(tg_txt, 1, tg_sz, f_tg) != tg_sz) {
|
||||
return false;
|
||||
}
|
||||
tg_txt[tg_sz] = 0;
|
||||
|
||||
/* Parse toggle buffer. */
|
||||
g_should_save_cheat_toggles = ParseCheatToggles(tg_txt, strlen(tg_txt));
|
||||
return g_should_save_cheat_toggles;
|
||||
}
|
||||
|
||||
void DmntCheatManager::SaveCheatToggles(u64 title_id) {
|
||||
FILE *f_tg = NULL;
|
||||
/* Open the file for title id. */
|
||||
{
|
||||
char path[FS_MAX_PATH+1] = {0};
|
||||
snprintf(path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/cheats/toggles.txt", title_id);
|
||||
|
||||
f_tg = fopen(path, "wb");
|
||||
}
|
||||
|
||||
if (f_tg == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT { fclose(f_tg); };
|
||||
|
||||
/* Save all non-master cheats. */
|
||||
for (size_t i = 1; i < DmntCheatManager::MaxCheatCount; i++) {
|
||||
if (g_cheat_entries[i].definition.num_opcodes != 0) {
|
||||
fprintf(f_tg, "[%s]\n", g_cheat_entries[i].definition.readable_name);
|
||||
if (g_cheat_entries[i].enabled) {
|
||||
fprintf(f_tg, "true\n");
|
||||
} else {
|
||||
fprintf(f_tg, "false\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result DmntCheatManager::GetCheatCount(u64 *out_count) {
|
||||
std::scoped_lock<HosMutex> lk(g_cheat_lock);
|
||||
|
||||
|
@ -752,6 +912,9 @@ Result DmntCheatManager::ForceOpenCheatProcess() {
|
|||
/* This is allowed to fail. We may not have any cheats. */
|
||||
LoadCheats(g_cheat_process_metadata.title_id, g_cheat_process_metadata.main_nso_build_id);
|
||||
|
||||
/* Load saved toggles, if present. */
|
||||
LoadCheatToggles(g_cheat_process_metadata.title_id);
|
||||
|
||||
/* Open a debug handle. */
|
||||
if (R_FAILED((rc = svcDebugActiveProcess(&g_cheat_process_debug_hnd, g_cheat_process_metadata.process_id)))) {
|
||||
return rc;
|
||||
|
@ -837,7 +1000,7 @@ void DmntCheatManager::OnNewApplicationLaunch() {
|
|||
}
|
||||
|
||||
/* Read cheats off the SD. */
|
||||
if (!LoadCheats(g_cheat_process_metadata.title_id, g_cheat_process_metadata.main_nso_build_id)) {
|
||||
if (!LoadCheats(g_cheat_process_metadata.title_id, g_cheat_process_metadata.main_nso_build_id) || !LoadCheatToggles(g_cheat_process_metadata.title_id)) {
|
||||
/* If we don't have cheats, or cheats are malformed, don't attach. */
|
||||
StartDebugProcess(g_cheat_process_metadata.process_id);
|
||||
g_cheat_process_metadata.process_id = 0;
|
||||
|
@ -955,6 +1118,10 @@ void DmntCheatManager::InitializeCheatManager() {
|
|||
if (R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", &en, sizeof(en)))) {
|
||||
g_enable_cheats_by_default = (en != 0);
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "dmnt_always_save_cheat_toggles", &en, sizeof(en)))) {
|
||||
g_always_save_cheat_toggles = (en != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize debug events manager. */
|
||||
|
|
|
@ -42,9 +42,14 @@ class DmntCheatManager {
|
|||
static void ResetAllCheatEntries();
|
||||
static CheatEntry *GetFreeCheatEntry();
|
||||
static CheatEntry *GetCheatEntryById(size_t i);
|
||||
static CheatEntry *GetCheatEntryByReadableName(const char *readable_name);
|
||||
static bool ParseCheats(const char *cht_txt, size_t len);
|
||||
static bool LoadCheats(u64 title_id, const u8 *build_id);
|
||||
|
||||
static bool ParseCheatToggles(const char *s, size_t len);
|
||||
static bool LoadCheatToggles(u64 title_id);
|
||||
static void SaveCheatToggles(u64 title_id);
|
||||
|
||||
static void ResetFrozenAddresses();
|
||||
public:
|
||||
static bool GetHasActiveCheatProcess();
|
||||
|
|
Loading…
Reference in a new issue