upgrade to rcheevos 10.4 (#13908)

This commit is contained in:
Jamiras 2022-05-03 20:46:47 -06:00 committed by GitHub
parent 4804d7015a
commit e3b59fd36d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 592 additions and 172 deletions

View File

@ -650,7 +650,7 @@ bool rcheevos_unload(void)
{
CHEEVOS_FREE(rcheevos_locals.menuitems);
rcheevos_locals.menuitems = NULL;
rcheevos_locals.menuitem_capacity =
rcheevos_locals.menuitem_capacity =
rcheevos_locals.menuitem_count = 0;
}
#endif
@ -663,13 +663,8 @@ bool rcheevos_unload(void)
rcheevos_locals.loaded = false;
rcheevos_locals.hardcore_active = false;
}
while (rcheevos_locals.game.hashes)
{
rcheevos_hash_entry_t* hash_entry = rcheevos_locals.game.hashes;
rcheevos_locals.game.hashes = hash_entry->next;
free(hash_entry);
rc_libretro_hash_set_destroy(&rcheevos_locals.game.hashes);
}
#ifdef HAVE_THREADS
@ -1011,6 +1006,21 @@ void rcheevos_validate_config_settings(void)
break;
}
}
if (rcheevos_locals.game.console_id &&
!rc_libretro_is_system_allowed(system->library_name, rcheevos_locals.game.console_id))
{
char buffer[256];
snprintf(buffer, sizeof(buffer),
"Hardcore paused. You cannot earn hardcore achievements for %s using %s",
rc_console_name(rcheevos_locals.game.console_id), system->library_name);
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", buffer);
rcheevos_pause_hardcore();
runloop_msg_queue_push(buffer, 0, 4 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
return;
}
}
static void rcheevos_runtime_event_handler(
@ -1645,6 +1655,10 @@ static void rcheevos_start_session(void)
return;
}
/* re-validate the config settings now that we know
* which console_id is active */
rcheevos_validate_config_settings();
task = task_init();
task->handler = rcheevos_start_session_async;
task->callback = rcheevos_start_session_finish;
@ -1726,7 +1740,9 @@ static void rcheevos_fetch_game_data(void)
struct rcheevos_identify_game_data
{
struct rc_hash_iterator iterator;
char* path;
uint8_t* datacopy;
char hash[33];
};
static void rcheevos_identify_game_callback(void* userdata)
@ -1734,47 +1750,82 @@ static void rcheevos_identify_game_callback(void* userdata)
struct rcheevos_identify_game_data* data =
(struct rcheevos_identify_game_data*)userdata;
if (data)
{
if (rcheevos_locals.game.id == 0)
{
/* previous hash didn't match, try the next one */
char hash[33];
if (rc_hash_iterate(hash, &data->iterator))
{
memcpy(rcheevos_locals.game.hashes->hash, hash, sizeof(hash));
rcheevos_locals.load_info.hashes_tried++;
rcheevos_locals.load_info.hashes_tried++;
rcheevos_client_identify_game(hash,
rcheevos_identify_game_callback, data);
return;
}
if (rcheevos_locals.game.id == 0)
{
/* previous hash didn't match, try the next one */
char new_hash[33];
int found_new_hash;
while ((found_new_hash = rc_hash_iterate(new_hash, &data->iterator)) != 0)
{
if (!rc_libretro_hash_set_get_game_id(&rcheevos_locals.game.hashes, new_hash))
break;
CHEEVOS_LOG(RCHEEVOS_TAG "Ignoring [%s]. Already tried.\n", new_hash);
}
/* no more hashes generated, free the iterator data */
if (data->datacopy)
free(data->datacopy);
free(data);
if (found_new_hash)
{
memcpy(data->hash, new_hash, sizeof(data->hash));
rcheevos_client_identify_game(data->hash,
rcheevos_identify_game_callback, data);
return;
}
}
rcheevos_locals.game.hashes->game_id = rcheevos_locals.game.id;
rc_libretro_hash_set_add(&rcheevos_locals.game.hashes,
data->path, rcheevos_locals.game.id, data->hash);
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, data->path);
if (data->iterator.path && strcmp(data->iterator.path, data->path) != 0)
{
rc_libretro_hash_set_add(&rcheevos_locals.game.hashes,
data->iterator.path, rcheevos_locals.game.id, data->hash);
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, data->iterator.path);
}
/* no more hashes generated, free the iterator data */
rc_hash_destroy_iterator(&data->iterator);
if (data->datacopy)
free(data->datacopy);
if (data->path)
free(data->path);
free(data);
/* hash resolution complete, proceed to fetching game data */
if (rcheevos_end_load_state() == 0)
rcheevos_fetch_game_data();
}
static int rcheevos_get_image_path(unsigned index, char* buffer, size_t buffer_size)
{
rarch_system_info_t* system = &runloop_state_get_ptr()->system;
if (!system->disk_control.cb.get_image_path)
return 0;
return system->disk_control.cb.get_image_path(index, buffer, buffer_size);
}
static bool rcheevos_identify_game(const struct retro_game_info* info)
{
struct rcheevos_identify_game_data* data;
struct rc_hash_filereader filereader;
struct rc_hash_iterator iterator;
size_t len;
char hash[33];
#ifndef DEBUG
settings_t* settings = config_get_ptr();
#endif
data = (struct rcheevos_identify_game_data*)
calloc(1, sizeof(struct rcheevos_identify_game_data));
if (!data)
{
CHEEVOS_LOG(RCHEEVOS_TAG "allocation failed\n");
return false;
}
/* provide hooks for reading files */
memset(&filereader, 0, sizeof(filereader));
filereader.open = rc_hash_handle_file_open;
@ -1797,59 +1848,44 @@ static bool rcheevos_identify_game(const struct retro_game_info* info)
rc_hash_reset_cdreader_hooks();
/* fetch the first hash */
rc_hash_initialize_iterator(&iterator,
rc_hash_initialize_iterator(&data->iterator,
info->path, (uint8_t*)info->data, info->size);
if (!rc_hash_iterate(hash, &iterator))
if (!rc_hash_iterate(data->hash, &data->iterator))
{
CHEEVOS_LOG(RCHEEVOS_TAG "no hashes generated\n");
rc_hash_destroy_iterator(&data->iterator);
free(data);
return false;
}
rcheevos_locals.game.hashes = (rcheevos_hash_entry_t*)calloc(1, sizeof(rcheevos_hash_entry_t));
rcheevos_locals.game.hashes->path_djb2 = djb2_calculate(info->path);
rcheevos_locals.game.hash = rcheevos_locals.game.hashes->hash;
memcpy(rcheevos_locals.game.hashes->hash, hash, sizeof(hash));
rcheevos_locals.load_info.hashes_tried++;
rc_libretro_hash_set_init(&rcheevos_locals.game.hashes, info->path, rcheevos_get_image_path);
data->path = strdup(info->path);
if (iterator.consoles[iterator.index] == 0)
if (data->iterator.consoles[data->iterator.index] != 0)
{
/* no more potential matches, just try the one hash */
rcheevos_begin_load_state(RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME);
rcheevos_client_identify_game(hash,
rcheevos_identify_game_callback, NULL);
return true;
}
/* multiple potential matches, clone the data for the next attempt */
data = (struct rcheevos_identify_game_data*)
calloc(1, sizeof(struct rcheevos_identify_game_data));
if (!data)
{
CHEEVOS_LOG(RCHEEVOS_TAG "allocation failed\n");
return false;
}
if (info->data)
{
len = info->size;
if (len > CHEEVOS_MB(64))
len = CHEEVOS_MB(64);
data->datacopy = (uint8_t*)malloc(len);
if (!data->datacopy)
/* multiple potential matches, clone the data for the next attempt */
if (info->data)
{
CHEEVOS_LOG(RCHEEVOS_TAG "allocation failed\n");
free(data);
return false;
len = info->size;
if (len > CHEEVOS_MB(64))
len = CHEEVOS_MB(64);
data->datacopy = (uint8_t*)malloc(len);
if (!data->datacopy)
{
CHEEVOS_LOG(RCHEEVOS_TAG "allocation failed\n");
rc_hash_destroy_iterator(&data->iterator);
free(data);
return false;
}
memcpy(data->datacopy, info->data, len);
data->iterator.buffer = data->datacopy;
}
memcpy(data->datacopy, info->data, len);
}
memcpy(&data->iterator, &iterator, sizeof(iterator));
rcheevos_begin_load_state(RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME);
rcheevos_client_identify_game(hash,
rcheevos_client_identify_game(data->hash,
rcheevos_identify_game_callback, data);
return true;
}
@ -2049,59 +2085,64 @@ bool rcheevos_load(const void *data)
return true;
}
static void rcheevos_add_hash(rcheevos_hash_entry_t* hash_entry)
struct rcheevos_identify_changed_disc_data
{
hash_entry->next = rcheevos_locals.game.hashes;
rcheevos_locals.game.hashes = hash_entry;
rcheevos_locals.game.hash = hash_entry->hash;
}
int real_game_id;
char* path;
char hash[33];
};
static void rcheevos_identify_game_disc_callback(void* userdata)
{
rcheevos_hash_entry_t* hash_entry = (rcheevos_hash_entry_t*)userdata;
struct rcheevos_identify_changed_disc_data* changed_disc_data =
(struct rcheevos_identify_changed_disc_data*)userdata;
/* rcheevos_locals.game.id has the game id for the new hash, swap it with the old game id */
const int hash_game_id = rcheevos_locals.game.id;
rcheevos_locals.game.id = changed_disc_data->real_game_id;
/* rcheevos_client_identify_game will update rcheevos_locals.game.id */
if (rcheevos_locals.game.id == hash_entry->game_id)
if (rcheevos_locals.game.id == hash_game_id)
{
CHEEVOS_LOG(RCHEEVOS_TAG "Hash valid for current game\n");
}
else if (hash_game_id != 0)
{
/* when changing discs, if the disc is recognized but belongs to another game, allow it.
* this allows loading known game discs for games that leverage user-provided discs. */
CHEEVOS_LOG(RCHEEVOS_TAG "Hash identified for game %d\n", hash_game_id);
}
else
{
/* rcheevos_locals.game.id has the game id for the new hash, swap it with the old game id */
const int hash_game_id = rcheevos_locals.game.id;
rcheevos_locals.game.id = hash_entry->game_id;
hash_entry->game_id = hash_game_id;
if (hash_game_id != 0)
CHEEVOS_LOG(RCHEEVOS_TAG "Disc not recognized\n");
if (rcheevos_hardcore_active())
{
/* when changing discs, if the disc is recognized but belongs to another game, allow it.
* this allows loading known game discs for games that leverage user-provided discs. */
CHEEVOS_LOG(RCHEEVOS_TAG "Hash identified for game %d\n", hash_game_id);
}
else
{
CHEEVOS_LOG(RCHEEVOS_TAG "Disc not recognized\n");
if (rcheevos_hardcore_active())
{
/* don't allow unknown game discs in hardcore.
* assume it's a modified version of the base game. */
runloop_msg_queue_push("Hardcore paused. Game disc unrecognized.", 0, 5 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
rcheevos_pause_hardcore();
}
/* don't allow unknown game discs in hardcore.
* assume it's a modified version of the base game. */
runloop_msg_queue_push("Hardcore paused. Game disc unrecognized.", 0, 5 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
rcheevos_pause_hardcore();
}
}
/* disc is valid, add it to the known disk list */
rcheevos_add_hash(hash_entry);
rc_libretro_hash_set_add(&rcheevos_locals.game.hashes,
changed_disc_data->path, hash_game_id, changed_disc_data->hash);
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, changed_disc_data->hash);
free(changed_disc_data->path);
free(changed_disc_data);
}
static void rcheevos_identify_initial_disc_callback(void* userdata)
{
rcheevos_hash_entry_t* hash_entry = (rcheevos_hash_entry_t*)userdata;
struct rcheevos_identify_changed_disc_data* changed_disc_data =
(struct rcheevos_identify_changed_disc_data*)userdata;
/* rcheevos_client_identify_game will update rcheevos_locals.game.id */
if (rcheevos_locals.game.id != hash_entry->game_id)
if (rcheevos_locals.game.id != changed_disc_data->real_game_id)
{
if (rcheevos_locals.game.id == 0)
{
@ -2116,15 +2157,22 @@ static void rcheevos_identify_initial_disc_callback(void* userdata)
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR);
}
rcheevos_locals.game.hash = NULL;
rcheevos_unload();
}
else
{
/* disc is valid, add it to the known disk list */
CHEEVOS_LOG(RCHEEVOS_TAG "Hash valid for current game\n");
rc_libretro_hash_set_add(&rcheevos_locals.game.hashes,
changed_disc_data->path, rcheevos_locals.game.id, changed_disc_data->hash);
free(hash_entry);
return;
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, changed_disc_data->hash);
}
/* disc is valid, add it to the known disk list */
CHEEVOS_LOG(RCHEEVOS_TAG "Hash valid for current game\n");
rcheevos_add_hash(hash_entry);
free(changed_disc_data->path);
free(changed_disc_data);
}
static void rcheevos_validate_initial_disc_handler(retro_task_t* task)
@ -2154,22 +2202,21 @@ static void rcheevos_validate_initial_disc_handler(retro_task_t* task)
void rcheevos_change_disc(const char* new_disc_path, bool initial_disc)
{
rcheevos_hash_entry_t* hash_entry = rcheevos_locals.game.hashes;
rcheevos_hash_entry_t* hash_entry2;
const uint32_t path_djb2 = djb2_calculate(new_disc_path);
struct rcheevos_identify_changed_disc_data* data;
char hash[33];
int hash_game_id;
/* no game loaded */
if (rcheevos_locals.game.id == 0)
return;
/* see if we've already identified this file */
for (; hash_entry; hash_entry = hash_entry->next)
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, new_disc_path);
if (rcheevos_locals.game.hash)
{
if (hash_entry->path_djb2 == path_djb2)
{
rcheevos_locals.game.hash = hash_entry->hash;
return;
}
CHEEVOS_LOG(RCHEEVOS_TAG "Switched to known hash: %s\n", rcheevos_locals.game.hash);
return;
}
/* don't check the disc until the game is done loading */
@ -2185,35 +2232,45 @@ void rcheevos_change_disc(const char* new_disc_path, bool initial_disc)
}
/* attempt to identify the file */
hash_entry = (rcheevos_hash_entry_t*)calloc(1, sizeof(rcheevos_hash_entry_t));
hash_entry->path_djb2 = path_djb2;
if (!rc_hash_generate_from_file(hash_entry->hash, rcheevos_locals.game.console_id, new_disc_path))
if (rc_hash_generate_from_file(hash, rcheevos_locals.game.console_id, new_disc_path))
{
/* check to see if the hash is already known */
hash_game_id = rc_libretro_hash_set_get_game_id(&rcheevos_locals.game.hashes, hash);
if (hash_game_id)
{
/* hash identical to some other file - probably the first disc matching the m3u. */
CHEEVOS_LOG(RCHEEVOS_TAG "Hash valid for current game\n");
}
}
else
{
/* when changing discs, if the disc is not supported by the system, allow it. this is
* primarily for games that support user-provided audio CDs, but does allow using discs
* from other systems for games that leverage user-provided discs. */
CHEEVOS_LOG(RCHEEVOS_TAG "No hash generated\n");
rcheevos_add_hash(hash_entry);
hash_game_id = -1;
strcpy(hash, "[NO HASH]");
}
if (hash_game_id)
{
/* we know how to handle this disc. no need to call the server */
rc_libretro_hash_set_add(&rcheevos_locals.game.hashes, new_disc_path, hash_game_id, hash);
rcheevos_locals.game.hash =
rc_libretro_hash_set_get_hash(&rcheevos_locals.game.hashes, new_disc_path);
return;
}
/* assume it's for the same game. we'll validate in the callback */
hash_entry->game_id = rcheevos_locals.game.id;
/* check to see if the hash is already known - may have generated it for the m3u */
for (hash_entry2 = rcheevos_locals.game.hashes; hash_entry2; hash_entry2 = hash_entry2->next)
{
if (string_is_equal(hash_entry->hash, hash_entry2->hash))
{
CHEEVOS_LOG(RCHEEVOS_TAG "Hash valid for current game\n");
rcheevos_add_hash(hash_entry);
return;
}
}
/* call the server to make sure the hash is valid for the loaded game */
rcheevos_client_identify_game(hash_entry->hash,
initial_disc ? rcheevos_identify_initial_disc_callback :
rcheevos_identify_game_disc_callback, hash_entry);
data = (struct rcheevos_identify_changed_disc_data*)
calloc(1, sizeof(struct rcheevos_identify_changed_disc_data));
if (data) {
data->real_game_id = rcheevos_locals.game.id;
data->path = strdup(new_disc_path);
memcpy(data->hash, hash, sizeof(data->hash));
rcheevos_client_identify_game(data->hash,
initial_disc ? rcheevos_identify_initial_disc_callback :
rcheevos_identify_game_disc_callback, data);
}
}

View File

@ -120,24 +120,16 @@ typedef struct rcheevos_load_info_t
#endif
} rcheevos_load_info_t;
typedef struct rcheevos_hash_entry_t
{
uint32_t path_djb2;
int game_id;
struct rcheevos_hash_entry_t* next;
char hash[33];
} rcheevos_hash_entry_t;
typedef struct rcheevos_game_info_t
{
int id;
int console_id;
char* title;
char badge_name[16];
char* hash;
const char* hash;
bool mastery_placard_shown;
rcheevos_hash_entry_t* hashes;
rc_libretro_hash_set_t hashes;
rcheevos_racheevo_t* achievements;
rcheevos_ralboard_t* leaderboards;

View File

@ -81,6 +81,7 @@ enum {
RC_CONSOLE_MEGADUCK = 69,
RC_CONSOLE_ZEEBO = 70,
RC_CONSOLE_ARDUBOY = 71,
RC_CONSOLE_WASM4 = 72,
RC_CONSOLE_HUBS = 100,
RC_CONSOLE_EVENTS = 101

View File

@ -201,6 +201,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_VIRTUAL_BOY:
return "Virtual Boy";
case RC_CONSOLE_WASM4:
return "WASM-4";
case RC_CONSOLE_WII:
return "Wii";
@ -242,6 +245,14 @@ static const rc_memory_region_t _rc_memory_regions_3do[] = {
};
static const rc_memory_regions_t rc_memory_regions_3do = { _rc_memory_regions_3do, 1 };
/* ===== Amiga ===== */
/* http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node00D3.html */
static const rc_memory_region_t _rc_memory_regions_amiga[] = {
{ 0x000000U, 0x07FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Main RAM" }, /* 512KB main RAM */
{ 0x080000U, 0x0FFFFFU, 0x080000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" }, /* 512KB extended RAM */
};
static const rc_memory_regions_t rc_memory_regions_amiga = { _rc_memory_regions_amiga, 2 };
/* ===== Amstrad CPC ===== */
/* http://www.cpcalive.com/docs/amstrad_cpc_6128_memory_map.html */
/* https://www.cpcwiki.eu/index.php/File:AWMG_page151.jpg */
@ -324,6 +335,20 @@ static const rc_memory_region_t _rc_memory_regions_colecovision[] = {
};
static const rc_memory_regions_t rc_memory_regions_colecovision = { _rc_memory_regions_colecovision, 1 };
/* ===== Commodore 64 ===== */
/* https://www.c64-wiki.com/wiki/Memory_Map */
/* https://sta.c64.org/cbm64mem.html */
static const rc_memory_region_t _rc_memory_regions_c64[] = {
{ 0x000000U, 0x0003FFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
{ 0x000400U, 0x0007FFU, 0x000400U, RC_MEMORY_TYPE_VIDEO_RAM, "Screen RAM" },
{ 0x000800U, 0x009FFFU, 0x000800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* BASIC Program Storage Area */
{ 0x00A000U, 0x00BFFFU, 0x00A000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area / BASIC ROM Area */
{ 0x00C000U, 0x00CFFFU, 0x00C000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area */
{ 0x00D000U, 0x00DFFFU, 0x00D000U, RC_MEMORY_TYPE_SYSTEM_RAM, "I/O Area" }, /* also Character ROM */
{ 0x00E000U, 0x00FFFFU, 0x00E000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Machine Language Storage Area / Kernal ROM */
};
static const rc_memory_regions_t rc_memory_regions_c64 = { _rc_memory_regions_c64, 7 };
/* ===== Dreamcast ===== */
/* http://archiv.sega-dc.de/munkeechuff/hardware/Memory.html */
static const rc_memory_region_t _rc_memory_regions_dreamcast[] = {
@ -331,6 +356,21 @@ static const rc_memory_region_t _rc_memory_regions_dreamcast[] = {
};
static const rc_memory_regions_t rc_memory_regions_dreamcast = { _rc_memory_regions_dreamcast, 1 };
/* ===== Fairchild Channel F ===== */
static const rc_memory_region_t _rc_memory_regions_fairchild_channel_f[] = {
/* "System RAM" is actually just a bunch of registers internal to CPU so all carts have it.
* "Video RAM" is part of the console so it's always available but it is write-only by the ROMs.
* "Cartridge RAM" is the cart BUS. Most carts only have ROMs on this bus. Exception are
* German Schach and homebrew carts that have 2K of RAM there in addition to ROM.
* "F2102 RAM" is used by Maze for 1K of RAM.
* https://discord.com/channels/310192285306454017/645777658319208448/967001438087708714 */
{ 0x00000000U, 0x0000003FU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x00000040U, 0x0000083FU, 0x00300000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" },
{ 0x00000840U, 0x0001083FU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
{ 0x00010840U, 0x00010C3FU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "F2102 RAM" }
};
static const rc_memory_regions_t rc_memory_regions_fairchild_channel_f = { _rc_memory_regions_fairchild_channel_f, 4 };
/* ===== GameBoy / GameBoy Color ===== */
static const rc_memory_region_t _rc_memory_regions_gameboy[] = {
{ 0x000000U, 0x0000FFU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Interrupt vector" },
@ -553,9 +593,10 @@ static const rc_memory_regions_t rc_memory_regions_playstation = { _rc_memory_re
/* https://psi-rockin.github.io/ps2tek/ */
static const rc_memory_region_t _rc_memory_regions_playstation2[] = {
{ 0x00000000U, 0x000FFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
{ 0x00100000U, 0x01FFFFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
{ 0x00100000U, 0x01FFFFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x02000000U, 0x02003FFFU, 0x70000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Scratchpad RAM" },
};
static const rc_memory_regions_t rc_memory_regions_playstation2 = { _rc_memory_regions_playstation2, 2 };
static const rc_memory_regions_t rc_memory_regions_playstation2 = { _rc_memory_regions_playstation2, 3 };
/* ===== PlayStation Portable ===== */
/* https://github.com/uofw/upspd/wiki/Memory-map */
@ -670,6 +711,17 @@ static const rc_memory_region_t _rc_memory_regions_watara_supervision[] = {
};
static const rc_memory_regions_t rc_memory_regions_watara_supervision = { _rc_memory_regions_watara_supervision, 3 };
/* ===== WASM-4 ===== */
/* fantasy console that runs specifically designed WebAssembly games */
/* https://github.com/aduros/wasm4/blob/main/site/docs/intro.md#hardware-specs */
static const rc_memory_region_t _rc_memory_regions_wasm4[] = {
{ 0x000000U, 0x00FFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
/* Persistent storage is not directly accessible from the game. It has to be loaded into System RAM first
{ 0x010000U, 0x0103FFU, 0x80000000U, RC_MEMORY_TYPE_SAVE_RAM, "Disk Storage"}
*/
};
static const rc_memory_regions_t rc_memory_regions_wasm4 = { _rc_memory_regions_wasm4, 1 };
/* ===== WonderSwan ===== */
/* http://daifukkat.su/docs/wsman/#ovr_memmap */
static const rc_memory_region_t _rc_memory_regions_wonderswan[] = {
@ -697,6 +749,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_3DO:
return &rc_memory_regions_3do;
case RC_CONSOLE_AMIGA:
return &rc_memory_regions_amiga;
case RC_CONSOLE_AMSTRAD_PC:
return &rc_memory_regions_amstrad_pc;
@ -721,9 +776,15 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_COLECOVISION:
return &rc_memory_regions_colecovision;
case RC_CONSOLE_COMMODORE_64:
return &rc_memory_regions_c64;
case RC_CONSOLE_DREAMCAST:
return &rc_memory_regions_dreamcast;
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;
@ -821,6 +882,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_VIRTUAL_BOY:
return &rc_memory_regions_virtualboy;
case RC_CONSOLE_WASM4:
return &rc_memory_regions_wasm4;
case RC_CONSOLE_WONDERSWAN:
return &rc_memory_regions_wonderswan;

View File

@ -14,6 +14,16 @@
#include <ctype.h>
#include <string.h>
/* internal helper functions in hash.c */
extern void* rc_file_open(const char* path);
extern void rc_file_seek(void* file_handle, int64_t offset, int origin);
extern int64_t rc_file_tell(void* file_handle);
extern size_t rc_file_read(void* file_handle, void* buffer, int requested_bytes);
extern void rc_file_close(void* file_handle);
extern int rc_path_compare_extension(const char* path, const char* ext);
extern int rc_hash_error(const char* message);
static rc_libretro_message_callback rc_libretro_verbose_message_callback = NULL;
/* a value that starts with a comma is a CSV.
@ -229,6 +239,41 @@ const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* l
return NULL;
}
typedef struct rc_disallowed_core_systems_t
{
const char* library_name;
const int disallowed_consoles[4];
} rc_disallowed_core_systems_t;
static const rc_disallowed_core_systems_t rc_disallowed_core_systems[] = {
/* https://github.com/libretro/Mesen-S/issues/8 */
{ "Mesen-S", { RC_CONSOLE_GAMEBOY, RC_CONSOLE_GAMEBOY_COLOR, 0 }},
{ NULL, { 0 } }
};
int rc_libretro_is_system_allowed(const char* library_name, int console_id) {
const rc_disallowed_core_systems_t* core_filter = rc_disallowed_core_systems;
size_t library_name_length;
size_t i;
if (!library_name || !library_name[0])
return 1;
library_name_length = strlen(library_name) + 1;
while (core_filter->library_name) {
if (memcmp(core_filter->library_name, library_name, library_name_length) == 0) {
for (i = 0; i < sizeof(core_filter->disallowed_consoles) / sizeof(core_filter->disallowed_consoles[0]); ++i) {
if (core_filter->disallowed_consoles[i] == console_id)
return 0;
}
break;
}
++core_filter;
}
return 1;
}
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
unsigned i;
@ -556,3 +601,168 @@ int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions) {
memset(regions, 0, sizeof(*regions));
}
void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
const char* m3u_path, rc_libretro_get_image_path_func get_image_path) {
char image_path[1024];
char* m3u_contents;
char* ptr;
size_t file_len;
void* file_handle;
int index = 0;
memset(hash_set, 0, sizeof(*hash_set));
if (!rc_path_compare_extension(m3u_path, "m3u"))
return;
file_handle = rc_file_open(m3u_path);
if (!file_handle)
{
rc_hash_error("Could not open playlist");
return;
}
rc_file_seek(file_handle, 0, SEEK_END);
file_len = rc_file_tell(file_handle);
rc_file_seek(file_handle, 0, SEEK_SET);
m3u_contents = (char*)malloc(file_len + 1);
rc_file_read(file_handle, m3u_contents, (int)file_len);
m3u_contents[file_len] = '\0';
rc_file_close(file_handle);
ptr = m3u_contents;
do
{
/* ignore whitespace */
while (isspace((int)*ptr))
++ptr;
if (*ptr == '#')
{
/* ignore comment unless it's the special SAVEDISK extension */
if (memcmp(ptr, "#SAVEDISK:", 10) == 0)
{
/* get the path to the save disk from the frontend, assign it a bogus hash so
* it doesn't get hashed later */
if (get_image_path(index, image_path, sizeof(image_path)))
{
const char save_disk_hash[33] = "[SAVE DISK]";
rc_libretro_hash_set_add(hash_set, image_path, -1, save_disk_hash);
++index;
}
}
}
else
{
/* non-empty line, tally a file */
++index;
}
/* find the end of the line */
while (*ptr && *ptr != '\n')
++ptr;
} while (*ptr);
free(m3u_contents);
if (hash_set->entries_count > 0)
{
/* at least one save disk was found. make sure the core supports the #SAVEDISK: extension by
* asking for the last expected disk. if it's not found, assume no #SAVEDISK: support */
if (!get_image_path(index - 1, image_path, sizeof(image_path)))
hash_set->entries_count = 0;
}
}
void rc_libretro_hash_set_destroy(struct rc_libretro_hash_set_t* hash_set) {
if (hash_set->entries)
free(hash_set->entries);
memset(hash_set, 0, sizeof(*hash_set));
}
static unsigned rc_libretro_djb2(const char* input)
{
unsigned result = 5381;
char c;
while ((c = *input++) != '\0')
result = ((result << 5) + result) + c; /* result = result * 33 + c */
return result;
}
void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* path, int game_id, const char hash[33]) {
const unsigned path_djb2 = (path != NULL) ? rc_libretro_djb2(path) : 0;
struct rc_libretro_hash_entry_t* entry = NULL;
struct rc_libretro_hash_entry_t* scan;
struct rc_libretro_hash_entry_t* stop = hash_set->entries + hash_set->entries_count;;
if (path_djb2)
{
/* attempt to match the path */
for (scan = hash_set->entries; scan < stop; ++scan)
{
if (scan->path_djb2 == path_djb2)
{
entry = scan;
break;
}
}
}
if (!entry)
{
/* entry not found, allocate a new one */
if (hash_set->entries_size == 0)
{
hash_set->entries_size = 4;
hash_set->entries = (struct rc_libretro_hash_entry_t*)
malloc(hash_set->entries_size * sizeof(struct rc_libretro_hash_entry_t));
}
else if (hash_set->entries_count == hash_set->entries_size)
{
hash_set->entries_size += 4;
hash_set->entries = (struct rc_libretro_hash_entry_t*)realloc(hash_set->entries,
hash_set->entries_size * sizeof(struct rc_libretro_hash_entry_t));
}
entry = hash_set->entries + hash_set->entries_count++;
}
/* update the entry */
entry->path_djb2 = path_djb2;
entry->game_id = game_id;
memcpy(entry->hash, hash, sizeof(entry->hash));
}
const char* rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* hash_set, const char* path)
{
const unsigned path_djb2 = rc_libretro_djb2(path);
struct rc_libretro_hash_entry_t* scan = hash_set->entries;
struct rc_libretro_hash_entry_t* stop = scan + hash_set->entries_count;
for (; scan < stop; ++scan)
{
if (scan->path_djb2 == path_djb2)
return scan->hash;
}
return NULL;
}
int rc_libretro_hash_set_get_game_id(const struct rc_libretro_hash_set_t* hash_set, const char* hash)
{
struct rc_libretro_hash_entry_t* scan = hash_set->entries;
struct rc_libretro_hash_entry_t* stop = scan + hash_set->entries_count;
for (; scan < stop; ++scan)
{
if (memcmp(scan->hash, hash, sizeof(scan->hash)) == 0)
return scan->game_id;
}
return 0;
}

View File

@ -21,6 +21,7 @@ typedef struct rc_disallowed_setting_t
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name);
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
int rc_libretro_is_system_allowed(const char* library_name, int console_id);
/*****************************************************************************\
| Memory Mapping |
@ -53,6 +54,35 @@ void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address);
/*****************************************************************************\
| Disk Identification |
\*****************************************************************************/
typedef struct rc_libretro_hash_entry_t
{
uint32_t path_djb2;
int game_id;
char hash[33];
} rc_libretro_hash_entry_t;
typedef struct rc_libretro_hash_set_t
{
struct rc_libretro_hash_entry_t* entries;
uint16_t entries_count;
uint16_t entries_size;
} rc_libretro_hash_set_t;
typedef int (*rc_libretro_get_image_path_func)(unsigned index, char* buffer, size_t buffer_size);
void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
const char* m3u_path, rc_libretro_get_image_path_func get_image_path);
void rc_libretro_hash_set_destroy(struct rc_libretro_hash_set_t* hash_set);
void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* path, int game_id, const char hash[33]);
const char* rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* hash_set, const char* path);
int rc_libretro_hash_set_get_game_id(const struct rc_libretro_hash_set_t* hash_set, const char* hash);
#ifdef __cplusplus
}
#endif

View File

@ -96,16 +96,29 @@ int rc_trigger_state_active(int state)
}
}
static int rc_condset_is_measured_from_hitcount(const rc_condset_t* condset, unsigned measured_value)
{
const rc_condition_t* condition;
for (condition = condset->conditions; condition; condition = condition->next) {
if (condition->type == RC_CONDITION_MEASURED && condition->required_hits &&
condition->current_hits == measured_value) {
return 1;
}
}
return 0;
}
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
rc_condset_t* condset;
if (self->requirement != 0) {
if (self->requirement) {
rc_reset_condset(self->requirement);
}
condset = self->alternative;
while (condset != 0) {
while (condset) {
rc_reset_condset(condset);
condset = condset->next;
}
@ -168,7 +181,7 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
sub_primed |= eval_state.primed;
condset = condset->next;
} while (condset != 0);
} while (condset);
/* to trigger, the core must be true and at least one alt must be true */
ret &= sub;
@ -192,13 +205,31 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
return RC_TRIGGER_STATE_WAITING;
}
/* if any ResetIf condition was true, reset the hit counts */
if (eval_state.was_reset) {
/* if any ResetIf condition was true, reset the hit counts */
rc_reset_trigger_hitcounts(self);
/* if the measured value came from a hit count, reset it too */
if (eval_state.measured_from_hits)
/* if the measured value came from a hit count, reset it. do this before calling
* rc_reset_trigger_hitcounts in case we need to call rc_condset_is_measured_from_hitcount */
if (eval_state.measured_from_hits) {
self->measured_value = 0;
}
else if (is_paused && self->measured_value) {
/* if the measured value is in a paused group, measured_from_hits won't have been set.
* attempt to determine if it should have been */
if (self->requirement && self->requirement->is_paused &&
rc_condset_is_measured_from_hitcount(self->requirement, self->measured_value)) {
self->measured_value = 0;
}
else {
for (condset = self->alternative; condset; condset = condset->next) {
if (condset->is_paused && rc_condset_is_measured_from_hitcount(condset, self->measured_value)) {
self->measured_value = 0;
break;
}
}
}
}
rc_reset_trigger_hitcounts(self);
/* if there were hit counts to clear, return RESET, but don't change the state */
if (self->has_hits) {
@ -206,7 +237,7 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
/* cannot be PRIMED while ResetIf is true */
if (self->state == RC_TRIGGER_STATE_PRIMED)
self->state = RC_TRIGGER_STATE_ACTIVE;
self->state = RC_TRIGGER_STATE_ACTIVE;
return RC_TRIGGER_STATE_RESET;
}

View File

@ -563,32 +563,34 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
static void* cdreader_open_gdi_track(const char* path, uint32_t track)
{
void* file_handle;
char buffer[1024];
char mode[16] = "MODE1/";
char sector_size[16];
char file[256];
int64_t track_size;
int track_type;
char *ptr, *ptr2, *end;
char* bin_path = "";
uint32_t current_track = 0;
char* ptr, *ptr2, *end;
int lba = 0;
uint32_t largest_track = 0;
int64_t largest_track_size = 0;
char largest_track_file[256];
char largest_track_sector_size[16];
char *bin_path = NULL;
uint32_t current_track = 0;
int lba = 0;
uint32_t largest_track = 0;
int64_t largest_track_size = 0;
int largest_track_lba = 0;
int largest_track_lba = 0;
int found = 0;
size_t num_read = 0;
int64_t file_offset = 0;
struct cdrom_t* cdrom = NULL;
void *file_handle = rc_file_open(path);
int found = 0;
size_t num_read = 0;
int64_t file_offset = 0;
struct cdrom_t* cdrom = NULL;
file_handle = rc_file_open(path);
if (!file_handle)
return NULL;
file[0] = '\0';
file[0] = '\0';
do
{
num_read = rc_file_read(file_handle, buffer, sizeof(buffer) - 1);

View File

@ -1609,6 +1609,8 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_COMMODORE_64:
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
case RC_CONSOLE_GAMEBOY:
case RC_CONSOLE_GAMEBOY_ADVANCE:
case RC_CONSOLE_GAMEBOY_COLOR:
@ -1629,6 +1631,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
case RC_CONSOLE_TIC80:
case RC_CONSOLE_VECTREX:
case RC_CONSOLE_VIRTUAL_BOY:
case RC_CONSOLE_WASM4:
case RC_CONSOLE_WONDERSWAN:
return rc_hash_buffer(hash, buffer, buffer_size);
@ -1898,6 +1901,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
case RC_CONSOLE_GAMEBOY:
case RC_CONSOLE_GAMEBOY_ADVANCE:
case RC_CONSOLE_GAMEBOY_COLOR:
@ -1916,12 +1920,14 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_TIC80:
case RC_CONSOLE_VECTREX:
case RC_CONSOLE_VIRTUAL_BOY:
case RC_CONSOLE_WASM4:
case RC_CONSOLE_WONDERSWAN:
/* generic whole-file hash - don't buffer */
return rc_hash_whole_file(hash, path);
case RC_CONSOLE_AMSTRAD_PC:
case RC_CONSOLE_APPLE_II:
case RC_CONSOLE_COMMODORE_64:
case RC_CONSOLE_MSX:
case RC_CONSOLE_PC8800:
/* generic whole-file hash with m3u support - don't buffer */
@ -2132,7 +2138,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
}
}
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, and MegaDuck.
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, MegaDuck, and Fairchild Channel F.
* Since they all use the same hashing algorithm, only specify one of them */
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
}
@ -2173,6 +2179,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_MSX;
}
else if (rc_path_compare_extension(ext, "chf"))
{
iterator->consoles[0] = RC_CONSOLE_FAIRCHILD_CHANNEL_F;
}
break;
case 'd':
@ -2180,6 +2190,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
rc_hash_initialize_dsk_iterator(iterator, path);
}
else if (rc_path_compare_extension(ext, "d64"))
{
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
}
else if (rc_path_compare_extension(ext, "d88"))
{
iterator->consoles[0] = RC_CONSOLE_PC8800;
@ -2320,6 +2334,11 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_NEOGEO_POCKET;
}
else if (rc_path_compare_extension(ext, "nib"))
{
/* also Apple II, but both are full-file hashes */
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
}
break;
case 'p':
@ -2332,8 +2351,9 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
case 'r':
if (rc_path_compare_extension(ext, "rom"))
{
/* rom is associated with MSX, Thomson TO-8, and Fairchild Channel F.
* Since they all use the same hashing algorithm, only specify one of them */
iterator->consoles[0] = RC_CONSOLE_MSX;
iterator->consoles[1] = RC_CONSOLE_THOMSONTO8; /* cartridge */
}
if (rc_path_compare_extension(ext, "ri"))
{
@ -2393,6 +2413,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_WONDERSWAN;
}
else if (rc_path_compare_extension(ext, "wasm"))
{
iterator->consoles[0] = RC_CONSOLE_WASM4;
}
else if (rc_path_compare_extension(ext, "woz"))
{
iterator->consoles[0] = RC_CONSOLE_APPLE_II;

View File

@ -331,7 +331,16 @@ bool disk_control_set_eject_state(
#ifdef HAVE_CHEEVOS
if (!error && !eject)
rcheevos_change_disc(disk_control->index_record.image_path, false);
{
if (disk_control->cb.get_image_index && disk_control->cb.get_image_path)
{
char image_path[PATH_MAX_LENGTH] = "";
unsigned image_index = disk_control->cb.get_image_index();
if (disk_control->cb.get_image_path(image_index, image_path, sizeof(image_path)))
rcheevos_change_disc(image_path, false);
}
}
#endif
return !error;