RetroArch/cheat_manager.c
2021-08-19 20:02:39 -04:00

1758 lines
57 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <file/config_file.h>
#include <file/file_path.h>
#include <compat/strl.h>
#include <compat/posix_string.h>
#include <string/stdstring.h>
#include <retro_miscellaneous.h>
#include <features/features_cpu.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_MENU
#include "menu/menu_driver.h"
#endif
#ifdef HAVE_CHEEVOS
#include "cheevos/cheevos.h"
#endif
#include "cheat_manager.h"
#include "msg_hash.h"
#include "configuration.h"
#include "retroarch.h"
#include "dynamic.h"
#include "core.h"
#include "verbosity.h"
/* TODO/FIXME - public global variables */
cheat_manager_t cheat_manager_state;
unsigned cheat_manager_get_buf_size(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
return cheat_st->buf_size;
}
unsigned cheat_manager_get_size(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
return cheat_st->size;
}
#ifdef HAVE_CHEEVOS
static void cheat_manager_pause_cheevos(void)
{
rcheevos_pause_hardcore();
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_DISABLED_CHEAT), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_LOG("%s\n", msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_DISABLED_CHEAT));
}
#endif
void cheat_manager_apply_cheats(void)
{
unsigned i, idx = 0;
settings_t *settings = config_get_ptr();
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
return;
core_reset_cheat();
for (i = 0; i < cheat_st->size; i++)
{
if ( cheat_st->cheats[i].state
&& cheat_st->cheats[i].handler == CHEAT_HANDLER_TYPE_EMU)
{
retro_ctx_cheat_info_t cheat_info;
cheat_info.index = idx++;
cheat_info.enabled = true;
cheat_info.code = cheat_st->cheats[i].code;
if (!string_is_empty(cheat_info.code))
core_set_cheat(&cheat_info);
}
}
if (cheat_st->size > 0 && settings->bools.notification_show_cheats_applied)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_APPLYING_CHEAT), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_LOG("%s\n", msg_hash_to_str(MSG_APPLYING_CHEAT));
}
#ifdef HAVE_CHEEVOS
if (idx != 0 && rcheevos_hardcore_active())
cheat_manager_pause_cheevos();
#endif
}
void cheat_manager_set_code(unsigned i, const char *str)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
return;
if (!string_is_empty(str))
strcpy(cheat_st->cheats[i].code, str);
cheat_st->cheats[i].state = true;
}
/**
* cheat_manager_save:
* @path : Path to cheats file (relative path).
*
* Saves cheats to file on disk.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool cheat_manager_save(
const char *path,
const char *cheat_database,
bool overwrite)
{
bool ret;
unsigned i;
char cheats_file[PATH_MAX_LENGTH];
config_file_t *conf = NULL;
cheat_manager_t *cheat_st = &cheat_manager_state;
unsigned int* data_ptrs[16] = { NULL};
char* keys[16] = {
(char*)"cheat%u_handler",
(char*)"cheat%u_memory_search_size",
(char*)"cheat%u_cheat_type",
(char*)"cheat%u_value",
(char*)"cheat%u_address",
(char*)"cheat%u_address_bit_position",
(char*)"cheat%u_rumble_type",
(char*)"cheat%u_rumble_value",
(char*)"cheat%u_rumble_port",
(char*)"cheat%u_rumble_primary_strength",
(char*)"cheat%u_rumble_primary_duration",
(char*)"cheat%u_rumble_secondary_strength",
(char*)"cheat%u_rumble_secondary_duration",
(char*)"cheat%u_repeat_count",
(char*)"cheat%u_repeat_add_to_value",
(char*)"cheat%u_repeat_add_to_address"
};
cheats_file[0] = '\0';
if (!cheat_st->cheats || cheat_st->size == 0)
return false;
if (!cheat_database)
strlcpy(cheats_file, path, sizeof(cheats_file));
else
fill_pathname_join_concat(cheats_file,
cheat_database, path,
".cht",
sizeof(cheats_file));
if (!overwrite)
conf = config_file_new_from_path_to_string(cheats_file);
if (!conf)
if (!(conf = config_file_new_alloc()))
return false;
conf->guaranteed_no_duplicates = true;
config_set_int(conf, "cheats", cheat_st->size);
for (i = 0; i < cheat_st->size; i++)
{
unsigned j;
char endian_key[100];
char key[256];
char desc_key[256];
char code_key[256];
char enable_key[256];
key[0] = endian_key[0] = desc_key[0] = code_key[0] = enable_key[0] = '\0';
snprintf(endian_key, sizeof(endian_key), "cheat%u_big_endian", i);
snprintf(desc_key, sizeof(desc_key), "cheat%u_desc", i);
snprintf(code_key, sizeof(code_key), "cheat%u_code", i);
snprintf(enable_key, sizeof(enable_key), "cheat%u_enable", i);
if (!string_is_empty(cheat_st->cheats[i].desc))
config_set_string(conf, desc_key, cheat_st->cheats[i].desc);
else
config_set_string(conf, desc_key, cheat_st->cheats[i].code);
config_set_string(conf, code_key, cheat_st->cheats[i].code);
config_set_bool(conf, enable_key, cheat_st->cheats[i].state);
config_set_bool(conf, endian_key, cheat_st->cheats[i].big_endian);
data_ptrs[0] = &cheat_st->cheats[i].handler;
data_ptrs[1] = &cheat_st->cheats[i].memory_search_size;
data_ptrs[2] = &cheat_st->cheats[i].cheat_type;
data_ptrs[3] = &cheat_st->cheats[i].value;
data_ptrs[4] = &cheat_st->cheats[i].address;
data_ptrs[5] = &cheat_st->cheats[i].address_mask;
data_ptrs[6] = &cheat_st->cheats[i].rumble_type;
data_ptrs[7] = &cheat_st->cheats[i].rumble_value;
data_ptrs[8] = &cheat_st->cheats[i].rumble_port;
data_ptrs[9] = &cheat_st->cheats[i].rumble_primary_strength;
data_ptrs[10] = &cheat_st->cheats[i].rumble_primary_duration;
data_ptrs[11] = &cheat_st->cheats[i].rumble_secondary_strength;
data_ptrs[12] = &cheat_st->cheats[i].rumble_secondary_duration;
data_ptrs[13] = &cheat_st->cheats[i].repeat_count;
data_ptrs[14] = &cheat_st->cheats[i].repeat_add_to_value;
data_ptrs[15] = &cheat_st->cheats[i].repeat_add_to_address;
for (j = 0; j < 16; j++)
{
key[0] = '\0';
snprintf(key, sizeof(key), keys[j], i);
config_set_uint(conf, key, *(data_ptrs[j]));
}
}
ret = config_file_write(conf, cheats_file, true);
config_file_free(conf);
return ret;
}
bool cheat_manager_copy_idx_to_working(unsigned idx)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || (cheat_st->size < idx + 1))
return false;
memcpy(&cheat_st->working_cheat,
&cheat_st->cheats[idx], sizeof(struct item_cheat));
if (cheat_st->cheats[idx].desc)
strlcpy(cheat_st->working_desc, cheat_st->cheats[idx].desc, CHEAT_DESC_SCRATCH_SIZE);
else
cheat_st->working_desc[0] = '\0';
if (cheat_st->cheats[idx].code)
strlcpy(cheat_st->working_code,
cheat_st->cheats[idx].code,
CHEAT_CODE_SCRATCH_SIZE);
else
cheat_st->working_code[0] = '\0';
return true;
}
bool cheat_manager_copy_working_to_idx(unsigned idx)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || (cheat_st->size < idx + 1))
return false;
memcpy(&cheat_st->cheats[idx], &cheat_st->working_cheat,
sizeof(struct item_cheat));
if (cheat_st->cheats[idx].desc)
free(cheat_st->cheats[idx].desc);
cheat_st->cheats[idx].desc = strdup(cheat_st->working_desc);
if (cheat_st->cheats[idx].code)
free(cheat_st->cheats[idx].code);
cheat_st->cheats[idx].code = strdup(cheat_st->working_code);
return true;
}
static void cheat_manager_free(void)
{
unsigned i = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
if (cheat_st->cheats)
{
for (i = 0; i < cheat_st->size; i++)
{
if (cheat_st->cheats[i].desc)
free(cheat_st->cheats[i].desc);
if (cheat_st->cheats[i].code)
free(cheat_st->cheats[i].code);
}
free(cheat_st->cheats);
}
if (cheat_st->prev_memory_buf)
free(cheat_st->prev_memory_buf);
if (cheat_st->matches)
free(cheat_st->matches);
if (cheat_st->memory_buf_list)
free(cheat_st->memory_buf_list);
if (cheat_st->memory_size_list)
free(cheat_st->memory_size_list);
cheat_st->cheats = NULL;
cheat_st->size = 0;
cheat_st->buf_size = 0;
cheat_st->prev_memory_buf = NULL;
cheat_st->curr_memory_buf = NULL;
cheat_st->memory_buf_list = NULL;
cheat_st->memory_size_list = NULL;
cheat_st->matches = NULL;
cheat_st->num_memory_buffers = 0;
cheat_st->total_memory_size = 0;
cheat_st->memory_initialized = false;
cheat_st->memory_search_initialized = false;
}
static void cheat_manager_new(unsigned size)
{
unsigned i;
cheat_manager_t *cheat_st = &cheat_manager_state;
cheat_manager_free();
cheat_st->buf_size = size;
cheat_st->size = size;
cheat_st->search_bit_size = 3;
cheat_st->cheats = (struct item_cheat*)
calloc(cheat_st->buf_size, sizeof(struct item_cheat));
if (!cheat_st->cheats)
{
cheat_st->buf_size = 0;
cheat_st->size = 0;
cheat_st->cheats = NULL;
return;
}
for (i = 0; i < cheat_st->size; i++)
{
cheat_st->cheats[i].desc = NULL;
cheat_st->cheats[i].code = NULL;
cheat_st->cheats[i].state = false;
cheat_st->cheats[i].repeat_count = 1;
cheat_st->cheats[i].repeat_add_to_value = 0;
cheat_st->cheats[i].repeat_add_to_address = 1;
}
}
static void cheat_manager_load_cb_first_pass(char *key, char *value)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
errno = 0;
if (string_is_equal(key, "cheats"))
{
cheat_st->loading_cheat_size = (unsigned)strtoul(value, NULL, 0);
if (errno != 0)
cheat_st->loading_cheat_size = 0;
}
}
static void cheat_manager_load_cb_second_pass(char *key, char *value)
{
char cheat_num_str[20];
unsigned cheat_num;
unsigned cheat_idx;
unsigned idx = 5;
size_t key_length = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
errno = 0;
if (strncmp(key, "cheat", 5) != 0)
return;
key_length = strlen((const char*)key);
while (idx < key_length && key[idx] >= '0' && key[idx] <= '9' && idx < 24)
{
cheat_num_str[idx - 5] = key[idx];
idx++;
}
cheat_num_str[idx - 5] = '\0';
cheat_num = (unsigned)strtoul(cheat_num_str, NULL, 0);
if (cheat_num + cheat_st->loading_cheat_offset >= cheat_st->size)
return;
key = key + idx + 1;
cheat_idx = cheat_num + cheat_st->loading_cheat_offset;
if (string_is_equal(key, "address"))
cheat_st->cheats[cheat_idx].address = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "address_bit_position"))
cheat_st->cheats[cheat_idx].address_mask = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "big_endian"))
cheat_st->cheats[cheat_idx].big_endian = (string_is_equal(value, "true") || string_is_equal(value, "1"));
else if (string_is_equal(key, "cheat_type"))
cheat_st->cheats[cheat_idx].cheat_type = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "code"))
cheat_st->cheats[cheat_idx].code = strdup(value);
else if (string_is_equal(key, "desc"))
cheat_st->cheats[cheat_idx].desc = strdup(value);
else if (string_is_equal(key, "enable"))
cheat_st->cheats[cheat_idx].state = (string_is_equal(value, "true") || string_is_equal(value, "1"));
else if (string_is_equal(key, "handler"))
cheat_st->cheats[cheat_idx].handler = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "memory_search_size"))
cheat_st->cheats[cheat_idx].memory_search_size = (unsigned)strtoul(value, NULL, 0);
else if (string_starts_with_size(key, "repeat_", STRLEN_CONST("repeat_")))
{
if (string_is_equal(key, "repeat_add_to_address"))
cheat_st->cheats[cheat_idx].repeat_add_to_address = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "repeat_add_to_value"))
cheat_st->cheats[cheat_idx].repeat_add_to_value = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "repeat_count"))
cheat_st->cheats[cheat_idx].repeat_count = (unsigned)strtoul(value, NULL, 0);
}
else if (string_starts_with_size(key, "rumble", STRLEN_CONST("rumble")))
{
if (string_is_equal(key, "rumble_port"))
cheat_st->cheats[cheat_idx].rumble_port = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_primary_duration"))
cheat_st->cheats[cheat_idx].rumble_primary_duration = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_primary_strength"))
cheat_st->cheats[cheat_idx].rumble_primary_strength = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_secondary_duration"))
cheat_st->cheats[cheat_idx].rumble_secondary_duration = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_secondary_strength"))
cheat_st->cheats[cheat_idx].rumble_secondary_strength = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_type"))
cheat_st->cheats[cheat_idx].rumble_type = (unsigned)strtoul(value, NULL, 0);
else if (string_is_equal(key, "rumble_value"))
cheat_st->cheats[cheat_idx].rumble_value = (unsigned)strtoul(value, NULL, 0);
}
else if (string_is_equal(key, "value"))
cheat_st->cheats[cheat_idx].value = (unsigned)strtoul(value, NULL, 0);
}
bool cheat_manager_load(const char *path, bool append)
{
config_file_cb_t cb;
unsigned orig_size = 0;
unsigned cheats = 0;
unsigned i = 0;
config_file_t *conf = NULL;
cheat_manager_t *cheat_st = &cheat_manager_state;
cb.config_file_new_entry_cb = cheat_manager_load_cb_first_pass;
cheat_st->loading_cheat_size = 0;
conf = config_file_new_with_callback(path, &cb);
if (!conf)
return false;
cheats = cheat_st->loading_cheat_size;
if (cheats == 0)
goto error;
config_file_free(conf);
conf = NULL;
cheat_manager_alloc_if_empty();
if (append)
{
orig_size = cheat_manager_get_size();
if (orig_size == 0)
cheat_manager_new(cheats);
else
{
cheats = cheats + orig_size;
if (cheat_manager_realloc(cheats, CHEAT_HANDLER_TYPE_EMU)) { }
}
}
else
{
orig_size = 0;
cheat_manager_new(cheats);
}
for (i = orig_size; cheat_st->cheats && i < cheats; i++)
{
cheat_st->cheats[i].idx = i;
cheat_st->cheats[i].desc = NULL;
cheat_st->cheats[i].code = NULL;
cheat_st->cheats[i].state = false;
cheat_st->cheats[i].big_endian = false;
cheat_st->cheats[i].cheat_type = CHEAT_TYPE_SET_TO_VALUE;
cheat_st->cheats[i].memory_search_size = 3;
}
cheat_st->loading_cheat_offset = orig_size;
cb.config_file_new_entry_cb =
cheat_manager_load_cb_second_pass;
conf = config_file_new_with_callback(path, &cb);
if (!conf)
return false;
config_file_free(conf);
return true;
error:
config_file_free(conf);
return false;
}
bool cheat_manager_realloc(unsigned new_size, unsigned default_handler)
{
unsigned i;
unsigned orig_size = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
{
cheat_st->cheats = (struct item_cheat*)
calloc(new_size, sizeof(struct item_cheat));
orig_size = 0;
}
else
{
struct item_cheat *val = NULL;
orig_size = cheat_st->size;
/* if size is decreasing, free the items that will be lost */
for (i = new_size; i < orig_size; i++)
{
if (cheat_st->cheats[i].code)
free(cheat_st->cheats[i].code);
if (cheat_st->cheats[i].desc)
free(cheat_st->cheats[i].desc);
}
val = (struct item_cheat*)
realloc(cheat_st->cheats,
new_size * sizeof(struct item_cheat));
cheat_st->cheats = val ? val : NULL;
}
if (!cheat_st->cheats)
{
cheat_st->buf_size = cheat_st->size = 0;
cheat_st->cheats = NULL;
return false;
}
cheat_st->buf_size = new_size;
cheat_st->size = new_size;
for (i = orig_size; i < cheat_st->size; i++)
{
memset(&cheat_st->cheats[i], 0, sizeof(cheat_st->cheats[i]));
cheat_st->cheats[i].state = false;
cheat_st->cheats[i].handler = default_handler;
cheat_st->cheats[i].cheat_type = CHEAT_TYPE_SET_TO_VALUE;
cheat_st->cheats[i].memory_search_size = 3;
cheat_st->cheats[i].idx = i;
cheat_st->cheats[i].repeat_count = 1;
cheat_st->cheats[i].repeat_add_to_value = 0;
cheat_st->cheats[i].repeat_add_to_address = 1;
}
return true;
}
void cheat_manager_update(cheat_manager_t *handle, unsigned handle_idx)
{
char msg[256];
if (!handle || !handle->cheats || handle->size == 0)
return;
snprintf(msg, sizeof(msg),
"Cheat: #%u [%s]: %s",
handle_idx,
handle->cheats[handle_idx].state ? "ON" : "OFF",
handle->cheats[handle_idx].desc
? (handle->cheats[handle_idx].desc)
: (handle->cheats[handle_idx].code)
);
runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
RARCH_LOG("%s\n", msg);
}
void cheat_manager_toggle_index(bool apply_cheats_after_toggle,
unsigned i)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || cheat_st->size == 0)
return;
cheat_st->cheats[i].state = !cheat_st->cheats[i].state;
cheat_manager_update(cheat_st, i);
if (apply_cheats_after_toggle)
cheat_manager_apply_cheats();
}
void cheat_manager_toggle(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || cheat_st->size == 0)
return;
cheat_st->cheats[cheat_st->ptr].state ^= true;
cheat_manager_apply_cheats();
cheat_manager_update(cheat_st, cheat_st->ptr);
}
void cheat_manager_index_next(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || cheat_st->size == 0)
return;
cheat_st->ptr = (cheat_st->ptr + 1) % cheat_st->size;
cheat_manager_update(cheat_st, cheat_st->ptr);
}
void cheat_manager_index_prev(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats || cheat_st->size == 0)
return;
if (cheat_st->ptr == 0)
cheat_st->ptr = cheat_st->size - 1;
else
cheat_st->ptr--;
cheat_manager_update(cheat_st, cheat_st->ptr);
}
const char *cheat_manager_get_code(unsigned i)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
return NULL;
return cheat_st->cheats[i].code;
}
const char *cheat_manager_get_desc(unsigned i)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
return NULL;
return cheat_st->cheats[i].desc;
}
bool cheat_manager_get_code_state(unsigned i)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
return false;
return cheat_st->cheats[i].state;
}
static bool cheat_manager_get_game_specific_filename(
char *s, size_t len,
const char *path_cheat_database,
bool saving)
{
char s1[PATH_MAX_LENGTH];
struct retro_system_info system_info;
global_t *global = global_get_ptr();
const char *core_name = NULL;
const char *game_name = NULL;
s1[0] = '\0';
if (!global || !core_get_system_info(&system_info))
return false;
core_name = system_info.library_name;
game_name = path_basename_nocompression(global->name.cheatfile);
if (string_is_empty(path_cheat_database) ||
string_is_empty(core_name) ||
string_is_empty(game_name))
return false;
s[0] = '\0';
fill_pathname_join(s1,
path_cheat_database, core_name,
sizeof(s1));
if (saving)
{
/* Check if directory is valid, if not, create it */
if (!path_is_valid(s1))
path_mkdir(s1);
}
fill_pathname_join(s, s1, game_name, len);
return true;
}
void cheat_manager_load_game_specific_cheats(const char *path_cheat_database)
{
char cheat_file[PATH_MAX_LENGTH];
if (cheat_manager_get_game_specific_filename(
cheat_file, sizeof(cheat_file),
path_cheat_database,
false))
{
if (cheat_manager_load(cheat_file, true))
RARCH_LOG("[Cheats]: Load game-specific cheatfile: %s\n", cheat_file);
}
}
void cheat_manager_save_game_specific_cheats(const char *path_cheat_database)
{
char cheat_file[PATH_MAX_LENGTH];
if (cheat_manager_get_game_specific_filename(
cheat_file, sizeof(cheat_file),
path_cheat_database,
true))
{
if (cheat_manager_save(cheat_file, NULL, true))
RARCH_LOG("[Cheats]: Save game-specific cheatfile: %s\n", cheat_file);
}
}
void cheat_manager_state_free(void)
{
cheat_manager_free();
}
bool cheat_manager_alloc_if_empty(void)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
if (!cheat_st->cheats)
cheat_manager_new(0);
return true;
}
int cheat_manager_initialize_memory(rarch_setting_t *setting, size_t idx, bool wraparound)
{
unsigned i;
retro_ctx_memory_info_t meminfo;
bool refresh = false;
bool is_search_initialization = (setting != NULL);
rarch_system_info_t *system = runloop_get_system_info();
unsigned offset = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
cheat_st->num_memory_buffers = 0;
cheat_st->total_memory_size = 0;
cheat_st->curr_memory_buf = NULL;
if (cheat_st->memory_buf_list)
{
free(cheat_st->memory_buf_list);
cheat_st->memory_buf_list = NULL;
}
if (cheat_st->memory_size_list)
{
free(cheat_st->memory_size_list);
cheat_st->memory_size_list = NULL;
}
if (system && system->mmaps.num_descriptors > 0)
{
for (i = 0; i < system->mmaps.num_descriptors; i++)
{
if ((system->mmaps.descriptors[i].core.flags
& RETRO_MEMDESC_SYSTEM_RAM) != 0 &&
system->mmaps.descriptors[i].core.ptr &&
system->mmaps.descriptors[i].core.len > 0)
{
cheat_st->num_memory_buffers++;
if (!cheat_st->memory_buf_list)
cheat_st->memory_buf_list = (uint8_t**)calloc(1, sizeof(uint8_t *));
else
cheat_st->memory_buf_list = (uint8_t**)realloc(
cheat_st->memory_buf_list, sizeof(uint8_t *) * cheat_st->num_memory_buffers);
if (!cheat_st->memory_size_list)
cheat_st->memory_size_list = (unsigned*)calloc(1, sizeof(unsigned));
else
{
unsigned *val = (unsigned*)realloc(
cheat_st->memory_size_list,
sizeof(unsigned) *
cheat_st->num_memory_buffers);
if (val)
cheat_st->memory_size_list = val;
}
cheat_st->memory_buf_list[cheat_st->num_memory_buffers - 1] = (uint8_t*)system->mmaps.descriptors[i].core.ptr;
cheat_st->memory_size_list[cheat_st->num_memory_buffers - 1] = (unsigned)system->mmaps.descriptors[i].core.len;
cheat_st->total_memory_size += system->mmaps.descriptors[i].core.len;
if (!cheat_st->curr_memory_buf)
cheat_st->curr_memory_buf = (uint8_t*)system->mmaps.descriptors[i].core.ptr;
}
}
}
if (cheat_st->num_memory_buffers == 0)
{
meminfo.id = RETRO_MEMORY_SYSTEM_RAM;
if (!core_get_memory(&meminfo))
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
if (meminfo.size == 0)
return 0;
cheat_st->memory_buf_list = (uint8_t**)
calloc(1, sizeof(uint8_t *));
cheat_st->memory_size_list = (unsigned*)
calloc(1, sizeof(unsigned));
cheat_st->num_memory_buffers = 1;
cheat_st->memory_buf_list[0] = (uint8_t*)meminfo.data;
cheat_st->memory_size_list[0] = (unsigned)meminfo.size;
cheat_st->total_memory_size = (unsigned)meminfo.size;
cheat_st->curr_memory_buf = (uint8_t*)meminfo.data;
}
cheat_st->num_matches = (cheat_st->total_memory_size * 8) / (1 << cheat_st->search_bit_size);
#if 0
/* Ensure we're aligned on 4-byte boundary */
if (meminfo.size % 4 > 0)
cheat_st->total_memory_size = cheat_st->total_memory_size + (4 - (meminfo.size % 4));
#endif
if (is_search_initialization)
{
if (cheat_st->prev_memory_buf)
{
free(cheat_st->prev_memory_buf);
cheat_st->prev_memory_buf = NULL;
}
cheat_st->prev_memory_buf = (uint8_t*)calloc(
cheat_st->total_memory_size, sizeof(uint8_t));
if (!cheat_st->prev_memory_buf)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
if (cheat_st->matches)
{
free(cheat_st->matches);
cheat_st->matches = NULL;
}
cheat_st->matches = (uint8_t*)calloc(
cheat_st->total_memory_size, sizeof(uint8_t));
if (!cheat_st->matches)
{
free(cheat_st->prev_memory_buf);
cheat_st->prev_memory_buf = NULL;
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
memset(cheat_st->matches, 0xFF, cheat_st->total_memory_size);
offset = 0;
for (i = 0; i < cheat_st->num_memory_buffers; i++)
{
memcpy(cheat_st->prev_memory_buf + offset,
cheat_st->memory_buf_list[i],
cheat_st->memory_size_list[i]);
offset += cheat_st->memory_size_list[i];
}
cheat_st->memory_search_initialized = true;
}
cheat_st->memory_initialized = true;
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_SUCCESS), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
#ifdef HAVE_MENU
if (!wraparound)
{
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
}
#endif
return 0;
}
static unsigned translate_address(unsigned address, unsigned char **curr)
{
unsigned offset = 0;
unsigned i = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
for (i = 0; i < cheat_st->num_memory_buffers; i++)
{
if ((address >= offset) && (address < offset + cheat_st->memory_size_list[i]))
{
*curr = cheat_st->memory_buf_list[i];
break;
}
else
offset += cheat_st->memory_size_list[i];
}
return offset;
}
static void cheat_manager_setup_search_meta(
unsigned int bitsize,
unsigned int *bytes_per_item,
unsigned int *mask,
unsigned int *bits)
{
switch (bitsize)
{
case 0:
*bytes_per_item = 1;
*bits = 1;
*mask = 0x01;
break;
case 1:
*bytes_per_item = 1;
*bits = 2;
*mask = 0x03;
break;
case 2:
*bytes_per_item = 1;
*bits = 4;
*mask = 0x0F;
break;
case 3:
*bytes_per_item = 1;
*bits = 8;
*mask = 0xFF;
break;
case 4:
*bytes_per_item = 2;
*bits = 8;
*mask = 0xFFFF;
break;
case 5:
*bytes_per_item = 4;
*bits = 8;
*mask = 0xFFFFFFFF;
break;
}
}
static int cheat_manager_search(enum cheat_search_type search_type)
{
char msg[100];
cheat_manager_t *cheat_st = &cheat_manager_state;
unsigned char *curr = cheat_st->curr_memory_buf;
unsigned char *prev = cheat_st->prev_memory_buf;
unsigned int idx = 0;
unsigned int curr_val = 0;
unsigned int prev_val = 0;
unsigned int mask = 0;
unsigned int bytes_per_item = 1;
unsigned int bits = 8;
unsigned int offset = 0;
unsigned int i = 0;
bool refresh = false;
if (cheat_st->num_memory_buffers == 0)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_NOT_INITIALIZED), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
cheat_manager_setup_search_meta(cheat_st->search_bit_size, &bytes_per_item, &mask, &bits);
/* little endian FF000000 = 256 */
for (idx = 0; idx < cheat_st->total_memory_size; idx = idx + bytes_per_item)
{
unsigned byte_part;
offset = translate_address(idx, &curr);
switch (bytes_per_item)
{
case 2:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256) + *(curr + idx + 1 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256);
prev_val = cheat_st->big_endian ?
(*(prev + idx) * 256) + *(prev + idx + 1) :
*(prev + idx) + (*(prev + idx + 1) * 256);
break;
case 4:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256 * 256 * 256) + (*(curr + idx + 1 - offset) * 256 * 256) + (*(curr + idx + 2 - offset) * 256) + *(curr + idx + 3 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256) + (*(curr + idx + 2 - offset) * 256 * 256) + (*(curr + idx + 3 - offset) * 256 * 256 * 256);
prev_val = cheat_st->big_endian ?
(*(prev + idx) * 256 * 256 * 256) + (*(prev + idx + 1) * 256 * 256) + (*(prev + idx + 2) * 256) + *(prev + idx + 3) :
*(prev + idx) + (*(prev + idx + 1) * 256) + (*(prev + idx + 2) * 256 * 256) + (*(prev + idx + 3) * 256 * 256 * 256);
break;
case 1:
default:
curr_val = *(curr - offset + idx);
prev_val = *(prev + idx);
break;
}
for (byte_part = 0; byte_part < 8 / bits; byte_part++)
{
unsigned int curr_subval = (curr_val >> (byte_part * bits)) & mask;
unsigned int prev_subval = (prev_val >> (byte_part * bits)) & mask;
unsigned int prev_match;
if (bits < 8)
prev_match = *(cheat_st->matches + idx) & (mask << (byte_part * bits));
else
prev_match = *(cheat_st->matches + idx);
if (prev_match > 0)
{
bool match = false;
switch (search_type)
{
case CHEAT_SEARCH_TYPE_EXACT:
match = (curr_subval == cheat_st->search_exact_value);
break;
case CHEAT_SEARCH_TYPE_LT:
match = (curr_subval < prev_subval);
break;
case CHEAT_SEARCH_TYPE_GT:
match = (curr_subval > prev_subval);
break;
case CHEAT_SEARCH_TYPE_LTE:
match = (curr_subval <= prev_subval);
break;
case CHEAT_SEARCH_TYPE_GTE:
match = (curr_subval >= prev_subval);
break;
case CHEAT_SEARCH_TYPE_EQ:
match = (curr_subval == prev_subval);
break;
case CHEAT_SEARCH_TYPE_NEQ:
match = (curr_subval != prev_subval);
break;
case CHEAT_SEARCH_TYPE_EQPLUS:
match = (curr_subval == prev_subval + cheat_st->search_eqplus_value);
break;
case CHEAT_SEARCH_TYPE_EQMINUS:
match = (curr_subval == prev_subval - cheat_st->search_eqminus_value);
break;
}
if (!match)
{
if (bits < 8)
*(cheat_st->matches + idx) = *(cheat_st->matches + idx) &
((~(mask << (byte_part * bits))) & 0xFF);
else
memset(cheat_st->matches + idx, 0, bytes_per_item);
if (cheat_st->num_matches > 0)
cheat_st->num_matches--;
}
}
}
}
offset = 0;
for (i = 0; i < cheat_st->num_memory_buffers; i++)
{
memcpy(cheat_st->prev_memory_buf + offset, cheat_st->memory_buf_list[i], cheat_st->memory_size_list[i]);
offset += cheat_st->memory_size_list[i];
}
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_CHEAT_SEARCH_FOUND_MATCHES), cheat_st->num_matches);
msg[sizeof(msg) - 1] = 0;
runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
#ifdef HAVE_MENU
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
#endif
return 0;
}
int cheat_manager_search_exact(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_EXACT);
}
int cheat_manager_search_lt(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_LT);
}
int cheat_manager_search_gt(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_GT);
}
int cheat_manager_search_lte(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_LTE);
}
int cheat_manager_search_gte(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_GTE);
}
int cheat_manager_search_eq(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_EQ);
}
int cheat_manager_search_neq(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_NEQ);
}
int cheat_manager_search_eqplus(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_EQPLUS);
}
int cheat_manager_search_eqminus(rarch_setting_t *setting, size_t idx, bool wraparound)
{
return cheat_manager_search(CHEAT_SEARCH_TYPE_EQMINUS);
}
unsigned cheat_manager_get_state_search_size(unsigned search_size)
{
uint32_t n[] = {1,3,15,255,0x0000ffff,0xffffffff};
return n[search_size];
}
bool cheat_manager_add_new_code(unsigned int memory_search_size, unsigned int address, unsigned int address_mask,
bool big_endian, unsigned int value)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
int new_size = cheat_manager_get_size() + 1;
if (!cheat_manager_realloc(new_size, CHEAT_HANDLER_TYPE_RETRO))
return false;
cheat_st->cheats[cheat_st->size - 1].address = address;
cheat_st->cheats[cheat_st->size - 1].address_mask = address_mask;
cheat_st->cheats[cheat_st->size - 1].memory_search_size = memory_search_size;
cheat_st->cheats[cheat_st->size - 1].value = value;
cheat_st->cheats[cheat_st->size - 1].big_endian = big_endian;
return true;
}
int cheat_manager_add_matches(const char *path,
const char *label, unsigned type, size_t menuidx, size_t entry_idx)
{
char msg[100];
bool refresh = false;
unsigned byte_part = 0;
unsigned int idx = 0;
unsigned int mask = 0;
unsigned int bytes_per_item = 1;
unsigned int bits = 8;
unsigned int curr_val = 0;
unsigned int num_added = 0;
unsigned int offset = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
unsigned char *curr = cheat_st->curr_memory_buf;
if (cheat_st->num_matches + cheat_st->size > 100)
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_TOO_MANY), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
cheat_manager_setup_search_meta(cheat_st->search_bit_size, &bytes_per_item, &mask, &bits);
for (idx = 0; idx < cheat_st->total_memory_size; idx = idx + bytes_per_item)
{
offset = translate_address(idx, &curr);
switch (bytes_per_item)
{
case 2:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256) + *(curr + idx + 1 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256);
break;
case 4:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256 * 256 * 256) + (*(curr + idx + 1 - offset) * 256 * 256) + (*(curr + idx + 2 - offset) * 256) + *(curr + idx + 3 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256) + (*(curr + idx + 2 - offset) * 256 * 256) + (*(curr + idx + 3 - offset) * 256 * 256 * 256);
break;
case 1:
default:
curr_val = *(curr - offset + idx);
break;
}
for (byte_part = 0; byte_part < 8 / bits; byte_part++)
{
unsigned int prev_match;
if (bits < 8)
{
prev_match = *(cheat_st->matches + idx) & (mask << (byte_part * bits));
if (prev_match)
{
if (!cheat_manager_add_new_code(cheat_st->search_bit_size, idx, (mask << (byte_part * bits)),
cheat_st->big_endian, curr_val))
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
num_added++;
}
}
else
{
prev_match = *(cheat_st->matches + idx);
if (prev_match)
{
if (!cheat_manager_add_new_code(cheat_st->search_bit_size, idx, 0xFF,
cheat_st->big_endian, curr_val))
{
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return 0;
}
num_added++;
}
}
}
}
snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_SUCCESS), cheat_st->num_matches);
msg[sizeof(msg) - 1] = 0;
runloop_msg_queue_push(msg, 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
#ifdef HAVE_MENU
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
#endif
return 0;
}
void cheat_manager_apply_rumble(struct item_cheat *cheat, unsigned int curr_value)
{
bool rumble = false;
retro_time_t current_time = cpu_features_get_time_usec();
switch (cheat->rumble_type)
{
case RUMBLE_TYPE_DISABLED:
return;
case RUMBLE_TYPE_CHANGES:
rumble = (curr_value != cheat->rumble_prev_value);
break;
case RUMBLE_TYPE_DOES_NOT_CHANGE:
rumble = (curr_value == cheat->rumble_prev_value);
break;
case RUMBLE_TYPE_INCREASE:
rumble = (curr_value > cheat->rumble_prev_value);
break;
case RUMBLE_TYPE_DECREASE:
rumble = (curr_value < cheat->rumble_prev_value);
break;
case RUMBLE_TYPE_EQ_VALUE:
rumble = (curr_value == cheat->rumble_value);
break;
case RUMBLE_TYPE_NEQ_VALUE:
rumble = (curr_value != cheat->rumble_value);
break;
case RUMBLE_TYPE_LT_VALUE:
rumble = (curr_value < cheat->rumble_value);
break;
case RUMBLE_TYPE_GT_VALUE:
rumble = (curr_value > cheat->rumble_value);
break;
case RUMBLE_TYPE_INCREASE_BY_VALUE:
rumble = (curr_value == cheat->rumble_prev_value + cheat->rumble_value);
break;
case RUMBLE_TYPE_DECREASE_BY_VALUE:
rumble = (curr_value == cheat->rumble_prev_value - cheat->rumble_value);
break;
}
cheat->rumble_prev_value = curr_value;
/* Give the emulator enough time
* to initialize, load state, etc */
if (cheat->rumble_initialized > 300)
{
if (rumble)
{
cheat->rumble_primary_end_time = current_time + (cheat->rumble_primary_duration * 1000);
cheat->rumble_secondary_end_time = current_time + (cheat->rumble_secondary_duration * 1000);
input_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_STRONG, cheat->rumble_primary_strength);
input_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, cheat->rumble_secondary_strength);
}
}
else
{
cheat->rumble_initialized++;
return;
}
if (cheat->rumble_primary_end_time <= current_time)
{
if (cheat->rumble_primary_end_time != 0)
input_set_rumble_state(cheat->rumble_port,
RETRO_RUMBLE_STRONG, 0);
cheat->rumble_primary_end_time = 0;
}
else
{
input_set_rumble_state(cheat->rumble_port,
RETRO_RUMBLE_STRONG, cheat->rumble_primary_strength);
}
if (cheat->rumble_secondary_end_time <= current_time)
{
if (cheat->rumble_secondary_end_time != 0)
input_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, 0);
cheat->rumble_secondary_end_time = 0;
}
else
input_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, cheat->rumble_secondary_strength);
}
void cheat_manager_apply_retro_cheats(void)
{
unsigned i;
unsigned int offset;
unsigned int mask = 0;
unsigned int bytes_per_item = 1;
unsigned int bits = 8;
unsigned int curr_val = 0;
bool run_cheat = true;
#ifdef HAVE_CHEEVOS
bool cheat_applied = false;
#endif
cheat_manager_t *cheat_st = &cheat_manager_state;
if ((!cheat_st->cheats))
return;
for (i = 0; i < cheat_st->size; i++)
{
unsigned char *curr = NULL;
bool set_value = false;
unsigned int idx = 0;
unsigned int value_to_set = 0;
unsigned int repeat_iter = 0;
unsigned int address_mask = cheat_st->cheats[i].address_mask;
if (cheat_st->cheats[i].handler != CHEAT_HANDLER_TYPE_RETRO || !cheat_st->cheats[i].state)
continue;
if (!cheat_st->memory_initialized)
cheat_manager_initialize_memory(NULL, 0, false);
/* If we're still not initialized, something
* must have gone wrong - just bail */
if (!cheat_st->memory_initialized)
return;
if (!run_cheat)
{
run_cheat = true;
continue;
}
cheat_manager_setup_search_meta(cheat_st->cheats[i].memory_search_size, &bytes_per_item, &mask, &bits);
curr = cheat_st->curr_memory_buf;
idx = cheat_st->cheats[i].address;
offset = translate_address(idx, &curr);
switch (bytes_per_item)
{
case 2:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256) + *(curr + idx + 1 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256);
break;
case 4:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256 * 256 * 256) + (*(curr + idx + 1 - offset) * 256 * 256) + (*(curr + idx + 2 - offset) * 256) + *(curr + idx + 3 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256) + (*(curr + idx + 2 - offset) * 256 * 256) + (*(curr + idx + 3 - offset) * 256 * 256 * 256);
break;
case 1:
default:
curr_val = *(curr + idx - offset);
break;
}
cheat_manager_apply_rumble(&cheat_st->cheats[i], curr_val);
switch (cheat_st->cheats[i].cheat_type)
{
case CHEAT_TYPE_SET_TO_VALUE:
set_value = true;
value_to_set = cheat_st->cheats[i].value;
break;
case CHEAT_TYPE_INCREASE_VALUE:
set_value = true;
value_to_set = curr_val + cheat_st->cheats[i].value;
break;
case CHEAT_TYPE_DECREASE_VALUE:
set_value = true;
value_to_set = curr_val - cheat_st->cheats[i].value;
break;
case CHEAT_TYPE_RUN_NEXT_IF_EQ:
if (!(curr_val == cheat_st->cheats[i].value))
run_cheat = false;
break;
case CHEAT_TYPE_RUN_NEXT_IF_NEQ:
if (!(curr_val != cheat_st->cheats[i].value))
run_cheat = false;
break;
case CHEAT_TYPE_RUN_NEXT_IF_LT:
if (!(cheat_st->cheats[i].value < curr_val))
run_cheat = false;
break;
case CHEAT_TYPE_RUN_NEXT_IF_GT:
if (!(cheat_st->cheats[i].value > curr_val))
run_cheat = false;
break;
}
if (set_value)
{
#ifdef HAVE_CHEEVOS
cheat_applied = true;
#endif
for (repeat_iter = 1; repeat_iter <= cheat_st->cheats[i].repeat_count; repeat_iter++)
{
switch (bytes_per_item)
{
case 2:
if (cheat_st->cheats[i].big_endian)
{
*(curr + idx - offset) = (value_to_set >> 8) & 0xFF;
*(curr + idx + 1 - offset) = value_to_set & 0xFF;
}
else
{
*(curr + idx - offset) = value_to_set & 0xFF;
*(curr + idx + 1 - offset) = (value_to_set >> 8) & 0xFF;
}
break;
case 4:
if (cheat_st->cheats[i].big_endian)
{
*(curr + idx - offset) = (value_to_set >> 24) & 0xFF;
*(curr + idx + 1 - offset) = (value_to_set >> 16) & 0xFF;
*(curr + idx + 2 - offset) = (value_to_set >> 8) & 0xFF;
*(curr + idx + 3 - offset) = value_to_set & 0xFF;
}
else
{
*(curr + idx - offset) = value_to_set & 0xFF;
*(curr + idx + 1 - offset) = (value_to_set >> 8) & 0xFF;
*(curr + idx + 2 - offset) = (value_to_set >> 16) & 0xFF;
*(curr + idx + 3 - offset) = (value_to_set >> 24) & 0xFF;
}
break;
case 1:
if (bits < 8)
{
unsigned bitpos;
unsigned char val = *(curr + idx - offset);
for (bitpos = 0; bitpos < 8; bitpos++)
{
if ((address_mask >> bitpos) & 0x01)
{
mask = (~(1 << bitpos) & 0xFF);
/* Clear current bit value */
val = val & mask;
/* Inject cheat bit value */
val = val | (((value_to_set >> bitpos) & 0x01) << bitpos);
}
}
*(curr + idx - offset) = val;
}
else
*(curr + idx - offset) = value_to_set & 0xFF;
break;
default:
*(curr + idx - offset) = value_to_set & 0xFF;
break;
}
value_to_set += cheat_st->cheats[i].repeat_add_to_value;
if (mask != 0)
value_to_set = value_to_set % mask;
if (bits < 8)
{
unsigned int bit_iter;
for (bit_iter = 0; bit_iter < cheat_st->cheats[i].repeat_add_to_address; bit_iter++)
{
address_mask = (address_mask << mask) & 0xFF;
if (address_mask == 0)
{
address_mask = mask;
idx++;
}
}
}
else
idx += (cheat_st->cheats[i].repeat_add_to_address * bytes_per_item);
idx = idx % cheat_st->total_memory_size;
offset = translate_address(idx, &curr);
}
}
}
#ifdef HAVE_CHEEVOS
if (cheat_applied && rcheevos_hardcore_active())
cheat_manager_pause_cheevos();
#endif
}
void cheat_manager_match_action(enum cheat_match_action_type match_action, unsigned int target_match_idx, unsigned int *address, unsigned int *address_mask,
unsigned int *prev_value, unsigned int *curr_value)
{
unsigned int byte_part;
unsigned int idx;
unsigned int start_idx;
unsigned int mask = 0;
unsigned int bytes_per_item = 1;
unsigned int bits = 8;
unsigned int curr_val = 0;
unsigned int prev_val = 0;
unsigned int offset = 0;
cheat_manager_t *cheat_st = &cheat_manager_state;
unsigned char *curr = cheat_st->curr_memory_buf;
unsigned char *prev = cheat_st->prev_memory_buf;
unsigned int curr_match_idx = 0;
if (target_match_idx > cheat_st->num_matches - 1)
return;
if (cheat_st->num_memory_buffers == 0)
return;
cheat_manager_setup_search_meta(cheat_st->search_bit_size, &bytes_per_item, &mask, &bits);
if (match_action == CHEAT_MATCH_ACTION_TYPE_BROWSE)
start_idx = *address;
else
start_idx = 0;
for (idx = start_idx; idx < cheat_st->total_memory_size; idx = idx + bytes_per_item)
{
offset = translate_address(idx, &curr);
switch (bytes_per_item)
{
case 2:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256) + *(curr + idx + 1 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256);
if (prev)
prev_val = cheat_st->big_endian ?
(*(prev + idx) * 256) + *(prev + idx + 1) :
*(prev + idx) + (*(prev + idx + 1) * 256);
break;
case 4:
curr_val = cheat_st->big_endian ?
(*(curr + idx - offset) * 256 * 256 * 256) + (*(curr + idx + 1 - offset) * 256 * 256) + (*(curr + idx + 2 - offset) * 256) + *(curr + idx + 3 - offset) :
*(curr + idx - offset) + (*(curr + idx + 1 - offset) * 256) + (*(curr + idx + 2 - offset) * 256 * 256) + (*(curr + idx + 3 - offset) * 256 * 256 * 256);
if (prev)
prev_val = cheat_st->big_endian ?
(*(prev + idx) * 256 * 256 * 256) + (*(prev + idx + 1) * 256 * 256) + (*(prev + idx + 2) * 256) + *(prev + idx + 3) :
*(prev + idx) + (*(prev + idx + 1) * 256) + (*(prev + idx + 2) * 256 * 256) + (*(prev + idx + 3) * 256 * 256 * 256);
break;
case 1:
default:
curr_val = *(curr + idx - offset);
if (prev)
prev_val = *(prev + idx);
break;
}
if (match_action == CHEAT_MATCH_ACTION_TYPE_BROWSE)
{
*curr_value = curr_val;
*prev_value = prev_val;
return;
}
if (!prev)
return;
for (byte_part = 0; byte_part < 8 / bits; byte_part++)
{
unsigned int prev_match;
if (bits < 8)
{
prev_match = *(cheat_st->matches + idx) & (mask << (byte_part * bits));
if (prev_match)
{
if (target_match_idx == curr_match_idx)
{
switch (match_action)
{
case CHEAT_MATCH_ACTION_TYPE_BROWSE:
return;
case CHEAT_MATCH_ACTION_TYPE_VIEW:
*address = idx;
*address_mask = (mask << (byte_part * bits));
*curr_value = curr_val;
*prev_value = prev_val;
return;
case CHEAT_MATCH_ACTION_TYPE_COPY:
if (!cheat_manager_add_new_code(cheat_st->search_bit_size, idx, (mask << (byte_part * bits)),
cheat_st->big_endian, curr_val))
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
else
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_SUCCESS), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return;
case CHEAT_MATCH_ACTION_TYPE_DELETE:
if (bits < 8)
*(cheat_st->matches + idx) = *(cheat_st->matches + idx) &
((~(mask << (byte_part * bits))) & 0xFF);
else
memset(cheat_st->matches + idx, 0, bytes_per_item);
if (cheat_st->num_matches > 0)
cheat_st->num_matches--;
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_DELETE_MATCH_SUCCESS), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return;
}
return;
}
curr_match_idx++;
}
}
else
{
prev_match = *(cheat_st->matches + idx);
if (prev_match)
{
if (target_match_idx == curr_match_idx)
{
switch (match_action)
{
case CHEAT_MATCH_ACTION_TYPE_BROWSE:
return;
case CHEAT_MATCH_ACTION_TYPE_VIEW:
*address = idx;
*address_mask = 0xFF;
*curr_value = curr_val;
*prev_value = prev_val;
return;
case CHEAT_MATCH_ACTION_TYPE_COPY:
if (!cheat_manager_add_new_code(cheat_st->search_bit_size, idx, 0xFF,
cheat_st->big_endian, curr_val))
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_FAIL), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
else
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_SUCCESS), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return;
case CHEAT_MATCH_ACTION_TYPE_DELETE:
if (bits < 8)
*(cheat_st->matches + idx) = *(cheat_st->matches + idx) &
((~(mask << (byte_part * bits))) & 0xFF);
else
memset(cheat_st->matches + idx, 0, bytes_per_item);
if (cheat_st->num_matches > 0)
cheat_st->num_matches--;
runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_DELETE_MATCH_SUCCESS), 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
return;
}
}
curr_match_idx++;
}
}
}
}
}
int cheat_manager_copy_match(rarch_setting_t *setting, size_t idx, bool wraparound)
{
cheat_manager_t *cheat_st = &cheat_manager_state;
cheat_manager_match_action(CHEAT_MATCH_ACTION_TYPE_COPY,
cheat_st->match_idx, NULL, NULL, NULL, NULL);
return 0;
}
int cheat_manager_delete_match(rarch_setting_t *setting, size_t idx, bool wraparound)
{
bool refresh = false;
cheat_manager_t *cheat_st = &cheat_manager_state;
cheat_manager_match_action(CHEAT_MATCH_ACTION_TYPE_DELETE,
cheat_st->match_idx, NULL, NULL, NULL, NULL);
#ifdef HAVE_MENU
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
#endif
return 0;
}