mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-06 09:31:16 -04: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
|
; Controls whether dmnt cheats should be toggled on or off by
|
||||||
; default. 1 = toggled on by default, 0 = toggled off by default.
|
; default. 1 = toggled on by default, 0 = toggled off by default.
|
||||||
dmnt_cheats_enabled_by_default = u8!0x1
|
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? */
|
/* Should we enable cheats by default? */
|
||||||
static bool g_enable_cheats_by_default = true;
|
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. */
|
/* For debug event thread management. */
|
||||||
static HosMutex g_debug_event_thread_lock, g_attach_lock;
|
static HosMutex g_debug_event_thread_lock, g_attach_lock;
|
||||||
|
@ -83,6 +85,14 @@ void DmntCheatManager::CloseActiveCheatProcess() {
|
||||||
/* Close process resources. */
|
/* Close process resources. */
|
||||||
svcCloseHandle(g_cheat_process_debug_hnd);
|
svcCloseHandle(g_cheat_process_debug_hnd);
|
||||||
g_cheat_process_debug_hnd = 0;
|
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};
|
g_cheat_process_metadata = (CheatProcessMetadata){0};
|
||||||
|
|
||||||
/* Clear cheat list. */
|
/* Clear cheat list. */
|
||||||
|
@ -278,6 +288,17 @@ CheatEntry *DmntCheatManager::GetCheatEntryById(size_t i) {
|
||||||
return nullptr;
|
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) {
|
bool DmntCheatManager::ParseCheats(const char *s, size_t len) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
CheatEntry *cur_entry = NULL;
|
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));
|
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) {
|
Result DmntCheatManager::GetCheatCount(u64 *out_count) {
|
||||||
std::scoped_lock<HosMutex> lk(g_cheat_lock);
|
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. */
|
/* 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);
|
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. */
|
/* Open a debug handle. */
|
||||||
if (R_FAILED((rc = svcDebugActiveProcess(&g_cheat_process_debug_hnd, g_cheat_process_metadata.process_id)))) {
|
if (R_FAILED((rc = svcDebugActiveProcess(&g_cheat_process_debug_hnd, g_cheat_process_metadata.process_id)))) {
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -837,7 +1000,7 @@ void DmntCheatManager::OnNewApplicationLaunch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read cheats off the SD. */
|
/* 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. */
|
/* If we don't have cheats, or cheats are malformed, don't attach. */
|
||||||
StartDebugProcess(g_cheat_process_metadata.process_id);
|
StartDebugProcess(g_cheat_process_metadata.process_id);
|
||||||
g_cheat_process_metadata.process_id = 0;
|
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)))) {
|
if (R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", &en, sizeof(en)))) {
|
||||||
g_enable_cheats_by_default = (en != 0);
|
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. */
|
/* Initialize debug events manager. */
|
||||||
|
|
|
@ -42,9 +42,14 @@ class DmntCheatManager {
|
||||||
static void ResetAllCheatEntries();
|
static void ResetAllCheatEntries();
|
||||||
static CheatEntry *GetFreeCheatEntry();
|
static CheatEntry *GetFreeCheatEntry();
|
||||||
static CheatEntry *GetCheatEntryById(size_t i);
|
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 ParseCheats(const char *cht_txt, size_t len);
|
||||||
static bool LoadCheats(u64 title_id, const u8 *build_id);
|
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();
|
static void ResetFrozenAddresses();
|
||||||
public:
|
public:
|
||||||
static bool GetHasActiveCheatProcess();
|
static bool GetHasActiveCheatProcess();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue