mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 03:25:36 +00:00
dep/rcheevos: Bump to 8a0178d
This commit is contained in:
parent
6cde51b7b5
commit
4530301820
@ -124,6 +124,9 @@ typedef struct rc_operand_t {
|
||||
|
||||
/* specifies how to read the memref for some types (RC_OPERAND_*) */
|
||||
uint8_t memref_access_type;
|
||||
|
||||
/* if set, this operand is combining the current condition with the previous one */
|
||||
uint8_t is_combining;
|
||||
}
|
||||
rc_operand_t;
|
||||
|
||||
|
@ -1304,14 +1304,14 @@ int rc_api_init_fetch_image_request_hosted(rc_api_request_t* request, const rc_a
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* username) {
|
||||
const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* image_name) {
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
|
||||
memset(&image_request, 0, sizeof(image_request));
|
||||
image_request.image_type = image_type;
|
||||
image_request.image_name = username;
|
||||
image_request.image_name = image_name;
|
||||
|
||||
result = rc_api_init_fetch_image_request(&request, &image_request);
|
||||
if (result == RC_OK)
|
||||
|
@ -80,7 +80,7 @@ const char* rc_api_default_host(void);
|
||||
void rc_api_url_build_dorequest_url(rc_api_request_t* request, const rc_api_host_t* host);
|
||||
int rc_api_url_build_dorequest(rc_api_url_builder_t* builder, const char* api, const char* username, const char* api_token);
|
||||
|
||||
const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* username);
|
||||
const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* image_name);
|
||||
|
||||
RC_END_C_DECLS
|
||||
|
||||
|
@ -64,6 +64,7 @@ typedef struct rc_client_load_state_t
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
rc_hash_iterator_t hash_iterator;
|
||||
rc_client_game_hash_t* tried_hashes[4];
|
||||
#endif
|
||||
rc_client_pending_media_t* pending_media;
|
||||
|
||||
@ -2235,40 +2236,31 @@ static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state)
|
||||
return;
|
||||
}
|
||||
|
||||
if (load_state->game->media_hash &&
|
||||
load_state->game->media_hash->game_hash &&
|
||||
load_state->game->media_hash->game_hash->next) {
|
||||
if (load_state->tried_hashes[1]) {
|
||||
/* multiple hashes were tried, create a CSV */
|
||||
struct rc_client_game_hash_t* game_hash = load_state->game->media_hash->game_hash;
|
||||
int count = 1;
|
||||
size_t i;
|
||||
size_t count = 0;
|
||||
char* ptr;
|
||||
size_t size;
|
||||
size_t size = 0;
|
||||
|
||||
size = strlen(game_hash->hash) + 1;
|
||||
while (game_hash->next) {
|
||||
game_hash = game_hash->next;
|
||||
size += strlen(game_hash->hash) + 1;
|
||||
for (i = 0; i < sizeof(load_state->tried_hashes) / sizeof(load_state->tried_hashes[0]); ++i) {
|
||||
if (!load_state->tried_hashes[i])
|
||||
break;
|
||||
|
||||
size += strlen(load_state->tried_hashes[i]->hash) + 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
ptr = (char*)rc_buffer_alloc(&load_state->game->buffer, size);
|
||||
ptr += size - 1;
|
||||
*ptr = '\0';
|
||||
game_hash = load_state->game->media_hash->game_hash;
|
||||
do {
|
||||
const size_t hash_len = strlen(game_hash->hash);
|
||||
ptr -= hash_len;
|
||||
memcpy(ptr, game_hash->hash, hash_len);
|
||||
|
||||
game_hash = game_hash->next;
|
||||
if (!game_hash)
|
||||
break;
|
||||
|
||||
ptr--;
|
||||
*ptr = ',';
|
||||
} while (1);
|
||||
|
||||
load_state->game->public_.hash = ptr;
|
||||
for (i = 0; i < count; i++) {
|
||||
const size_t hash_len = strlen(load_state->tried_hashes[i]->hash);
|
||||
memcpy(ptr, load_state->tried_hashes[i]->hash, hash_len);
|
||||
ptr += hash_len;
|
||||
*ptr++ = ',';
|
||||
}
|
||||
*(ptr - 1) = '\0';
|
||||
|
||||
load_state->game->public_.console_id = RC_CONSOLE_UNKNOWN;
|
||||
} else {
|
||||
/* only a single hash was tried, capture it */
|
||||
@ -2293,6 +2285,12 @@ static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state)
|
||||
|
||||
if (load_state->hash->game_id == 0) {
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||
if (client->state.raintegration && client->state.raintegration->set_console_id) {
|
||||
if (load_state->game->public_.console_id != RC_CONSOLE_UNKNOWN)
|
||||
client->state.raintegration->set_console_id(load_state->game->public_.console_id);
|
||||
}
|
||||
#endif
|
||||
if (client->state.external_client) {
|
||||
if (client->state.external_client->load_unknown_game) {
|
||||
client->state.external_client->load_unknown_game(load_state->game->public_.hash);
|
||||
@ -2504,6 +2502,9 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa
|
||||
{
|
||||
rc_client_t* client = load_state->client;
|
||||
rc_client_game_hash_t* old_hash;
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
size_t i;
|
||||
#endif
|
||||
|
||||
if (!rc_client_attach_load_state(client, load_state)) {
|
||||
rc_client_free_load_state(load_state);
|
||||
@ -2513,6 +2514,24 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa
|
||||
old_hash = load_state->hash;
|
||||
load_state->hash = rc_client_find_game_hash(client, hash);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
i = 0;
|
||||
do {
|
||||
if (!load_state->tried_hashes[i]) {
|
||||
load_state->tried_hashes[i] = load_state->hash;
|
||||
break;
|
||||
}
|
||||
|
||||
if (load_state->tried_hashes[i] == load_state->hash)
|
||||
break;
|
||||
|
||||
if (++i == sizeof(load_state->tried_hashes) / sizeof(load_state->tried_hashes[0])) {
|
||||
RC_CLIENT_LOG_VERBOSE(client, "tried_hashes buffer is full");
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
#endif
|
||||
|
||||
if (file_path) {
|
||||
rc_client_media_hash_t* media_hash =
|
||||
(rc_client_media_hash_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(*media_hash));
|
||||
@ -2553,6 +2572,11 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa
|
||||
|
||||
rc_api_destroy_request(&request);
|
||||
}
|
||||
else if (load_state->hash->game_id != RC_CLIENT_UNKNOWN_GAME_ID &&
|
||||
client->state.external_client && client->state.external_client->begin_load_game) {
|
||||
rc_client_begin_async(client, &load_state->async_handle);
|
||||
client->state.external_client->begin_load_game(client, load_state->hash->hash, rc_client_external_load_state_callback, load_state);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
rc_client_begin_fetch_game_data(load_state);
|
||||
@ -2561,6 +2585,26 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa
|
||||
return (client->state.load == load_state) ? &load_state->async_handle : NULL;
|
||||
}
|
||||
|
||||
static void rc_client_abort_load_in_progress(rc_client_t* client)
|
||||
{
|
||||
rc_client_load_state_t* load_state;
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
|
||||
load_state = client->state.load;
|
||||
if (load_state) {
|
||||
/* this mimics rc_client_abort_async without nesting the lock */
|
||||
load_state->async_handle.aborted = RC_CLIENT_ASYNC_ABORTED;
|
||||
|
||||
client->state.load = NULL;
|
||||
}
|
||||
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (load_state && load_state->callback)
|
||||
load_state->callback(RC_ABORTED, "The requested game is no longer active", load_state->client, load_state->callback_userdata);
|
||||
}
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const char* hash, rc_client_callback_t callback, void* callback_userdata)
|
||||
{
|
||||
rc_client_load_state_t* load_state;
|
||||
@ -2575,6 +2619,8 @@ rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const c
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc_client_abort_load_in_progress(client);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->begin_load_game)
|
||||
return client->state.external_client->begin_load_game(client, hash, callback, callback_userdata);
|
||||
@ -2616,6 +2662,8 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc_client_abort_load_in_progress(client);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
/* if a add_game_hash handler exists, do the identification locally, then pass the
|
||||
* resulting game_id/hash to the external client */
|
||||
@ -5991,6 +6039,9 @@ void rc_client_reset(rc_client_t* client)
|
||||
|
||||
int rc_client_can_pause(rc_client_t* client, uint32_t* frames_remaining)
|
||||
{
|
||||
if (!client)
|
||||
return 1;
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->can_pause)
|
||||
return client->state.external_client->can_pause(frames_remaining);
|
||||
|
@ -3,17 +3,45 @@
|
||||
#include "rc_client_external_versions.h"
|
||||
#include "rc_client_internal.h"
|
||||
|
||||
#include "rc_api_runtime.h"
|
||||
|
||||
#define RC_CONVERSION_FILL(obj, obj_type, src_type) memset((uint8_t*)obj + sizeof(src_type), 0, sizeof(obj_type) - sizeof(src_type))
|
||||
|
||||
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
|
||||
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
|
||||
|
||||
typedef struct rc_client_external_conversions_t {
|
||||
rc_client_user_t user;
|
||||
rc_client_game_t game;
|
||||
rc_client_subset_t subsets[4];
|
||||
rc_client_achievement_t achievements[16];
|
||||
char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char achievement_badge_locked_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
uint32_t next_subset_index;
|
||||
uint32_t next_achievement_index;
|
||||
} rc_client_external_conversions_t;
|
||||
|
||||
static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)
|
||||
{
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
|
||||
memset(&image_request, 0, sizeof(image_request));
|
||||
image_request.image_type = image_type;
|
||||
image_request.image_name = image_name;
|
||||
|
||||
result = rc_api_init_fetch_image_request(&request, &image_request);
|
||||
if (result != RC_OK)
|
||||
return NULL;
|
||||
|
||||
strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void rc_client_external_conversions_init(const rc_client_t* client)
|
||||
{
|
||||
if (!client->state.external_client_conversions) {
|
||||
@ -39,6 +67,10 @@ const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* cl
|
||||
converted = &client->state.external_client_conversions->user;
|
||||
memcpy(converted, v1_user, sizeof(v1_rc_client_user_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
|
||||
|
||||
converted->avatar_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
@ -54,12 +86,17 @@ const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* cl
|
||||
converted = &client->state.external_client_conversions->game;
|
||||
memcpy(converted, v1_game, sizeof(v1_rc_client_game_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t* client, const rc_client_subset_t* v1_subset)
|
||||
{
|
||||
rc_client_subset_t* converted = NULL;
|
||||
char* badge_url = NULL;
|
||||
const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]);
|
||||
uint32_t index;
|
||||
|
||||
@ -71,23 +108,31 @@ const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t
|
||||
for (index = 0; index < num_subsets; ++index) {
|
||||
if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) {
|
||||
converted = &client->state.external_client_conversions->subsets[index];
|
||||
badge_url = client->state.external_client_conversions->subset_badge_url[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!converted) {
|
||||
converted = &client->state.external_client_conversions->subsets[client->state.external_client_conversions->next_subset_index];
|
||||
client->state.external_client_conversions->next_subset_index = (client->state.external_client_conversions->next_subset_index + 1) % num_subsets;
|
||||
index = client->state.external_client_conversions->next_subset_index;
|
||||
converted = &client->state.external_client_conversions->subsets[index];
|
||||
badge_url = client->state.external_client_conversions->subset_badge_url[client->state.external_client_conversions->next_subset_index];
|
||||
client->state.external_client_conversions->next_subset_index = (index + 1) % num_subsets;
|
||||
}
|
||||
|
||||
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const rc_client_t* client, const rc_client_achievement_t* v1_achievement)
|
||||
{
|
||||
rc_client_achievement_t* converted = NULL;
|
||||
char* badge_url = NULL;
|
||||
char* badge_locked_url = NULL;
|
||||
const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]);
|
||||
uint32_t index;
|
||||
|
||||
@ -99,17 +144,26 @@ const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const r
|
||||
for (index = 0; index < num_achievements; ++index) {
|
||||
if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) {
|
||||
converted = &client->state.external_client_conversions->achievements[index];
|
||||
badge_url = client->state.external_client_conversions->achievement_badge_url[index];
|
||||
badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!converted) {
|
||||
converted = &client->state.external_client_conversions->achievements[client->state.external_client_conversions->next_achievement_index];
|
||||
client->state.external_client_conversions->next_achievement_index = (client->state.external_client_conversions->next_achievement_index + 1) % num_achievements;
|
||||
index = client->state.external_client_conversions->next_achievement_index;
|
||||
converted = &client->state.external_client_conversions->achievements[index];
|
||||
badge_url = client->state.external_client_conversions->achievement_badge_url[index];
|
||||
badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];
|
||||
client->state.external_client_conversions->next_achievement_index = (index + 1) % num_achievements;
|
||||
}
|
||||
|
||||
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
@ -118,6 +172,7 @@ typedef struct rc_client_achievement_list_wrapper_t {
|
||||
rc_client_achievement_list_t* source_list;
|
||||
rc_client_achievement_t* achievements;
|
||||
rc_client_achievement_t** achievements_pointers;
|
||||
char* badge_url_buffer;
|
||||
} rc_client_achievement_list_wrapper_t;
|
||||
|
||||
static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info)
|
||||
@ -130,6 +185,8 @@ static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_lis
|
||||
free(wrapper->achievements_pointers);
|
||||
if (wrapper->info.public_.buckets)
|
||||
free((void*)wrapper->info.public_.buckets);
|
||||
if (wrapper->badge_url_buffer)
|
||||
free(wrapper->badge_url_buffer);
|
||||
|
||||
rc_client_destroy_achievement_list(wrapper->source_list);
|
||||
|
||||
@ -156,6 +213,7 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
||||
const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets;
|
||||
rc_client_achievement_bucket_t* bucket;
|
||||
uint32_t num_achievements = 0;
|
||||
char* badge_url = NULL;
|
||||
|
||||
new_list->info.public_.buckets = bucket = (rc_client_achievement_bucket_t*)calloc(v1_achievement_list->num_buckets, sizeof(*new_list->info.public_.buckets));
|
||||
if (!new_list->info.public_.buckets)
|
||||
@ -167,7 +225,8 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
||||
if (num_achievements) {
|
||||
new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->achievements));
|
||||
new_list->achievements_pointers = (rc_client_achievement_t**)malloc(num_achievements * sizeof(rc_client_achievement_t*));
|
||||
if (!new_list->achievements || !new_list->achievements_pointers)
|
||||
new_list->badge_url_buffer = badge_url = (char*)malloc(num_achievements * 2 * RC_CLIENT_IMAGE_URL_BUFFER_SIZE);
|
||||
if (!new_list->achievements || !new_list->achievements_pointers || !new_list->badge_url_buffer)
|
||||
return (rc_client_achievement_list_t*)new_list;
|
||||
}
|
||||
|
||||
@ -186,6 +245,11 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
||||
for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) {
|
||||
*achievement = &new_list->achievements[num_achievements++];
|
||||
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
|
||||
|
||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +364,7 @@
|
||||
<Type Name="rc_client_t">
|
||||
<Expand>
|
||||
<Item Name="game">game</Item>
|
||||
<Item Name="hashes">*((__rc_client_game_hash_list_t*)&hashes)</Item>
|
||||
<Item Name="hashes">*((__rc_client_game_hash_list_t*)this)</Item>
|
||||
<Item Name="user">user</Item>
|
||||
<Item Name="callbacks">callbacks</Item>
|
||||
<Item Name="state">state</Item>
|
||||
|
@ -432,6 +432,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
/* type determined by leaf */
|
||||
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
||||
condition->operand1.is_combining = 1;
|
||||
}
|
||||
|
||||
memcpy(&parse->remember, &condition->operand1, sizeof(parse->remember));
|
||||
@ -465,6 +466,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
/* type determined by leaf */
|
||||
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
||||
condition->operand1.is_combining = 1;
|
||||
|
||||
if (parse->buffer)
|
||||
condition->optimized_comparator = rc_condition_determine_comparator(condition);
|
||||
@ -476,6 +478,49 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
}
|
||||
}
|
||||
|
||||
static const rc_modified_memref_t* rc_operand_get_modified_memref(const rc_operand_t* operand) {
|
||||
if (!rc_operand_is_memref(operand))
|
||||
return NULL;
|
||||
|
||||
if (operand->value.memref->value.memref_type != RC_MEMREF_TYPE_MODIFIED_MEMREF)
|
||||
return NULL;
|
||||
|
||||
return (rc_modified_memref_t*)operand->value.memref;
|
||||
}
|
||||
|
||||
/* rc_condition_update_parse_state will mutate the operand1 to point at the modified memref
|
||||
* containing the accumulated result up until that point. this function returns the original
|
||||
* unmodified operand1 from parsing the definition.
|
||||
*/
|
||||
const rc_operand_t* rc_condition_get_real_operand1(const rc_condition_t* self) {
|
||||
const rc_operand_t* operand = &self->operand1;
|
||||
const rc_modified_memref_t* modified_memref;
|
||||
|
||||
if (operand->is_combining) {
|
||||
/* operand = "previous + current" - extract current */
|
||||
const rc_modified_memref_t* combining_modified_memref = rc_operand_get_modified_memref(operand);
|
||||
if (combining_modified_memref)
|
||||
operand = &combining_modified_memref->modifier;
|
||||
}
|
||||
|
||||
/* modifying operators are merged into an rc_modified_memref_t
|
||||
* if operand1 is a modified memref, assume it's been merged with the right side and
|
||||
* extract the parent which is the actual operand1. */
|
||||
modified_memref = rc_operand_get_modified_memref(operand);
|
||||
if (modified_memref) {
|
||||
if (modified_memref->modifier_type == RC_OPERATOR_INDIRECT_READ) {
|
||||
/* if the modified memref is an indirect read, the parent is the indirect
|
||||
* address and the modifier is the offset. the actual size and address are
|
||||
* stored in the modified memref - use it */
|
||||
} else if (rc_operator_is_modifying(self->oper) && self->oper != RC_OPERATOR_NONE) {
|
||||
/* operand = "parent*modifier" - extract parent.modifier will already be in operand2 */
|
||||
operand = &modified_memref->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
int rc_condition_is_combining(const rc_condition_t* self) {
|
||||
switch (self->type) {
|
||||
case RC_CONDITION_STANDARD:
|
||||
|
@ -833,12 +833,13 @@ static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_pokemini = { _rc_memory_regions_pokemini, 2 };
|
||||
|
||||
/* ===== Sega CD ===== */
|
||||
/* https://en.wikibooks.org/wiki/Genesis_Programming#MegaCD_Changes */
|
||||
/* https://en.wikibooks.org/wiki/Genesis_Programming/68K_Memory_map/ */
|
||||
static const rc_memory_region_t _rc_memory_regions_segacd[] = {
|
||||
{ 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "68000 RAM" },
|
||||
{ 0x010000U, 0x08FFFFU, 0x80020000U, RC_MEMORY_TYPE_SAVE_RAM, "CD PRG RAM" } /* normally banked into $020000-$03FFFF */
|
||||
{ 0x010000U, 0x08FFFFU, 0x80020000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD PRG RAM" }, /* normally banked into $020000-$03FFFF */
|
||||
{ 0x090000U, 0x0AFFFFU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD WORD RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_segacd = { _rc_memory_regions_segacd, 2 };
|
||||
static const rc_memory_regions_t rc_memory_regions_segacd = { _rc_memory_regions_segacd, 3 };
|
||||
|
||||
/* ===== Sega Saturn ===== */
|
||||
/* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */
|
||||
|
@ -150,7 +150,7 @@ rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t
|
||||
memset(modified_memref, 0, sizeof(*modified_memref));
|
||||
modified_memref->memref.value.memref_type = RC_MEMREF_TYPE_MODIFIED_MEMREF;
|
||||
modified_memref->memref.value.size = size;
|
||||
modified_memref->memref.value.type = (size == RC_MEMSIZE_FLOAT) ? RC_VALUE_TYPE_FLOAT : RC_VALUE_TYPE_UNSIGNED;
|
||||
modified_memref->memref.value.type = rc_memsize_is_float(size) ? RC_VALUE_TYPE_FLOAT : RC_VALUE_TYPE_UNSIGNED;
|
||||
memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent));
|
||||
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
|
||||
modified_memref->modifier_type = modifier_type;
|
||||
|
@ -55,6 +55,7 @@ static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr, r
|
||||
}
|
||||
else {
|
||||
memcpy(self, &parse->remember, sizeof(*self));
|
||||
self->is_combining = 0;
|
||||
self->memref_access_type = self->type;
|
||||
}
|
||||
self->type = RC_OPERAND_RECALL;
|
||||
@ -117,16 +118,11 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
}
|
||||
|
||||
if (parse->indirect_parent.type != RC_OPERAND_NONE) {
|
||||
if (parse->indirect_parent.type == RC_OPERAND_CONST) {
|
||||
self->value.memref = rc_alloc_memref(parse, address + parse->indirect_parent.value.num, size);
|
||||
}
|
||||
else {
|
||||
rc_operand_t offset;
|
||||
rc_operand_set_const(&offset, address);
|
||||
rc_operand_t offset;
|
||||
rc_operand_set_const(&offset, address);
|
||||
|
||||
self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse,
|
||||
size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset);
|
||||
}
|
||||
self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse,
|
||||
size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset);
|
||||
}
|
||||
else {
|
||||
self->value.memref = rc_alloc_memref(parse, address, size);
|
||||
@ -147,6 +143,8 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t*
|
||||
int negative;
|
||||
int allow_decimal = 0;
|
||||
|
||||
self->is_combining = 0;
|
||||
|
||||
switch (*aux) {
|
||||
case 'h': case 'H': /* hex constant */
|
||||
if (aux[2] == 'x' || aux[2] == 'X') {
|
||||
@ -318,7 +316,7 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
|
||||
case RC_OPERAND_FP:
|
||||
return (left->value.dbl == right->value.dbl);
|
||||
case RC_OPERAND_RECALL:
|
||||
return 1;
|
||||
return (left->value.memref == right->value.memref);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -360,7 +358,7 @@ int rc_operator_is_modifying(int oper) {
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_memsize_is_float(uint8_t size) {
|
||||
int rc_memsize_is_float(uint8_t size) {
|
||||
switch (size) {
|
||||
case RC_MEMSIZE_FLOAT:
|
||||
case RC_MEMSIZE_FLOAT_BE:
|
||||
|
@ -335,10 +335,12 @@ int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_condition_is_combining(const rc_condition_t* self);
|
||||
void rc_condition_convert_to_operand(const rc_condition_t* condition, rc_operand_t* operand, rc_parse_state_t* parse);
|
||||
const rc_operand_t* rc_condition_get_real_operand1(const rc_condition_t* self);
|
||||
|
||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
void rc_evaluate_operand(rc_typed_value_t* value, const rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||
int rc_operator_is_modifying(int oper);
|
||||
int rc_memsize_is_float(uint8_t size);
|
||||
int rc_operand_is_float_memref(const rc_operand_t* self);
|
||||
int rc_operand_is_float(const rc_operand_t* self);
|
||||
int rc_operand_is_recall(const rc_operand_t* self);
|
||||
|
@ -120,7 +120,7 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="__rc_operand_memref_t">
|
||||
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">... {*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x}</DisplayString>
|
||||
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">... {*((__rc_memsize_enum_func_t*)&operand.size)} {((rc_modified_memref_t*)operand.value.memref)->parent,na}</DisplayString>
|
||||
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_VALUE">value {((rc_value_t*)operand.value.memref)->name,s}</DisplayString>
|
||||
<DisplayString>{*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x}</DisplayString>
|
||||
<Expand>
|
||||
|
@ -199,8 +199,10 @@ static uint32_t rc_max_chain_value(const rc_operand_t* operand)
|
||||
{
|
||||
if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
|
||||
const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref;
|
||||
const uint32_t op_max = rc_max_chain_value(&modified_memref->parent);
|
||||
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier);
|
||||
if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) {
|
||||
const uint32_t op_max = rc_max_chain_value(&modified_memref->parent);
|
||||
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier);
|
||||
}
|
||||
}
|
||||
|
||||
return rc_max_value(operand);
|
||||
@ -304,11 +306,13 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
|
||||
}
|
||||
|
||||
for (cond = condset->conditions; cond; cond = cond->next, ++index) {
|
||||
const int is_memref1 = rc_operand_is_memref(&cond->operand1);
|
||||
/* validate the original operands first */
|
||||
const rc_operand_t* operand1 = rc_condition_get_real_operand1(cond);
|
||||
int is_memref1 = rc_operand_is_memref(operand1);
|
||||
const int is_memref2 = rc_operand_is_memref(&cond->operand2);
|
||||
|
||||
if (!in_add_address) {
|
||||
if (is_memref1 && !rc_validate_memref(cond->operand1.value.memref, buffer, sizeof(buffer), console_id, max_address)) {
|
||||
if (is_memref1 && !rc_validate_memref(operand1->value.memref, buffer, sizeof(buffer), console_id, max_address)) {
|
||||
snprintf(result, result_size, "Condition %d: %s", index, buffer);
|
||||
return 0;
|
||||
}
|
||||
@ -321,8 +325,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
|
||||
in_add_address = 0;
|
||||
}
|
||||
|
||||
if (rc_operand_is_recall(&cond->operand1)) {
|
||||
if (rc_operand_type_is_memref(cond->operand1.memref_access_type) && !cond->operand1.value.memref) {
|
||||
if (rc_operand_is_recall(operand1)) {
|
||||
if (rc_operand_type_is_memref(operand1->memref_access_type) && !operand1->value.memref) {
|
||||
snprintf(result, result_size, "Condition %d: Recall used before Remember", index);
|
||||
return 0;
|
||||
}
|
||||
@ -343,10 +347,18 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
|
||||
continue;
|
||||
|
||||
case RC_CONDITION_ADD_ADDRESS:
|
||||
if (cond->operand1.type == RC_OPERAND_DELTA || cond->operand1.type == RC_OPERAND_PRIOR) {
|
||||
if (operand1->type == RC_OPERAND_DELTA || operand1->type == RC_OPERAND_PRIOR) {
|
||||
snprintf(result, result_size, "Condition %d: Using pointer from previous frame", index);
|
||||
return 0;
|
||||
}
|
||||
if (rc_operand_is_float(&cond->operand1) || rc_operand_is_float(&cond->operand2)) {
|
||||
snprintf(result, result_size, "Condition %d: Using non-integer value in AddAddress calcuation", index);
|
||||
return 0;
|
||||
}
|
||||
if (rc_operand_type_is_transform(cond->operand1.type)) {
|
||||
snprintf(result, result_size, "Condition %d: Using transformed value in AddAddress calcuation", index);
|
||||
return 0;
|
||||
}
|
||||
in_add_address = 1;
|
||||
is_combining = 1;
|
||||
continue;
|
||||
@ -390,22 +402,26 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
|
||||
break;
|
||||
}
|
||||
|
||||
/* original operands are valid. now switch to the derived operands for logic
|
||||
* combining/comparing them */
|
||||
operand1 = &cond->operand1;
|
||||
is_memref1 = rc_operand_is_memref(operand1);
|
||||
|
||||
/* check for comparing two differently sized memrefs */
|
||||
if (is_memref1 && is_memref2 &&
|
||||
cond->operand1.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF &&
|
||||
operand1->value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF &&
|
||||
cond->operand2.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF &&
|
||||
rc_max_value(&cond->operand1) != rc_max_value(&cond->operand2)) {
|
||||
rc_max_value(operand1) != rc_max_value(&cond->operand2)) {
|
||||
snprintf(result, result_size, "Condition %d: Comparing different memory sizes", index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_memref1 && rc_operand_is_float(&cond->operand1)) {
|
||||
if (is_memref1 && rc_operand_is_float(operand1)) {
|
||||
/* if left side is a float, right side will be converted to a float, so don't do range validation */
|
||||
}
|
||||
else if (is_memref1 || is_memref2) {
|
||||
/* if either side is a memref, check for impossible comparisons */
|
||||
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index);
|
||||
const rc_operand_t* operand1 = &cond->operand1;
|
||||
const rc_operand_t* operand2 = &cond->operand2;
|
||||
uint8_t oper = cond->oper;
|
||||
uint32_t max = rc_max_chain_value(operand1);
|
||||
@ -414,8 +430,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
|
||||
|
||||
if (!is_memref1) {
|
||||
/* pretend constant was on right side */
|
||||
operand2 = operand1;
|
||||
operand1 = &cond->operand2;
|
||||
operand2 = &cond->operand1;
|
||||
max_val = max;
|
||||
max = rc_max_value(&cond->operand2);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user