From b15411dad864413d62ea6c479b902ef072c0001f Mon Sep 17 00:00:00 2001 From: Jamiras Date: Thu, 16 Jul 2020 13:35:34 -0600 Subject: [PATCH] generic memory mapping using rcheevos --- Makefile.common | 2 +- cheevos/cheevos.c | 117 +++----- cheevos/cheevos.h | 4 +- cheevos/fixup.c | 314 --------------------- cheevos/memory.c | 340 +++++++++++++++++++++++ cheevos/{fixup.h => memory.h} | 29 +- deps/rcheevos/src/rcheevos/consoleinfo.c | 2 +- griffin/griffin.c | 2 +- retroarch.c | 12 +- 9 files changed, 410 insertions(+), 412 deletions(-) delete mode 100644 cheevos/fixup.c create mode 100644 cheevos/memory.c rename cheevos/{fixup.h => memory.h} (62%) diff --git a/Makefile.common b/Makefile.common index cdcab25402..60e1d844df 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1952,7 +1952,7 @@ ifeq ($(HAVE_NETWORKING), 1) OBJ += cheevos/cheevos.o \ cheevos/badges.o \ - cheevos/fixup.o \ + cheevos/memory.o \ cheevos/parser.o \ cheevos/hash.o \ $(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \ diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 435fa3ea26..239727f385 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -52,7 +52,7 @@ #include "badges.h" #include "cheevos.h" -#include "fixup.h" +#include "memory.h" #include "parser.h" #include "hash.h" #include "util.h" @@ -79,9 +79,6 @@ /* Define this macro to prevent cheevos from being deactivated. */ #undef CHEEVOS_DONT_DEACTIVATE -/* Define this macro to dump all cheevos' addresses. */ -#undef CHEEVOS_DUMP_ADDRS - /* Define this macro to load a JSON file from disk instead of downloading * from retroachievements.org. */ #undef CHEEVOS_JSON_OVERRIDE @@ -166,7 +163,7 @@ typedef struct rcheevos_lboard_t* lboards; rcheevos_richpresence_t richpresence; - rcheevos_fixups_t fixups; + rcheevos_memory_regions_t memory; char token[32]; char hash[33]; @@ -185,7 +182,7 @@ static rcheevos_locals_t rcheevos_locals = NULL, /* unofficial */ NULL, /* lboards */ {0}, /* rich presence */ - {0}, /* fixups */ + {{0}},/* memory */ {0}, /* token */ "N/A",/* hash */ }; @@ -377,31 +374,29 @@ static void rcheevos_log_post_url( #endif } +uint8_t* rcheevos_patch_address(unsigned address) +{ + return rcheevos_memory_find(&rcheevos_locals.memory, address); +} + static unsigned rcheevos_peek(unsigned address, unsigned num_bytes, void* ud) { - const uint8_t* data = rcheevos_fixup_find(&rcheevos_locals.fixups, - address, rcheevos_locals.patchdata.console_id); - unsigned value = 0; - - if (data) + uint8_t* data = rcheevos_memory_find(&rcheevos_locals.memory, address); + if (data != NULL) { switch (num_bytes) { - case 4: - value |= data[2] << 16 | data[3] << 24; - case 2: - value |= data[1] << 8; - case 1: - value |= data[0]; + case 4: return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0]); + case 3: return (data[2] << 16) | (data[1] << 8) | (data[0]); + case 2: return (data[1] << 8) | (data[0]); + case 1: return data[0]; } } - else - rcheevos_locals.invalid_peek_address = true; - return value; + rcheevos_locals.invalid_peek_address = true; + return 0; } - static void rcheevos_async_award_achievement(rcheevos_async_io_request* request); static void rcheevos_async_submit_lboard(rcheevos_async_io_request* request); @@ -564,8 +559,6 @@ static int rcheevos_parse(const char* json) rcheevos_lboard_t* lboard = NULL; rcheevos_racheevo_t* rac = NULL; - rcheevos_fixup_init(&rcheevos_locals.fixups); - res = rcheevos_get_patchdata(json, &rcheevos_locals.patchdata); if (res != 0) @@ -595,52 +588,18 @@ static int rcheevos_parse(const char* json) return 0; } - /* Achievement memory accesses are 0-based, regardless of - * where the memory is accessed by the - * emulated code. As such, address 0 should always be - * accessible and serves as an indicator that - * other addresses will also be accessible. - * Individual achievements will be "Unsupported" if - * they contain addresses that cannot be resolved. - * This check gives the user immediate feedback - * if the core they're trying to use will disable all - * achievements as "Unsupported". - */ - if (!rcheevos_patch_address(0, rcheevos_locals.patchdata.console_id)) + if (!rcheevos_memory_init(&rcheevos_locals.memory, rcheevos_locals.patchdata.console_id)) { - int delay_judgment = 0; - rarch_system_info_t* system = runloop_get_system_info(); - - if (system->mmaps.num_descriptors == 0) + /* some cores (like Mupen64-Plus) don't expose the memory until the first call to retro_run. + * in that case, there will be a total_size of memory reported by the core, but init will return + * false, as all of the pointers were null. + */ + if (rcheevos_locals.memory.total_size != 0) { - /* Special case: the mupen64plus-nx core doesn't - * initialize the RAM immediately. To avoid a race - * condition - if the core says there's SYSTEM_RAM, - * but the pointer is NULL, proceed. If the memory - * isn't exposed when the achievements start processing, - * they'll be marked "Unsupported" individually. - */ - retro_ctx_memory_info_t meminfo; - meminfo.id = RETRO_MEMORY_SYSTEM_RAM; - core_get_memory(&meminfo); - - delay_judgment |= (meminfo.size > 0); + /* reset the memory count and we'll re-evaluate in rcheevos_test() */ + rcheevos_locals.memory.count = 0; } else - { - /* Special case: the sameboy core exposes the RAM - * at $8000, but not the ROM at $0000. NES and - * Gameboy achievements do attempt to map the - * entire bus, and it's unlikely that an achievement - * will reference the ROM data, so if the RAM is - * still present, allow the core to load. If any - * achievements do reference the ROM data, they'll - * be marked "Unsupported" individually. - */ - delay_judgment |= (rcheevos_patch_address(0x8000, rcheevos_locals.patchdata.console_id) != NULL); - } - - if (!delay_judgment) { CHEEVOS_ERR(RCHEEVOS_TAG "No memory exposed by core\n"); @@ -802,7 +761,7 @@ error: CHEEVOS_FREE(rcheevos_locals.unofficial); CHEEVOS_FREE(rcheevos_locals.lboards); rcheevos_free_patchdata(&rcheevos_locals.patchdata); - rcheevos_fixup_destroy(&rcheevos_locals.fixups); + rcheevos_memory_destroy(&rcheevos_locals.memory); return -1; } @@ -1128,6 +1087,10 @@ void rcheevos_reset_game(void) } rcheevos_locals.richpresence.last_update = cpu_features_get_time_usec(); + + /* some cores reallocate memory on reset, make sure we update our pointers */ + if (rcheevos_locals.memory.total_size > 0) + rcheevos_memory_init(&rcheevos_locals.memory, rcheevos_locals.patchdata.console_id); } #ifdef HAVE_MENU @@ -1348,7 +1311,7 @@ bool rcheevos_unload(void) CHEEVOS_FREE(rcheevos_locals.lboards); CHEEVOS_FREE(rcheevos_locals.richpresence.richpresence); rcheevos_free_patchdata(&rcheevos_locals.patchdata); - rcheevos_fixup_destroy(&rcheevos_locals.fixups); + rcheevos_memory_destroy(&rcheevos_locals.memory); rcheevos_locals.core = NULL; rcheevos_locals.unofficial = NULL; @@ -1411,6 +1374,21 @@ void rcheevos_test(void) { settings_t *settings = config_get_ptr(); + if (rcheevos_locals.memory.count == 0) + { + /* we were unable to initialize memory earlier, try now */ + if (!rcheevos_memory_init(&rcheevos_locals.memory, rcheevos_locals.patchdata.console_id)) + { + CHEEVOS_ERR(RCHEEVOS_TAG "No memory exposed by core\n"); + + if (settings->bools.cheevos_verbose_enable) + runloop_msg_queue_push("Cannot activate achievements using this core.", 0, 4 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING); + + rcheevos_loaded = false; + return; + } + } + rcheevos_test_cheevo_set(true); if (settings) @@ -1435,11 +1413,6 @@ bool rcheevos_get_support_cheevos(void) return rcheevos_locals.core_supports; } -int rcheevos_get_console(void) -{ - return rcheevos_locals.patchdata.console_id; -} - const char* rcheevos_get_hash(void) { return rcheevos_locals.hash; diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h index b1096be9a5..6844bf3a23 100644 --- a/cheevos/cheevos.h +++ b/cheevos/cheevos.h @@ -61,12 +61,12 @@ void rcheevos_set_support_cheevos(bool state); bool rcheevos_get_support_cheevos(void); -int rcheevos_get_console(void); - const char* rcheevos_get_hash(void); const char *rcheevos_get_richpresence(void); +uint8_t* rcheevos_patch_address(unsigned address); + extern bool rcheevos_loaded; extern bool rcheevos_hardcore_active; extern bool rcheevos_hardcore_paused; diff --git a/cheevos/fixup.c b/cheevos/fixup.c deleted file mode 100644 index 67bc4cf2a7..0000000000 --- a/cheevos/fixup.c +++ /dev/null @@ -1,314 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2015-2018 - Andre Leiradella - * - * 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 "fixup.h" -#include "cheevos.h" -#include "util.h" - -#include "../retroarch.h" -#include "../core.h" - -#include "../deps/rcheevos/include/rcheevos.h" -#include "../deps/rcheevos/include/rconsoles.h" - -static int rcheevos_cmpaddr(const void* e1, const void* e2) -{ - const rcheevos_fixup_t* f1 = (const rcheevos_fixup_t*)e1; - const rcheevos_fixup_t* f2 = (const rcheevos_fixup_t*)e2; - - if (f1->address < f2->address) - return -1; - else if (f1->address > f2->address) - return 1; - return 0; -} - -static size_t rcheevos_var_reduce(size_t addr, size_t mask) -{ - while (mask) - { - size_t tmp = (mask - 1) & ~mask; - addr = (addr & tmp) | ((addr >> 1) & ~tmp); - mask = (mask & (mask - 1)) >> 1; - } - - return addr; -} - -static size_t rcheevos_var_highest_bit(size_t n) -{ - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - - return n ^ (n >> 1); -} - -void rcheevos_fixup_init(rcheevos_fixups_t* fixups) -{ - fixups->elements = NULL; - fixups->capacity = fixups->count = 0; - fixups->dirty = false; -} - -void rcheevos_fixup_destroy(rcheevos_fixups_t* fixups) -{ - CHEEVOS_FREE(fixups->elements); - rcheevos_fixup_init(fixups); -} - -const uint8_t* rcheevos_fixup_find( - rcheevos_fixups_t* fixups, unsigned address, int console) -{ - rcheevos_fixup_t key; - rcheevos_fixup_t* found; - const uint8_t* location; - - if (fixups->dirty) - { - qsort(fixups->elements, fixups->count, - sizeof(rcheevos_fixup_t), rcheevos_cmpaddr); - fixups->dirty = false; - } - - key.address = address; - found = (rcheevos_fixup_t*)bsearch(&key, - fixups->elements, fixups->count, - sizeof(rcheevos_fixup_t), rcheevos_cmpaddr); - - if (found) - return found->location; - - if (fixups->count == fixups->capacity) - { - unsigned new_capacity = fixups->capacity == 0 ? 16 : fixups->capacity * 2; - rcheevos_fixup_t* new_elements = (rcheevos_fixup_t*) - realloc(fixups->elements, new_capacity * sizeof(rcheevos_fixup_t)); - - if (!new_elements) - return NULL; - - fixups->elements = new_elements; - fixups->capacity = new_capacity; - } - - fixups->elements[fixups->count].address = address; - fixups->elements[fixups->count++].location = location = - rcheevos_patch_address(address, console); - fixups->dirty = true; - - return location; -} - -const uint8_t* rcheevos_patch_address(unsigned address, int console) -{ - rarch_system_info_t* system = runloop_get_system_info(); - const void* pointer = NULL; - unsigned original_address = address; - - switch (console) - { - case RC_CONSOLE_NINTENDO: - if (address >= 0x0800 && address < 0x2000) - { - /* Address in the mirrorred RAM, - * adjust to real RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "NES memory address in mirrorred RAM %X, adjusted to %X\n", address, address & 0x07ff); - address &= 0x07ff; - } - break; - case RC_CONSOLE_GAMEBOY_COLOR: - if (address >= 0xe000 && address <= 0xfdff) - { - /* Address in the echo RAM, adjust to real RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "GBC memory address in echo RAM %X, adjusted to %X\n", address, address - 0x2000); - address -= 0x2000; - } - break; - default: - break; - } - - if (system->mmaps.num_descriptors != 0) - { - /* We have memory descriptors, use it. */ - const rarch_memory_descriptor_t* desc = NULL; - const rarch_memory_descriptor_t* end = NULL; - - /* Patch the address to correctly map it to the mmaps. */ - switch (console) - { - case RC_CONSOLE_GAMEBOY_ADVANCE: - if (address < 0x8000) - { - /* Internal RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "GBA memory address %X adjusted to %X\n", address, address + 0x3000000); - address += 0x3000000; - } - else - { - /* Work RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "GBA memory address %X adjusted to %X\n", address, address + 0x2000000 - 0x8000); - address += 0x2000000 - 0x8000; - } - break; - case RC_CONSOLE_PC_ENGINE: - if (address < 0x002000) - { - /* RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "PCE memory address %X adjusted to %X\n", address, address + 0x1f0000); - address += 0x1f0000; - } - else if (address < 0x012000) - { - /* CD-ROM RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "PCE memory address %X adjusted to %X\n", address, address + 0x100000 - 0x002000); - address += 0x100000 - 0x002000; - } - else if (address < 0x042000) - { - /* Super System Card RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "PCE memory address %X adjusted to %X\n", address, address + 0x0d0000 - 0x012000); - address += 0x0d0000 - 0x012000; - } - else - { - /* CD-ROM battery backed RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "PCE memory address %X adjusted to %X\n", address, address + 0x1ee000 - 0x042000); - address += 0x1ee000 - 0x042000; - } - break; - case RC_CONSOLE_SUPER_NINTENDO: - if (address < 0x020000) - { - /* Work RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "SNES memory address %X adjusted to %X\n", address, address + 0x7e0000); - address += 0x7e0000; - } - else - { - /* Save RAM. */ - CHEEVOS_LOG(RCHEEVOS_TAG "SNES memory address %X adjusted to %X\n", address, address + 0x006000 - 0x020000); - address += 0x006000 - 0x020000; - } - break; - case RC_CONSOLE_SEGA_CD: - if (address < 0x010000) - { - /* Work RAM. */ - address += 0xFF0000; - CHEEVOS_LOG(RCHEEVOS_TAG "Sega CD memory address %X adjusted to %X\n", original_address, address); - } - else - { - /* CD-ROM peripheral RAM - exposed at virtual address to avoid banking */ - address += 0x80020000 - 0x010000; - CHEEVOS_LOG(RCHEEVOS_TAG "Sega CD memory address %X adjusted to %X\n", original_address, address); - } - break; - default: - break; - } - - desc = system->mmaps.descriptors; - end = desc + system->mmaps.num_descriptors; - - for (; desc < end; desc++) - { - if (((desc->core.start ^ address) & desc->core.select) == 0) - { - pointer = desc->core.ptr; - address -= desc->core.start; - - if (desc->disconnect_mask) - address = (unsigned)rcheevos_var_reduce( - address & desc->disconnect_mask, desc->core.disconnect); - - if (address >= desc->core.len) - address -= rcheevos_var_highest_bit(address); - - address += desc->core.offset; - - CHEEVOS_LOG(RCHEEVOS_TAG "address %X set to descriptor %d at offset %X\n", original_address, - (int)((desc - system->mmaps.descriptors) + 1), address); - break; - } - } - } - else if (console == RC_CONSOLE_GAMEBOY_ADVANCE) - { - /* The RetroAchievements implementation of memory access - * for GBA puts the save RAM first, - * so the default looping behavior below is backwards. - * - * If the core doesn't expose a - * memory map, say it isn't supported. - */ - pointer = NULL; - } - else - { - unsigned i; - - for (i = 0; i < 4; i++) - { - retro_ctx_memory_info_t meminfo; - - switch (i) - { - case 0: - meminfo.id = RETRO_MEMORY_SYSTEM_RAM; - break; - case 1: - meminfo.id = RETRO_MEMORY_SAVE_RAM; - break; - case 2: - meminfo.id = RETRO_MEMORY_VIDEO_RAM; - break; - case 3: - meminfo.id = RETRO_MEMORY_RTC; - break; - } - - core_get_memory(&meminfo); - - if (address < meminfo.size) - { - pointer = meminfo.data; - break; - } - - /** - * HACK Subtract the correct amount of bytes to - * reach the save RAM as its size is not always - * set correctly in the core. - */ - if (i == 0 && console == RC_CONSOLE_NINTENDO) - address -= 0x6000; - else - address -= meminfo.size; - } - } - - if (!pointer) - { - CHEEVOS_LOG(RCHEEVOS_TAG "address %X not supported\n", original_address); - return NULL; - } - - return (const uint8_t*)pointer + address; -} diff --git a/cheevos/memory.c b/cheevos/memory.c new file mode 100644 index 0000000000..37e73897ab --- /dev/null +++ b/cheevos/memory.c @@ -0,0 +1,340 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2015-2016 - Andre Leiradella + * + * 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 "memory.h" + +#include "util.h" + +#include "../retroarch.h" +#include "../verbosity.h" + +#include "../deps/rcheevos/include/rcheevos.h" + +#include + +uint8_t* rcheevos_memory_find(const rcheevos_memory_regions_t* regions, unsigned address) +{ + unsigned i; + + for (i = 0; i < regions->count; ++i) + { + const size_t size = regions->size[i]; + if (address < size) + { + if (regions->data[i] == NULL) + break; + + return ®ions->data[i][address]; + } + + address -= size; + } + + return NULL; +} + +static const char* rcheevos_memory_type(int type) +{ + switch (type) + { + case RC_MEMORY_TYPE_SAVE_RAM: return "SRAM"; + case RC_MEMORY_TYPE_VIDEO_RAM: return "VRAM"; + case RC_MEMORY_TYPE_UNUSED: return "UNUSED"; + default: return "SYSTEM RAM"; + } +} + +static void rcheevos_memory_register_region(rcheevos_memory_regions_t* regions, + int type, uint8_t* data, size_t size, const char* description) +{ + if (size == 0) + return; + + if (regions->count == MAX_MEMORY_REGIONS) + { + CHEEVOS_LOG(RCHEEVOS_TAG "Too many memory memory regions to register\n"); + return; + } + + if (!data && regions->count > 0 && !regions->data[regions->count - 1]) + { + /* extend null region */ + regions->size[regions->count - 1] += size; + } + else if (data && regions->count > 0 && + data == (regions->data[regions->count - 1] + regions->size[regions->count - 1])) + { + /* extend non-null region */ + regions->size[regions->count - 1] += size; + } + else + { + /* create new region */ + regions->data[regions->count] = data; + regions->size[regions->count] = size; + ++regions->count; + } + + regions->total_size += size; + + CHEEVOS_LOG(RCHEEVOS_TAG "Registered 0x%04X bytes of %s at $%06X (%s)\n", size, + rcheevos_memory_type(type), regions->total_size - size, description); +} + +static void rcheevos_memory_init_without_regions(rcheevos_memory_regions_t* regions) +{ + /* no regions specified, assume system RAM followed by save RAM */ + char description[64]; + retro_ctx_memory_info_t meminfo; + sprintf(description, "offset 0x%06x", 0); + + meminfo.id = RETRO_MEMORY_SYSTEM_RAM; + core_get_memory(&meminfo); + rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, meminfo.data, meminfo.size, description); + + meminfo.id = RETRO_MEMORY_SAVE_RAM; + core_get_memory(&meminfo); + rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, meminfo.data, meminfo.size, description); +} + +static const rarch_memory_descriptor_t* rcheevos_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned real_address) +{ + if (mmap->num_descriptors == 0) + return NULL; + + const rarch_memory_descriptor_t* desc = mmap->descriptors; + const rarch_memory_descriptor_t* end = desc + mmap->num_descriptors; + + for (; desc < end; desc++) + { + if (desc->core.select == 0) + { + /* if select is 0, attempt to explcitly match the address */ + if (real_address >= desc->core.start && real_address < desc->core.start + desc->core.len) + return desc; + } + else + { + /* otherwise, attempt to match the address by matching the select bits */ + if (((desc->core.start ^ real_address) & desc->core.select) == 0) + { + /* sanity check - make sure the descriptor is large enough to hold the target address */ + if (real_address - desc->core.start < desc->core.len) + return desc; + } + } + } + + return NULL; +} + +void rcheevos_memory_init_from_memory_map(rcheevos_memory_regions_t* regions, const rarch_memory_map_t* mmap, const rc_memory_regions_t* console_regions) +{ + char description[64]; + unsigned i; + uint8_t* region_start; + uint8_t* desc_start; + size_t desc_size; + size_t offset; + + for (i = 0; i < console_regions->num_regions; ++i) + { + const rc_memory_region_t* console_region = &console_regions->region[i]; + size_t console_region_size = console_region->end_address - console_region->start_address + 1; + unsigned real_address = console_region->real_address; + + while (console_region_size > 0) + { + const rarch_memory_descriptor_t* desc = rcheevos_memory_get_descriptor(mmap, real_address); + if (!desc) + { + if (console_region->type != RC_MEMORY_TYPE_UNUSED) + { + CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n", + real_address - console_region->real_address + console_region->start_address); + } + + rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler"); + break; + } + + offset = real_address - desc->core.start; + sprintf(description, "descriptor %u, offset 0x%06X", (int)(desc - mmap->descriptors) + 1, (int)offset); + + if (desc->core.ptr) + { + desc_start = (uint8_t*)desc->core.ptr + desc->core.offset; + region_start = desc_start + offset; + } + else + { + region_start = NULL; + } + + desc_size = desc->core.len - offset; + + if (console_region_size > desc_size) + { + if (desc_size == 0) + { + if (console_region->type != RC_MEMORY_TYPE_UNUSED) + { + CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n", + real_address - console_region->real_address + console_region->start_address); + } + + rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler"); + console_region_size = 0; + } + else + { + rcheevos_memory_register_region(regions, console_region->type, region_start, desc_size, description); + console_region_size -= desc_size; + real_address += desc_size; + } + } + else + { + rcheevos_memory_register_region(regions, console_region->type, region_start, console_region_size, description); + console_region_size = 0; + } + } + } +} + +static unsigned rcheevos_memory_console_region_to_ram_type(int region_type) +{ + switch (region_type) + { + case RC_MEMORY_TYPE_SAVE_RAM: + return RETRO_MEMORY_SAVE_RAM; + case RC_MEMORY_TYPE_VIDEO_RAM: + return RETRO_MEMORY_VIDEO_RAM; + default: + return RETRO_MEMORY_SYSTEM_RAM; + } +} + +static void rcheevos_memory_init_from_unmapped_memory(rcheevos_memory_regions_t* regions, const rc_memory_regions_t* console_regions, int console) +{ + char description[64]; + unsigned i; + + for (i = 0; i < console_regions->num_regions; ++i) + { + const rc_memory_region_t* console_region = &console_regions->region[i]; + const size_t console_region_size = console_region->end_address - console_region->start_address + 1; + retro_ctx_memory_info_t meminfo; + size_t offset; + unsigned base_address = 0; + unsigned j; + + meminfo.id = rcheevos_memory_console_region_to_ram_type(console_region->type); + + for (j = 0; j <= i; ++j) + { + const rc_memory_region_t* console_region2 = &console_regions->region[j]; + if (rcheevos_memory_console_region_to_ram_type(console_region2->type) == meminfo.id) + { + base_address = console_region2->start_address; + break; + } + } + offset = console_region->start_address - base_address; + + core_get_memory(&meminfo); + + if (offset < meminfo.size) + { + meminfo.size -= offset; + + if (meminfo.data != NULL) + { + sprintf(description, "offset 0x%06X", (int)offset); + meminfo.data = (uint8_t*)meminfo.data + offset; + } + else + { + sprintf(description, "null filler"); + } + } + else + { + if (console_region->type != RC_MEMORY_TYPE_UNUSED) + { + CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n", console_region->start_address); + } + + meminfo.data = NULL; + meminfo.size = 0; + } + + if (console_region_size > meminfo.size) + { + /* want more than what is available, take what we can and null fill the rest */ + rcheevos_memory_register_region(regions, console_region->type, meminfo.data, meminfo.size, description); + rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size - meminfo.size, "null filler"); + } + else + { + /* only take as much as we need */ + rcheevos_memory_register_region(regions, console_region->type, meminfo.data, console_region_size, description); + } + } +} + +void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions) +{ + memset(regions, 0, sizeof(*regions)); +} + +bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console) +{ + const rc_memory_regions_t* console_regions = rc_console_memory_regions(console); + rcheevos_memory_regions_t new_regions; + bool has_valid_region = false; + unsigned i; + + if (regions == NULL) + return false; + + memset(&new_regions, 0, sizeof(new_regions)); + + if (console_regions == NULL || console_regions->num_regions == 0) + { + rcheevos_memory_init_without_regions(&new_regions); + } + else + { + rarch_system_info_t* system = runloop_get_system_info(); + if (system->mmaps.num_descriptors != 0) + rcheevos_memory_init_from_memory_map(&new_regions, &system->mmaps, console_regions); + else + rcheevos_memory_init_from_unmapped_memory(&new_regions, console_regions, console); + } + + /* determine if any valid regions were found */ + for (i = 0; i < new_regions.count; i++) + { + if (new_regions.data[i] != NULL) + { + has_valid_region = true; + break; + } + } + + memcpy(regions, &new_regions, sizeof(*regions)); + return has_valid_region; +} diff --git a/cheevos/fixup.h b/cheevos/memory.h similarity index 62% rename from cheevos/fixup.h rename to cheevos/memory.h index c0021fb6d9..a99fe7b0c9 100644 --- a/cheevos/fixup.h +++ b/cheevos/memory.h @@ -12,36 +12,31 @@ * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ - -#ifndef __RARCH_CHEEVOS_FIXUP_H -#define __RARCH_CHEEVOS_FIXUP_H +#ifndef __RARCH_CHEEVOS_MEMORY_H +#define __RARCH_CHEEVOS_MEMORY_H #include +#include #include #include RETRO_BEGIN_DECLS -typedef struct -{ - unsigned address; - const uint8_t* location; -} rcheevos_fixup_t; +#define MAX_MEMORY_REGIONS 32 typedef struct { - rcheevos_fixup_t* elements; - unsigned capacity, count; - bool dirty; -} rcheevos_fixups_t; + uint8_t* data[MAX_MEMORY_REGIONS]; + size_t size[MAX_MEMORY_REGIONS]; + unsigned count; + size_t total_size; +} rcheevos_memory_regions_t; -void rcheevos_fixup_init(rcheevos_fixups_t* fixups); -void rcheevos_fixup_destroy(rcheevos_fixups_t* fixups); +bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console); +void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions); -const uint8_t* rcheevos_fixup_find(rcheevos_fixups_t* fixups, unsigned address, int console); - -const uint8_t* rcheevos_patch_address(unsigned address, int console); +uint8_t* rcheevos_memory_find(const rcheevos_memory_regions_t* regions, unsigned address); RETRO_END_DECLS diff --git a/deps/rcheevos/src/rcheevos/consoleinfo.c b/deps/rcheevos/src/rcheevos/consoleinfo.c index 063e3a8209..75879f9cbe 100644 --- a/deps/rcheevos/src/rcheevos/consoleinfo.c +++ b/deps/rcheevos/src/rcheevos/consoleinfo.c @@ -259,7 +259,7 @@ static const rc_memory_region_t _rc_memory_regions_gameboy[] = { { 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (bank 1)" }, { 0x00E000U, 0x00FDFFU, 0x00C000U, RC_MEMORY_TYPE_VIRTUAL_RAM, "Echo RAM" }, { 0x00FE00U, 0x00FE9FU, 0x00FE00U, RC_MEMORY_TYPE_VIDEO_RAM, "Sprite RAM"}, - { 0x00FEA0U, 0x00FEFFU, 0x00FEA0U, RC_MEMORY_TYPE_READONLY, "Unusable"}, + { 0x00FEA0U, 0x00FEFFU, 0x00FEA0U, RC_MEMORY_TYPE_UNUSED, ""}, { 0x00FF00U, 0x00FF7FU, 0x00FF00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Hardware I/O"}, { 0x00FF80U, 0x00FFFEU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "Quick RAM"}, { 0x00FFFFU, 0x00FFFFU, 0x00FFFFU, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt enable"}, diff --git a/griffin/griffin.c b/griffin/griffin.c index 9c0bf60a89..18bce931f5 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -195,7 +195,7 @@ ACHIEVEMENTS #include "../cheevos/cheevos.c" #include "../cheevos/badges.c" -#include "../cheevos/fixup.c" +#include "../cheevos/memory.c" #include "../cheevos/hash.c" #include "../cheevos/parser.c" diff --git a/retroarch.c b/retroarch.c index 6a88c3108e..d89651d22d 100644 --- a/retroarch.c +++ b/retroarch.c @@ -170,7 +170,6 @@ #ifdef HAVE_CHEEVOS #include "cheevos/cheevos.h" -#include "cheevos/fixup.h" #endif #ifdef HAVE_TRANSLATE @@ -12767,7 +12766,7 @@ static bool command_read_ram(const char *arg) reply_at = reply + snprintf( reply, alloc_size - 1, "READ_CORE_RAM" " %x", addr); - if ((data = rcheevos_patch_address(addr, rcheevos_get_console()))) + if ((data = rcheevos_patch_address(addr))) { for (i = 0; i < nbytes; i++) snprintf(reply_at + 3 * i, 4, " %.2X", data[i]); @@ -12787,12 +12786,17 @@ static bool command_read_ram(const char *arg) static bool command_write_ram(const char *arg) { unsigned int addr = strtoul(arg, (char**)&arg, 16); - uint8_t *data = (uint8_t *)rcheevos_patch_address( - addr, rcheevos_get_console()); + uint8_t *data = (uint8_t *)rcheevos_patch_address(addr); if (!data) return false; + if (rcheevos_hardcore_active && rcheevos_loaded && !rcheevos_hardcore_paused) + { + RARCH_LOG("Achievements hardcore mode disabled by WRITE_CORE_RAM\n"); + rcheevos_pause_hardcore(); + } + while (*arg) { *data = strtoul(arg, (char**)&arg, 16);