diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index f55175fd1db..83f0bc9ce0b 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -339,8 +339,7 @@ static rcheevos_racheevo_t* rcheevos_find_cheevo(unsigned id) static bool rcheevos_is_game_loaded(void) { #ifdef HAVE_RC_CLIENT - const rc_client_game_t* game = rc_client_get_game_info(rcheevos_locals.client); - return (game && game->id); + return rc_client_is_game_loaded(rcheevos_locals.client); #else return rcheevos_locals.loaded; #endif @@ -1189,7 +1188,13 @@ void rcheevos_refresh_memory(void) bool rcheevos_hardcore_active(void) { #ifdef HAVE_RC_CLIENT - return rcheevos_locals.client && rc_client_get_hardcore_enabled(rcheevos_locals.client); + /* normal hardcore check */ + if (rcheevos_locals.client && rc_client_get_hardcore_enabled(rcheevos_locals.client)) + return true; + + /* if we're trying to enable hardcore, pretend it's on so the caller can decide to disable + * it (by calling rcheevos_pause_hardcore) before we actually turn it on. */ + return rcheevos_locals.hardcore_allowed; #else return rcheevos_locals.hardcore_active; #endif diff --git a/deps/rcheevos/include/rc_client.h b/deps/rcheevos/include/rc_client.h index 1f977c189f4..1fa50357c55 100644 --- a/deps/rcheevos/include/rc_client.h +++ b/deps/rcheevos/include/rc_client.h @@ -251,6 +251,11 @@ enum { RC_CLIENT_LOAD_GAME_STATE_ABORTED }; +/** + * Determines if a game was successfully identified and loaded. + */ +RC_EXPORT int RC_CCONV rc_client_is_game_loaded(const rc_client_t* client); + /** * Unloads the current game. */ @@ -266,6 +271,7 @@ typedef struct rc_client_game_t { /** * Get information about the current game. Returns NULL if no game is loaded. + * NOTE: returns a dummy game record if an unidentified game is loaded. */ RC_EXPORT const rc_client_game_t* RC_CCONV rc_client_get_game_info(const rc_client_t* client); diff --git a/deps/rcheevos/include/rc_client_raintegration.h b/deps/rcheevos/include/rc_client_raintegration.h index 676f0fb6006..2aa33dfdb46 100644 --- a/deps/rcheevos/include/rc_client_raintegration.h +++ b/deps/rcheevos/include/rc_client_raintegration.h @@ -27,6 +27,14 @@ typedef struct rc_client_raintegration_menu_t { uint32_t num_items; } rc_client_raintegration_menu_t; +enum { + RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_NONE = 0, + RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_PUBLISHED = 1, + RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_LOCAL = 2, + RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_MODIFIED = 3, + RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_INSECURE = 4, +}; + enum { RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE = 0, RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED = 1, /* [menu_item] checked changed */ @@ -77,11 +85,14 @@ RC_EXPORT int RC_CCONV rc_client_raintegration_activate_menu_item(const rc_clien RC_EXPORT void RC_CCONV rc_client_raintegration_set_write_memory_function(rc_client_t* client, rc_client_raintegration_write_memory_func_t handler); RC_EXPORT void RC_CCONV rc_client_raintegration_set_get_game_name_function(rc_client_t* client, rc_client_raintegration_get_game_name_func_t handler); +RC_EXPORT void RC_CCONV rc_client_raintegration_set_console_id(rc_client_t* client, uint32_t console_id); RC_EXPORT int RC_CCONV rc_client_raintegration_has_modifications(const rc_client_t* client); RC_EXPORT void RC_CCONV rc_client_raintegration_set_event_handler(rc_client_t* client, rc_client_raintegration_event_handler_t handler); +RC_EXPORT int RC_CCONV rc_client_raintegration_get_achievement_state(const rc_client_t* client, uint32_t achievement_id); + #endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */ RC_END_C_DECLS diff --git a/deps/rcheevos/include/rc_runtime_types.h b/deps/rcheevos/include/rc_runtime_types.h index 2bda4610406..f836dcb688e 100644 --- a/deps/rcheevos/include/rc_runtime_types.h +++ b/deps/rcheevos/include/rc_runtime_types.h @@ -175,7 +175,8 @@ enum { RC_OPERATOR_MULT, RC_OPERATOR_DIV, RC_OPERATOR_AND, - RC_OPERATOR_XOR + RC_OPERATOR_XOR, + RC_OPERATOR_MOD }; typedef struct rc_condition_t rc_condition_t; diff --git a/deps/rcheevos/src/rc_client.c b/deps/rcheevos/src/rc_client.c index 8b90e2ed689..9aeb2893486 100644 --- a/deps/rcheevos/src/rc_client.c +++ b/deps/rcheevos/src/rc_client.c @@ -1947,13 +1947,13 @@ static void rc_client_fetch_game_data_callback(const rc_api_server_response_t* s if (!subset->public_.title) { const char* core_subset_title = rc_client_subset_extract_title(load_state->game, load_state->game->public_.title); if (core_subset_title) { - rc_client_subset_info_t* scan = load_state->game->subsets; - for (; scan; scan = scan->next) { - if (scan->public_.title == load_state->game->public_.title) { - scan->public_.title = core_subset_title; - break; - } - } + scan = load_state->game->subsets; + for (; scan; scan = scan->next) { + if (scan->public_.title == load_state->game->public_.title) { + scan->public_.title = core_subset_title; + break; + } + } } subset->public_.title = rc_buffer_strcpy(&load_state->game->buffer, fetch_game_data_response.title); @@ -2058,8 +2058,15 @@ static void rc_client_begin_fetch_game_data(rc_client_load_state_t* load_state) #endif /* RC_CLIENT_SUPPORTS_HASH */ if (load_state->hash->game_id == 0) { + rc_client_subset_info_t* subset; + + subset = (rc_client_subset_info_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(rc_client_subset_info_t)); + memset(subset, 0, sizeof(*subset)); + subset->public_.title = ""; + load_state->game->public_.title = "Unknown Game"; load_state->game->public_.badge_name = ""; + load_state->game->subsets = subset; client->game = load_state->game; load_state->game = NULL; @@ -2405,6 +2412,23 @@ int rc_client_get_load_game_state(const rc_client_t* client) return state; } +int rc_client_is_game_loaded(const rc_client_t* client) +{ + const rc_client_game_t* game; + + if (!client) + return 0; + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->get_game_info) + game = client->state.external_client->get_game_info(); + else +#endif + game = client->game ? &client->game->public_ : NULL; + + return (game && game->id != 0); +} + static void rc_client_game_mark_ui_to_be_hidden(rc_client_t* client, rc_client_game_info_t* game) { rc_client_achievement_info_t* achievement; diff --git a/deps/rcheevos/src/rc_client_raintegration.c b/deps/rcheevos/src/rc_client_raintegration.c index 4ad9defa7c1..a686f7fd3c2 100644 --- a/deps/rcheevos/src/rc_client_raintegration.c +++ b/deps/rcheevos/src/rc_client_raintegration.c @@ -69,6 +69,7 @@ static void rc_client_raintegration_load_dll(rc_client_t* client, raintegration->get_host_url = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_HostUrl"); raintegration->init_client = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitClient"); raintegration->init_client_offline = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitOffline"); + raintegration->set_console_id = (rc_client_raintegration_set_int_func_t)GetProcAddress(hDLL, "_RA_SetConsoleID"); raintegration->shutdown = (rc_client_raintegration_action_func_t)GetProcAddress(hDLL, "_RA_Shutdown"); raintegration->update_main_window_handle = (rc_client_raintegration_hwnd_action_func_t)GetProcAddress(hDLL, "_RA_UpdateHWnd"); @@ -80,6 +81,7 @@ static void rc_client_raintegration_load_dll(rc_client_t* client, raintegration->set_get_game_name_function = (rc_client_raintegration_set_get_game_name_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationGetGameNameFunction"); raintegration->set_event_handler = (rc_client_raintegration_set_event_handler_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationEventHandler"); raintegration->has_modifications = (rc_client_raintegration_get_int_func_t)GetProcAddress(hDLL, "_Rcheevos_HasModifications"); + raintegration->get_achievement_state = (rc_client_raintegration_get_achievement_state_func_t)GetProcAddress(hDLL, "_Rcheevos_GetAchievementState"); if (!raintegration->get_version || !raintegration->init_client || @@ -204,6 +206,7 @@ static void rc_client_init_raintegration(rc_client_t* client, /* attach the external client and call the callback */ client->state.external_client = external_client; + client->state.raintegration->hMainWindow = version_validation_callback_data->main_window_handle; client->state.raintegration->bIsInited = 1; version_validation_callback_data->callback(RC_OK, NULL, @@ -352,12 +355,14 @@ rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client void rc_client_raintegration_update_main_window_handle(rc_client_t* client, HWND main_window_handle) { - if (client && client->state.raintegration && - client->state.raintegration->bIsInited && - client->state.raintegration->update_main_window_handle) - { + if (client && client->state.raintegration) { + client->state.raintegration->hMainWindow = main_window_handle; + + if (client->state.raintegration->bIsInited && + client->state.raintegration->update_main_window_handle) { client->state.raintegration->update_main_window_handle(main_window_handle); - } + } + } } void rc_client_raintegration_set_write_memory_function(rc_client_t* client, rc_client_raintegration_write_memory_func_t handler) @@ -383,26 +388,41 @@ const rc_client_raintegration_menu_t* rc_client_raintegration_get_menu(const rc_ { if (!client || !client->state.raintegration || !client->state.raintegration->bIsInited || - !client->state.raintegration->get_menu) - { + !client->state.raintegration->get_menu) { return NULL; } return client->state.raintegration->get_menu(); } +void rc_client_raintegration_set_console_id(rc_client_t* client, uint32_t console_id) +{ + if (client && client->state.raintegration && client->state.raintegration->set_console_id) + client->state.raintegration->set_console_id(console_id); +} + int rc_client_raintegration_has_modifications(const rc_client_t* client) { if (!client || !client->state.raintegration || - !client->state.raintegration->bIsInited || - !client->state.raintegration->has_modifications) - { + !client->state.raintegration->bIsInited || + !client->state.raintegration->has_modifications) { return 0; } return client->state.raintegration->has_modifications(); } +int rc_client_raintegration_get_achievement_state(const rc_client_t* client, uint32_t achievement_id) +{ + if (!client || !client->state.raintegration || + !client->state.raintegration->bIsInited || + !client->state.raintegration->get_achievement_state) { + return RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_NONE; + } + + return client->state.raintegration->get_achievement_state(achievement_id); +} + void rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu) { HMENU hPopupMenu = NULL; @@ -464,6 +484,9 @@ void rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu) AppendMenuA(hMenu, flags, (UINT_PTR)hPopupMenu, menuText); else ModifyMenuA(hMenu, nIndex, flags | MF_BYPOSITION, (UINT_PTR)hPopupMenu, menuText); + + if (client->state.raintegration->hMainWindow && GetMenu(client->state.raintegration->hMainWindow) == hMenu) + DrawMenuBar(client->state.raintegration->hMainWindow); } client->state.raintegration->hPopupMenu = hPopupMenu; diff --git a/deps/rcheevos/src/rc_client_raintegration_internal.h b/deps/rcheevos/src/rc_client_raintegration_internal.h index ce7c98b03be..ce3a99dda58 100644 --- a/deps/rcheevos/src/rc_client_raintegration_internal.h +++ b/deps/rcheevos/src/rc_client_raintegration_internal.h @@ -17,16 +17,19 @@ typedef const char* (RC_CCONV* rc_client_raintegration_get_string_func_t)(void); typedef int (RC_CCONV* rc_client_raintegration_init_client_func_t)(HWND hMainWnd, const char* sClientName, const char* sClientVersion); typedef int (RC_CCONV* rc_client_raintegration_get_external_client_func_t)(rc_client_external_t* pClient, int nVersion); typedef void (RC_CCONV* rc_client_raintegration_hwnd_action_func_t)(HWND hWnd); +typedef int (RC_CCONV* rc_client_raintegration_get_achievement_state_func_t)(uint32_t nMenuItemId); typedef const rc_client_raintegration_menu_t* (RC_CCONV* rc_client_raintegration_get_menu_func_t)(void); typedef int (RC_CCONV* rc_client_raintegration_activate_menuitem_func_t)(uint32_t nMenuItemId); typedef void (RC_CCONV* rc_client_raintegration_set_write_memory_func_t)(rc_client_t* pClient, rc_client_raintegration_write_memory_func_t handler); typedef void (RC_CCONV* rc_client_raintegration_set_get_game_name_func_t)(rc_client_t* pClient, rc_client_raintegration_get_game_name_func_t handler); typedef void (RC_CCONV* rc_client_raintegration_set_event_handler_func_t)(rc_client_t* pClient, rc_client_raintegration_event_handler_t handler); +typedef void (RC_CCONV* rc_client_raintegration_set_int_func_t)(int); typedef int (RC_CCONV* rc_client_raintegration_get_int_func_t)(void); typedef struct rc_client_raintegration_t { HINSTANCE hDLL; + HWND hMainWindow; HMENU hPopupMenu; uint8_t bIsInited; @@ -34,6 +37,7 @@ typedef struct rc_client_raintegration_t rc_client_raintegration_get_string_func_t get_host_url; rc_client_raintegration_init_client_func_t init_client; rc_client_raintegration_init_client_func_t init_client_offline; + rc_client_raintegration_set_int_func_t set_console_id; rc_client_raintegration_action_func_t shutdown; rc_client_raintegration_hwnd_action_func_t update_main_window_handle; @@ -44,6 +48,7 @@ typedef struct rc_client_raintegration_t rc_client_raintegration_get_menu_func_t get_menu; rc_client_raintegration_activate_menuitem_func_t activate_menu_item; rc_client_raintegration_get_int_func_t has_modifications; + rc_client_raintegration_get_achievement_state_func_t get_achievement_state; rc_client_raintegration_get_external_client_func_t get_external_client; diff --git a/deps/rcheevos/src/rcheevos/condition.c b/deps/rcheevos/src/rcheevos/condition.c index 236dff37347..2e7ccce1cbe 100644 --- a/deps/rcheevos/src/rcheevos/condition.c +++ b/deps/rcheevos/src/rcheevos/condition.c @@ -139,6 +139,10 @@ static int rc_parse_operator(const char** memaddr) { ++(*memaddr); return RC_OPERATOR_XOR; + case '%': + ++(*memaddr); + return RC_OPERATOR_MOD; + case '\0':/* end of string */ case '_': /* next condition */ case 'S': /* next condset */ @@ -226,6 +230,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse case RC_OPERATOR_DIV: case RC_OPERATOR_AND: case RC_OPERATOR_XOR: + case RC_OPERATOR_MOD: /* modifying operators are only valid on modifying statements */ if (can_modify) break; @@ -551,5 +556,9 @@ void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED); value->value.u32 ^= amount.value.u32; break; + + case RC_OPERATOR_MOD: + rc_typed_value_modulus(value, &amount); + break; } } diff --git a/deps/rcheevos/src/rcheevos/condset.c b/deps/rcheevos/src/rcheevos/condset.c index 23a0e30e1f6..2d5d505b624 100644 --- a/deps/rcheevos/src/rcheevos/condset.c +++ b/deps/rcheevos/src/rcheevos/condset.c @@ -87,6 +87,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in case RC_OPERATOR_XOR: case RC_OPERATOR_DIV: case RC_OPERATOR_MULT: + case RC_OPERATOR_MOD: case RC_OPERATOR_NONE: /* measuring value. leave required_hits at 0 */ break; diff --git a/deps/rcheevos/src/rcheevos/consoleinfo.c b/deps/rcheevos/src/rcheevos/consoleinfo.c index 427db73b220..90f0020f2f7 100644 --- a/deps/rcheevos/src/rcheevos/consoleinfo.c +++ b/deps/rcheevos/src/rcheevos/consoleinfo.c @@ -418,7 +418,7 @@ static const rc_memory_region_t _rc_memory_regions_fairchild_channel_f[] = { }; static const rc_memory_regions_t rc_memory_regions_fairchild_channel_f = { _rc_memory_regions_fairchild_channel_f, 4 }; -/* ===== GameBoy / GameBoy Color ===== */ +/* ===== GameBoy / MegaDuck ===== */ static const rc_memory_region_t _rc_memory_regions_gameboy[] = { { 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" }, { 0x000100U, 0x00014FU, 0x000100U, RC_MEMORY_TYPE_READONLY, "Cartridge header" }, @@ -427,8 +427,37 @@ static const rc_memory_region_t _rc_memory_regions_gameboy[] = { { 0x008000U, 0x0097FFU, 0x008000U, RC_MEMORY_TYPE_VIDEO_RAM, "Tile RAM" }, { 0x009800U, 0x009BFFU, 0x009800U, RC_MEMORY_TYPE_VIDEO_RAM, "BG1 map data" }, { 0x009C00U, 0x009FFFU, 0x009C00U, RC_MEMORY_TYPE_VIDEO_RAM, "BG2 map data" }, - { 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM"}, + { 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM (bank 0)"}, { 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (fixed)" }, + { 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (fixed)" }, + { 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_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"}, + + /* GameBoy's cartridge RAM may have a total of up to 16 banks that can be paged through $A000-$BFFF. + * It is desirable to always have access to these extra banks. We do this by expecting the extra banks + * to be addressable at addresses not supported by the native system. 0x10000-0x16000 is reserved + * for the extra banks of system memory that are exclusive to the GameBoy Color. */ + { 0x010000U, 0x015FFFU, 0x010000U, RC_MEMORY_TYPE_UNUSED, "Unused (GameBoy Color exclusive)" }, + { 0x016000U, 0x033FFFU, 0x016000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM (banks 1-15)" }, +}; +static const rc_memory_regions_t rc_memory_regions_megaduck = { _rc_memory_regions_gameboy, 16 }; +static const rc_memory_regions_t rc_memory_regions_gameboy = { _rc_memory_regions_gameboy, 18 }; + +/* ===== GameBoy Color ===== */ +static const rc_memory_region_t _rc_memory_regions_gameboy_color[] = { + { 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" }, + { 0x000100U, 0x00014FU, 0x000100U, RC_MEMORY_TYPE_READONLY, "Cartridge header" }, + { 0x000150U, 0x003FFFU, 0x000150U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM (fixed)" }, /* bank 0 */ + { 0x004000U, 0x007FFFU, 0x004000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM (paged)" }, /* bank 1-XX (switchable) */ + { 0x008000U, 0x0097FFU, 0x008000U, RC_MEMORY_TYPE_VIDEO_RAM, "Tile RAM" }, + { 0x009800U, 0x009BFFU, 0x009800U, RC_MEMORY_TYPE_VIDEO_RAM, "BG1 map data" }, + { 0x009C00U, 0x009FFFU, 0x009C00U, RC_MEMORY_TYPE_VIDEO_RAM, "BG2 map data" }, + { 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM (bank 0)"}, + { 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (bank 0)" }, { 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"}, @@ -437,14 +466,14 @@ static const rc_memory_region_t _rc_memory_regions_gameboy[] = { { 0x00FF80U, 0x00FFFEU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "Quick RAM"}, { 0x00FFFFU, 0x00FFFFU, 0x00FFFFU, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt enable"}, - /* GameBoy Color provides six extra banks of memory that can be paged out through the $DXXX - * memory space, but the timing of that does not correspond with blanks, which is when achievements - * are processed. As such, it is desirable to always have access to these extra banks. We do this - * by expecting the extra banks to be addressable at addresses not supported by the native system. */ - { 0x010000U, 0x015FFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (banks 2-7, GBC only)" } + /* GameBoy Color provides 6 extra banks of system memory that can be paged out through the $D000-$DFFF, + * and the cartridge RAM may have a total of up to 16 banks page through $A000-$BFFF. + * It is desirable to always have access to these extra banks. We do this by expecting the extra banks + * to be addressable at addresses not supported by the native system. */ + { 0x010000U, 0x015FFFU, 0x010000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM (banks 2-7)" }, + { 0x016000U, 0x033FFFU, 0x016000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM (banks 1-15)" }, }; -static const rc_memory_regions_t rc_memory_regions_gameboy = { _rc_memory_regions_gameboy, 16 }; -static const rc_memory_regions_t rc_memory_regions_gameboy_color = { _rc_memory_regions_gameboy, 17 }; +static const rc_memory_regions_t rc_memory_regions_gameboy_color = { _rc_memory_regions_gameboy_color, 18 }; /* ===== GameBoy Advance ===== */ /* http://problemkaputt.de/gbatek-gba-memory-map.htm */ @@ -463,11 +492,19 @@ static const rc_memory_region_t _rc_memory_regions_gamecube[] = { static const rc_memory_regions_t rc_memory_regions_gamecube = { _rc_memory_regions_gamecube, 1 }; /* ===== Game Gear ===== */ -/* http://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/Mappers */ static const rc_memory_region_t _rc_memory_regions_game_gear[] = { - { 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" } + { 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, + /* GG/SMS have various possible mappings for cartridge memory depending on the mapper used. + * However, these ultimately do not map all of their memory at once, typically requiring banking. + * Thus, the "real address" used is just a virtual address mapping all cartridge memory in one contiguous block. + * Note that this may possibly refer to non-battery backed "extended RAM" so this isn't strictly RC_MEMORY_TYPE_SAVE_RAM. + * libretro cores expose "extended RAM" as RETRO_MEMORY_SAVE_RAM regardless however. + */ + { 0x002000U, 0x009FFFU, 0x010000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" } }; -static const rc_memory_regions_t rc_memory_regions_game_gear = { _rc_memory_regions_game_gear, 1 }; +static const rc_memory_regions_t rc_memory_regions_game_gear = { _rc_memory_regions_game_gear, 2 }; /* ===== Intellivision ===== */ /* http://wiki.intellivision.us/index.php/Memory_Map */ @@ -531,14 +568,22 @@ static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = { static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 }; /* ===== Master System ===== */ -/* http://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/Mappers */ static const rc_memory_region_t _rc_memory_regions_master_system[] = { - { 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" } + { 0x000000U, 0x001FFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, + /* GG/SMS have various possible mappings for cartridge memory depending on the mapper used. + * However, these ultimately do not map all of their memory at once, typically requiring banking. + * Thus, the "real address" used is just a virtual address mapping all cartridge memory in one contiguous block. + * Note that this may possibly refer to non-battery backed "extended RAM" so this isn't strictly RC_MEMORY_TYPE_SAVE_RAM. + * libretro cores expose "extended RAM" as RETRO_MEMORY_SAVE_RAM regardless however. + */ + { 0x002000U, 0x009FFFU, 0x010000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" } }; -static const rc_memory_regions_t rc_memory_regions_master_system = { _rc_memory_regions_master_system, 1 }; +static const rc_memory_regions_t rc_memory_regions_master_system = { _rc_memory_regions_master_system, 2 }; /* ===== MegaDrive (Genesis) ===== */ -/* http://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/MemoryMap */ static const rc_memory_region_t _rc_memory_regions_megadrive[] = { { 0x000000U, 0x00FFFFU, 0xFF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, { 0x010000U, 0x01FFFFU, 0x000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" } @@ -772,7 +817,7 @@ static const rc_memory_region_t _rc_memory_regions_saturn[] = { static const rc_memory_regions_t rc_memory_regions_saturn = { _rc_memory_regions_saturn, 2 }; /* ===== SG-1000 ===== */ -/* http://www.smspower.org/Development/MemoryMap */ +/* https://www.smspower.org/Development/MemoryMap */ static const rc_memory_region_t _rc_memory_regions_sg1000[] = { { 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* https://github.com/libretro/FBNeo/blob/697801c6262be6ca91615cf905444d3e039bc06f/src/burn/drv/sg1000/d_sg1000.cpp#L210-L237 */ @@ -957,8 +1002,7 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id) case RC_CONSOLE_FAIRCHILD_CHANNEL_F: return &rc_memory_regions_fairchild_channel_f; - - case RC_CONSOLE_MEGADUCK: + case RC_CONSOLE_GAMEBOY: return &rc_memory_regions_gameboy; @@ -989,6 +1033,9 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id) case RC_CONSOLE_MEGA_DRIVE: return &rc_memory_regions_megadrive; + case RC_CONSOLE_MEGADUCK: + return &rc_memory_regions_megaduck; + case RC_CONSOLE_SEGA_32X: return &rc_memory_regions_megadrive_32x; diff --git a/deps/rcheevos/src/rcheevos/rc_internal.h b/deps/rcheevos/src/rcheevos/rc_internal.h index 13542398096..39154cd6853 100644 --- a/deps/rcheevos/src/rcheevos/rc_internal.h +++ b/deps/rcheevos/src/rcheevos/rc_internal.h @@ -181,6 +181,7 @@ void rc_typed_value_convert(rc_typed_value_t* value, char new_type); void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount); void rc_typed_value_multiply(rc_typed_value_t* value, const rc_typed_value_t* amount); void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amount); +void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amount); void rc_typed_value_negate(rc_typed_value_t* value); int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper); void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref); diff --git a/deps/rcheevos/src/rcheevos/value.c b/deps/rcheevos/src/rcheevos/value.c index dc91df6b7d4..f7da42d8049 100644 --- a/deps/rcheevos/src/rcheevos/value.c +++ b/deps/rcheevos/src/rcheevos/value.c @@ -3,6 +3,7 @@ #include /* memset */ #include /* isdigit */ #include /* FLT_EPSILON */ +#include /* fmod */ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) { rc_condset_t** next_clause; @@ -112,6 +113,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat case RC_OPERATOR_DIV: case RC_OPERATOR_AND: case RC_OPERATOR_XOR: + case RC_OPERATOR_MOD: case RC_OPERATOR_NONE: break; @@ -628,6 +630,72 @@ void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amou value->value.f32 /= amount->value.f32; } +void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amount) { + rc_typed_value_t converted; + + switch (amount->type) + { + case RC_VALUE_TYPE_UNSIGNED: + if (amount->value.u32 == 0) { /* divide by zero */ + value->type = RC_VALUE_TYPE_NONE; + return; + } + + switch (value->type) { + case RC_VALUE_TYPE_UNSIGNED: /* integer math */ + value->value.u32 %= amount->value.u32; + return; + case RC_VALUE_TYPE_SIGNED: /* integer math */ + value->value.i32 %= (int)amount->value.u32; + return; + case RC_VALUE_TYPE_FLOAT: + amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); + break; + default: + value->type = RC_VALUE_TYPE_NONE; + return; + } + break; + + case RC_VALUE_TYPE_SIGNED: + if (amount->value.i32 == 0) { /* divide by zero */ + value->type = RC_VALUE_TYPE_NONE; + return; + } + + switch (value->type) { + case RC_VALUE_TYPE_SIGNED: /* integer math */ + value->value.i32 %= amount->value.i32; + return; + case RC_VALUE_TYPE_UNSIGNED: /* integer math */ + value->value.u32 %= (unsigned)amount->value.i32; + return; + case RC_VALUE_TYPE_FLOAT: + amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT); + break; + default: + value->type = RC_VALUE_TYPE_NONE; + return; + } + break; + + case RC_VALUE_TYPE_FLOAT: + break; + + default: + value->type = RC_VALUE_TYPE_NONE; + return; + } + + if (amount->value.f32 == 0.0) { /* divide by zero */ + value->type = RC_VALUE_TYPE_NONE; + return; + } + + rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT); + value->value.f32 = (float)fmod(value->value.f32, amount->value.f32); +} + static int rc_typed_value_compare_floats(float f1, float f2, char oper) { if (f1 == f2) { /* exactly equal */ diff --git a/deps/rcheevos/src/rhash/hash.c b/deps/rcheevos/src/rhash/hash.c index 8f7bc958a11..5e2f19628a9 100644 --- a/deps/rcheevos/src/rhash/hash.c +++ b/deps/rcheevos/src/rhash/hash.c @@ -11,6 +11,7 @@ #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include +#include #endif /* arbitrary limit to prevent allocating and hashing large files */ @@ -53,9 +54,9 @@ static void rc_hash_verbose(const char* message) static struct rc_hash_filereader filereader_funcs; static struct rc_hash_filereader* filereader = NULL; +#if defined(WINVER) && WINVER >= 0x0500 static void* filereader_open(const char* path) { -#if defined(WINVER) && WINVER >= 0x0500 /* Windows requires using wchar APIs for Unicode paths */ /* Note that MultiByteToWideChar will only be defined for >= Windows 2000 */ wchar_t* wpath; @@ -76,21 +77,34 @@ static void* filereader_open(const char* path) free(wpath); return NULL; } -#if defined(__STDC_WANT_SECURE_LIB__) - _wfopen_s(&fp, wpath, L"rb"); -#else + + #if defined(__STDC_WANT_SECURE_LIB__) + /* have to use _SH_DENYNO because some cores lock the file while its loaded */ + fp = _wfsopen(wpath, L"rb", _SH_DENYNO); + #else fp = _wfopen(wpath, L"rb"); -#endif + #endif + free(wpath); return fp; -#elif defined(__STDC_WANT_SECURE_LIB__) - FILE* fp; - fopen_s(&fp, path, "rb"); - return fp; -#else +} +#else /* !WINVER >= 0x0500 */ +static void* filereader_open(const char* path) +{ + #if defined(__STDC_WANT_SECURE_LIB__) + #if defined(WINVER) + /* have to use _SH_DENYNO because some cores lock the file while its loaded */ + return _fsopen(path, "rb", _SH_DENYNO); + #else /* !WINVER */ + FILE *fp; + fopen_s(&fp, path, "rb"); + return fp; + #endif + #else /* !__STDC_WANT_SECURE_LIB__ */ return fopen(path, "rb"); -#endif + #endif } +#endif /* WINVER >= 0x0500 */ static void filereader_seek(void* file_handle, int64_t offset, int origin) { @@ -2756,6 +2770,14 @@ static int rc_hash_psp(char hash[33], const char* path) uint32_t size; md5_state_t md5; + /* https://www.psdevwiki.com/psp/PBP + * A PBP file is an archive containing the PARAM.SFO, primary executable, and a bunch of metadata. + * While we could extract the PARAM.SFO and primary executable to mimic the normal PSP hashing logic, + * it's easier to just hash the entire file. This also helps alleviate issues where the primary + * executable is just a game engine and the only differentiating data would be the metadata. */ + if (rc_path_compare_extension(path, "pbp")) + return rc_hash_whole_file(hash, path); + track_handle = rc_cd_open_track(path, 1); if (!track_handle) return rc_hash_error("Could not open track"); @@ -3772,6 +3794,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* { iterator->consoles[0] = RC_CONSOLE_PC_ENGINE; } + else if (rc_path_compare_extension(ext, "pbp")) + { + iterator->consoles[0] = RC_CONSOLE_PSP; + } else if (rc_path_compare_extension(ext, "pgm")) { iterator->consoles[0] = RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER;