(cheevos) upgrade to rcheevos 11.1 (#16151)

* move pause spam management into rc_client

* update to rcheevos 11.1

* show [m] on missables

* update makefile
This commit is contained in:
Jamiras 2024-01-21 19:12:30 -07:00 committed by GitHub
parent e3329dbbab
commit 4ba5fc0333
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 3785 additions and 585 deletions

View file

@ -2148,6 +2148,7 @@ ifeq ($(HAVE_NETWORKING), 1)
deps/rcheevos/src/rcheevos/runtime_progress.o \
deps/rcheevos/src/rcheevos/trigger.o \
deps/rcheevos/src/rcheevos/value.o \
deps/rcheevos/src/rhash/aes.o \
deps/rcheevos/src/rhash/cdreader.o \
deps/rcheevos/src/rhash/hash.o \
deps/rcheevos/src/rapi/rc_api_common.o \

View file

@ -115,6 +115,7 @@ static rcheevos_locals_t rcheevos_locals =
0.0, /* tracker_progress */
#endif
{RCHEEVOS_LOAD_STATE_NONE, 0, 0 }, /* load_info */
0, /* unpaused_frames */
false,/* hardcore_active */
false,/* loaded */
#ifdef HAVE_GFX_WIDGETS
@ -370,6 +371,15 @@ void rcheevos_spectating_changed(void)
#endif
}
bool rcheevos_is_pause_allowed(void)
{
#ifdef HAVE_RC_CLIENT
return rc_client_can_pause(rcheevos_locals.client, NULL);
#else
return (rcheevos_locals.unpaused_frames == 0);
#endif
}
#ifdef HAVE_RC_CLIENT
static void rcheevos_show_mastery_placard(void)
@ -1964,6 +1974,11 @@ void rcheevos_test(void)
rcheevos_locals.tracker_progress = 0.0;
}
#endif
/* We processed a frame - if there's a pause delay in effect, process it */
if (rcheevos_locals.unpaused_frames > 0)
rcheevos_locals.unpaused_frames--;
#endif /* HAVE_RC_CLIENT */
}

View file

@ -45,6 +45,7 @@ void rcheevos_hardcore_enabled_changed(void);
void rcheevos_toggle_hardcore_paused(void);
bool rcheevos_hardcore_active(void);
bool rcheevos_is_pause_allowed(void);
void rcheevos_spectating_changed(void);
void rcheevos_validate_config_settings(void);

View file

@ -230,6 +230,8 @@ typedef struct rcheevos_locals_t
rcheevos_load_info_t load_info; /* load info */
uint32_t unpaused_frames; /* number of unpaused frames before next pause is allowed */
bool hardcore_active; /* hardcore functionality is active */
bool loaded; /* load task has completed */
#ifdef HAVE_GFX_WIDGETS

View file

@ -52,14 +52,20 @@ bool rcheevos_menu_get_state(unsigned menu_offset, char* buffer, size_t buffer_s
const rc_client_achievement_t* cheevo = menuitem->achievement;
if (cheevo)
{
if (cheevo->measured_progress[0])
if (cheevo->state != RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE)
{
snprintf(buffer, buffer_size, "%s - %s",
msg_hash_to_str(menuitem->state_label_idx), cheevo->measured_progress);
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), buffer_size);
}
else
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), buffer_size);
{
const char* missable = cheevo->type == RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE ? "[m] " : "";
if (cheevo->measured_progress[0])
snprintf(buffer, buffer_size, "%s%s - %s", missable,
msg_hash_to_str(menuitem->state_label_idx), cheevo->measured_progress);
else
snprintf(buffer, buffer_size, "%s%s", missable,
msg_hash_to_str(menuitem->state_label_idx));
}
return true;
}
}
@ -405,10 +411,12 @@ void rcheevos_menu_populate(void* data)
do
{
if (menuitem->achievement)
{
menu_entries_append(info->list, menuitem->achievement->title,
menuitem->achievement->description,
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0, NULL);
}
else
{
if (menuitem->subset_id)

View file

@ -1,3 +1,26 @@
# v11.1.0
* add rc_client_get_user_agent_clause to generate substring to include in client User-Agents
* add rc_client_can_pause function to control pause spam
* add achievement type and rarity to rc_api_fetch_game_data_response_t and rc_client_achievement_t
* add RC_CLIENT_ACHIEVEMENT_BUCKET_UNSYNCED for achievements that have been unlocked locally but not synced to the server
* add RC_CONSOLE_NEO_GEO_CD to supported consoles for chd file extension
* add hash logic for RC_CONSOLE_NINTENDO_3DS (note: added new file rhash/aes.c to support this)
* add hash logic for RC_CONSOLE_MS_DOS
* add game_hash and hardcore fields to rc_api_start_session_request_t and rc_api_ping_request_t
* add RC_FORMAT_FIXED1/2/3, RC_FORMAT_TENS, RC_FORMAT_HUNDREDS, RC_FORMAT_THOUSANDS, and RC_FORMAT_UNSIGNED_VALUE
* add RC_CONSOLE_STANDALONE
* add extern "C" and __cdecl attributes to public functions
* add __declspec(dllexport/dllimport) attributes to public functions via #define enablement
* add rc_version and rc_version_string functions for accessing version from external linkage
* add unicode path support to default filereader (Windows builds)
* add rc_mutex support for GEKKO (libogc)
* fix async_handle being returned when rc_client_begin_login is aborted synchronously
* fix logic error hashing CD files smaller than one sector
* fix read across region boundary in rc_libretro_memory_read
* fix RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW event not being raised if achievement is reset in the same frame that it's primed
* moved rc_util.h from src/ to include/
* initial (incomplete) support for rc_client_external_t and rc_client_raintegration_t
# v11.0.0
* add rc_client_t and related functions
* add RC_MEMSIZE_FLOAT_BE

View file

@ -2,13 +2,11 @@
#define RC_API_REQUEST_H
#include "rc_error.h"
#include "../src/rc_util.h"
#include "rc_util.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
/**
* A constructed request to send to the retroachievements server.
@ -42,10 +40,10 @@ typedef struct rc_api_response_t {
}
rc_api_response_t;
void rc_api_destroy_request(rc_api_request_t* request);
RC_EXPORT void RC_CCONV rc_api_destroy_request(rc_api_request_t* request);
void rc_api_set_host(const char* hostname);
void rc_api_set_image_host(const char* hostname);
RC_EXPORT void RC_CCONV rc_api_set_host(const char* hostname);
RC_EXPORT void RC_CCONV rc_api_set_image_host(const char* hostname);
typedef struct rc_api_server_response_t {
/* Pointer to the data returned from the server */
@ -61,8 +59,6 @@ enum {
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR = -2
};
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_API_REQUEST_H */

View file

@ -6,9 +6,7 @@
#include <stdint.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
/* --- Fetch Image --- */
@ -29,7 +27,7 @@ rc_api_fetch_image_request_t;
#define RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED 3
#define RC_IMAGE_TYPE_USER 4
int rc_api_init_fetch_image_request(rc_api_request_t* request, const rc_api_fetch_image_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_init_fetch_image_request(rc_api_request_t* request, const rc_api_fetch_image_request_t* api_params);
/* --- Resolve Hash --- */
@ -58,10 +56,10 @@ typedef struct rc_api_resolve_hash_response_t {
}
rc_api_resolve_hash_response_t;
int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_resolve_hash_request_t* api_params);
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response);
int rc_api_process_resolve_hash_server_response(rc_api_resolve_hash_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_resolve_hash_response(rc_api_resolve_hash_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_resolve_hash_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_resolve_hash_server_response(rc_api_resolve_hash_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_resolve_hash_response(rc_api_resolve_hash_response_t* response);
/* --- Fetch Game Data --- */
@ -119,12 +117,23 @@ typedef struct rc_api_achievement_definition_t {
time_t created;
/* When the achievement was last modified on the server */
time_t updated;
/* The achievement type (win/progression/missable) */
uint32_t type;
/* The approximate rarity of the achievement (X% of users have earned the achievement) */
float rarity;
/* The approximate rarity of the achievement in hardcore (X% of users have earned the achievement in hardcore) */
float rarity_hardcore;
}
rc_api_achievement_definition_t;
#define RC_ACHIEVEMENT_CATEGORY_CORE 3
#define RC_ACHIEVEMENT_CATEGORY_UNOFFICIAL 5
#define RC_ACHIEVEMENT_TYPE_STANDARD 0
#define RC_ACHIEVEMENT_TYPE_MISSABLE 1
#define RC_ACHIEVEMENT_TYPE_PROGRESSION 2
#define RC_ACHIEVEMENT_TYPE_WIN 3
/**
* Response data for a fetch game data request.
*/
@ -155,10 +164,10 @@ typedef struct rc_api_fetch_game_data_response_t {
}
rc_api_fetch_game_data_response_t;
int rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_fetch_game_data_request_t* api_params);
int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response);
int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_fetch_game_data_response(rc_api_fetch_game_data_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_fetch_game_data_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_fetch_game_data_response(rc_api_fetch_game_data_response_t* response);
/* --- Ping --- */
@ -174,6 +183,10 @@ typedef struct rc_api_ping_request_t {
uint32_t game_id;
/* (optional) The current rich presence evaluation for the user */
const char* rich_presence;
/* (recommended) The hash associated to the game being played */
const char* game_hash;
/* Non-zero if hardcore is currently enabled (ignored if game_hash is null) */
uint32_t hardcore;
}
rc_api_ping_request_t;
@ -186,10 +199,10 @@ typedef struct rc_api_ping_response_t {
}
rc_api_ping_response_t;
int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_request_t* api_params);
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response);
int rc_api_process_ping_server_response(rc_api_ping_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_ping_response(rc_api_ping_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_ping_server_response(rc_api_ping_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_ping_response(rc_api_ping_response_t* response);
/* --- Award Achievement --- */
@ -229,10 +242,10 @@ typedef struct rc_api_award_achievement_response_t {
}
rc_api_award_achievement_response_t;
int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_api_award_achievement_request_t* api_params);
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response);
int rc_api_process_award_achievement_server_response(rc_api_award_achievement_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_award_achievement_response(rc_api_award_achievement_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_api_award_achievement_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_award_achievement_server_response(rc_api_award_achievement_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_award_achievement_response(rc_api_award_achievement_response_t* response);
/* --- Submit Leaderboard Entry --- */
@ -287,13 +300,11 @@ typedef struct rc_api_submit_lboard_entry_response_t {
}
rc_api_submit_lboard_entry_response_t;
int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_api_submit_lboard_entry_request_t* api_params);
int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response);
int rc_api_process_submit_lboard_entry_server_response(rc_api_submit_lboard_entry_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_api_submit_lboard_entry_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_submit_lboard_entry_server_response(rc_api_submit_lboard_entry_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_API_RUNTIME_H */

View file

@ -6,9 +6,7 @@
#include <stdint.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
/* --- Login --- */
@ -48,10 +46,10 @@ typedef struct rc_api_login_response_t {
}
rc_api_login_response_t;
int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_request_t* api_params);
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response);
int rc_api_process_login_server_response(rc_api_login_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_login_response(rc_api_login_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_login_server_response(rc_api_login_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_login_response(rc_api_login_response_t* response);
/* --- Start Session --- */
@ -65,6 +63,10 @@ typedef struct rc_api_start_session_request_t {
const char* api_token;
/* The unique identifier of the game */
uint32_t game_id;
/* (recommended) The hash associated to the game being played */
const char* game_hash;
/* Non-zero if hardcore is currently enabled (ignored if game_hash is null) */
uint32_t hardcore;
}
rc_api_start_session_request_t;
@ -101,10 +103,10 @@ typedef struct rc_api_start_session_response_t {
}
rc_api_start_session_response_t;
int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_start_session_request_t* api_params);
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response);
int rc_api_process_start_session_server_response(rc_api_start_session_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_start_session_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_start_session_server_response(rc_api_start_session_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_start_session_response(rc_api_start_session_response_t* response);
/* --- Fetch User Unlocks --- */
@ -137,13 +139,11 @@ typedef struct rc_api_fetch_user_unlocks_response_t {
}
rc_api_fetch_user_unlocks_response_t;
int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_api_fetch_user_unlocks_request_t* api_params);
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response);
int rc_api_process_fetch_user_unlocks_server_response(rc_api_fetch_user_unlocks_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response);
RC_EXPORT int RC_CCONV rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_api_fetch_user_unlocks_request_t* api_params);
RC_EXPORT int RC_CCONV rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response);
RC_EXPORT int RC_CCONV rc_api_process_fetch_user_unlocks_server_response(rc_api_fetch_user_unlocks_response_t* response, const rc_api_server_response_t* server_response);
RC_EXPORT void RC_CCONV rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_API_H */

View file

@ -1,10 +1,6 @@
#ifndef RC_CLIENT_H
#define RC_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_api_request.h"
#include "rc_error.h"
@ -12,6 +8,8 @@ extern "C" {
#include <stdint.h>
#include <time.h>
RC_BEGIN_C_DECLS
/* implementation abstracted in rc_client_internal.h */
typedef struct rc_client_t rc_client_t;
typedef struct rc_client_async_handle_t rc_client_async_handle_t;
@ -24,27 +22,27 @@ typedef struct rc_client_async_handle_t rc_client_async_handle_t;
* Callback used to read num_bytes bytes from memory starting at address into buffer.
* Returns the number of bytes read. A return value of 0 indicates the address was invalid.
*/
typedef uint32_t (*rc_client_read_memory_func_t)(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client);
typedef uint32_t (RC_CCONV *rc_client_read_memory_func_t)(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client);
/**
* Internal method passed to rc_client_server_call_t to process the server response.
*/
typedef void (*rc_client_server_callback_t)(const rc_api_server_response_t* server_response, void* callback_data);
typedef void (RC_CCONV *rc_client_server_callback_t)(const rc_api_server_response_t* server_response, void* callback_data);
/**
* Callback used to issue a request to the server.
*/
typedef void (*rc_client_server_call_t)(const rc_api_request_t* request, rc_client_server_callback_t callback, void* callback_data, rc_client_t* client);
typedef void (RC_CCONV *rc_client_server_call_t)(const rc_api_request_t* request, rc_client_server_callback_t callback, void* callback_data, rc_client_t* client);
/**
* Generic callback for asynchronous eventing.
*/
typedef void (*rc_client_callback_t)(int result, const char* error_message, rc_client_t* client, void* userdata);
typedef void (RC_CCONV *rc_client_callback_t)(int result, const char* error_message, rc_client_t* client, void* userdata);
/**
* Callback for logging or displaying a message.
*/
typedef void (*rc_client_message_callback_t)(const char* message, const rc_client_t* client);
typedef void (RC_CCONV *rc_client_message_callback_t)(const char* message, const rc_client_t* client);
/*****************************************************************************\
| Runtime |
@ -53,13 +51,13 @@ typedef void (*rc_client_message_callback_t)(const char* message, const rc_clien
/**
* Creates a new rc_client_t object.
*/
rc_client_t* rc_client_create(rc_client_read_memory_func_t read_memory_function, rc_client_server_call_t server_call_function);
RC_EXPORT rc_client_t* RC_CCONV rc_client_create(rc_client_read_memory_func_t read_memory_function, rc_client_server_call_t server_call_function);
/**
* Releases resources associated to a rc_client_t object.
* Pointer will no longer be valid after making this call.
*/
void rc_client_destroy(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_destroy(rc_client_t* client);
/**
* Sets whether hardcore is enabled (on by default).
@ -67,34 +65,34 @@ void rc_client_destroy(rc_client_t* client);
* Enabling hardcore with a game loaded will raise an RC_CLIENT_EVENT_RESET
* event. Processing will be disabled until rc_client_reset is called.
*/
void rc_client_set_hardcore_enabled(rc_client_t* client, int enabled);
RC_EXPORT void RC_CCONV rc_client_set_hardcore_enabled(rc_client_t* client, int enabled);
/**
* Gets whether hardcore is enabled (on by default).
*/
int rc_client_get_hardcore_enabled(const rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_get_hardcore_enabled(const rc_client_t* client);
/**
* Sets whether encore mode is enabled (off by default).
* Evaluated when loading a game. Has no effect while a game is loaded.
*/
void rc_client_set_encore_mode_enabled(rc_client_t* client, int enabled);
RC_EXPORT void RC_CCONV rc_client_set_encore_mode_enabled(rc_client_t* client, int enabled);
/**
* Gets whether encore mode is enabled (off by default).
*/
int rc_client_get_encore_mode_enabled(const rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_get_encore_mode_enabled(const rc_client_t* client);
/**
* Sets whether unofficial achievements should be loaded.
* Evaluated when loading a game. Has no effect while a game is loaded.
*/
void rc_client_set_unofficial_enabled(rc_client_t* client, int enabled);
RC_EXPORT void RC_CCONV rc_client_set_unofficial_enabled(rc_client_t* client, int enabled);
/**
* Gets whether unofficial achievements should be loaded.
*/
int rc_client_get_unofficial_enabled(const rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_get_unofficial_enabled(const rc_client_t* client);
/**
* Sets whether spectator mode is enabled (off by default).
@ -103,40 +101,45 @@ int rc_client_get_unofficial_enabled(const rc_client_t* client);
* Can be modified while a game is loaded. Evaluated at unlock/submit time.
* Cannot be modified if disabled before a game is loaded.
*/
void rc_client_set_spectator_mode_enabled(rc_client_t* client, int enabled);
RC_EXPORT void RC_CCONV rc_client_set_spectator_mode_enabled(rc_client_t* client, int enabled);
/**
* Gets whether spectator mode is enabled (off by default).
*/
int rc_client_get_spectator_mode_enabled(const rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_get_spectator_mode_enabled(const rc_client_t* client);
/**
* Attaches client-specific data to the runtime.
*/
void rc_client_set_userdata(rc_client_t* client, void* userdata);
RC_EXPORT void RC_CCONV rc_client_set_userdata(rc_client_t* client, void* userdata);
/**
* Gets the client-specific data attached to the runtime.
*/
void* rc_client_get_userdata(const rc_client_t* client);
RC_EXPORT void* RC_CCONV rc_client_get_userdata(const rc_client_t* client);
/**
* Sets the name of the server to use.
*/
void rc_client_set_host(const rc_client_t* client, const char* hostname);
RC_EXPORT void RC_CCONV rc_client_set_host(const rc_client_t* client, const char* hostname);
typedef uint64_t rc_clock_t;
typedef rc_clock_t (*rc_get_time_millisecs_func_t)(const rc_client_t* client);
typedef rc_clock_t (RC_CCONV *rc_get_time_millisecs_func_t)(const rc_client_t* client);
/**
* Specifies a function that returns a value that increases once per millisecond.
*/
void rc_client_set_get_time_millisecs_function(rc_client_t* client, rc_get_time_millisecs_func_t handler);
RC_EXPORT void RC_CCONV rc_client_set_get_time_millisecs_function(rc_client_t* client, rc_get_time_millisecs_func_t handler);
/**
* Marks an async process as aborted. The associated callback will not be called.
*/
void rc_client_abort_async(rc_client_t* client, rc_client_async_handle_t* async_handle);
RC_EXPORT void RC_CCONV rc_client_abort_async(rc_client_t* client, rc_client_async_handle_t* async_handle);
/**
* Gets a clause that can be added to the User-Agent to identify the version of rcheevos being used.
*/
RC_EXPORT size_t RC_CCONV rc_client_get_user_agent_clause(rc_client_t* client, char buffer[], size_t buffer_size);
/*****************************************************************************\
| Logging |
@ -145,7 +148,7 @@ void rc_client_abort_async(rc_client_t* client, rc_client_async_handle_t* async_
/**
* Sets the logging level and provides a callback to be called to do the logging.
*/
void rc_client_enable_logging(rc_client_t* client, int level, rc_client_message_callback_t callback);
RC_EXPORT void RC_CCONV rc_client_enable_logging(rc_client_t* client, int level, rc_client_message_callback_t callback);
enum {
RC_CLIENT_LOG_LEVEL_NONE = 0,
RC_CLIENT_LOG_LEVEL_ERROR = 1,
@ -162,21 +165,21 @@ enum {
/**
* Attempt to login a user.
*/
rc_client_async_handle_t* rc_client_begin_login_with_password(rc_client_t* client,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_login_with_password(rc_client_t* client,
const char* username, const char* password,
rc_client_callback_t callback, void* callback_userdata);
/**
* Attempt to login a user.
*/
rc_client_async_handle_t* rc_client_begin_login_with_token(rc_client_t* client,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_login_with_token(rc_client_t* client,
const char* username, const char* token,
rc_client_callback_t callback, void* callback_userdata);
/**
* Logout the user.
*/
void rc_client_logout(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_logout(rc_client_t* client);
typedef struct rc_client_user_t {
const char* display_name;
@ -190,16 +193,15 @@ typedef struct rc_client_user_t {
/**
* Gets information about the logged in user. Will return NULL if the user is not logged in.
*/
const rc_client_user_t* rc_client_get_user_info(const rc_client_t* client);
RC_EXPORT const rc_client_user_t* RC_CCONV rc_client_get_user_info(const rc_client_t* client);
/**
* Gets the URL for the user's profile picture.
* Returns RC_OK on success.
*/
int rc_client_user_get_image_url(const rc_client_user_t* user, char buffer[], size_t buffer_size);
RC_EXPORT int RC_CCONV rc_client_user_get_image_url(const rc_client_user_t* user, char buffer[], size_t buffer_size);
typedef struct rc_client_user_game_summary_t
{
typedef struct rc_client_user_game_summary_t {
uint32_t num_core_achievements;
uint32_t num_unofficial_achievements;
uint32_t num_unlocked_achievements;
@ -213,7 +215,7 @@ typedef struct rc_client_user_game_summary_t
* Gets a breakdown of the number of achievements in the game, and how many the user has unlocked.
* Used for the "You have unlocked X of Y achievements" message shown when the game starts.
*/
void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_game_summary_t* summary);
RC_EXPORT void RC_CCONV rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_game_summary_t* summary);
/*****************************************************************************\
| Game |
@ -222,7 +224,7 @@ void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_g
/**
* Start loading an unidentified game.
*/
rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* client,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_identify_and_load_game(rc_client_t* client,
uint32_t console_id, const char* file_path,
const uint8_t* data, size_t data_size,
rc_client_callback_t callback, void* callback_userdata);
@ -230,13 +232,13 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl
/**
* Start loading a game.
*/
rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const char* hash,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_load_game(rc_client_t* client, const char* hash,
rc_client_callback_t callback, void* callback_userdata);
/**
* Unloads the current game.
*/
void rc_client_unload_game(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_unload_game(rc_client_t* client);
typedef struct rc_client_game_t {
uint32_t id;
@ -249,18 +251,18 @@ typedef struct rc_client_game_t {
/**
* Get information about the current game. Returns NULL if no game is loaded.
*/
const rc_client_game_t* rc_client_get_game_info(const rc_client_t* client);
RC_EXPORT const rc_client_game_t* RC_CCONV rc_client_get_game_info(const rc_client_t* client);
/**
* Gets the URL for the game image.
* Returns RC_OK on success.
*/
int rc_client_game_get_image_url(const rc_client_game_t* game, char buffer[], size_t buffer_size);
RC_EXPORT int RC_CCONV rc_client_game_get_image_url(const rc_client_game_t* game, char buffer[], size_t buffer_size);
/**
* Changes the active disc in a multi-disc game.
*/
rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, const char* file_path,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_change_media(rc_client_t* client, const char* file_path,
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
/*****************************************************************************\
@ -276,7 +278,7 @@ typedef struct rc_client_subset_t {
uint32_t num_leaderboards;
} rc_client_subset_t;
const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_t subset_id);
RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client_t* client, uint32_t subset_id);
/*****************************************************************************\
| Achievements |
@ -297,6 +299,13 @@ enum {
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE | RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL
};
enum {
RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD = 0,
RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE = 1,
RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION = 2,
RC_CLIENT_ACHIEVEMENT_TYPE_WIN = 3
};
enum {
RC_CLIENT_ACHIEVEMENT_BUCKET_UNKNOWN = 0,
RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED = 1,
@ -306,7 +315,8 @@ enum {
RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED = 5,
RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE = 6,
RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE = 7,
NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS = 8
RC_CLIENT_ACHIEVEMENT_BUCKET_UNSYNCED = 8,
NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS = 9
};
enum {
@ -329,18 +339,22 @@ typedef struct rc_client_achievement_t {
uint8_t category;
uint8_t bucket;
uint8_t unlocked;
/* minimum version: 11.1 */
float rarity;
float rarity_hardcore;
uint8_t type;
} rc_client_achievement_t;
/**
* Get information about an achievement. Returns NULL if not found.
*/
const rc_client_achievement_t* rc_client_get_achievement_info(rc_client_t* client, uint32_t id);
RC_EXPORT const rc_client_achievement_t* RC_CCONV rc_client_get_achievement_info(rc_client_t* client, uint32_t id);
/**
* Gets the URL for the achievement image.
* Returns RC_OK on success.
*/
int rc_client_achievement_get_image_url(const rc_client_achievement_t* achievement, int state, char buffer[], size_t buffer_size);
RC_EXPORT int RC_CCONV rc_client_achievement_get_image_url(const rc_client_achievement_t* achievement, int state, char buffer[], size_t buffer_size);
typedef struct rc_client_achievement_bucket_t {
rc_client_achievement_t** achievements;
@ -365,17 +379,17 @@ enum {
* Creates a list of achievements matching the specified category and grouping.
* Returns an allocated list that must be free'd by calling rc_client_destroy_achievement_list.
*/
rc_client_achievement_list_t* rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
/**
* Destroys a list allocated by rc_client_get_achievement_list.
*/
void rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
/**
* Returns non-zero if there are any achievements that can be queried through rc_client_create_achievement_list().
*/
int rc_client_has_achievements(rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_has_achievements(rc_client_t* client);
/*****************************************************************************\
| Leaderboards |
@ -411,7 +425,7 @@ typedef struct rc_client_leaderboard_t {
/**
* Get information about a leaderboard. Returns NULL if not found.
*/
const rc_client_leaderboard_t* rc_client_get_leaderboard_info(const rc_client_t* client, uint32_t id);
RC_EXPORT const rc_client_leaderboard_t* RC_CCONV rc_client_get_leaderboard_info(const rc_client_t* client, uint32_t id);
typedef struct rc_client_leaderboard_tracker_t {
char display[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
@ -450,17 +464,17 @@ enum {
* Creates a list of leaderboards matching the specified grouping.
* Returns an allocated list that must be free'd by calling rc_client_destroy_leaderboard_list.
*/
rc_client_leaderboard_list_t* rc_client_create_leaderboard_list(rc_client_t* client, int grouping);
RC_EXPORT rc_client_leaderboard_list_t* RC_CCONV rc_client_create_leaderboard_list(rc_client_t* client, int grouping);
/**
* Destroys a list allocated by rc_client_get_leaderboard_list.
*/
void rc_client_destroy_leaderboard_list(rc_client_leaderboard_list_t* list);
RC_EXPORT void RC_CCONV rc_client_destroy_leaderboard_list(rc_client_leaderboard_list_t* list);
/**
* Returns non-zero if the current game has any leaderboards.
*/
int rc_client_has_leaderboards(rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_has_leaderboards(rc_client_t* client);
typedef struct rc_client_leaderboard_entry_t {
const char* user;
@ -476,33 +490,33 @@ typedef struct rc_client_leaderboard_entry_list_t {
int32_t user_index;
} rc_client_leaderboard_entry_list_t;
typedef void (*rc_client_fetch_leaderboard_entries_callback_t)(int result, const char* error_message,
typedef void (RC_CCONV *rc_client_fetch_leaderboard_entries_callback_t)(int result, const char* error_message,
rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* callback_userdata);
/**
* Fetches a list of leaderboard entries from the server.
* Callback receives an allocated list that must be free'd by calling rc_client_destroy_leaderboard_entry_list.
*/
rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries(rc_client_t* client, uint32_t leaderboard_id,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_leaderboard_entries(rc_client_t* client, uint32_t leaderboard_id,
uint32_t first_entry, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
/**
* Fetches a list of leaderboard entries from the server containing the logged-in user.
* Callback receives an allocated list that must be free'd by calling rc_client_destroy_leaderboard_entry_list.
*/
rc_client_async_handle_t* rc_client_begin_fetch_leaderboard_entries_around_user(rc_client_t* client, uint32_t leaderboard_id,
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_leaderboard_entries_around_user(rc_client_t* client, uint32_t leaderboard_id,
uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
/**
* Gets the URL for the profile picture of the user associated to a leaderboard entry.
* Returns RC_OK on success.
*/
int rc_client_leaderboard_entry_get_user_image_url(const rc_client_leaderboard_entry_t* entry, char buffer[], size_t buffer_size);
RC_EXPORT int RC_CCONV rc_client_leaderboard_entry_get_user_image_url(const rc_client_leaderboard_entry_t* entry, char buffer[], size_t buffer_size);
/**
* Destroys a list allocated by rc_client_begin_fetch_leaderboard_entries or rc_client_begin_fetch_leaderboard_entries_around_user.
*/
void rc_client_destroy_leaderboard_entry_list(rc_client_leaderboard_entry_list_t* list);
RC_EXPORT void RC_CCONV rc_client_destroy_leaderboard_entry_list(rc_client_leaderboard_entry_list_t* list);
/**
* Used for scoreboard events. Contains the response from the server when a leaderboard entry is submitted.
@ -543,13 +557,13 @@ typedef struct rc_client_leaderboard_scoreboard_t {
/**
* Returns non-zero if the current game supports rich presence.
*/
int rc_client_has_rich_presence(rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_has_rich_presence(rc_client_t* client);
/**
* Gets the current rich presence message.
* Returns the number of characters written to buffer.
*/
size_t rc_client_get_rich_presence_message(rc_client_t* client, char buffer[], size_t buffer_size);
RC_EXPORT size_t RC_CCONV rc_client_get_rich_presence_message(rc_client_t* client, char buffer[], size_t buffer_size);
/*****************************************************************************\
| Processing |
@ -577,16 +591,14 @@ enum {
RC_CLIENT_EVENT_RECONNECTED = 18 /* all pending unlocks have been completed */
};
typedef struct rc_client_server_error_t
{
typedef struct rc_client_server_error_t {
const char* error_message;
const char* api;
int result;
uint32_t related_id;
} rc_client_server_error_t;
typedef struct rc_client_event_t
{
typedef struct rc_client_event_t {
uint32_t type;
rc_client_achievement_t* achievement;
@ -600,60 +612,66 @@ typedef struct rc_client_event_t
/**
* Callback used to notify the client when certain events occur.
*/
typedef void (*rc_client_event_handler_t)(const rc_client_event_t* event, rc_client_t* client);
typedef void (RC_CCONV *rc_client_event_handler_t)(const rc_client_event_t* event, rc_client_t* client);
/**
* Provides a callback for event handling.
*/
void rc_client_set_event_handler(rc_client_t* client, rc_client_event_handler_t handler);
RC_EXPORT void RC_CCONV rc_client_set_event_handler(rc_client_t* client, rc_client_event_handler_t handler);
/**
* Provides a callback for reading memory.
*/
void rc_client_set_read_memory_function(rc_client_t* client, rc_client_read_memory_func_t handler);
RC_EXPORT void RC_CCONV rc_client_set_read_memory_function(rc_client_t* client, rc_client_read_memory_func_t handler);
/**
* Determines if there are any active achievements/leaderboards/rich presence that need processing.
*/
int rc_client_is_processing_required(rc_client_t* client);
RC_EXPORT int RC_CCONV rc_client_is_processing_required(rc_client_t* client);
/**
* Processes achievements for the current frame.
*/
void rc_client_do_frame(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_do_frame(rc_client_t* client);
/**
* Processes the periodic queue.
* Called internally by rc_client_do_frame.
* Should be explicitly called if rc_client_do_frame is not being called because emulation is paused.
*/
void rc_client_idle(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_idle(rc_client_t* client);
/**
* Determines if a sufficient amount of frames have been processed since the last call to rc_client_can_pause.
* Should not be called unless the client is trying to pause.
* If false is returned, and frames_remaining is not NULL, frames_remaining will be set to the number of frames
* still required before pause is allowed, which can be converted to a time in seconds for displaying to the user.
*/
RC_EXPORT int RC_CCONV rc_client_can_pause(rc_client_t* client, uint32_t* frames_remaining);
/**
* Informs the runtime that the emulator has been reset. Will reset all achievements and leaderboards
* to their initial state (includes hiding indicators/trackers).
*/
void rc_client_reset(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_reset(rc_client_t* client);
/**
* Gets the number of bytes needed to serialized the runtime state.
*/
size_t rc_client_progress_size(rc_client_t* client);
RC_EXPORT size_t RC_CCONV rc_client_progress_size(rc_client_t* client);
/**
* Serializes the runtime state into a buffer.
* Returns RC_OK on success, or an error indicator.
*/
int rc_client_serialize_progress(rc_client_t* client, uint8_t* buffer);
RC_EXPORT int RC_CCONV rc_client_serialize_progress(rc_client_t* client, uint8_t* buffer);
/**
* Deserializes the runtime state from a buffer.
* Returns RC_OK on success, or an error indicator.
*/
int rc_client_deserialize_progress(rc_client_t* client, const uint8_t* serialized);
RC_EXPORT int RC_CCONV rc_client_deserialize_progress(rc_client_t* client, const uint8_t* serialized);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_RUNTIME_H */

View file

@ -0,0 +1,85 @@
#ifndef RC_CLIENT_RAINTEGRATION_H
#define RC_CLIENT_RAINTEGRATION_H
#ifndef _WIN32
#undef RC_CLIENT_SUPPORTS_RAINTEGRATION /* Windows required for RAIntegration */
#endif
#include <stdint.h>
#include "rc_export.h"
RC_BEGIN_C_DECLS
typedef struct rc_client_t rc_client_t; /* forward reference; in rc_client.h */
/* types needed to implement raintegration */
typedef struct rc_client_raintegration_menu_item_t {
const char* label;
uint32_t id;
uint8_t checked;
uint8_t enabled;
} rc_client_raintegration_menu_item_t;
typedef struct rc_client_raintegration_menu_t {
rc_client_raintegration_menu_item_t* items;
uint32_t num_items;
} rc_client_raintegration_menu_t;
enum {
RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE = 0,
RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED = 1, /* [menu_item] checked changed */
RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED = 2, /* hardcore was enabled or disabled */
RC_CLIENT_RAINTEGRATION_EVENT_PAUSE = 3 /* emulated system should be paused */
};
typedef struct rc_client_raintegration_event_t {
uint32_t type;
const rc_client_raintegration_menu_item_t* menu_item;
} rc_client_raintegration_event_t;
typedef void (RC_CCONV *rc_client_raintegration_event_handler_t)(const rc_client_raintegration_event_t* event,
rc_client_t* client);
typedef void (RC_CCONV *rc_client_raintegration_write_memory_func_t)(uint32_t address, uint8_t* buffer,
uint32_t num_bytes, rc_client_t* client);
/* types needed to integrate raintegration */
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
#ifndef RC_CLIENT_SUPPORTS_EXTERNAL
#define RC_CLIENT_SUPPORTS_EXTERNAL /* external rc_client required for RAIntegration */
#endif
#include <wtypes.h> /* HWND */
#include "rc_client.h"
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_load_raintegration(rc_client_t* client,
const wchar_t* search_directory, HWND main_window_handle,
const char* client_name, const char* client_version,
rc_client_callback_t callback, void* callback_userdata);
RC_EXPORT void RC_CCONV rc_client_unload_raintegration(rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_raintegration_update_main_window_handle(rc_client_t* client, HWND main_window_handle);
RC_EXPORT const rc_client_raintegration_menu_t* RC_CCONV rc_client_raintegration_get_menu(const rc_client_t* client);
RC_EXPORT void RC_CCONV rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu);
RC_EXPORT void RC_CCONV rc_client_raintegration_update_menu_item(const rc_client_t* client, const rc_client_raintegration_menu_item_t* menu_item);
RC_EXPORT int RC_CCONV rc_client_raintegration_activate_menu_item(const rc_client_t* client, uint32_t nMenuItemId);
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_event_handler(rc_client_t* client,
rc_client_raintegration_event_handler_t handler);
#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */
RC_END_C_DECLS
#endif /* RC_CLIENT_RAINTEGRATION_H */

View file

@ -1,12 +1,12 @@
#ifndef RC_CONSOLES_H
#define RC_CONSOLES_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_export.h"
#include <stdint.h>
RC_BEGIN_C_DECLS
/*****************************************************************************\
| Console identifiers |
\*****************************************************************************/
@ -95,10 +95,11 @@ enum {
RC_CONSOLE_UZEBOX = 80,
RC_CONSOLE_HUBS = 100,
RC_CONSOLE_EVENTS = 101
RC_CONSOLE_EVENTS = 101,
RC_CONSOLE_STANDALONE = 102
};
const char* rc_console_name(int console_id);
RC_EXPORT const char* RC_CCONV rc_console_name(uint32_t console_id);
/*****************************************************************************\
| Memory mapping |
@ -129,11 +130,8 @@ typedef struct rc_memory_regions_t {
}
rc_memory_regions_t;
const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id);
RC_EXPORT const rc_memory_regions_t* RC_CCONV rc_console_memory_regions(uint32_t console_id);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_CONSOLES_H */

View file

@ -1,9 +1,9 @@
#ifndef RC_ERROR_H
#define RC_ERROR_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_export.h"
RC_BEGIN_C_DECLS
/*****************************************************************************\
| Return values |
@ -48,10 +48,8 @@ enum {
RC_EXPIRED_TOKEN = -35
};
const char* rc_error_str(int ret);
RC_EXPORT const char* RC_CCONV rc_error_str(int ret);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_ERROR_H */

100
deps/rcheevos/include/rc_export.h vendored Normal file
View file

@ -0,0 +1,100 @@
#ifndef RC_EXPORT_H
#define RC_EXPORT_H
/* These macros control how callbacks and public functions are defined */
/* RC_SHARED should be defined when building rcheevos as a shared library (e.g. dll/dylib/so). External code should not define this macro. */
/* RC_STATIC should be defined when building rcheevos as a static library. External code should also define this macro. */
/* RC_IMPORT should be defined for external code using rcheevos as a shared library. */
/* For compatibility, if none of these three macros are defined, then the build is assumed to be RC_STATIC */
#if !defined(RC_SHARED) && !defined(RC_STATIC) && !defined(RC_IMPORT)
#define RC_STATIC
#endif
#if (defined(RC_SHARED) && defined(RC_STATIC)) || (defined(RC_SHARED) && defined(RC_IMPORT)) || (defined(RC_STATIC) && defined(RC_IMPORT))
#error RC_SHARED, RC_STATIC, and RC_IMPORT are mutually exclusive
#endif
/* RC_BEGIN_C_DECLS and RC_END_C_DECLS should be used for all headers, to enforce C linkage and the C calling convention */
/* RC_BEGIN_C_DECLS should be placed after #include's and before header declarations */
/* RC_END_C_DECLS should be placed after header declarations */
/* example usage
*
* #ifndef RC_HEADER_H
* #define RC_HEADER_H
*
* #include <stdint.h>
*
* RC_BEGIN_C_DECLS
*
* uint8_t rc_function(void);
*
* RC_END_C_DECLS
*
* #endif
*/
#ifdef __cplusplus
#define RC_BEGIN_C_DECLS extern "C" {
#define RC_END_C_DECLS }
#else
#define RC_BEGIN_C_DECLS
#define RC_END_C_DECLS
#endif
/* RC_CCONV should be used for public functions and callbacks, to enforce the cdecl calling convention, if applicable */
/* RC_CCONV should be placed after the return type, and between the ( and * for callbacks */
/* example usage */
/* void RC_CCONV rc_function(void) */
/* void (RC_CCONV *rc_callback)(void) */
#if defined(_WIN32)
/* Windows compilers will ignore __cdecl when not applicable */
#define RC_CCONV __cdecl
#elif defined(__GNUC__) && defined(__i386__)
/* GNU C compilers will warn if cdecl is defined on an unsupported platform */
#define RC_CCONV __attribute__((cdecl))
#else
#define RC_CCONV
#endif
/* RC_EXPORT should be used for public functions */
/* RC_EXPORT will provide necessary hints for shared library usage, if applicable */
/* RC_EXPORT should be placed before the return type */
/* example usage */
/* RC_EXPORT void rc_function(void) */
#ifdef RC_SHARED
#if defined(_WIN32)
#define RC_EXPORT __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#define RC_EXPORT __attribute__((visibility("default")))
#else
#define RC_EXPORT
#endif
#endif
#ifdef RC_IMPORT
#if defined(_WIN32)
#define RC_EXPORT __declspec(dllimport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#define RC_EXPORT __attribute__((visibility("default")))
#else
#define RC_EXPORT
#endif
#endif
#ifdef RC_STATIC
#if defined(__GNUC__) && __GNUC__ >= 4
#define RC_EXPORT __attribute__((visibility("default")))
#else
#define RC_EXPORT
#endif
#endif
#endif /* RC_EXPORT_H */

View file

@ -7,21 +7,19 @@
#include "rc_consoles.h"
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
/* ===================================================== */
/* generates a hash from a block of memory.
* returns non-zero on success, or zero on failure.
*/
int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* buffer, size_t buffer_size);
RC_EXPORT int RC_CCONV rc_hash_generate_from_buffer(char hash[33], uint32_t console_id, const uint8_t* buffer, size_t buffer_size);
/* generates a hash from a file.
* returns non-zero on success, or zero on failure.
*/
int rc_hash_generate_from_file(char hash[33], int console_id, const char* path);
RC_EXPORT int RC_CCONV rc_hash_generate_from_file(char hash[33], uint32_t console_id, const char* path);
/* ===================================================== */
@ -40,44 +38,44 @@ extern "C" {
* - path must be provided
* - if buffer and buffer_size are provided, path may be a filename (i.e. for something extracted from a zip file)
*/
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, const uint8_t* buffer, size_t buffer_size);
RC_EXPORT void RC_CCONV rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, const uint8_t* buffer, size_t buffer_size);
/* releases resources associated to a rc_hash_iterator
*/
void rc_hash_destroy_iterator(struct rc_hash_iterator* iterator);
RC_EXPORT void RC_CCONV rc_hash_destroy_iterator(struct rc_hash_iterator* iterator);
/* generates the next hash for the data in the rc_hash_iterator.
* returns non-zero if a hash was generated, or zero if no more hashes can be generated for the data.
*/
int rc_hash_iterate(char hash[33], struct rc_hash_iterator* iterator);
RC_EXPORT int RC_CCONV rc_hash_iterate(char hash[33], struct rc_hash_iterator* iterator);
/* ===================================================== */
/* specifies a function to call when an error occurs to display the error message */
typedef void (*rc_hash_message_callback)(const char*);
void rc_hash_init_error_message_callback(rc_hash_message_callback callback);
typedef void (RC_CCONV *rc_hash_message_callback)(const char*);
RC_EXPORT void RC_CCONV rc_hash_init_error_message_callback(rc_hash_message_callback callback);
/* specifies a function to call for verbose logging */
void rc_hash_init_verbose_message_callback(rc_hash_message_callback callback);
RC_EXPORT void rc_hash_init_verbose_message_callback(rc_hash_message_callback callback);
/* ===================================================== */
/* opens a file */
typedef void* (*rc_hash_filereader_open_file_handler)(const char* path_utf8);
typedef void* (RC_CCONV *rc_hash_filereader_open_file_handler)(const char* path_utf8);
/* moves the file pointer - standard fseek parameters */
typedef void (*rc_hash_filereader_seek_handler)(void* file_handle, int64_t offset, int origin);
typedef void (RC_CCONV *rc_hash_filereader_seek_handler)(void* file_handle, int64_t offset, int origin);
/* locates the file pointer */
typedef int64_t (*rc_hash_filereader_tell_handler)(void* file_handle);
typedef int64_t (RC_CCONV *rc_hash_filereader_tell_handler)(void* file_handle);
/* reads the specified number of bytes from the file starting at the read pointer.
* returns the number of bytes actually read.
*/
typedef size_t (*rc_hash_filereader_read_handler)(void* file_handle, void* buffer, size_t requested_bytes);
typedef size_t (RC_CCONV *rc_hash_filereader_read_handler)(void* file_handle, void* buffer, size_t requested_bytes);
/* closes the file */
typedef void (*rc_hash_filereader_close_file_handler)(void* file_handle);
typedef void (RC_CCONV *rc_hash_filereader_close_file_handler)(void* file_handle);
struct rc_hash_filereader
{
@ -88,7 +86,7 @@ extern "C" {
rc_hash_filereader_close_file_handler close;
};
void rc_hash_init_custom_filereader(struct rc_hash_filereader* reader);
RC_EXPORT void RC_CCONV rc_hash_init_custom_filereader(struct rc_hash_filereader* reader);
/* ===================================================== */
@ -100,18 +98,18 @@ extern "C" {
/* opens a track from the specified file. see the RC_HASH_CDTRACK_ defines for special tracks.
* returns a handle to be passed to the other functions, or NULL if the track could not be opened.
*/
typedef void* (*rc_hash_cdreader_open_track_handler)(const char* path, uint32_t track);
typedef void* (RC_CCONV *rc_hash_cdreader_open_track_handler)(const char* path, uint32_t track);
/* attempts to read the specified number of bytes from the file starting at the specified absolute sector.
* returns the number of bytes actually read.
*/
typedef size_t (*rc_hash_cdreader_read_sector_handler)(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes);
typedef size_t (RC_CCONV *rc_hash_cdreader_read_sector_handler)(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes);
/* closes the track handle */
typedef void (*rc_hash_cdreader_close_track_handler)(void* track_handle);
typedef void (RC_CCONV *rc_hash_cdreader_close_track_handler)(void* track_handle);
/* gets the absolute sector index for the first sector of a track */
typedef uint32_t(*rc_hash_cdreader_first_track_sector_handler)(void* track_handle);
typedef uint32_t(RC_CCONV *rc_hash_cdreader_first_track_sector_handler)(void* track_handle);
struct rc_hash_cdreader
{
@ -121,14 +119,34 @@ extern "C" {
rc_hash_cdreader_first_track_sector_handler first_track_sector;
};
void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader);
void rc_hash_init_default_cdreader(void);
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
RC_EXPORT void RC_CCONV rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader);
RC_EXPORT void RC_CCONV rc_hash_init_default_cdreader(void);
RC_EXPORT void RC_CCONV rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
/* specifies a function called to obtain a 3DS CIA decryption normal key.
* this key would be derived from slot0x3DKeyX and the common key specified by the passed index.
* the normal key should be written in big endian format
* returns non-zero on success, or zero on failure.
*/
typedef int (RC_CCONV *rc_hash_3ds_get_cia_normal_key_func)(uint8_t common_key_index, uint8_t out_normal_key[16]);
RC_EXPORT void RC_CCONV rc_hash_init_3ds_get_cia_normal_key_func(rc_hash_3ds_get_cia_normal_key_func func);
/* specifies a function called to obtain 3DS NCCH decryption normal keys.
* the primary key will always use slot0x2CKeyX and the passed primary KeyY.
* the secondary key will use the KeyX slot passed
* the secondary KeyY will be identical to the primary keyY if the passed program id is NULL
* if the program id is not null, then the secondary KeyY will be obtained with "seed crypto"
* with "seed crypto" the 8 byte program id can be used to obtain a 16 byte "seed" within the seeddb.bin firmware file
* the primary KeyY then the seed will then be hashed with SHA256, and the upper 16 bytes of the digest will be the secondary KeyY used
* the normal keys should be written in big endian format
* returns non-zero on success, or zero on failure.
*/
typedef int (RC_CCONV *rc_hash_3ds_get_ncch_normal_keys_func)(uint8_t primary_key_y[16], uint8_t secondary_key_x_slot, uint8_t* optional_program_id,
uint8_t out_primary_key[16], uint8_t out_secondary_key[16]);
RC_EXPORT void RC_CCONV rc_hash_init_3ds_get_ncch_normal_keys_func(rc_hash_3ds_get_ncch_normal_keys_func func);
/* ===================================================== */
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_HASH_H */

View file

@ -1,15 +1,13 @@
#ifndef RC_RUNTIME_H
#define RC_RUNTIME_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_error.h"
#include <stddef.h>
#include <stdint.h>
RC_BEGIN_C_DECLS
/*****************************************************************************\
| Forward Declarations (defined in rc_runtime_types.h) |
\*****************************************************************************/
@ -35,7 +33,7 @@ typedef struct rc_value_t rc_value_t;
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef uint32_t(*rc_runtime_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
typedef uint32_t(RC_CCONV *rc_runtime_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
/*****************************************************************************\
| Runtime |
@ -94,24 +92,24 @@ typedef struct rc_runtime_t {
}
rc_runtime_t;
rc_runtime_t* rc_runtime_alloc(void);
void rc_runtime_init(rc_runtime_t* runtime);
void rc_runtime_destroy(rc_runtime_t* runtime);
RC_EXPORT rc_runtime_t* RC_CCONV rc_runtime_alloc(void);
RC_EXPORT void RC_CCONV rc_runtime_init(rc_runtime_t* runtime);
RC_EXPORT void RC_CCONV rc_runtime_destroy(rc_runtime_t* runtime);
int rc_runtime_activate_achievement(rc_runtime_t* runtime, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, uint32_t id);
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, uint32_t id);
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, uint32_t id, unsigned* measured_value, unsigned* measured_target);
int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, uint32_t id, char *buffer, size_t buffer_size);
RC_EXPORT int RC_CCONV rc_runtime_activate_achievement(rc_runtime_t* runtime, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx);
RC_EXPORT void RC_CCONV rc_runtime_deactivate_achievement(rc_runtime_t* runtime, uint32_t id);
RC_EXPORT rc_trigger_t* RC_CCONV rc_runtime_get_achievement(const rc_runtime_t* runtime, uint32_t id);
RC_EXPORT int RC_CCONV rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, uint32_t id, unsigned* measured_value, unsigned* measured_target);
RC_EXPORT int RC_CCONV rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, uint32_t id, char *buffer, size_t buffer_size);
int rc_runtime_activate_lboard(rc_runtime_t* runtime, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, uint32_t id);
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, uint32_t id);
int rc_runtime_format_lboard_value(char* buffer, int size, int32_t value, int format);
RC_EXPORT int RC_CCONV rc_runtime_activate_lboard(rc_runtime_t* runtime, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx);
RC_EXPORT void RC_CCONV rc_runtime_deactivate_lboard(rc_runtime_t* runtime, uint32_t id);
RC_EXPORT rc_lboard_t* RC_CCONV rc_runtime_get_lboard(const rc_runtime_t* runtime, uint32_t id);
RC_EXPORT int RC_CCONV rc_runtime_format_lboard_value(char* buffer, int size, int32_t value, int format);
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
int rc_runtime_get_richpresence(const rc_runtime_t* runtime, char* buffer, size_t buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
RC_EXPORT int RC_CCONV rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
RC_EXPORT int RC_CCONV rc_runtime_get_richpresence(const rc_runtime_t* runtime, char* buffer, size_t buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
enum {
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
@ -136,21 +134,19 @@ typedef struct rc_runtime_event_t {
}
rc_runtime_event_t;
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
typedef void (RC_CCONV *rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
void rc_runtime_reset(rc_runtime_t* runtime);
RC_EXPORT void RC_CCONV rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
RC_EXPORT void RC_CCONV rc_runtime_reset(rc_runtime_t* runtime);
typedef int (*rc_runtime_validate_address_t)(uint32_t address);
void rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
void rc_runtime_invalidate_address(rc_runtime_t* runtime, uint32_t address);
typedef int (RC_CCONV *rc_runtime_validate_address_t)(uint32_t address);
RC_EXPORT void RC_CCONV rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
RC_EXPORT void RC_CCONV rc_runtime_invalidate_address(rc_runtime_t* runtime, uint32_t address);
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const uint8_t* serialized, lua_State* L);
RC_EXPORT int RC_CCONV rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
RC_EXPORT int RC_CCONV rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua_State* L);
RC_EXPORT int RC_CCONV rc_runtime_deserialize_progress(rc_runtime_t* runtime, const uint8_t* serialized, lua_State* L);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_RUNTIME_H */

View file

@ -1,15 +1,13 @@
#ifndef RC_RUNTIME_TYPES_H
#define RC_RUNTIME_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_error.h"
#include <stddef.h>
#include <stdint.h>
RC_BEGIN_C_DECLS
#ifndef RC_RUNTIME_H /* prevents pedantic redefiniton error */
typedef struct lua_State lua_State;
@ -31,7 +29,7 @@ typedef struct rc_value_t rc_value_t;
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef uint32_t(*rc_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
typedef uint32_t(RC_CCONV *rc_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
/*****************************************************************************\
| Memory References |
@ -132,7 +130,7 @@ typedef struct rc_operand_t {
}
rc_operand_t;
int rc_operand_is_memref(const rc_operand_t* operand);
RC_EXPORT int RC_CCONV rc_operand_is_memref(const rc_operand_t* operand);
/*****************************************************************************\
| Conditions |
@ -276,11 +274,11 @@ struct rc_trigger_t {
uint8_t measured_as_percent;
};
int rc_trigger_size(const char* memaddr);
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
void rc_reset_trigger(rc_trigger_t* self);
RC_EXPORT int RC_CCONV rc_trigger_size(const char* memaddr);
RC_EXPORT rc_trigger_t* RC_CCONV rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
RC_EXPORT int RC_CCONV rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
RC_EXPORT int RC_CCONV rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
RC_EXPORT void RC_CCONV rc_reset_trigger(rc_trigger_t* self);
/*****************************************************************************\
| Values |
@ -303,9 +301,9 @@ struct rc_value_t {
rc_value_t* next;
};
int rc_value_size(const char* memaddr);
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int32_t rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
RC_EXPORT int RC_CCONV rc_value_size(const char* memaddr);
RC_EXPORT rc_value_t* RC_CCONV rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
RC_EXPORT int32_t RC_CCONV rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
/*****************************************************************************\
| Leaderboards |
@ -333,10 +331,10 @@ struct rc_lboard_t {
uint8_t state;
};
int rc_lboard_size(const char* memaddr);
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_lboard(rc_lboard_t* lboard, int32_t* value, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_reset_lboard(rc_lboard_t* lboard);
RC_EXPORT int RC_CCONV rc_lboard_size(const char* memaddr);
RC_EXPORT rc_lboard_t* RC_CCONV rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
RC_EXPORT int RC_CCONV rc_evaluate_lboard(rc_lboard_t* lboard, int32_t* value, rc_peek_t peek, void* peek_ud, lua_State* L);
RC_EXPORT void RC_CCONV rc_reset_lboard(rc_lboard_t* lboard);
/*****************************************************************************\
| Value formatting |
@ -356,11 +354,18 @@ enum {
RC_FORMAT_FLOAT3,
RC_FORMAT_FLOAT4,
RC_FORMAT_FLOAT5,
RC_FORMAT_FLOAT6
RC_FORMAT_FLOAT6,
RC_FORMAT_FIXED1,
RC_FORMAT_FIXED2,
RC_FORMAT_FIXED3,
RC_FORMAT_TENS,
RC_FORMAT_HUNDREDS,
RC_FORMAT_THOUSANDS,
RC_FORMAT_UNSIGNED_VALUE
};
int rc_parse_format(const char* format_str);
int rc_format_value(char* buffer, int size, int32_t value, int format);
RC_EXPORT int RC_CCONV rc_parse_format(const char* format_str);
RC_EXPORT int RC_CCONV rc_format_value(char* buffer, int size, int32_t value, int format);
/*****************************************************************************\
| Rich Presence |
@ -411,16 +416,14 @@ struct rc_richpresence_t {
rc_value_t* variables;
};
int rc_richpresence_size(const char* script);
int rc_richpresence_size_lines(const char* script, int* lines_read);
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx);
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, size_t buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L);
int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, size_t buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_reset_richpresence(rc_richpresence_t* self);
RC_EXPORT int RC_CCONV rc_richpresence_size(const char* script);
RC_EXPORT int RC_CCONV rc_richpresence_size_lines(const char* script, int* lines_read);
RC_EXPORT rc_richpresence_t* RC_CCONV rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx);
RC_EXPORT int RC_CCONV rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, size_t buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
RC_EXPORT void RC_CCONV rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L);
RC_EXPORT int RC_CCONV rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, size_t buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
RC_EXPORT void RC_CCONV rc_reset_richpresence(rc_richpresence_t* self);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_RUNTIME_TYPES_H */

View file

@ -1,38 +1,36 @@
#ifndef RC_URL_H
#define RC_URL_H
#include "rc_export.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore, const char* game_hash);
RC_EXPORT int RC_CCONV rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore, const char* game_hash);
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
RC_EXPORT int RC_CCONV rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
int rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count);
int rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count);
RC_EXPORT int RC_CCONV rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count);
RC_EXPORT int RC_CCONV rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count);
int rc_url_get_gameid(char* buffer, size_t size, const char* hash);
RC_EXPORT int RC_CCONV rc_url_get_gameid(char* buffer, size_t size, const char* hash);
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
RC_EXPORT int RC_CCONV rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
int rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name);
RC_EXPORT int RC_CCONV rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name);
int rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password);
RC_EXPORT int RC_CCONV rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password);
int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token);
RC_EXPORT int RC_CCONV rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token);
int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore);
RC_EXPORT int RC_CCONV rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore);
int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
RC_EXPORT int RC_CCONV rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
RC_EXPORT int RC_CCONV rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_URL_H */

View file

@ -1,12 +1,12 @@
#ifndef RC_UTIL_H
#define RC_UTIL_H
#include "rc_export.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
/**
* A block of memory for variable length data (like strings and arrays).
@ -46,8 +46,6 @@ uint32_t rc_djb2(const char* input);
void rc_format_md5(char checksum[33], const uint8_t digest[16]);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_UTIL_H */

View file

@ -475,13 +475,13 @@ int rc_json_get_required_array(uint32_t* num_entries, rc_json_field_t* array_fie
return 0;
#endif
if (!rc_json_get_optional_array(num_entries, array_field, response, field, field_name))
if (!rc_json_get_optional_array(num_entries, array_field, field, field_name))
return rc_json_missing_field(response, field);
return 1;
}
int rc_json_get_optional_array(uint32_t* num_entries, rc_json_field_t* array_field, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
int rc_json_get_optional_array(uint32_t* num_entries, rc_json_field_t* array_field, const rc_json_field_t* field, const char* field_name) {
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
return 0;
@ -783,6 +783,52 @@ int rc_json_get_required_unum(uint32_t* out, rc_api_response_t* response, const
return rc_json_missing_field(response, field);
}
int rc_json_get_float(float* out, const rc_json_field_t* field, const char* field_name) {
int32_t whole, fraction, fraction_denominator;
const char* decimal = field->value_start;
if (!decimal) {
*out = 0.0f;
return 0;
}
if (!rc_json_get_num(&whole, field, field_name))
return 0;
while (decimal < field->value_end && *decimal != '.')
++decimal;
fraction = 0;
fraction_denominator = 1;
if (decimal) {
++decimal;
while (decimal < field->value_end && *decimal >= '0' && *decimal <= '9') {
fraction *= 10;
fraction += *decimal - '0';
fraction_denominator *= 10;
++decimal;
}
}
if (whole < 0)
fraction = -fraction;
*out = (float)whole + ((float)fraction / (float)fraction_denominator);
return 1;
}
void rc_json_get_optional_float(float* out, const rc_json_field_t* field, const char* field_name, float default_value) {
if (!rc_json_get_float(out, field, field_name))
*out = default_value;
}
int rc_json_get_required_float(float* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
if (rc_json_get_float(out, field, field_name))
return 1;
return rc_json_missing_field(response, field);
}
int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char* field_name) {
struct tm tm;

View file

@ -6,9 +6,7 @@
#include <stddef.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
#define RC_CONTENT_TYPE_URLENCODED "application/x-www-form-urlencoded"
@ -47,16 +45,19 @@ int rc_json_parse_server_response(rc_api_response_t* response, const rc_api_serv
int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_field_t* field, const char* field_name);
int rc_json_get_num(int32_t* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_unum(uint32_t* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_float(float* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_bool(int* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char* field_name);
void rc_json_get_optional_string(const char** out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name, const char* default_value);
void rc_json_get_optional_num(int32_t* out, const rc_json_field_t* field, const char* field_name, int default_value);
void rc_json_get_optional_unum(uint32_t* out, const rc_json_field_t* field, const char* field_name, uint32_t default_value);
void rc_json_get_optional_float(float* out, const rc_json_field_t* field, const char* field_name, float default_value);
void rc_json_get_optional_bool(int* out, const rc_json_field_t* field, const char* field_name, int default_value);
int rc_json_get_optional_array(uint32_t* num_entries, rc_json_field_t* iterator, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_optional_array(uint32_t* num_entries, rc_json_field_t* iterator, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_string(const char** out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_num(int32_t* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_unum(uint32_t* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_float(float* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_datetime(time_t* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_object(rc_json_field_t* fields, size_t field_count, rc_api_response_t* response, rc_json_field_t* field, const char* field_name);
@ -74,8 +75,6 @@ void rc_url_builder_append_str_param(rc_api_url_builder_t* builder, const char*
void rc_api_url_build_dorequest_url(rc_api_request_t* request);
int rc_api_url_build_dorequest(rc_api_url_builder_t* builder, const char* api, const char* username, const char* api_token);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_API_COMMON_H */

View file

@ -92,6 +92,20 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
return rc_api_process_fetch_game_data_server_response(response, &response_obj);
}
static int rc_parse_achievement_type(const char* type)
{
if (strcmp(type, "missable") == 0)
return RC_ACHIEVEMENT_TYPE_MISSABLE;
if (strcmp(type, "win_condition") == 0)
return RC_ACHIEVEMENT_TYPE_WIN;
if (strcmp(type, "progression") == 0)
return RC_ACHIEVEMENT_TYPE_PROGRESSION;
return RC_ACHIEVEMENT_TYPE_STANDARD;
}
int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_response_t* response, const rc_api_server_response_t* server_response) {
rc_api_achievement_definition_t* achievement;
rc_api_leaderboard_definition_t* leaderboard;
@ -120,10 +134,6 @@ int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_respon
RC_JSON_NEW_FIELD("RichPresencePatch"),
RC_JSON_NEW_FIELD("Achievements"), /* array */
RC_JSON_NEW_FIELD("Leaderboards") /* array */
/* unused fields
RC_JSON_NEW_FIELD("ForumTopicID"),
RC_JSON_NEW_FIELD("Flags")
* unused fields */
};
rc_json_field_t achievement_fields[] = {
@ -136,7 +146,10 @@ int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_respon
RC_JSON_NEW_FIELD("Author"),
RC_JSON_NEW_FIELD("BadgeName"),
RC_JSON_NEW_FIELD("Created"),
RC_JSON_NEW_FIELD("Modified")
RC_JSON_NEW_FIELD("Modified"),
RC_JSON_NEW_FIELD("Type"),
RC_JSON_NEW_FIELD("Rarity"),
RC_JSON_NEW_FIELD("RarityHardcore")
};
rc_json_field_t leaderboard_fields[] = {
@ -247,6 +260,35 @@ int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_respon
return RC_MISSING_VALUE;
achievement->updated = (time_t)timet;
achievement->type = RC_ACHIEVEMENT_TYPE_STANDARD;
if (achievement_fields[10].value_end) {
len = achievement_fields[10].value_end - achievement_fields[10].value_start - 2;
if (len < sizeof(format) - 1) {
memcpy(format, achievement_fields[10].value_start + 1, len);
format[len] = '\0';
achievement->type = rc_parse_achievement_type(format);
}
}
/* legacy support : if title contains[m], change type to missable and remove[m] from title */
if (memcmp(achievement->title, "[m]", 3) == 0) {
len = 3;
while (achievement->title[len] == ' ')
++len;
achievement->title += len;
achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE;
}
else if (achievement_fields[1].value_end && memcmp(achievement_fields[1].value_end - 4, "[m]", 3) == 0) {
len = strlen(achievement->title) - 3;
while (achievement->title[len - 1] == ' ')
--len;
((char*)achievement->title)[len] = '\0';
achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE;
}
rc_json_get_optional_float(&achievement->rarity, &achievement_fields[11], "Rarity", 100.0);
rc_json_get_optional_float(&achievement->rarity_hardcore, &achievement_fields[12], "RarityHardcore", 100.0);
++achievement;
}
}
@ -318,6 +360,11 @@ int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_reques
if (api_params->rich_presence && *api_params->rich_presence)
rc_url_builder_append_str_param(&builder, "m", api_params->rich_presence);
if (api_params->game_hash && *api_params->game_hash) {
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore);
rc_url_builder_append_str_param(&builder, "x", api_params->game_hash);
}
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}

View file

@ -94,7 +94,14 @@ int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_st
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "startsession", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
if (api_params->game_hash && *api_params->game_hash) {
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore);
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
}
rc_url_builder_append_str_param(&builder, "l", RCHEEVOS_VERSION_STRING);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
@ -139,7 +146,7 @@ int rc_api_process_start_session_server_response(rc_api_start_session_response_t
if (result != RC_OK || !response->response.succeeded)
return result;
if (rc_json_get_optional_array(&response->num_unlocks, &array_field, &response->response, &fields[2], "Unlocks") && response->num_unlocks) {
if (rc_json_get_optional_array(&response->num_unlocks, &array_field, &fields[2], "Unlocks") && response->num_unlocks) {
response->unlocks = (rc_api_unlock_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_unlocks * sizeof(rc_api_unlock_entry_t));
if (!response->unlocks)
return RC_OUT_OF_MEMORY;
@ -160,7 +167,7 @@ int rc_api_process_start_session_server_response(rc_api_start_session_response_t
}
}
if (rc_json_get_optional_array(&response->num_hardcore_unlocks, &array_field, &response->response, &fields[3], "HardcoreUnlocks") && response->num_hardcore_unlocks) {
if (rc_json_get_optional_array(&response->num_hardcore_unlocks, &array_field, &fields[3], "HardcoreUnlocks") && response->num_hardcore_unlocks) {
response->hardcore_unlocks = (rc_api_unlock_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_hardcore_unlocks * sizeof(rc_api_unlock_entry_t));
if (!response->hardcore_unlocks)
return RC_OUT_OF_MEMORY;

File diff suppressed because it is too large Load diff

132
deps/rcheevos/src/rc_client_external.h vendored Normal file
View file

@ -0,0 +1,132 @@
#ifndef RC_CLIENT_EXTERNAL_H
#define RC_CLIENT_EXTERNAL_H
#include "rc_client.h"
RC_BEGIN_C_DECLS
/* NOTE: any function that is passed a callback also needs to be passed a client instance to pass
* to the callback, and the external interface has to capture both. */
typedef void (RC_CCONV *rc_client_external_enable_logging_func_t)(rc_client_t* client, int level, rc_client_message_callback_t callback);
typedef void (RC_CCONV *rc_client_external_set_event_handler_func_t)(rc_client_t* client, rc_client_event_handler_t handler);
typedef void (RC_CCONV *rc_client_external_set_read_memory_func_t)(rc_client_t* client, rc_client_read_memory_func_t handler);
typedef void (RC_CCONV *rc_client_external_set_get_time_millisecs_func_t)(rc_client_t* client, rc_get_time_millisecs_func_t handler);
typedef int (RC_CCONV *rc_client_external_can_pause_func_t)(uint32_t* frames_remaining);
typedef void (RC_CCONV *rc_client_external_set_int_func_t)(int value);
typedef int (RC_CCONV *rc_client_external_get_int_func_t)(void);
typedef void (RC_CCONV *rc_client_external_set_string_func_t)(const char* value);
typedef size_t (RC_CCONV *rc_client_external_copy_string_func_t)(char buffer[], size_t buffer_size);
typedef void (RC_CCONV *rc_client_external_action_func_t)(void);
typedef void (RC_CCONV *rc_client_external_async_handle_func_t)(rc_client_async_handle_t* handle);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_login_func_t)(rc_client_t* client,
const char* username, const char* pass_token, rc_client_callback_t callback, void* callback_userdata);
typedef const rc_client_user_t* (RC_CCONV *rc_client_external_get_user_info_func_t)(void);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_identify_and_load_game_func_t)(
rc_client_t* client, uint32_t console_id, const char* file_path,
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_load_game_func_t)(rc_client_t* client,
const char* hash, rc_client_callback_t callback, void* callback_userdata);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_load_subset_t)(rc_client_t* client,
uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata);
typedef const rc_client_game_t* (RC_CCONV *rc_client_external_get_game_info_func_t)(void);
typedef const rc_client_subset_t* (RC_CCONV *rc_client_external_get_subset_info_func_t)(uint32_t subset_id);
typedef void (RC_CCONV *rc_client_external_get_user_game_summary_func_t)(rc_client_user_game_summary_t* summary);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_change_media_func_t)(rc_client_t* client, const char* file_path,
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
/* NOTE: rc_client_external_create_achievement_list_func_t returns an internal wrapper structure which contains the public list
* and a destructor function. */
struct rc_client_achievement_list_info_t;
typedef struct rc_client_achievement_list_info_t* (RC_CCONV *rc_client_external_create_achievement_list_func_t)(int category, int grouping);
typedef const rc_client_achievement_t* (RC_CCONV *rc_client_external_get_achievement_info_func_t)(uint32_t id);
/* NOTE: rc_client_external_create_leaderboard_list_func_t returns an internal wrapper structure which contains the public list
* and a destructor function. */
struct rc_client_leaderboard_list_info_t;
typedef struct rc_client_leaderboard_list_info_t* (RC_CCONV *rc_client_external_create_leaderboard_list_func_t)(int grouping);
typedef const rc_client_leaderboard_t* (RC_CCONV *rc_client_external_get_leaderboard_info_func_t)(uint32_t id);
/* NOTE: rc_client_external_begin_fetch_leaderboard_entries_func_t and rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t
* pass an internal wrapper structure around the list, which contains the public list and a destructor function. */
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_func_t)(rc_client_t* client,
uint32_t leaderboard_id, uint32_t first_entry, uint32_t count,
rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client,
uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void);
typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer);
typedef int (RC_CCONV *rc_client_external_deserialize_progress_func_t)(const uint8_t* buffer);
typedef struct rc_client_external_t
{
rc_client_external_action_func_t destroy;
rc_client_external_enable_logging_func_t enable_logging;
rc_client_external_set_event_handler_func_t set_event_handler;
rc_client_external_set_read_memory_func_t set_read_memory;
rc_client_external_set_get_time_millisecs_func_t set_get_time_millisecs;
rc_client_external_set_string_func_t set_host;
rc_client_external_copy_string_func_t get_user_agent_clause;
rc_client_external_set_int_func_t set_hardcore_enabled;
rc_client_external_get_int_func_t get_hardcore_enabled;
rc_client_external_set_int_func_t set_unofficial_enabled;
rc_client_external_get_int_func_t get_unofficial_enabled;
rc_client_external_set_int_func_t set_encore_mode_enabled;
rc_client_external_get_int_func_t get_encore_mode_enabled;
rc_client_external_set_int_func_t set_spectator_mode_enabled;
rc_client_external_get_int_func_t get_spectator_mode_enabled;
rc_client_external_async_handle_func_t abort_async;
rc_client_external_begin_login_func_t begin_login_with_password;
rc_client_external_begin_login_func_t begin_login_with_token;
rc_client_external_action_func_t logout;
rc_client_external_get_user_info_func_t get_user_info;
rc_client_external_begin_identify_and_load_game_func_t begin_identify_and_load_game;
rc_client_external_begin_load_game_func_t begin_load_game;
rc_client_external_get_game_info_func_t get_game_info;
rc_client_external_begin_load_subset_t begin_load_subset;
rc_client_external_get_subset_info_func_t get_subset_info;
rc_client_external_action_func_t unload_game;
rc_client_external_get_user_game_summary_func_t get_user_game_summary;
rc_client_external_begin_change_media_func_t begin_change_media;
rc_client_external_create_achievement_list_func_t create_achievement_list;
rc_client_external_get_int_func_t has_achievements;
rc_client_external_get_achievement_info_func_t get_achievement_info;
rc_client_external_create_leaderboard_list_func_t create_leaderboard_list;
rc_client_external_get_int_func_t has_leaderboards;
rc_client_external_get_leaderboard_info_func_t get_leaderboard_info;
rc_client_external_begin_fetch_leaderboard_entries_func_t begin_fetch_leaderboard_entries;
rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t begin_fetch_leaderboard_entries_around_user;
rc_client_external_copy_string_func_t get_rich_presence_message;
rc_client_external_get_int_func_t has_rich_presence;
rc_client_external_action_func_t do_frame;
rc_client_external_action_func_t idle;
rc_client_external_get_int_func_t is_processing_required;
rc_client_external_can_pause_func_t can_pause;
rc_client_external_action_func_t reset;
rc_client_external_progress_size_func_t progress_size;
rc_client_external_serialize_progress_func_t serialize_progress;
rc_client_external_deserialize_progress_func_t deserialize_progress;
} rc_client_external_t;
#define RC_CLIENT_EXTERNAL_VERSION 1
RC_END_C_DECLS
#endif /* RC_CLIENT_EXTERNAL_H */

View file

@ -1,26 +1,31 @@
#ifndef RC_CLIENT_INTERNAL_H
#define RC_CLIENT_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_client.h"
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
#include "rc_client_raintegration_internal.h"
#endif
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
#include "rc_client_external.h"
#endif
#include "rc_compat.h"
#include "rc_runtime.h"
#include "rc_runtime_types.h"
RC_BEGIN_C_DECLS
/*****************************************************************************\
| Callbacks |
\*****************************************************************************/
struct rc_api_fetch_game_data_response_t;
typedef void (*rc_client_post_process_game_data_response_t)(const rc_api_server_response_t* server_response,
typedef void (RC_CCONV *rc_client_post_process_game_data_response_t)(const rc_api_server_response_t* server_response,
struct rc_api_fetch_game_data_response_t* game_data_response, rc_client_t* client, void* userdata);
typedef int (*rc_client_can_submit_achievement_unlock_t)(uint32_t achievement_id, rc_client_t* client);
typedef int (*rc_client_can_submit_leaderboard_entry_t)(uint32_t leaderboard_id, rc_client_t* client);
typedef int (*rc_client_rich_presence_override_t)(rc_client_t* client, char buffer[], size_t buffersize);
typedef int (RC_CCONV *rc_client_can_submit_achievement_unlock_t)(uint32_t achievement_id, rc_client_t* client);
typedef int (RC_CCONV *rc_client_can_submit_leaderboard_entry_t)(uint32_t leaderboard_id, rc_client_t* client);
typedef int (RC_CCONV *rc_client_rich_presence_override_t)(rc_client_t* client, char buffer[], size_t buffersize);
typedef struct rc_client_callbacks_t {
rc_client_read_memory_func_t read_memory;
@ -37,7 +42,7 @@ typedef struct rc_client_callbacks_t {
} rc_client_callbacks_t;
struct rc_client_scheduled_callback_data_t;
typedef void (*rc_client_scheduled_callback_t)(struct rc_client_scheduled_callback_data_t* callback_data, rc_client_t* client, rc_clock_t now);
typedef void (RC_CCONV *rc_client_scheduled_callback_t)(struct rc_client_scheduled_callback_data_t* callback_data, rc_client_t* client, rc_clock_t now);
typedef struct rc_client_scheduled_callback_data_t
{
@ -50,6 +55,12 @@ typedef struct rc_client_scheduled_callback_data_t
void rc_client_schedule_callback(rc_client_t* client, rc_client_scheduled_callback_data_t* scheduled_callback);
struct rc_client_async_handle_t {
uint8_t aborted;
};
int rc_client_async_handle_aborted(rc_client_t* client, rc_client_async_handle_t* async_handle);
/*****************************************************************************\
| Achievements |
\*****************************************************************************/
@ -78,6 +89,14 @@ typedef struct rc_client_achievement_info_t {
time_t updated_time;
} rc_client_achievement_info_t;
struct rc_client_achievement_list_info_t;
typedef void (RC_CCONV *rc_client_destroy_achievement_list_func_t)(struct rc_client_achievement_list_info_t* list);
typedef struct rc_client_achievement_list_info_t {
rc_client_achievement_list_t public_;
rc_client_destroy_achievement_list_func_t destroy_func;
} rc_client_achievement_list_info_t;
enum {
RC_CLIENT_PROGRESS_TRACKER_ACTION_NONE,
RC_CLIENT_PROGRESS_TRACKER_ACTION_SHOW,
@ -145,6 +164,22 @@ typedef struct rc_client_leaderboard_info_t {
uint8_t hidden;
} rc_client_leaderboard_info_t;
struct rc_client_leaderboard_list_info_t;
typedef void (RC_CCONV *rc_client_destroy_leaderboard_list_func_t)(struct rc_client_leaderboard_list_info_t* list);
typedef struct rc_client_leaderboard_list_info_t {
rc_client_leaderboard_list_t public_;
rc_client_destroy_leaderboard_list_func_t destroy_func;
} rc_client_leaderboard_list_info_t;
struct rc_client_leaderboard_entry_list_info_t;
typedef void (RC_CCONV *rc_client_destroy_leaderboard_entry_list_func_t)(struct rc_client_leaderboard_entry_list_info_t* list);
typedef struct rc_client_leaderboard_entry_list_info_t {
rc_client_leaderboard_entry_list_t public_;
rc_client_destroy_leaderboard_entry_list_func_t destroy_func;
} rc_client_leaderboard_entry_list_info_t;
uint8_t rc_client_map_leaderboard_format(int format);
/*****************************************************************************\
@ -177,7 +212,7 @@ typedef struct rc_client_subset_info_t {
uint8_t pending_events;
} rc_client_subset_info_t;
void rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata);
rc_client_async_handle_t* rc_client_begin_load_subset(rc_client_t* client, uint32_t subset_id, rc_client_callback_t callback, void* callback_userdata);
/*****************************************************************************\
| Game |
@ -273,6 +308,16 @@ typedef struct rc_client_state_t {
rc_client_scheduled_callback_data_t* scheduled_callbacks;
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
rc_client_external_t* external_client;
#endif
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
rc_client_raintegration_t* raintegration;
#endif
uint16_t unpaused_frame_decay;
uint16_t required_unpaused_frames;
uint8_t hardcore;
uint8_t encore_mode;
uint8_t spectator_mode;
@ -280,6 +325,7 @@ typedef struct rc_client_state_t {
uint8_t log_level;
uint8_t user;
uint8_t disconnect;
uint8_t allow_leaderboards_in_softcore;
struct rc_client_load_state_t* load;
struct rc_client_async_handle_t* async_handles[4];
@ -343,8 +389,6 @@ void rc_client_set_legacy_peek(rc_client_t* client, int method);
void rc_client_release_leaderboard_tracker(rc_client_game_info_t* game, rc_client_leaderboard_info_t* leaderboard);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_CLIENT_INTERNAL_H */

View file

@ -0,0 +1,493 @@
#include "rc_client_raintegration_internal.h"
#include "rc_client_internal.h"
#include "rapi/rc_api_common.h"
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
static void rc_client_raintegration_load_dll(rc_client_t* client,
const wchar_t* search_directory, rc_client_callback_t callback, void* callback_userdata)
{
wchar_t sPath[_MAX_PATH];
const int nPathSize = sizeof(sPath) / sizeof(sPath[0]);
rc_client_raintegration_t* raintegration;
int sPathIndex = 0;
DWORD dwAttrib;
HINSTANCE hDLL;
if (search_directory) {
sPathIndex = swprintf_s(sPath, nPathSize, L"%s\\", search_directory);
if (sPathIndex > nPathSize - 22) {
callback(RC_INVALID_STATE, "search_directory too long", client, callback_userdata);
return;
}
}
#if defined(_M_X64) || defined(__amd64__)
wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration-x64.dll");
dwAttrib = GetFileAttributesW(sPath);
if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll");
dwAttrib = GetFileAttributesW(sPath);
}
#else
wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll");
dwAttrib = GetFileAttributesW(sPath);
#endif
if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
callback(RC_MISSING_VALUE, "RA_Integration.dll not found in search directory", client, callback_userdata);
return;
}
hDLL = LoadLibraryW(sPath);
if (hDLL == NULL) {
char error_message[512];
const DWORD last_error = GetLastError();
int offset = snprintf(error_message, sizeof(error_message), "Failed to load RA_Integration.dll (%u)", last_error);
if (last_error != 0) {
LPSTR messageBuffer = NULL;
const DWORD size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
snprintf(&error_message[offset], sizeof(error_message) - offset, ": %.*s", size, messageBuffer);
LocalFree(messageBuffer);
}
callback(RC_ABORTED, error_message, client, callback_userdata);
return;
}
raintegration = (rc_client_raintegration_t*)rc_buffer_alloc(&client->state.buffer, sizeof(rc_client_raintegration_t));
memset(raintegration, 0, sizeof(*raintegration));
raintegration->hDLL = hDLL;
raintegration->get_version = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_IntegrationVersion");
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->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");
raintegration->get_external_client = (rc_client_raintegration_get_external_client_func_t)GetProcAddress(hDLL, "_Rcheevos_GetExternalClient");
raintegration->get_menu = (rc_client_raintegration_get_menu_func_t)GetProcAddress(hDLL, "_Rcheevos_RAIntegrationGetMenu");
raintegration->activate_menu_item = (rc_client_raintegration_activate_menuitem_func_t)GetProcAddress(hDLL, "_Rcheevos_ActivateRAIntegrationMenuItem");
raintegration->set_write_memory_function = (rc_client_raintegration_set_write_memory_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationWriteMemoryFunction");
raintegration->set_event_handler = (rc_client_raintegration_set_event_handler_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationEventHandler");
if (!raintegration->get_version ||
!raintegration->init_client ||
!raintegration->get_external_client) {
FreeLibrary(hDLL);
callback(RC_ABORTED, "One or more required exports was not found in RA_Integration.dll", client, callback_userdata);
}
else {
rc_mutex_lock(&client->state.mutex);
client->state.raintegration = raintegration;
rc_mutex_unlock(&client->state.mutex);
RC_CLIENT_LOG_INFO_FORMATTED(client, "RA_Integration.dll %s loaded", client->state.raintegration->get_version());
}
}
typedef struct rc_client_version_validation_callback_data_t {
rc_client_t* client;
rc_client_callback_t callback;
void* callback_userdata;
HWND main_window_handle;
char* client_name;
char* client_version;
rc_client_async_handle_t async_handle;
} rc_client_version_validation_callback_data_t;
int rc_client_version_less(const char* left, const char* right)
{
do {
int left_len = 0;
int right_len = 0;
while (*left && *left == '0')
++left;
while (left[left_len] && left[left_len] != '.')
++left_len;
while (*right && *right == '0')
++right;
while (right[right_len] && right[right_len] != '.')
++right_len;
if (left_len != right_len)
return (left_len < right_len);
while (left_len--) {
if (*left != *right)
return (*left < *right);
++left;
++right;
}
if (*left == '.')
++left;
if (*right == '.')
++right;
} while (*left || *right);
return 0;
}
static void rc_client_init_raintegration(rc_client_t* client,
rc_client_version_validation_callback_data_t* version_validation_callback_data)
{
rc_client_raintegration_init_client_func_t init_func = client->state.raintegration->init_client;
if (client->state.raintegration->get_host_url) {
const char* host_url = client->state.raintegration->get_host_url();
if (host_url) {
if (strcmp(host_url, "OFFLINE") != 0) {
rc_client_set_host(client, host_url);
}
else if (client->state.raintegration->init_client_offline) {
init_func = client->state.raintegration->init_client_offline;
RC_CLIENT_LOG_INFO(client, "Initializing in offline mode");
}
}
}
if (!init_func || !init_func(version_validation_callback_data->main_window_handle,
version_validation_callback_data->client_name,
version_validation_callback_data->client_version)) {
const char* error_message = "RA_Integration initialization failed";
rc_client_unload_raintegration(client);
RC_CLIENT_LOG_ERR(client, error_message);
version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
}
else {
rc_client_external_t* external_client = (rc_client_external_t*)
rc_buffer_alloc(&client->state.buffer, sizeof(*external_client));
memset(external_client, 0, sizeof(*external_client));
if (!client->state.raintegration->get_external_client(external_client, RC_CLIENT_EXTERNAL_VERSION)) {
const char* error_message = "RA_Integration external client export failed";
rc_client_unload_raintegration(client);
RC_CLIENT_LOG_ERR(client, error_message);
version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
}
else {
/* copy state to the external client */
if (external_client->enable_logging)
external_client->enable_logging(client, client->state.log_level, client->callbacks.log_call);
if (external_client->set_event_handler)
external_client->set_event_handler(client, client->callbacks.event_handler);
if (external_client->set_read_memory)
external_client->set_read_memory(client, client->callbacks.read_memory);
if (external_client->set_hardcore_enabled)
external_client->set_hardcore_enabled(rc_client_get_hardcore_enabled(client));
if (external_client->set_unofficial_enabled)
external_client->set_unofficial_enabled(rc_client_get_unofficial_enabled(client));
if (external_client->set_encore_mode_enabled)
external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client));
if (external_client->set_spectator_mode_enabled)
external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client));
/* attach the external client and call the callback */
client->state.external_client = external_client;
client->state.raintegration->bIsInited = 1;
version_validation_callback_data->callback(RC_OK, NULL,
client, version_validation_callback_data->callback_userdata);
}
}
}
static void rc_client_version_validation_callback(const rc_api_server_response_t* server_response, void* callback_data)
{
rc_client_version_validation_callback_data_t* version_validation_callback_data =
(rc_client_version_validation_callback_data_t*)callback_data;
rc_client_t* client = version_validation_callback_data->client;
if (rc_client_async_handle_aborted(client, &version_validation_callback_data->async_handle)) {
RC_CLIENT_LOG_VERBOSE(client, "Version validation aborted");
}
else {
rc_api_response_t response;
int result;
const char* current_version;
const char* minimum_version = "";
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("MinimumVersion"),
};
memset(&response, 0, sizeof(response));
rc_buffer_init(&response.buffer);
result = rc_json_parse_server_response(&response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result == RC_OK) {
if (!rc_json_get_required_string(&minimum_version, &response, &fields[2], "MinimumVersion"))
result = RC_MISSING_VALUE;
}
if (result != RC_OK) {
RC_CLIENT_LOG_ERR_FORMATTED(client, "Failed to fetch latest integration version: %.*s", server_response->body_length, server_response->body);
rc_client_unload_raintegration(client);
version_validation_callback_data->callback(result, rc_error_str(result),
client, version_validation_callback_data->callback_userdata);
}
else {
current_version = client->state.raintegration->get_version();
if (rc_client_version_less(current_version, minimum_version)) {
char error_message[256];
rc_client_unload_raintegration(client);
snprintf(error_message, sizeof(error_message),
"RA_Integration version %s is lower than minimum version %s", current_version, minimum_version);
RC_CLIENT_LOG_WARN(client, error_message);
version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
}
else {
RC_CLIENT_LOG_INFO_FORMATTED(client, "Validated RA_Integration version %s (minimum %s)", current_version, minimum_version);
rc_client_init_raintegration(client, version_validation_callback_data);
}
}
rc_buffer_destroy(&response.buffer);
}
free(version_validation_callback_data->client_name);
free(version_validation_callback_data->client_version);
free(version_validation_callback_data);
}
rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client,
const wchar_t* search_directory, HWND main_window_handle,
const char* client_name, const char* client_version,
rc_client_callback_t callback, void* callback_userdata)
{
rc_client_version_validation_callback_data_t* callback_data;
rc_api_url_builder_t builder;
rc_api_request_t request;
if (!client) {
callback(RC_INVALID_STATE, "client is required", client, callback_userdata);
return NULL;
}
if (!client_name) {
callback(RC_INVALID_STATE, "client_name is required", client, callback_userdata);
return NULL;
}
if (!client_version) {
callback(RC_INVALID_STATE, "client_version is required", client, callback_userdata);
return NULL;
}
if (client->state.user != RC_CLIENT_USER_STATE_NONE) {
callback(RC_INVALID_STATE, "Cannot initialize RAIntegration after login", client, callback_userdata);
return NULL;
}
if (!client->state.raintegration) {
if (!main_window_handle) {
callback(RC_INVALID_STATE, "main_window_handle is required", client, callback_userdata);
return NULL;
}
rc_client_raintegration_load_dll(client, search_directory, callback, callback_userdata);
if (!client->state.raintegration)
return NULL;
}
if (client->state.raintegration->get_host_url) {
const char* host_url = client->state.raintegration->get_host_url();
if (host_url && strcmp(host_url, "https://retroachievements.org") != 0 &&
strcmp(host_url, "OFFLINE") != 0) {
/* if the DLL specifies a custom host, use it */
rc_client_set_host(client, host_url);
}
}
memset(&request, 0, sizeof(request));
rc_api_url_build_dorequest_url(&request);
rc_url_builder_init(&builder, &request.buffer, 48);
rc_url_builder_append_str_param(&builder, "r", "latestintegration");
request.post_data = rc_url_builder_finalize(&builder);
callback_data = calloc(1, sizeof(*callback_data));
if (!callback_data) {
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata);
return NULL;
}
callback_data->client = client;
callback_data->callback = callback;
callback_data->callback_userdata = callback_userdata;
callback_data->client_name = strdup(client_name);
callback_data->client_version = strdup(client_version);
callback_data->main_window_handle = main_window_handle;
client->callbacks.server_call(&request, rc_client_version_validation_callback, callback_data, client);
return &callback_data->async_handle;
}
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)
{
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)
{
if (client && client->state.raintegration && client->state.raintegration->set_write_memory_function)
client->state.raintegration->set_write_memory_function(client, handler);
}
void rc_client_raintegration_set_event_handler(rc_client_t* client,
rc_client_raintegration_event_handler_t handler)
{
if (client && client->state.raintegration && client->state.raintegration->set_event_handler)
client->state.raintegration->set_event_handler(client, handler);
}
const rc_client_raintegration_menu_t* rc_client_raintegration_get_menu(const rc_client_t* client)
{
if (!client || !client->state.raintegration ||
!client->state.raintegration->bIsInited ||
!client->state.raintegration->get_menu)
{
return NULL;
}
return client->state.raintegration->get_menu();
}
void rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu)
{
HMENU hPopupMenu = NULL;
const rc_client_raintegration_menu_t* menu;
if (!client || !client->state.raintegration)
return;
/* destroy the existing menu */
if (client->state.raintegration->hPopupMenu)
DestroyMenu(client->state.raintegration->hPopupMenu);
/* create the popup menu */
hPopupMenu = CreatePopupMenu();
menu = rc_client_raintegration_get_menu(client);
if (menu && menu->num_items)
{
const rc_client_raintegration_menu_item_t* menuitem = menu->items;
const rc_client_raintegration_menu_item_t* stop = menu->items + menu->num_items;
for (; menuitem < stop; ++menuitem)
{
if (menuitem->id == 0)
AppendMenuA(hPopupMenu, MF_SEPARATOR, 0U, NULL);
else
{
UINT flags = MF_STRING;
if (menuitem->checked)
flags |= MF_CHECKED;
if (!menuitem->enabled)
flags |= MF_DISABLED | MF_GRAYED;
AppendMenuA(hPopupMenu, flags, menuitem->id, menuitem->label);
}
}
}
/* add/update the item containing the popup menu */
{
int nIndex = GetMenuItemCount(hMenu);
const char* menuText = "&RetroAchievements";
char buffer[64];
UINT flags = MF_POPUP | MF_STRING;
if (!menu || !menu->num_items)
flags |= MF_DISABLED | MF_GRAYED;
while (--nIndex >= 0)
{
if (GetMenuStringA(hMenu, nIndex, buffer, sizeof(buffer) - 1, MF_BYPOSITION))
{
if (strcmp(buffer, menuText) == 0)
break;
}
}
if (nIndex == -1)
AppendMenuA(hMenu, flags, (UINT_PTR)hPopupMenu, menuText);
else
ModifyMenuA(hMenu, nIndex, flags | MF_BYPOSITION, (UINT_PTR)hPopupMenu, menuText);
}
client->state.raintegration->hPopupMenu = hPopupMenu;
}
void rc_client_raintegration_update_menu_item(const rc_client_t* client, const rc_client_raintegration_menu_item_t* menuitem)
{
if (client && client->state.raintegration && client->state.raintegration->hPopupMenu)
{
UINT flags = MF_STRING;
if (menuitem->checked)
flags |= MF_CHECKED;
CheckMenuItem(client->state.raintegration->hPopupMenu, menuitem->id, flags | MF_BYCOMMAND);
}
}
int rc_client_raintegration_activate_menu_item(const rc_client_t* client, uint32_t nMenuItemId)
{
if (!client || !client->state.raintegration || !client->state.raintegration->activate_menu_item)
return 0;
return client->state.raintegration->activate_menu_item(nMenuItemId);
}
void rc_client_unload_raintegration(rc_client_t* client)
{
HINSTANCE hDLL;
if (!client || !client->state.raintegration)
return;
RC_CLIENT_LOG_INFO(client, "Unloading RA_Integration")
if (client->state.raintegration->shutdown)
client->state.raintegration->shutdown();
rc_mutex_lock(&client->state.mutex);
hDLL = client->state.raintegration->hDLL;
client->state.raintegration = NULL;
client->state.external_client = NULL;
rc_mutex_unlock(&client->state.mutex);
if (hDLL)
FreeLibrary(hDLL);
}
#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */

View file

@ -0,0 +1,52 @@
#ifndef RC_CLIENT_RAINTEGRATION_INTERNAL_H
#define RC_CLIENT_RAINTEGRATION_INTERNAL_H
#include "rc_client_raintegration.h"
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
#include "rc_client_external.h"
#include "rc_compat.h"
RC_BEGIN_C_DECLS
/* RAIntegration follows the same calling convention as rcheevos */
typedef void (RC_CCONV* rc_client_raintegration_action_func_t)(void);
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 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_event_handler_func_t)(rc_client_t* pClient, rc_client_raintegration_event_handler_t handler);
typedef struct rc_client_raintegration_t
{
HINSTANCE hDLL;
HMENU hPopupMenu;
uint8_t bIsInited;
rc_client_raintegration_get_string_func_t get_version;
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_action_func_t shutdown;
rc_client_raintegration_hwnd_action_func_t update_main_window_handle;
rc_client_raintegration_set_write_memory_func_t set_write_memory_function;
rc_client_raintegration_set_event_handler_func_t set_event_handler;
rc_client_raintegration_get_menu_func_t get_menu;
rc_client_raintegration_activate_menuitem_func_t activate_menu_item;
rc_client_raintegration_get_external_client_func_t get_external_client;
} rc_client_raintegration_t;
RC_END_C_DECLS
#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */
#endif /* RC_CLIENT_RAINTEGRATION_INTERNAL_H */

View file

@ -116,7 +116,9 @@ void rc_mutex_unlock(rc_mutex_t* mutex)
#elif defined(GEKKO)
void rc_mutex_init(rc_mutex_t *mutex)
/* https://github.com/libretro/RetroArch/pull/16116 */
void rc_mutex_init(rc_mutex_t* mutex)
{
LWP_MutexInit(mutex, NULL);
}

View file

@ -1,14 +1,14 @@
#ifndef RC_COMPAT_H
#define RC_COMPAT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_export.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
RC_BEGIN_C_DECLS
#if defined(MINGW) || defined(__MINGW32__) || defined(__MINGW64__)
/* MinGW redefinitions */
@ -92,8 +92,6 @@ extern "C" {
void rc_mutex_unlock(rc_mutex_t* mutex);
#endif
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_COMPAT_H */

View file

@ -51,6 +51,11 @@ static const rc_disallowed_setting_t _rc_disallowed_dolphin_settings[] = {
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_dosbox_pure_settings[] = {
{ "dosbox_pure_strict_mode", "false" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_duckstation_settings[] = {
{ "duckstation_CDROM.LoadImagePatches", "true" },
{ NULL, NULL }
@ -96,6 +101,11 @@ static const rc_disallowed_setting_t _rc_disallowed_mesen_s_settings[] = {
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_neocd_settings[] = {
{ "neocd_bios", "uni-bios*" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = {
{ "pcsx_rearmed_region", "pal" },
{ NULL, NULL }
@ -144,6 +154,7 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
{ "bsnes-mercury", _rc_disallowed_bsnes_settings },
{ "cap32", _rc_disallowed_cap32_settings },
{ "dolphin-emu", _rc_disallowed_dolphin_settings },
{ "DOSBox-pure", _rc_disallowed_dosbox_pure_settings },
{ "DuckStation", _rc_disallowed_duckstation_settings },
{ "ecwolf", _rc_disallowed_ecwolf_settings },
{ "FCEUmm", _rc_disallowed_fceumm_settings },
@ -152,6 +163,7 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
{ "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings },
{ "Mesen", _rc_disallowed_mesen_settings },
{ "Mesen-S", _rc_disallowed_mesen_s_settings },
{ "NeoCD", _rc_disallowed_neocd_settings },
{ "PPSSPP", _rc_disallowed_ppsspp_settings },
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
{ "PicoDrive", _rc_disallowed_picodrive_settings },
@ -318,27 +330,38 @@ uint8_t* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, ui
uint32_t rc_libretro_memory_read(const rc_libretro_memory_regions_t* regions, uint32_t address,
uint8_t* buffer, uint32_t num_bytes) {
uint32_t i;
uint32_t bytes_read = 0;
uint32_t avail;
uint32_t i;
for (i = 0; i < regions->count; ++i) {
const size_t size = regions->size[i];
if (address < size) {
if (regions->data[i] == NULL)
break;
avail = (unsigned)(size - address);
if (avail < num_bytes)
return avail;
memcpy(buffer, &regions->data[i][address], num_bytes);
return num_bytes;
if (address >= size) {
/* address is not in this block, adjust and look at next block */
address -= (unsigned)size;
continue;
}
address -= (unsigned)size;
if (regions->data[i] == NULL) /* no memory associated to this block. abort */
break;
avail = (unsigned)(size - address);
if (avail >= num_bytes) {
/* requested memory is fully within this block, copy and return it */
memcpy(buffer, &regions->data[i][address], num_bytes);
bytes_read += num_bytes;
return bytes_read;
}
/* copy whatever is available in this block, and adjust for the next block */
memcpy(buffer, &regions->data[i][address], avail);
buffer += avail;
bytes_read += avail;
num_bytes -= avail;
address = 0;
}
return 0;
return bytes_read;
}
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
@ -616,7 +639,7 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
}
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id) {
rc_libretro_get_core_memory_info_func get_core_memory_info, uint32_t console_id) {
const rc_memory_regions_t* console_regions = rc_console_memory_regions(console_id);
rc_libretro_memory_regions_t new_regions;
int has_valid_region = 0;
@ -665,8 +688,7 @@ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
return;
file_handle = rc_file_open(m3u_path);
if (!file_handle)
{
if (!file_handle) {
rc_hash_error("Could not open playlist");
return;
}
@ -676,8 +698,7 @@ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
rc_file_seek(file_handle, 0, SEEK_SET);
m3u_contents = (char*)malloc((size_t)file_len + 1);
if (m3u_contents)
{
if (m3u_contents) {
rc_file_read(file_handle, m3u_contents, (int)file_len);
m3u_contents[file_len] = '\0';
@ -690,23 +711,19 @@ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
while (isspace((int)*ptr))
++ptr;
if (*ptr == '#')
{
if (*ptr == '#') {
/* ignore comment unless it's the special SAVEDISK extension */
if (memcmp(ptr, "#SAVEDISK:", 10) == 0)
{
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)))
{
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
{
else {
/* non-empty line, tally a file */
++index;
}
@ -720,8 +737,7 @@ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
free(m3u_contents);
}
if (hash_set->entries_count > 0)
{
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)))
@ -753,13 +769,10 @@ void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
struct rc_libretro_hash_entry_t* scan;
struct rc_libretro_hash_entry_t* stop = hash_set->entries + hash_set->entries_count;;
if (path_djb2)
{
if (path_djb2) {
/* attempt to match the path */
for (scan = hash_set->entries; scan < stop; ++scan)
{
if (scan->path_djb2 == path_djb2)
{
for (scan = hash_set->entries; scan < stop; ++scan) {
if (scan->path_djb2 == path_djb2) {
entry = scan;
break;
}
@ -769,19 +782,20 @@ void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
if (!entry)
{
/* entry not found, allocate a new one */
if (hash_set->entries_size == 0)
{
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)
{
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));
}
if (hash_set->entries == NULL) /* unexpected, but better than crashing */
return;
entry = hash_set->entries + hash_set->entries_count++;
}
@ -796,8 +810,7 @@ const char* rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* h
const uint32_t 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)
{
for (; scan < stop; ++scan) {
if (scan->path_djb2 == path_djb2)
return scan->hash;
}
@ -809,8 +822,7 @@ int rc_libretro_hash_set_get_game_id(const struct rc_libretro_hash_set_t* hash_s
{
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)
{
for (; scan < stop; ++scan) {
if (memcmp(scan->hash, hash, sizeof(scan->hash)) == 0)
return scan->game_id;
}

View file

@ -1,9 +1,7 @@
#ifndef RC_LIBRETRO_H
#define RC_LIBRETRO_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_export.h"
/* this file comes from the libretro repository, which is not an explicit submodule.
* the integration must set up paths appropriately to find it. */
@ -12,6 +10,8 @@ extern "C" {
#include <stddef.h>
#include <stdint.h>
RC_BEGIN_C_DECLS
/*****************************************************************************\
| Disallowed Settings |
\*****************************************************************************/
@ -22,17 +22,17 @@ typedef struct rc_disallowed_setting_t
const char* value;
} 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, uint32_t console_id);
RC_EXPORT const rc_disallowed_setting_t* RC_CCONV rc_libretro_get_disallowed_settings(const char* library_name);
RC_EXPORT int RC_CCONV rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
RC_EXPORT int RC_CCONV rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id);
/*****************************************************************************\
| Memory Mapping |
\*****************************************************************************/
/* specifies a function to call for verbose logging */
typedef void (*rc_libretro_message_callback)(const char*);
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback);
typedef void (RC_CCONV *rc_libretro_message_callback)(const char*);
RC_EXPORT void RC_CCONV rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback);
#define RC_LIBRETRO_MAX_MEMORY_REGIONS 32
typedef struct rc_libretro_memory_regions_t
@ -49,15 +49,15 @@ typedef struct rc_libretro_core_memory_info_t
size_t size;
} rc_libretro_core_memory_info_t;
typedef void (*rc_libretro_get_core_memory_info_func)(uint32_t id, rc_libretro_core_memory_info_t* info);
typedef void (RC_CCONV *rc_libretro_get_core_memory_info_func)(uint32_t id, rc_libretro_core_memory_info_t* info);
int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
rc_libretro_get_core_memory_info_func get_core_memory_info, int console_id);
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
RC_EXPORT int RC_CCONV rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
rc_libretro_get_core_memory_info_func get_core_memory_info, uint32_t console_id);
RC_EXPORT void RC_CCONV rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
uint8_t* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, uint32_t address);
uint8_t* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, uint32_t address, uint32_t* avail);
uint32_t rc_libretro_memory_read(const rc_libretro_memory_regions_t* regions, uint32_t address, uint8_t* buffer, uint32_t num_bytes);
RC_EXPORT uint8_t* RC_CCONV rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, uint32_t address);
RC_EXPORT uint8_t* RC_CCONV rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, uint32_t address, uint32_t* avail);
RC_EXPORT uint32_t RC_CCONV rc_libretro_memory_read(const rc_libretro_memory_regions_t* regions, uint32_t address, uint8_t* buffer, uint32_t num_bytes);
/*****************************************************************************\
| Disk Identification |
@ -77,19 +77,17 @@ typedef struct rc_libretro_hash_set_t
uint16_t entries_size;
} rc_libretro_hash_set_t;
typedef int (*rc_libretro_get_image_path_func)(uint32_t index, char* buffer, size_t buffer_size);
typedef int (RC_CCONV *rc_libretro_get_image_path_func)(uint32_t index, char* buffer, size_t buffer_size);
void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
RC_EXPORT void RC_CCONV 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);
RC_EXPORT void RC_CCONV 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, uint32_t 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);
RC_EXPORT void RC_CCONV rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* path, uint32_t game_id, const char hash[33]);
RC_EXPORT const char* RC_CCONV rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* hash_set, const char* path);
RC_EXPORT int RC_CCONV rc_libretro_hash_set_get_game_id(const struct rc_libretro_hash_set_t* hash_set, const char* hash);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_LIBRETRO_H */

View file

@ -35,7 +35,7 @@ void rc_buffer_destroy(rc_buffer_t* buffer)
{
rc_buffer_chunk_t* next = chunk->next;
#ifdef DEBUG_BUFFERS
total += (int)(chunk->end - chunk->data);
total += (int)(chunk->end - chunk->start);
wasted += (int)(chunk->end - chunk->write);
++count;
#endif

11
deps/rcheevos/src/rc_version.c vendored Normal file
View file

@ -0,0 +1,11 @@
#include "rc_version.h"
uint32_t rc_version(void)
{
return RCHEEVOS_VERSION;
}
const char* rc_version_string(void)
{
return RCHEEVOS_VERSION_STRING;
}

View file

@ -1,12 +1,14 @@
#ifndef RC_VERSION_H
#define RC_VERSION_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_export.h"
#include <stdint.h>
RC_BEGIN_C_DECLS
#define RCHEEVOS_VERSION_MAJOR 11
#define RCHEEVOS_VERSION_MINOR 0
#define RCHEEVOS_VERSION_MINOR 1
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
@ -22,8 +24,9 @@ extern "C" {
#define RCHEEVOS_VERSION_STRING RCHEEVOS_MAKE_VERSION_STRING_SHORT(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR)
#endif
#ifdef __cplusplus
}
#endif
RC_EXPORT uint32_t rc_version(void);
RC_EXPORT const char* rc_version_string(void);
RC_END_C_DECLS
#endif /* RC_VERSION_H */

View file

@ -5,7 +5,7 @@
void* rc_alloc_scratch(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset)
{
rc_scratch_buffer_t* buffer;
void* data;
/* if we have a real buffer, then allocate the data there */
if (pointer)
@ -19,49 +19,13 @@ void* rc_alloc_scratch(void* pointer, int32_t* offset, uint32_t size, uint32_t a
}
/* find a scratch buffer to hold the temporary data */
buffer = &scratch->buffer;
do {
const uint32_t aligned_buffer_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
if (aligned_buffer_offset < sizeof(buffer->buffer)) {
const uint32_t remaining = sizeof(buffer->buffer) - aligned_buffer_offset;
if (remaining >= size) {
/* claim the required space from an existing buffer */
return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1);
}
}
if (!buffer->next)
break;
buffer = buffer->next;
} while (1);
/* not enough space in any existing buffer, allocate more */
if (size > (uint32_t)sizeof(buffer->buffer)) {
/* caller is asking for more than we can fit in a standard rc_scratch_buffer_t.
* leverage the fact that the buffer is the last field and extend its size.
* this chunk will be exactly large enough to hold the needed data, and since offset
* will exceed sizeof(buffer->buffer), it will never be eligible to hold anything else.
*/
const size_t needed = sizeof(rc_scratch_buffer_t) - sizeof(buffer->buffer) + size;
buffer->next = (rc_scratch_buffer_t*)malloc(needed);
}
else {
buffer->next = (rc_scratch_buffer_t*)malloc(sizeof(rc_scratch_buffer_t));
}
if (!buffer->next) {
data = rc_buffer_alloc(&scratch->buffer, size);
if (!data) {
*offset = RC_OUT_OF_MEMORY;
return NULL;
}
buffer = buffer->next;
buffer->offset = 0;
buffer->next = NULL;
/* claim the required space from the new buffer */
return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1);
return data;
}
void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset) {
@ -73,7 +37,7 @@ void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment
/* valid buffer, grab the next chunk */
ptr = (void*)((char*)pointer + *offset);
}
else if (scratch != 0 && scratch_object_pointer_offset >= 0) {
else if (scratch != 0 && scratch_object_pointer_offset < sizeof(scratch->objs)) {
/* only allocate one instance of each object type (indentified by scratch_object_pointer_offset) */
void** scratch_object_pointer = (void**)((char*)&scratch->objs + scratch_object_pointer_offset);
ptr = *scratch_object_pointer;
@ -137,9 +101,8 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in
parse->L = L;
parse->funcs_ndx = funcs_ndx;
parse->buffer = buffer;
parse->scratch.buffer.offset = 0;
parse->scratch.buffer.next = NULL;
parse->scratch.strings = NULL;
rc_buffer_init(&parse->scratch.buffer);
memset(&parse->scratch.objs, 0, sizeof(parse->scratch.objs));
parse->first_memref = 0;
parse->variables = 0;
@ -151,12 +114,5 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in
void rc_destroy_parse_state(rc_parse_state_t* parse)
{
rc_scratch_buffer_t* buffer = parse->scratch.buffer.next;
rc_scratch_buffer_t* next;
while (buffer) {
next = buffer->next;
free(buffer);
buffer = next;
}
rc_buffer_destroy(&parse->scratch.buffer);
}

View file

@ -60,7 +60,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
/* right hand side is not required when Measured is used in a value */
if (is_value)
break;
/* fallthrough to default */
/* fallthrough */ /* to default */
default:
parse->offset = RC_INVALID_OPERATOR;

View file

@ -2,7 +2,7 @@
#include <ctype.h>
const char* rc_console_name(int console_id)
const char* rc_console_name(uint32_t console_id)
{
switch (console_id)
{
@ -195,6 +195,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_SHARPX1:
return "Sharp X1";
case RC_CONSOLE_STANDALONE:
return "Standalone";
case RC_CONSOLE_SUPER_NINTENDO:
return "Super Nintendo Entertainment System";
@ -564,6 +567,29 @@ static const rc_memory_region_t _rc_memory_regions_msx[] = {
};
static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_msx, 1 };
/* ===== MS DOS ===== */
static const rc_memory_region_t _rc_memory_regions_ms_dos[] = {
/* DOS emulators split the 640 KB conventional memory into two regions.
* First the part of the conventional memory given to the running game at $000000.
* The part of the conventional memory containing DOS and BIOS controlled memory
* is at $100000. The length of these can vary depending on the hardware
* and DOS version (or emulated DOS shell).
* These first two regions will only ever total to 640 KB but the regions map
* to 1 MB bounds to make resulting memory addresses more readable.
* When emulating a game not under DOS (so called 'PC Booter' games), the entirety
* of the 640 KB conventional memory block will be at $000000.
*/
{ 0x00000000U, 0x0009FFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Game Conventional Memory" },
{ 0x000A0000U, 0x000FFFFFU, 0x000A0000U, RC_MEMORY_TYPE_UNUSED, "Padding to align OS Conventional Memory" },
{ 0x00100000U, 0x0019FFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "OS Conventional Memory" },
{ 0x001A0000U, 0x001FFFFFU, 0x001A0000U, RC_MEMORY_TYPE_UNUSED, "Padding to align Expanded Memory" },
/* Last is all the expanded memory which for now we map up to 64 MB which should be
* enough for the games we want to cover. An emulator might emulate more than that.
*/
{ 0x00200000U, 0x041FFFFFU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Expanded Memory" }
};
static const rc_memory_regions_t rc_memory_regions_ms_dos = { _rc_memory_regions_ms_dos, 5 };
/* ===== Neo Geo Pocket ===== */
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
@ -969,6 +995,9 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id)
case RC_CONSOLE_MSX:
return &rc_memory_regions_msx;
case RC_CONSOLE_MS_DOS:
return &rc_memory_regions_ms_dos;
case RC_CONSOLE_NEOGEO_POCKET:
return &rc_memory_regions_neo_geo_pocket;

View file

@ -14,6 +14,9 @@ int rc_parse_format(const char* format_str) {
if (!strncmp(format_str, "LOAT", 4) && format_str[4] >= '1' && format_str[4] <= '6' && format_str[5] == '\0') {
return RC_FORMAT_FLOAT1 + (format_str[4] - '1');
}
if (!strncmp(format_str, "IXED", 4) && format_str[4] >= '1' && format_str[4] <= '3' && format_str[5] == '\0') {
return RC_FORMAT_FIXED1 + (format_str[4] - '1');
}
break;
@ -24,6 +27,12 @@ int rc_parse_format(const char* format_str) {
if (!strcmp(format_str, "IMESECS")) {
return RC_FORMAT_SECONDS;
}
if (!strcmp(format_str, "HOUSANDS")) {
return RC_FORMAT_THOUSANDS;
}
if (!strcmp(format_str, "ENS")) {
return RC_FORMAT_TENS;
}
break;
@ -64,11 +73,25 @@ int rc_parse_format(const char* format_str) {
break;
case 'U':
if (!strcmp(format_str, "NSIGNED")) {
return RC_FORMAT_UNSIGNED_VALUE;
}
break;
case 'O':
if (!strcmp(format_str, "THER")) {
return RC_FORMAT_SCORE;
}
break;
case 'H':
if (!strcmp(format_str, "UNDREDS")) {
return RC_FORMAT_HUNDREDS;
}
break;
}
@ -119,6 +142,22 @@ static int rc_format_value_centiseconds(char* buffer, size_t size, uint32_t cent
return chars;
}
static int rc_format_value_fixed(char* buffer, size_t size, const char* format, int32_t value, int32_t factor)
{
if (value >= 0)
return snprintf(buffer, size, format, value / factor, value % factor);
return snprintf(buffer, size, format, value / factor, (-value) % factor);
}
static int rc_format_value_padded(char* buffer, size_t size, const char* format, int32_t value)
{
if (value == 0)
return snprintf(buffer, size, "0");
return snprintf(buffer, size, format, value);
}
int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* value, int format) {
int chars;
rc_typed_value_t converted_value;
@ -192,6 +231,41 @@ int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* val
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT);
chars = snprintf(buffer, size, "%.6f", converted_value.value.f32);
break;
case RC_FORMAT_FIXED1:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_fixed(buffer, size, "%d.%u", converted_value.value.i32, 10);
break;
case RC_FORMAT_FIXED2:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_fixed(buffer, size, "%d.%02u", converted_value.value.i32, 100);
break;
case RC_FORMAT_FIXED3:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_fixed(buffer, size, "%d.%03u", converted_value.value.i32, 1000);
break;
case RC_FORMAT_TENS:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_padded(buffer, size, "%d0", converted_value.value.i32);
break;
case RC_FORMAT_HUNDREDS:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_padded(buffer, size, "%d00", converted_value.value.i32);
break;
case RC_FORMAT_THOUSANDS:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED);
chars = rc_format_value_padded(buffer, size, "%d000", converted_value.value.i32);
break;
case RC_FORMAT_UNSIGNED_VALUE:
rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED);
chars = snprintf(buffer, size, "%u", converted_value.value.u32);
break;
}
return chars;

View file

@ -235,7 +235,7 @@ int rc_evaluate_lboard(rc_lboard_t* self, int32_t* value, rc_peek_t peek, void*
*value = rc_evaluate_value(self->progress, peek, peek_ud, L);
break;
}
/* fallthrough to RC_LBOARD_STATE_TRIGGERED */
/* fallthrough */ /* to RC_LBOARD_STATE_TRIGGERED */
case RC_LBOARD_STATE_TRIGGERED:
*value = rc_evaluate_value(&self->value, peek, peek_ud, L);

View file

@ -6,16 +6,12 @@
#ifndef RC_DISABLE_LUA
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
#include <lua.h>
#include <lauxlib.h>
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_DISABLE_LUA */
@ -238,7 +234,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire
case '0':
if (aux[1] == 'x' || aux[1] == 'X') { /* hex integer constant */
/* fall through */
/* fallthrough */ /* to default */
default:
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
@ -247,8 +243,7 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indire
break;
}
/* fall through for case '0' where not '0x' */
/* fallthrough */ /* to case '1' for case '0' where not '0x' */
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
case '6': case '7': case '8': case '9':
value = strtoul(aux, &end, 10);

View file

@ -2,10 +2,9 @@
#define RC_INTERNAL_H
#include "rc_runtime_types.h"
#include "rc_util.h"
#ifdef __cplusplus
extern "C" {
#endif
RC_BEGIN_C_DECLS
typedef struct rc_scratch_string {
char* value;
@ -39,15 +38,8 @@ RC_ALLOW_ALIGN(char)
/* force alignment to 4 bytes on 32-bit systems, or 8 bytes on 64-bit systems */
#define RC_ALIGN(n) (((n) + (sizeof(void*)-1)) & ~(sizeof(void*)-1))
typedef struct rc_scratch_buffer {
struct rc_scratch_buffer* next;
int32_t offset;
uint8_t buffer[512 - 16];
}
rc_scratch_buffer_t;
typedef struct {
rc_scratch_buffer_t buffer;
rc_buffer_t buffer;
rc_scratch_string_t* strings;
struct objs {
@ -200,8 +192,6 @@ int rc_lboard_state_active(int state);
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
#ifdef __cplusplus
}
#endif
RC_END_C_DECLS
#endif /* RC_INTERNAL_H */

View file

@ -189,6 +189,10 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
{"Float4", 6, RC_FORMAT_FLOAT4},
{"Float5", 6, RC_FORMAT_FLOAT5},
{"Float6", 6, RC_FORMAT_FLOAT6},
{"Fixed1", 6, RC_FORMAT_FIXED1},
{"Fixed2", 6, RC_FORMAT_FIXED2},
{"Fixed3", 6, RC_FORMAT_FIXED3},
{"Unsigned", 8, RC_FORMAT_UNSIGNED_VALUE}
};
size_t i;
@ -279,7 +283,6 @@ static void rc_rebalance_richpresence_lookup_rebuild(rc_richpresence_lookup_item
static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** root, rc_parse_state_t* parse)
{
rc_richpresence_lookup_item_t** items;
rc_scratch_buffer_t* buffer;
int index;
int size;
@ -288,29 +291,13 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo
if (count < 3)
return;
/* allocate space for the flattened list - prefer scratch memory if available */
/* allocate space for the flattened list in scratch memory */
size = count * sizeof(rc_richpresence_lookup_item_t*);
buffer = &parse->scratch.buffer;
do {
const int aligned_offset = RC_ALIGN(buffer->offset);
const int remaining = sizeof(buffer->buffer) - aligned_offset;
items = (rc_richpresence_lookup_item_t**)rc_buffer_alloc(&parse->scratch.buffer, size);
if (remaining >= size) {
items = (rc_richpresence_lookup_item_t**)&buffer->buffer[aligned_offset];
break;
}
buffer = buffer->next;
if (buffer == NULL) {
/* could not find large enough block of scratch memory; allocate. if allocation fails,
* we can still use the unbalanced tree, so just bail out */
items = (rc_richpresence_lookup_item_t**)malloc(size);
if (items == NULL)
return;
break;
}
} while (1);
/* if allocation fails, we can still use the unbalanced tree, so just bail out */
if (items == NULL)
return;
/* flatten the list */
index = 0;
@ -318,9 +305,6 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo
/* and rebuild it as a balanced tree */
rc_rebalance_richpresence_lookup_rebuild(root, items, 0, count - 1);
if (buffer == NULL)
free(items);
}
static void rc_insert_richpresence_lookup_item(rc_richpresence_lookup_t* lookup,

View file

@ -1,7 +1,7 @@
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rc_util.h"
#include "rc_util.h"
#include "../rhash/md5.h"
#include <stdlib.h>

View file

@ -447,7 +447,7 @@ void rc_typed_value_negate(rc_typed_value_t* value) {
{
case RC_VALUE_TYPE_UNSIGNED:
rc_typed_value_convert(value, RC_VALUE_TYPE_SIGNED);
/* fallthrough to RC_VALUE_TYPE_SIGNED */
/* fallthrough */ /* to RC_VALUE_TYPE_SIGNED */
case RC_VALUE_TYPE_SIGNED:
value->value.i32 = -(value->value.i32);

480
deps/rcheevos/src/rhash/aes.c vendored Normal file
View file

@ -0,0 +1,480 @@
/* This file is sourced from https://github.com/kokke/tiny-AES-c, with unused code excised.
* This code is licensed under the Unlicense license, effectively public domain.
* https://github.com/kokke/tiny-AES-c/blob/f06ac37/unlicense.txt
*/
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> /* CBC mode, for memset */
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
/* The number of columns comprising a state in AES. This is a constant in AES. Value=4 */
#define Nb 4
#define Nk 4 /* The number of 32 bit words in a key. */
#define Nr 10 /* The number of rounds in AES Cipher. */
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
/* state - array holding the intermediate results during decryption. */
typedef uint8_t state_t[4][4];
/* The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
* The numbers below can be computed dynamically trading ROM for RAM -
* This can be useful in (embedded) bootloader applications, where ROM is often limited.
*/
static const uint8_t sbox[256] = {
/*0 1 2 3 4 5 6 7 8 9 A B C D E F */
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
/* The round constant word array, Rcon[i], contains the values given by
* x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
*/
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
#define getSBoxValue(num) (sbox[(num)])
/* This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. */
static void KeyExpansion(uint8_t RoundKey[AES_keyExpSize], const uint8_t Key[AES_KEYLEN])
{
unsigned i, j, k;
uint8_t tempa[4]; /* Used for the column/row operations */
/* The first round key is the key itself. */
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
/* All other round keys are found from the previous round keys. */
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
/* This function shifts the 4 bytes in a word to the left once. */
/* [a0,a1,a2,a3] becomes [a1,a2,a3,a0] */
/* Function RotWord() */
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
/* SubWord() is a function that takes a four-byte input word and
* applies the S-box to each of the four bytes to produce an output word.
*/
/* Function Subword() */
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t key[AES_KEYLEN])
{
KeyExpansion(ctx->RoundKey, key);
}
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t key[AES_KEYLEN], const uint8_t iv[AES_BLOCKLEN])
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t iv[AES_BLOCKLEN])
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
/* This function adds the round key to state.
* The round key is added to the state by an XOR function.
*/
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t RoundKey[AES_keyExpSize])
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
/* The SubBytes Function Substitutes the values in the
* state matrix with values in an S-box.
*/
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
/* The ShiftRows() function shifts the rows in the state to the left.
* Each row is shifted with different offset.
* Offset = Row number. So the first row is not shifted.
*/
static void ShiftRows(state_t* state)
{
uint8_t temp;
/* Rotate first row 1 columns to left */
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
/* Rotate second row 2 columns to left */
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
/* Rotate third row 3 columns to left */
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
/* MixColumns function mixes the columns of the state matrix */
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
/* Multiply is used to multiply numbers in the field GF(2^8)
* Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
* The compiler seems to be able to vectorize the operation better this way.
* See https://github.com/kokke/tiny-AES-c/pull/34
*/
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#define getSBoxInvert(num) (rsbox[(num)])
/* MixColumns function mixes the columns of the state matrix.
* The method used to multiply may be difficult to understand for the inexperienced.
* Please use the references to gain more information.
*/
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
/* The SubBytes Function Substitutes the values in the
* state matrix with values in an S-box.
*/
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
/* Rotate first row 1 columns to right */
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
/* Rotate second row 2 columns to right */
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
/* Rotate third row 3 columns to right */
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
/* Cipher is the main function that encrypts the PlainText. */
static void Cipher(state_t* state, const uint8_t RoundKey[AES_keyExpSize])
{
uint8_t round = 0;
/* Add the First round key to the state before starting the rounds. */
AddRoundKey(0, state, RoundKey);
/* There will be Nr rounds.
* The first Nr-1 rounds are identical.
* These Nr rounds are executed in the loop below.
* Last one without MixColumns()
*/
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
/* Add round key to last round */
AddRoundKey(Nr, state, RoundKey);
}
static void InvCipher(state_t* state, const uint8_t RoundKey[AES_keyExpSize])
{
uint8_t round = 0;
/* Add the First round key to the state before starting the rounds. */
AddRoundKey(Nr, state, RoundKey);
/* There will be Nr rounds.
* The first Nr-1 rounds are identical.
* These Nr rounds are executed in the loop below.
* Last one without InvMixColumn()
*/
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
static void XorWithIv(uint8_t* buf, const uint8_t Iv[AES_BLOCKLEN])
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) /* The block in AES is always 128bit no matter the key size */
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];
size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer, ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}

49
deps/rcheevos/src/rhash/aes.h vendored Normal file
View file

@ -0,0 +1,49 @@
#ifndef AES_H
#define AES_H
/* This file is sourced from https://github.com/kokke/tiny-AES-c, with unused code excised.
* This code is licensed under the Unlicense license, effectively public domain.
* https://github.com/kokke/tiny-AES-c/blob/f06ac37/unlicense.txt
*/
#include <stdint.h>
#include <stddef.h>
#define AES_BLOCKLEN 16 /* Block length in bytes - AES is 128b block only */
#define AES_KEYLEN 16 /* Key length in bytes */
#define AES_keyExpSize 176
struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
uint8_t Iv[AES_BLOCKLEN];
};
#ifdef __cplusplus
extern "C" {
#endif
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t key[AES_KEYLEN]);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t key[AES_KEYLEN], const uint8_t iv[AES_BLOCKLEN]);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t iv[AES_BLOCKLEN]);
/* buffer size MUST be mutile of AES_BLOCKLEN;
* Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
* NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
* no IV should ever be reused with the same key
*/
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
/* Same function for encrypting as for decrypting.
* IV is incremented for every block, and used after encryption as XOR-compliment for output
* Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
* NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
* no IV should ever be reused with the same key
*/
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
#ifdef __cplusplus
}
#endif
#endif /* AES_H */

File diff suppressed because it is too large Load diff

View file

@ -223,6 +223,7 @@ ACHIEVEMENTS
#include "../deps/rcheevos/src/rcheevos/runtime_progress.c"
#include "../deps/rcheevos/src/rcheevos/trigger.c"
#include "../deps/rcheevos/src/rcheevos/value.c"
#include "../deps/rcheevos/src/rhash/aes.c"
#include "../deps/rcheevos/src/rhash/cdreader.c"
#include "../deps/rcheevos/src/rhash/hash.c"

View file

@ -6247,22 +6247,23 @@ static enum runloop_state_enum runloop_check_state(
#ifdef HAVE_CHEEVOS
if (cheevos_hardcore_active)
{
static int unpaused_frames = 0;
if (runloop_st->flags & RUNLOOP_FLAG_PAUSED)
unpaused_frames = 0;
else
/* Frame advance is not allowed when achievement hardcore is active */
if (!(runloop_st->flags & RUNLOOP_FLAG_PAUSED))
{
/* Limit pause to approximately three times per second (depending on core framerate) */
if (unpaused_frames < 20)
/* In hardcore mode, the user is only allowed to pause infrequently. */
if ((pause_pressed && !old_pause_pressed) ||
(!focused && old_focus && pause_nonactive))
{
++unpaused_frames;
pause_pressed = false;
/* If the user is trying to pause, check to see if it's allowed. */
if (!rcheevos_is_pause_allowed())
{
pause_pressed = false;
if (pause_nonactive)
focused = true;
}
}
}
}
else
else /* frame advance not allowed in hardcore */
#endif
{
frameadvance_pressed = BIT256_GET(current_bits, RARCH_FRAMEADVANCE);