/* 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 . */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "../config.h" #endif #ifdef HAVE_MENU #include "../menu/menu_driver.h" #include "../menu/widgets/menu_input_dialog.h" #include "../menu/widgets/menu_input_bind_dialog.h" #endif #ifdef HAVE_CHEEVOS #include "../cheevos/cheevos.h" #endif #include "cheat_manager.h" #include "../msg_hash.h" #include "../retroarch.h" #include "../dynamic.h" #include "../core.h" #include "../verbosity.h" #include "../input/input_driver.h" #include "../configuration.h" unsigned cheat_manager_get_buf_size(void) { return cheat_manager_state.buf_size; } unsigned cheat_manager_get_size(void) { return cheat_manager_state.size; } void cheat_manager_apply_cheats(void) { #ifdef HAVE_CHEEVOS bool data_bool = false; #endif unsigned i, idx = 0; if (!cheat_manager_state.cheats) return; core_reset_cheat(); for (i = 0; i < cheat_manager_state.size; i++) { if (cheat_manager_state.cheats[i].state && cheat_manager_state.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_manager_state.cheats[i].code; if (!string_is_empty(cheat_info.code)) core_set_cheat(&cheat_info); } } runloop_msg_queue_push(msg_hash_to_str(MSG_APPLYING_CHEAT), 1, 180, true); RARCH_LOG("%s\n", msg_hash_to_str(MSG_APPLYING_CHEAT)); #ifdef HAVE_CHEEVOS data_bool = idx != 0; cheevos_apply_cheats(&data_bool); #endif } void cheat_manager_set_code(unsigned i, const char *str) { if (!cheat_manager_state.cheats) return; if (!string_is_empty(str)) strcpy(cheat_manager_state.cheats[i].code,str) ; cheat_manager_state.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 buf[PATH_MAX_LENGTH]; char cheats_file[PATH_MAX_LENGTH]; config_file_t *conf = NULL; buf[0] = cheats_file[0] = '\0'; if ( (!cheat_manager_state.cheats) || cheat_manager_state.size==0 ) return false ; if ( cheat_database == NULL ) { strncpy(cheats_file, path, PATH_MAX_LENGTH) ; } else { fill_pathname_join(buf, cheat_database, path, sizeof(buf)); fill_pathname_noext(cheats_file, buf, ".cht", sizeof(cheats_file)); } if ( !overwrite ) { conf = config_file_new(cheats_file); } else { conf = config_file_new(NULL); } if (!conf) conf = config_file_new(NULL); if (!conf) return false; config_set_int(conf, "cheats", cheat_manager_state.size); for (i = 0; i < cheat_manager_state.size; i++) { unsigned j; char endian_key[100]; char key[256]; char desc_key[256]; char code_key[256]; char enable_key[256]; char* keys[13] = { "cheat%u_handler", "cheat%u_memory_search_size", "cheat%u_cheat_type", "cheat%u_value", "cheat%u_address", "cheat%u_address_bit_position", "cheat%u_rumble_type", "cheat%u_rumble_value", "cheat%u_rumble_port", "cheat%u_rumble_primary_strength", "cheat%u_rumble_primary_duration", "cheat%u_rumble_secondary_strength", "cheat%u_rumble_secondary_duration", }; 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_manager_state.cheats[i].desc)) config_set_string(conf, desc_key, cheat_manager_state.cheats[i].desc); else config_set_string(conf, desc_key, cheat_manager_state.cheats[i].code); config_set_string(conf, code_key, cheat_manager_state.cheats[i].code); config_set_bool(conf, enable_key, cheat_manager_state.cheats[i].state); config_set_bool(conf, endian_key, cheat_manager_state.cheats[i].big_endian); for (j = 0; j < 13; j++) { unsigned int* data_ptrs[13] = { &cheat_manager_state.cheats[i].handler, &cheat_manager_state.cheats[i].memory_search_size, &cheat_manager_state.cheats[i].cheat_type, &cheat_manager_state.cheats[i].value, &cheat_manager_state.cheats[i].address, &cheat_manager_state.cheats[i].address_mask, &cheat_manager_state.cheats[i].rumble_type, &cheat_manager_state.cheats[i].rumble_value , &cheat_manager_state.cheats[i].rumble_port, &cheat_manager_state.cheats[i].rumble_primary_strength, &cheat_manager_state.cheats[i].rumble_primary_duration, &cheat_manager_state.cheats[i].rumble_secondary_strength, &cheat_manager_state.cheats[i].rumble_secondary_duration } ; 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); config_file_free(conf); return ret; } bool cheat_manager_copy_idx_to_working(unsigned idx) { if ( (!cheat_manager_state.cheats) || (cheat_manager_state.size < idx+1)) { return false; } memcpy(&(cheat_manager_state.working_cheat), &(cheat_manager_state.cheats[idx]), sizeof(struct item_cheat)) ; return true ; } bool cheat_manager_copy_working_to_idx(unsigned idx) { if ( (!cheat_manager_state.cheats) || (cheat_manager_state.size < idx+1)) { return false; } memcpy(&(cheat_manager_state.cheats[idx]), &(cheat_manager_state.working_cheat), sizeof(struct item_cheat)) ; return true ; } static void cheat_manager_new(unsigned size) { unsigned i; cheat_manager_free() ; cheat_manager_state.buf_size = size; cheat_manager_state.size = size; cheat_manager_state.search_bit_size = 3; cheat_manager_state.cheats = (struct item_cheat*) calloc(cheat_manager_state.buf_size, sizeof(struct item_cheat)); if (!cheat_manager_state.cheats) { cheat_manager_state.buf_size = 0; cheat_manager_state.size = 0; cheat_manager_state.cheats = NULL; return ; } for (i = 0; i < cheat_manager_state.size; i++) { cheat_manager_state.cheats[i].desc[0] = 0 ; cheat_manager_state.cheats[i].code[0] = 0 ; cheat_manager_state.cheats[i].state = false; } return ; } bool cheat_manager_load(const char *path, bool append) { unsigned cheats = 0, i; config_file_t *conf = config_file_new(path); unsigned orig_size ; if (!conf) return false; config_get_uint(conf, "cheats", &cheats); if (cheats == 0) goto error; 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; i < cheats; i++) { unsigned j; unsigned int* data_ptrs[13] = { &cheat_manager_state.cheats[i].handler, &cheat_manager_state.cheats[i].memory_search_size, &cheat_manager_state.cheats[i].cheat_type, &cheat_manager_state.cheats[i].value, &cheat_manager_state.cheats[i].address, &cheat_manager_state.cheats[i].address_mask, &cheat_manager_state.cheats[i].rumble_type, &cheat_manager_state.cheats[i].rumble_value , &cheat_manager_state.cheats[i].rumble_port, &cheat_manager_state.cheats[i].rumble_primary_strength, &cheat_manager_state.cheats[i].rumble_primary_duration, &cheat_manager_state.cheats[i].rumble_secondary_strength, &cheat_manager_state.cheats[i].rumble_secondary_duration } ; char* keys[13] = { "cheat%u_handler", "cheat%u_memory_search_size", "cheat%u_cheat_type", "cheat%u_value", "cheat%u_address", "cheat%u_address_bit_position", "cheat%u_rumble_type", "cheat%u_rumble_value", "cheat%u_rumble_port", "cheat%u_rumble_primary_strength", "cheat%u_rumble_primary_duration", "cheat%u_rumble_secondary_strength", "cheat%u_rumble_secondary_duration", }; char desc_key[256]; char code_key[256]; char enable_key[256]; char endian_key[256]; char *tmp = NULL; bool tmp_bool = false; endian_key[0] = desc_key[0] = code_key[0] = enable_key[0] = '\0'; snprintf(desc_key, sizeof(desc_key), "cheat%u_desc", i-orig_size); snprintf(code_key, sizeof(code_key), "cheat%u_code", i-orig_size); snprintf(enable_key, sizeof(enable_key), "cheat%u_enable", i-orig_size); snprintf(endian_key, sizeof(endian_key), "cheat%u_endian", i-orig_size); cheat_manager_state.cheats[i].idx = i ; cheat_manager_state.cheats[i].desc[0] = 0 ; cheat_manager_state.cheats[i].code[0] = 0 ; cheat_manager_state.cheats[i].state = false ; cheat_manager_state.cheats[i].big_endian = false ; if (config_get_string(conf, desc_key, &tmp) && !string_is_empty(tmp)) strcpy(cheat_manager_state.cheats[i].desc,tmp) ; if (config_get_string(conf, code_key, &tmp) && !string_is_empty(tmp)) strcpy(cheat_manager_state.cheats[i].code,tmp) ; if (config_get_bool(conf, enable_key, &tmp_bool)) cheat_manager_state.cheats[i].state = tmp_bool; if (config_get_bool(conf, endian_key, &tmp_bool)) cheat_manager_state.cheats[i].big_endian = tmp_bool; if (tmp) free(tmp); cheat_manager_state.cheats[i].cheat_type = CHEAT_TYPE_SET_TO_VALUE ; cheat_manager_state.cheats[i].memory_search_size = 3; for (j = 0 ; j < 13 ; j++ ) { char key[50] ; unsigned val = 0; snprintf(key, sizeof(key), keys[j], i-orig_size); if ( config_get_uint(conf, key, &val)) *(data_ptrs[j]) = val ; } } 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 ; if (!cheat_manager_state.cheats) { cheat_manager_state.cheats = (struct item_cheat*) calloc(new_size, sizeof(struct item_cheat)); orig_size = 0 ; } else { orig_size = cheat_manager_state.size ; cheat_manager_state.cheats = (struct item_cheat*) realloc(cheat_manager_state.cheats, new_size * sizeof(struct item_cheat)); } if (!cheat_manager_state.cheats) { cheat_manager_state.buf_size = cheat_manager_state.size = 0; cheat_manager_state.cheats = NULL; return false; } cheat_manager_state.buf_size = new_size; cheat_manager_state.size = new_size; for (i = orig_size; i < cheat_manager_state.size; i++) { memset(&(cheat_manager_state.cheats[i]), 0, sizeof(cheat_manager_state.cheats[i])) ; cheat_manager_state.cheats[i].state = false; cheat_manager_state.cheats[i].handler = default_handler; cheat_manager_state.cheats[i].cheat_type = CHEAT_TYPE_SET_TO_VALUE ; cheat_manager_state.cheats[i].memory_search_size = 3; cheat_manager_state.cheats[i].idx = i; } return true; } void cheat_manager_free(void) { if (cheat_manager_state.cheats) free(cheat_manager_state.cheats); if ( cheat_manager_state.prev_memory_buf ) free(cheat_manager_state.prev_memory_buf) ; if ( cheat_manager_state.matches ) free(cheat_manager_state.matches) ; cheat_manager_state.cheats = NULL ; cheat_manager_state.size = 0 ; cheat_manager_state.buf_size = 0 ; cheat_manager_state.prev_memory_buf = NULL ; cheat_manager_state.curr_memory_buf = NULL ; cheat_manager_state.matches = NULL ; cheat_manager_state.total_memory_size = 0 ; cheat_manager_state.actual_memory_size = 0 ; cheat_manager_state.memory_initialized = false ; cheat_manager_state.memory_search_initialized = false ; } void cheat_manager_update(cheat_manager_t *handle, unsigned handle_idx) { char msg[256]; if (!handle || !handle->cheats) return; snprintf(msg, sizeof(msg), "Cheat: #%u [%s]: %s", handle_idx, handle->cheats[handle_idx].state ? "ON" : "OFF", (handle->cheats[handle_idx].desc[0]) ? (handle->cheats[handle_idx].desc) : (handle->cheats[handle_idx].code) ); runloop_msg_queue_push(msg, 1, 180, true); RARCH_LOG("%s\n", msg); } void cheat_manager_toggle_index(unsigned i) { if (!cheat_manager_state.cheats) return; cheat_manager_state.cheats[i].state = !cheat_manager_state.cheats[i].state; cheat_manager_update(&cheat_manager_state, i); settings_t *settings = config_get_ptr(); if ( !settings ) return ; if ( settings->bools.apply_cheats_after_toggle ) cheat_manager_apply_cheats(); } void cheat_manager_toggle(void) { if (!cheat_manager_state.cheats) return; cheat_manager_state.cheats[cheat_manager_state.ptr].state ^= true; cheat_manager_apply_cheats(); cheat_manager_update(&cheat_manager_state, cheat_manager_state.ptr); } void cheat_manager_index_next(void) { if (!cheat_manager_state.cheats) return; cheat_manager_state.ptr = (cheat_manager_state.ptr + 1) % cheat_manager_state.size; cheat_manager_update(&cheat_manager_state, cheat_manager_state.ptr); } void cheat_manager_index_prev(void) { if (!cheat_manager_state.cheats) return; if (cheat_manager_state.ptr == 0) cheat_manager_state.ptr = cheat_manager_state.size - 1; else cheat_manager_state.ptr--; cheat_manager_update(&cheat_manager_state, cheat_manager_state.ptr); } const char *cheat_manager_get_code(unsigned i) { if (!cheat_manager_state.cheats) return NULL; return cheat_manager_state.cheats[i].code; } const char *cheat_manager_get_desc(unsigned i) { if (!cheat_manager_state.cheats) return NULL; return cheat_manager_state.cheats[i].desc; } bool cheat_manager_get_code_state(unsigned i) { if (!cheat_manager_state.cheats) return false; return cheat_manager_state.cheats[i].state; } bool cheat_manager_get_game_specific_filename(char * cheat_filename, size_t max_length) { settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); const char *core_name = NULL; const char *game_name = NULL; struct retro_system_info system_info; if ( settings == NULL || global == NULL || cheat_filename == NULL) return false ; if ( !core_get_system_info(&system_info) ) return false ; core_name = system_info.library_name; game_name = path_basename(global->name.cheatfile); if ( string_is_empty(settings->paths.path_cheat_database) || string_is_empty(core_name) || string_is_empty(game_name) ) return false ; cheat_filename[0] = '\0'; strlcat(cheat_filename, settings->paths.path_cheat_database, max_length-1) ; fill_pathname_slash(cheat_filename, max_length) ; strlcat(cheat_filename, core_name, max_length-strlen(cheat_filename)-1) ; fill_pathname_slash(cheat_filename, max_length) ; if (!filestream_exists(cheat_filename)) path_mkdir(cheat_filename); strlcat(cheat_filename, game_name, max_length-strlen(cheat_filename)-1) ; return true ; } void cheat_manager_load_game_specific_cheats() { char cheat_file[PATH_MAX_LENGTH] ; if (cheat_manager_get_game_specific_filename(cheat_file, PATH_MAX_LENGTH) ) cheat_manager_load(cheat_file,true) ; } void cheat_manager_save_game_specific_cheats() { char cheat_file[PATH_MAX_LENGTH] ; if (cheat_manager_get_game_specific_filename(cheat_file, PATH_MAX_LENGTH) ) cheat_manager_save(cheat_file, NULL, true) ; } void cheat_manager_state_free(void) { cheat_manager_free(); } bool cheat_manager_alloc_if_empty(void) { if (!cheat_manager_state.cheats) { cheat_manager_new(0); } return true; } int cheat_manager_initialize_memory(void *data, bool wraparound) { retro_ctx_memory_info_t meminfo ; bool refresh = false; bool is_search_initialization = (data != NULL) ; 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); return 0 ; } cheat_manager_state.actual_memory_size = meminfo.size ; cheat_manager_state.curr_memory_buf = meminfo.data ; cheat_manager_state.total_memory_size = meminfo.size ; cheat_manager_state.num_matches = (cheat_manager_state.total_memory_size*8)/((int)pow(2,cheat_manager_state.search_bit_size)) ; //ensure we're aligned on 4-byte boundary //if ( meminfo.size % 4 > 0 ) { //cheat_manager_state.total_memory_size = cheat_manager_state.total_memory_size + (4 - (meminfo.size%4)) ; //} if ( is_search_initialization ) { cheat_manager_state.prev_memory_buf = (uint8_t*) calloc(cheat_manager_state.total_memory_size, sizeof(uint8_t)); if (!cheat_manager_state.prev_memory_buf ) { runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_FAIL), 1, 180, true); return 0 ; } cheat_manager_state.matches = (uint8_t*) calloc(cheat_manager_state.total_memory_size, sizeof(uint8_t)); if (!cheat_manager_state.matches ) { free(cheat_manager_state.prev_memory_buf) ; cheat_manager_state.prev_memory_buf = NULL ; runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_FAIL), 1, 180, true); return 0 ; } memset(cheat_manager_state.matches, 0xFF, cheat_manager_state.total_memory_size) ; memcpy(cheat_manager_state.prev_memory_buf, cheat_manager_state.curr_memory_buf, cheat_manager_state.actual_memory_size); cheat_manager_state.memory_search_initialized = true ; } cheat_manager_state.memory_initialized = true ; runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_INIT_SUCCESS), 1, 180, true); if ( !wraparound ) { menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); } return 0 ; } 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 ; } } } int cheat_manager_search_exact(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_EXACT) ; } int cheat_manager_search_lt(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_LT) ; } int cheat_manager_search_gt(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_GT) ; } int cheat_manager_search_lte(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_LTE) ; } int cheat_manager_search_gte(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_GTE) ; } int cheat_manager_search_eq(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_EQ) ; } int cheat_manager_search_neq(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_NEQ) ; } int cheat_manager_search_eqplus(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_EQPLUS) ; } int cheat_manager_search_eqminus(void *data, bool wraparound) { return cheat_manager_search(CHEAT_SEARCH_TYPE_EQMINUS) ; } int cheat_manager_search(enum cheat_search_type search_type) { char msg[100]; unsigned char *curr = cheat_manager_state.curr_memory_buf ; unsigned char *prev = cheat_manager_state.prev_memory_buf ; unsigned int idx = 0 ; unsigned int num_matches = 0 ; unsigned int curr_val ; unsigned int prev_val ; unsigned int mask = 0 ; unsigned int bytes_per_item = 1 ; unsigned int bits = 8 ; bool refresh = false; if ( cheat_manager_state.curr_memory_buf == NULL ) { runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_NOT_INITIALIZED), 1, 180, true); return 0 ; } cheat_manager_setup_search_meta(cheat_manager_state.search_bit_size, &bytes_per_item, &mask, &bits) ; //little endian FF000000 = 256 for ( idx = 0 ; idx < cheat_manager_state.total_memory_size ; idx = idx + bytes_per_item) { unsigned byte_part; switch ( bytes_per_item ) { case 2 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256) + *(curr+idx+1) : *(curr+idx) + (*(curr+idx+1)*256) ; prev_val = cheat_manager_state.big_endian ? (*(prev+idx)*256) + *(prev+idx+1) : *(prev+idx) + (*(prev+idx+1)*256) ; break ; } case 4 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256*256*256) + (*(curr+idx+1)*256*256) + (*(curr+idx+2)*256) + *(curr+idx+3) : *(curr+idx) + (*(curr+idx+1)*256) + (*(curr+idx+2)*256*256) + (*(curr+idx+3)*256*256*256) ; prev_val = cheat_manager_state.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) ; 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_manager_state.matches+idx) & (mask << (byte_part*bits)) ; } else { prev_match = *(cheat_manager_state.matches+idx) ; } if ( prev_match > 0 ) { bool match = false ; switch ( search_type ) { case CHEAT_SEARCH_TYPE_EXACT : { match = ( curr_subval == cheat_manager_state.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_manager_state.search_eqplus_value) ; break; } case CHEAT_SEARCH_TYPE_EQMINUS : { match = ( curr_subval == prev_subval-cheat_manager_state.search_eqminus_value) ; break; } } if (!match ) { if ( bits < 8 ) { *(cheat_manager_state.matches+idx) = *(cheat_manager_state.matches+idx) & (( ~(mask << (byte_part*bits))) & 0xFF ); } else { memset(cheat_manager_state.matches+idx,0,bytes_per_item) ; } if ( cheat_manager_state.num_matches > 0 ) { cheat_manager_state.num_matches-- ; } } } } } memcpy(cheat_manager_state.prev_memory_buf, cheat_manager_state.curr_memory_buf, cheat_manager_state.actual_memory_size); snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_CHEAT_SEARCH_FOUND_MATCHES), cheat_manager_state.num_matches) ; msg[sizeof(msg) - 1] = 0; runloop_msg_queue_push(msg, 1, 180, true); menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); return 0 ; } bool cheat_manager_add_new_code(unsigned int memory_search_size, unsigned int address, unsigned int address_mask, bool big_endian, unsigned int value) { int new_size = cheat_manager_get_size() + 1; if ( !cheat_manager_realloc(new_size, CHEAT_HANDLER_TYPE_RETRO) ) return false ; cheat_manager_state.cheats[cheat_manager_state.size-1].address = address ; cheat_manager_state.cheats[cheat_manager_state.size-1].address_mask = address_mask ; cheat_manager_state.cheats[cheat_manager_state.size-1].memory_search_size = memory_search_size ; cheat_manager_state.cheats[cheat_manager_state.size-1].value = value ; cheat_manager_state.cheats[cheat_manager_state.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 char *curr = cheat_manager_state.curr_memory_buf; if ( cheat_manager_state.num_matches + cheat_manager_state.size > 100 ) { runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_TOO_MANY), 1, 180, true); return 0 ; } cheat_manager_setup_search_meta(cheat_manager_state.search_bit_size, &bytes_per_item, &mask, &bits) ; for (idx = 0 ; idx < cheat_manager_state.total_memory_size ; idx = idx + bytes_per_item) { switch ( bytes_per_item ) { case 2 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256) + *(curr+idx+1) : *(curr+idx) + (*(curr+idx+1)*256) ; break ; } case 4 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256*256*256) + (*(curr+idx+1)*256*256) + (*(curr+idx+2)*256) + *(curr+idx+3) : *(curr+idx) + (*(curr+idx+1)*256) + (*(curr+idx+2)*256*256) + (*(curr+idx+3)*256*256*256) ; break ; } case 1 : default : { curr_val = *(curr+idx) ; break ; } } for (byte_part = 0 ; byte_part < 8/bits ; byte_part++) { unsigned int prev_match; if ( bits < 8 ) { prev_match = *(cheat_manager_state.matches+idx) & (mask << (byte_part*bits)) ; if ( prev_match ) { if ( !cheat_manager_add_new_code(cheat_manager_state.search_bit_size, idx, (mask << (byte_part*bits)), cheat_manager_state.big_endian, curr_val) ) { runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_FAIL), 1, 180, true); return 0 ; } num_added++ ; } } else { prev_match = *(cheat_manager_state.matches+idx) ; if ( prev_match ) { if ( !cheat_manager_add_new_code(cheat_manager_state.search_bit_size, idx, 0xFF, cheat_manager_state.big_endian, curr_val) ) { runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_FAIL), 1, 180, true); return 0 ; } num_added++ ; } } } } snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_CHEAT_SEARCH_ADDED_MATCHES_SUCCESS), cheat_manager_state.num_matches) ; msg[sizeof(msg) - 1] = 0; runloop_msg_queue_push(msg, 1, 180, true); menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); return 0 ; } void cheat_manager_apply_rumble(struct item_cheat *cheat, unsigned int curr_value) { bool rumble = false ; 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 ; } } 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 = cpu_features_get_time_usec() + (cheat->rumble_primary_duration*1000) ; cheat->rumble_secondary_end_time = cpu_features_get_time_usec() + (cheat->rumble_secondary_duration*1000) ; input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_STRONG, cheat->rumble_primary_strength); input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, cheat->rumble_secondary_strength); } } else { cheat->rumble_initialized++ ; return ; } if ( cheat->rumble_primary_end_time <= cpu_features_get_time_usec() ) { if ( cheat->rumble_primary_end_time != 0 ) input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_STRONG, 0); cheat->rumble_primary_end_time = 0; } else { input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_STRONG, cheat->rumble_primary_strength); } if ( cheat->rumble_secondary_end_time <= cpu_features_get_time_usec() ) { if ( cheat->rumble_secondary_end_time != 0 ) input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, 0); cheat->rumble_secondary_end_time = 0 ; } else { input_driver_set_rumble_state(cheat->rumble_port, RETRO_RUMBLE_WEAK, cheat->rumble_secondary_strength); } } void cheat_manager_apply_retro_cheats(void) { unsigned i; unsigned int mask = 0; unsigned int bytes_per_item = 1; unsigned int bits = 8; unsigned int curr_val = 0; unsigned int num_added = 0; bool run_cheat = true; if ((!cheat_manager_state.cheats)) return; for (i = 0 ; i < cheat_manager_state.size ; i++ ) { unsigned char *curr; unsigned int idx; bool set_value = false; unsigned int value_to_set = 0; if (cheat_manager_state.cheats[i].handler != CHEAT_HANDLER_TYPE_RETRO || !cheat_manager_state.cheats[i].state) continue ; if ( !cheat_manager_state.memory_initialized ) cheat_manager_initialize_memory(NULL, false) ; /* If we're still not initialized, something must have gone wrong - just bail */ if ( !cheat_manager_state.memory_initialized ) return; if ( !run_cheat ) { run_cheat = true ; continue ; } cheat_manager_setup_search_meta(cheat_manager_state.cheats[i].memory_search_size, &bytes_per_item, &mask, &bits) ; curr = cheat_manager_state.curr_memory_buf ; idx = cheat_manager_state.cheats[i].address ; switch ( bytes_per_item ) { case 2 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256) + *(curr+idx+1) : *(curr+idx) + (*(curr+idx+1)*256) ; break ; } case 4 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256*256*256) + (*(curr+idx+1)*256*256) + (*(curr+idx+2)*256) + *(curr+idx+3) : *(curr+idx) + (*(curr+idx+1)*256) + (*(curr+idx+2)*256*256) + (*(curr+idx+3)*256*256*256) ; break ; } case 1 : default : { curr_val = *(curr+idx) ; break ; } } cheat_manager_apply_rumble(&cheat_manager_state.cheats[i], curr_val) ; switch ( cheat_manager_state.cheats[i].cheat_type ) { case CHEAT_TYPE_SET_TO_VALUE : { set_value = true ; value_to_set = cheat_manager_state.cheats[i].value ; break ; } case CHEAT_TYPE_INCREASE_VALUE: { set_value = true ; value_to_set = curr_val + cheat_manager_state.cheats[i].value ; break; } case CHEAT_TYPE_DECREASE_VALUE: { set_value = true ; value_to_set = curr_val - cheat_manager_state.cheats[i].value ; break; } case CHEAT_TYPE_RUN_NEXT_IF_EQ: { if (!( curr_val == cheat_manager_state.cheats[i].value )) { run_cheat = false ; } break; } case CHEAT_TYPE_RUN_NEXT_IF_NEQ: { if (!( curr_val != cheat_manager_state.cheats[i].value )) { run_cheat = false ; } break; } case CHEAT_TYPE_RUN_NEXT_IF_LT: { if (!( cheat_manager_state.cheats[i].value < curr_val)) { run_cheat = false ; } break; } case CHEAT_TYPE_RUN_NEXT_IF_GT: { if (!( cheat_manager_state.cheats[i].value > curr_val)) { run_cheat = false ; } break; } } if ( set_value ) { switch ( bytes_per_item ) { case 2 : { if ( cheat_manager_state.cheats[i].big_endian) { *(curr+idx) = (value_to_set >> 8) & 0xFF ; *(curr+idx+1) = value_to_set & 0xFF ; } else { *(curr+idx) = value_to_set & 0xFF ; *(curr+idx+1) = (value_to_set >> 8) & 0xFF ; } break ; } case 4 : { if ( cheat_manager_state.cheats[i].big_endian) { *(curr+idx) = (value_to_set >> 24) & 0xFF ; *(curr+idx+1) = (value_to_set >> 16) & 0xFF ; *(curr+idx+2) = (value_to_set >> 8) & 0xFF ; *(curr+idx+3) = value_to_set & 0xFF ; } else { *(curr+idx) = value_to_set & 0xFF ; *(curr+idx+1) = (value_to_set >> 8) & 0xFF ; *(curr+idx+2) = (value_to_set >> 16) & 0xFF ; *(curr+idx+3) = (value_to_set >> 24) & 0xFF ; } break ; } case 1 : { if ( bits < 8 ) { unsigned bitpos; unsigned char val = *(curr+idx); for (bitpos = 0 ; bitpos < 8 ; bitpos++) { if ((cheat_manager_state.cheats[i].address_mask>>bitpos)&0x01 ) { mask = (~(1<>bitpos)&0x01)< cheat_manager_state.num_matches-1) return; if ( curr == NULL ) return ; cheat_manager_setup_search_meta(cheat_manager_state.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_manager_state.total_memory_size ; idx = idx + bytes_per_item) { switch ( bytes_per_item ) { case 2 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256) + *(curr+idx+1) : *(curr+idx) + (*(curr+idx+1)*256) ; if ( prev != NULL ) prev_val = cheat_manager_state.big_endian ? (*(prev+idx)*256) + *(prev+idx+1) : *(prev+idx) + (*(prev+idx+1)*256) ; break ; } case 4 : { curr_val = cheat_manager_state.big_endian ? (*(curr+idx)*256*256*256) + (*(curr+idx+1)*256*256) + (*(curr+idx+2)*256) + *(curr+idx+3) : *(curr+idx) + (*(curr+idx+1)*256) + (*(curr+idx+2)*256*256) + (*(curr+idx+3)*256*256*256) ; if ( prev != NULL ) prev_val = cheat_manager_state.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) ; if ( prev != NULL ) prev_val = *(prev+idx) ; break ; } } if ( match_action == CHEAT_MATCH_ACTION_TYPE_BROWSE) { *curr_value = curr_val ; *prev_value = prev_val ; return ; } if ( prev == NULL ) return ; for (byte_part = 0 ; byte_part < 8/bits ; byte_part++) { unsigned int prev_match ; if ( bits < 8 ) { prev_match = *(cheat_manager_state.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_manager_state.search_bit_size, idx, (mask << (byte_part*bits)), cheat_manager_state.big_endian, curr_val) ) runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_FAIL), 1, 180, true); else runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_SUCCESS), 1, 180, true); return ; } case CHEAT_MATCH_ACTION_TYPE_DELETE : { if ( bits < 8 ) *(cheat_manager_state.matches+idx) = *(cheat_manager_state.matches+idx) & (( ~(mask << (byte_part*bits))) & 0xFF ); else memset(cheat_manager_state.matches+idx,0,bytes_per_item) ; if ( cheat_manager_state.num_matches > 0 ) cheat_manager_state.num_matches-- ; runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_DELETE_MATCH_SUCCESS), 1, 180, true); return ; } } return ; } curr_match_idx++ ; } } else { prev_match = *(cheat_manager_state.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_manager_state.search_bit_size, idx, 0xFF, cheat_manager_state.big_endian, curr_val) ) runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_FAIL), 1, 180, true); else runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_ADD_MATCH_SUCCESS), 1, 180, true); return ; } case CHEAT_MATCH_ACTION_TYPE_DELETE : { if ( bits < 8 ) *(cheat_manager_state.matches+idx) = *(cheat_manager_state.matches+idx) & (( ~(mask << (byte_part*bits))) & 0xFF ); else memset(cheat_manager_state.matches+idx,0,bytes_per_item) ; if ( cheat_manager_state.num_matches > 0 ) cheat_manager_state.num_matches-- ; runloop_msg_queue_push(msg_hash_to_str(MSG_CHEAT_SEARCH_DELETE_MATCH_SUCCESS), 1, 180, true); return ; } } } curr_match_idx++ ; } } } } } int cheat_manager_copy_match(void *data, bool wraparound) { cheat_manager_match_action(CHEAT_MATCH_ACTION_TYPE_COPY, cheat_manager_state.match_idx, NULL, NULL, NULL, NULL) ; return 0 ; } int cheat_manager_delete_match(void *data, bool wraparound) { bool refresh = false ; cheat_manager_match_action(CHEAT_MATCH_ACTION_TYPE_DELETE, cheat_manager_state.match_idx, NULL, NULL, NULL, NULL) ; menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); return 0 ; }