[cheevos] upgrade to rcheevos 11.0 (#15859)

* update rcheevos

* update rcheevos
This commit is contained in:
Jamiras 2023-11-02 12:52:36 -06:00 committed by GitHub
parent 636a6e9d55
commit 8523eaf5c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 7857 additions and 1196 deletions

View file

@ -2101,12 +2101,24 @@ ifeq ($(HAVE_NETWORKING), 1)
DEFINES += -DHAVE_CHEEVOS
INCLUDE_DIRS += -Ideps/rcheevos/include
ifneq ($(HAVE_THREADS), 1)
DEFINES += -DRC_NO_THREADS
else ifneq (,$(filter GEKKO,$(CFLAGS)))
# Gekko (Wii) and 3DS use custom pthread wrappers (see rthreads.c)
DEFINES += -DRC_NO_THREADS
else ifneq (,$(filter _3DS,$(CFLAGS)))
DEFINES += -DRC_NO_THREADS
endif
OBJ += cheevos/cheevos.o \
cheevos/cheevos_client.o \
cheevos/cheevos_menu.o \
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
deps/rcheevos/src/rc_client.o \
deps/rcheevos/src/rc_compat.o \
deps/rcheevos/src/rc_libretro.o \
deps/rcheevos/src/rc_util.o \
deps/rcheevos/src/rcheevos/alloc.o \
deps/rcheevos/src/rcheevos/compat.o \
deps/rcheevos/src/rcheevos/condition.o \
deps/rcheevos/src/rcheevos/condset.o \
deps/rcheevos/src/rcheevos/consoleinfo.o \
@ -2114,7 +2126,6 @@ ifeq ($(HAVE_NETWORKING), 1)
deps/rcheevos/src/rcheevos/lboard.o \
deps/rcheevos/src/rcheevos/memref.o \
deps/rcheevos/src/rcheevos/operand.o \
deps/rcheevos/src/rcheevos/rc_libretro.o \
deps/rcheevos/src/rcheevos/richpresence.o \
deps/rcheevos/src/rcheevos/runtime.o \
deps/rcheevos/src/rcheevos/runtime_progress.o \
@ -2123,6 +2134,7 @@ ifeq ($(HAVE_NETWORKING), 1)
deps/rcheevos/src/rhash/cdreader.o \
deps/rcheevos/src/rhash/hash.o \
deps/rcheevos/src/rapi/rc_api_common.o \
deps/rcheevos/src/rapi/rc_api_info.o \
deps/rcheevos/src/rapi/rc_api_runtime.o \
deps/rcheevos/src/rapi/rc_api_user.o \

View file

@ -75,7 +75,7 @@
#include "../deps/rcheevos/include/rc_runtime.h"
#include "../deps/rcheevos/include/rc_runtime_types.h"
#include "../deps/rcheevos/include/rc_hash.h"
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
#include "../deps/rcheevos/src/rc_libretro.h"
/* Define this macro to prevent cheevos from being deactivated when they trigger. */
#undef CHEEVOS_DONT_DEACTIVATE
@ -163,7 +163,7 @@ static void rcheevos_handle_log_message(const char* message)
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", message);
}
static void rcheevos_get_core_memory_info(unsigned id,
static void rcheevos_get_core_memory_info(uint32_t id,
rc_libretro_core_memory_info_t* info)
{
retro_ctx_memory_info_t ctx_info;
@ -220,10 +220,10 @@ uint8_t* rcheevos_patch_address(unsigned address)
return rc_libretro_memory_find(&rcheevos_locals.memory, address);
}
static unsigned rcheevos_peek(unsigned address,
unsigned num_bytes, void* ud)
static uint32_t rcheevos_peek(uint32_t address,
uint32_t num_bytes, void* ud)
{
unsigned avail;
uint32_t avail;
uint8_t* data = rc_libretro_memory_find_avail(
&rcheevos_locals.memory, address, &avail);
@ -1321,7 +1321,7 @@ static void rcheevos_runtime_event_handler(
}
}
static int rcheevos_runtime_address_validator(unsigned address)
static int rcheevos_runtime_address_validator(uint32_t address)
{
return rc_libretro_memory_find(
&rcheevos_locals.memory, address) != NULL;
@ -2066,7 +2066,7 @@ static void rcheevos_identify_game_callback(void* userdata)
rcheevos_fetch_game_data();
}
static int rcheevos_get_image_path(unsigned index, char* buffer, size_t buffer_size)
static int rcheevos_get_image_path(uint32_t index, char* buffer, size_t buffer_size)
{
rarch_system_info_t *sys_info = &runloop_state_get_ptr()->system;
if (!sys_info->disk_control.cb.get_image_path)

View file

@ -1756,7 +1756,7 @@ static void rcheevos_async_award_achievement_callback(
{
if ((int)api_response.awarded_achievement_id != request->id)
snprintf(buffer, buffer_size, "Achievement %u awarded instead",
api_response.awarded_achievement_id);
(unsigned)api_response.awarded_achievement_id);
else if (api_response.response.error_message)
{
/* previously unlocked achievements are returned as a "successful" error */

View file

@ -18,7 +18,7 @@
#define __RARCH_CHEEVOS_LOCALS_H
#include "../deps/rcheevos/include/rc_runtime.h"
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
#include "../deps/rcheevos/src/rc_libretro.h"
#include <boolean.h>
#include <queues/task_queue.h>

View file

@ -1,3 +1,25 @@
# v11.0.0
* add rc_client_t and related functions
* add RC_MEMSIZE_FLOAT_BE
* add Game Pak SRAM to GBA memory map
* add hash method for Super Cassettevision
* add PSP to potential consoles for chd iterator
* add content_type to rc_api_request_t for client to pass to server
* add rc_api_process_X_server_response methods to pass status_code and body_length to response processing functions
* add additional error codes to rc_api_process_login_response: RC_INVALID_CREDENTIALS, RC_EXPIRED_TOKEN, RC_ACCESS_DENIED
* rc_api_start_session now also returns unlocks without having to explicitly call rc_api_fetch_user_unlocks separately
* add validation warning for using hit target of 1 on ResetIf condition
* move compat.c up a directory and rename to rc_compat.c as it's shared by all subfolders
* move rc_libretro.c up a directory as it uses files from all subfolders
* convert loosely sized types to strongly sized types (unsigned -> uint32t, unsigned char -> uint8_t, etc)
# v10.7.1
* add rc_runtime_alloc
* add rc_libretro_memory_find_avail
* extract nginx errors from HTML returned for JSON endpoints
* fix real address for 32X extension RAM
* fix crash attempting to calculate gamecube hash for non-existent file
# v10.7.0
* add hash method and memory map for Gamecube
* add console enum, hash method, and memory map for DSi

View file

@ -6,13 +6,11 @@ Keep in mind that **rcheevos** does *not* provide HTTP network connections. Clie
Not all structures defined by **rcheevos** can be created via the public API, but are exposed to allow interactions beyond just creation, destruction, and testing, such as the ones required by UI code that helps to create them.
Finally, **rcheevos** does *not* allocate or manage memory by itself. All structures that can be returned by it have a function to determine the number of bytes needed to hold the structure, and another one that actually builds the structure using a caller-provided buffer to bake it.
## Lua
RetroAchievements is considering the use of the [Lua](https://www.lua.org) language to expand the syntax supported for creating achievements. The current expression-based implementation is often limiting on newer systems.
RetroAchievements previously considered the use of the [Lua](https://www.lua.org) language to expand the syntax supported for creating achievements.
At this point, to enable Lua support, you must compile with an additional compilation flag: `HAVE_LUA`, as neither the backend nor the UI for editing achievements are currently Lua-enabled.
To enable Lua support, you must compile with an additional compilation flag: `HAVE_LUA`, as neither the backend nor the UI for editing achievements are currently Lua-enabled. We do not foresee enabling it any time soon, but the code has not yet been completely eliminated as many of the low-level API fuctions have parameters for LUA data.
> **rcheevos** does *not* create or maintain a Lua state, you have to create your own state and provide it to **rcheevos** to be used when Lua-coded achievements are found. Calls to **rcheevos** may allocate and/or free additional memory as part of the Lua runtime.
@ -28,49 +26,9 @@ An understanding about how achievements are developed may be useful, you can rea
Most of the exposed APIs are documented [here](https://github.com/RetroAchievements/rcheevos/wiki)
### User Configuration
There's only one thing that can be configured by users of **rcheevos**: `RC_ALIGNMENT`. This macro holds the alignment of allocations made in the buffer provided to the parsing functions, and the default value is `sizeof(void*)`.
If your platform will benefit from a different value, define a new value for it on your compiler flags before compiling the code. It has to be a power of 2, but no checking is done.
### Return values
Any function in the rcheevos library that returns a success indicator will return one of the following values.
These are in `rc_error.h`.
```c
enum {
RC_OK = 0,
RC_INVALID_LUA_OPERAND = -1,
RC_INVALID_MEMORY_OPERAND = -2,
RC_INVALID_CONST_OPERAND = -3,
RC_INVALID_FP_OPERAND = -4,
RC_INVALID_CONDITION_TYPE = -5,
RC_INVALID_OPERATOR = -6,
RC_INVALID_REQUIRED_HITS = -7,
RC_DUPLICATED_START = -8,
RC_DUPLICATED_CANCEL = -9,
RC_DUPLICATED_SUBMIT = -10,
RC_DUPLICATED_VALUE = -11,
RC_DUPLICATED_PROGRESS = -12,
RC_MISSING_START = -13,
RC_MISSING_CANCEL = -14,
RC_MISSING_SUBMIT = -15,
RC_MISSING_VALUE = -16,
RC_INVALID_LBOARD_FIELD = -17,
RC_MISSING_DISPLAY_STRING = -18,
RC_OUT_OF_MEMORY = -19,
RC_INVALID_VALUE_FLAG = -20,
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23,
RC_INVALID_COMPARISON = -24,
RC_INVALID_STATE = -25,
RC_INVALID_JSON = -26
};
```
Any function in the rcheevos library that returns a success indicator will return `RC_OK` or one of the constants defined in `rc_error.h`.
To convert the return code into something human-readable, pass it to:
```c
@ -79,189 +37,17 @@ const char* rc_error_str(int ret);
### Console identifiers
This enumeration uniquely identifies each of the supported platforms in RetroAchievements.
These are in `rc_consoles.h`.
```c
enum {
RC_CONSOLE_MEGA_DRIVE = 1,
RC_CONSOLE_NINTENDO_64 = 2,
RC_CONSOLE_SUPER_NINTENDO = 3,
RC_CONSOLE_GAMEBOY = 4,
RC_CONSOLE_GAMEBOY_ADVANCE = 5,
RC_CONSOLE_GAMEBOY_COLOR = 6,
RC_CONSOLE_NINTENDO = 7,
RC_CONSOLE_PC_ENGINE = 8,
RC_CONSOLE_SEGA_CD = 9,
RC_CONSOLE_SEGA_32X = 10,
RC_CONSOLE_MASTER_SYSTEM = 11,
RC_CONSOLE_PLAYSTATION = 12,
RC_CONSOLE_ATARI_LYNX = 13,
RC_CONSOLE_NEOGEO_POCKET = 14,
RC_CONSOLE_GAME_GEAR = 15,
RC_CONSOLE_GAMECUBE = 16,
RC_CONSOLE_ATARI_JAGUAR = 17,
RC_CONSOLE_NINTENDO_DS = 18,
RC_CONSOLE_WII = 19,
RC_CONSOLE_WII_U = 20,
RC_CONSOLE_PLAYSTATION_2 = 21,
RC_CONSOLE_XBOX = 22,
RC_CONSOLE_MAGNAVOX_ODYSSEY2 = 23,
RC_CONSOLE_POKEMON_MINI = 24,
RC_CONSOLE_ATARI_2600 = 25,
RC_CONSOLE_MS_DOS = 26,
RC_CONSOLE_ARCADE = 27,
RC_CONSOLE_VIRTUAL_BOY = 28,
RC_CONSOLE_MSX = 29,
RC_CONSOLE_COMMODORE_64 = 30,
RC_CONSOLE_ZX81 = 31,
RC_CONSOLE_ORIC = 32,
RC_CONSOLE_SG1000 = 33,
RC_CONSOLE_VIC20 = 34,
RC_CONSOLE_AMIGA = 35,
RC_CONSOLE_ATARI_ST = 36,
RC_CONSOLE_AMSTRAD_PC = 37,
RC_CONSOLE_APPLE_II = 38,
RC_CONSOLE_SATURN = 39,
RC_CONSOLE_DREAMCAST = 40,
RC_CONSOLE_PSP = 41,
RC_CONSOLE_CDI = 42,
RC_CONSOLE_3DO = 43,
RC_CONSOLE_COLECOVISION = 44,
RC_CONSOLE_INTELLIVISION = 45,
RC_CONSOLE_VECTREX = 46,
RC_CONSOLE_PC8800 = 47,
RC_CONSOLE_PC9800 = 48,
RC_CONSOLE_PCFX = 49,
RC_CONSOLE_ATARI_5200 = 50,
RC_CONSOLE_ATARI_7800 = 51,
RC_CONSOLE_X68K = 52,
RC_CONSOLE_WONDERSWAN = 53,
RC_CONSOLE_CASSETTEVISION = 54,
RC_CONSOLE_SUPER_CASSETTEVISION = 55,
RC_CONSOLE_NEO_GEO_CD = 56,
RC_CONSOLE_FAIRCHILD_CHANNEL_F = 57,
RC_CONSOLE_FM_TOWNS = 58,
RC_CONSOLE_ZX_SPECTRUM = 59,
RC_CONSOLE_GAME_AND_WATCH = 60,
RC_CONSOLE_NOKIA_NGAGE = 61,
RC_CONSOLE_NINTENDO_3DS = 62,
RC_CONSOLE_SUPERVISION = 63,
RC_CONSOLE_SHARPX1 = 64,
RC_CONSOLE_TIC80 = 65,
RC_CONSOLE_THOMSONTO8 = 66
};
```
Platforms supported by RetroAchievements are enumerated in `rc_consoles.h`. Note that some consoles in the enum are not yet fully supported (may require a memory map or some way to uniquely identify games).
## Runtime support
The runtime encapsulates a set of achievements, leaderboards, and rich presence for a game and manages processing them for each frame. When important things occur, events are raised for the caller via a callback.
Provides a set of functions for managing an active game - initializing and processing achievements, leaderboards, and rich presence. When important things occur, events are raised for the caller via a callback.
These are in `rc_runtime.h`.
The `rc_runtime_t` structure uses several forward-defines. If you need access to the actual contents of any of the forward-defined structures, those definitions are in `rc_runtime_types.h`
Note: `rc_runtime_t` still requires the client implement all of the logic that calls the APIs to retrieve the data and perform the unlocks.
```c
typedef struct rc_runtime_t {
rc_runtime_trigger_t* triggers;
unsigned trigger_count;
unsigned trigger_capacity;
rc_runtime_lboard_t* lboards;
unsigned lboard_count;
unsigned lboard_capacity;
rc_runtime_richpresence_t* richpresence;
rc_memref_value_t* memrefs;
rc_memref_value_t** next_memref;
rc_value_t* variables;
rc_value_t** next_variable;
}
rc_runtime_t;
```
The runtime must first be initialized.
```c
void rc_runtime_init(rc_runtime_t* runtime);
```
Then individual achievements, leaderboards, and even rich presence can be loaded into the runtime. These functions return RC_OK, or one of the negative value error codes listed above.
```c
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
```
The runtime should be called once per frame to evaluate the state of the active achievements/leaderboards:
```c
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);
```
The `event_handler` is a callback function that is called for each event that occurs when processing the frame.
```c
typedef struct rc_runtime_event_t {
unsigned id;
int value;
char type;
}
rc_runtime_event_t;
typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_event);
```
The `event.type` field will be one of the following:
* RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED (id=achievement id)
An achievement starts in the RC_TRIGGER_STATE_WAITING state and cannot trigger until it has been false for at least one frame. This event indicates the achievement is no longer waiting and may trigger on a future frame.
* RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED (id=achievement id)
One or more conditions in the achievement have disabled the achievement.
* RC_RUNTIME_EVENT_ACHIEVEMENT_RESET (id=achievement id)
One or more conditions in the achievement have reset any progress captured in the achievement.
* RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED (id=achievement id)
All conditions for the achievement have been met and the user should be informed.
NOTE: If `rc_runtime_reset` is called without deactivating the achievement, it may trigger again.
* RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED (id=achievement id)
All non-trigger conditions for the achievement have been met. This typically indicates the achievement is a challenge achievement and the challenge is active.
* RC_RUNTIME_EVENT_LBOARD_STARTED (id=leaderboard id, value=leaderboard value)
The leaderboard's start condition has been met and the user should be informed that a leaderboard attempt has started.
* RC_RUNTIME_EVENT_LBOARD_CANCELED (id=leaderboard id, value=leaderboard value)
The leaderboard's cancel condition has been met and the user should be informed that a leaderboard attempt has failed.
* RC_RUNTIME_EVENT_LBOARD_UPDATED (id=leaderboard id, value=leaderboard value)
The leaderboard value has changed.
* RC_RUNTIME_EVENT_LBOARD_TRIGGERED (id=leaderboard id, value=leaderboard value)
The leaderboard's submit condition has been met and the user should be informed that a leaderboard attempt was successful. The value should be submitted.
* RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED (id=achievement id)
The achievement has been disabled by a call to `rc_invalidate_address`.
* RC_RUNTIME_EVENT_LBOARD_DISABLED (id=leaderboard id)
The achievement has been disabled by a call to `rc_invalidate_address`.
When an achievement triggers, it should be deactivated so it won't trigger again:
```c
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
```
Additionally, the unlock should be submitted to the server.
When a leaderboard triggers, it should not be deactivated in case the player wants to try again for a better score. The value should be submitted to the server.
For `RC_RUNTIME_EVENT_LBOARD_UPDATED` and `RC_RUNTIME_EVENT_LBOARD_TRIGGERED` events, there is a helper function to call if you wish to display the leaderboard value on screen.
```c
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format);
```
`rc_runtime_do_frame` also periodically updates the rich presense string (every 60 frames). To get the current value, call
```c
const char* rc_runtime_get_richpresence(const rc_runtime_t* runtime);
```
When the game is reset, the runtime should also be reset:
```c
void rc_runtime_reset(rc_runtime_t* runtime);
```
This ensures any active achievements/leaderboards are set back to their initial states and prevents unexpected triggers when the memory changes in atypical way.
The `rc_client_t` functions wrap a `rc_runtime_t` and manage the API calls and other common functionality (like managing the user information, identifying/loading a game, and building the active/inactive achievements list for the UI). Please see [the wiki](https://github.com/RetroAchievements/rcheevos/wiki/rc_client-integration) for details on using the `rc_client_t` functions.
## Server Communication
@ -271,6 +57,8 @@ This ensures any active achievements/leaderboards are set back to their initial
NOTE: **rapi** is a replacement for **rurl**. **rurl** has been deprecated.
NOTE: `rc_client` is the preferred way to have a client interact with the server.
These are in `rc_api_user.h`, `rc_api_runtime.h` and `rc_api_common.h`.
The basic process of making an **rapi** call is to initialize a params object, call a function to convert it to a URL, send that to the server, then pass the response to a function to convert it into a response object, and handle the response values.
@ -291,6 +79,3 @@ These are in `rc_hash.h`.
int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer, size_t buffer_size);
int rc_hash_generate_from_file(char hash[33], int console_id, const char* path);
```

View file

@ -1 +0,0 @@
theme: jekyll-theme-midnight

View file

@ -3,6 +3,7 @@
#include "rc_api_request.h"
#include <stdint.h>
#include <time.h>
#ifdef __cplusplus
@ -20,13 +21,13 @@ typedef struct rc_api_fetch_achievement_info_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the achievement */
unsigned achievement_id;
uint32_t achievement_id;
/* The 1-based index of the first entry to retrieve */
unsigned first_entry;
uint32_t first_entry;
/* The number of entries to retrieve */
unsigned count;
uint32_t count;
/* Non-zero to only return unlocks earned by the user's friends */
unsigned friends_only;
uint32_t friends_only;
}
rc_api_fetch_achievement_info_request_t;
@ -44,18 +45,18 @@ rc_api_achievement_awarded_entry_t;
*/
typedef struct rc_api_fetch_achievement_info_response_t {
/* The unique identifier of the achievement */
unsigned id;
uint32_t id;
/* The unique identifier of the game to which the leaderboard is associated */
unsigned game_id;
uint32_t game_id;
/* The number of times the achievement has been awarded */
unsigned num_awarded;
uint32_t num_awarded;
/* The number of players that have earned at least one achievement for the game */
unsigned num_players;
uint32_t num_players;
/* An array of recently rewarded entries */
rc_api_achievement_awarded_entry_t* recently_awarded;
/* The number of items in the recently_awarded array */
unsigned num_recently_awarded;
uint32_t num_recently_awarded;
/* Common server-provided response information */
rc_api_response_t response;
@ -64,6 +65,7 @@ rc_api_fetch_achievement_info_response_t;
int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const rc_api_fetch_achievement_info_request_t* api_params);
int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response);
int rc_api_process_fetch_achievement_info_server_response(rc_api_fetch_achievement_info_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response);
/* --- Fetch Leaderboard Info --- */
@ -73,11 +75,11 @@ void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_inf
*/
typedef struct rc_api_fetch_leaderboard_info_request_t {
/* The unique identifier of the leaderboard */
unsigned leaderboard_id;
uint32_t leaderboard_id;
/* The number of entries to retrieve */
unsigned count;
uint32_t count;
/* The 1-based index of the first entry to retrieve */
unsigned first_entry;
uint32_t first_entry;
/* The username of the player around whom the entries should be returned */
const char* username;
}
@ -88,11 +90,11 @@ typedef struct rc_api_lboard_info_entry_t {
/* The user associated to the entry */
const char* username;
/* The rank of the entry */
unsigned rank;
uint32_t rank;
/* The index of the entry */
unsigned index;
uint32_t index;
/* The value of the entry */
int score;
int32_t score;
/* When the entry was submitted */
time_t submitted;
}
@ -103,11 +105,11 @@ rc_api_lboard_info_entry_t;
*/
typedef struct rc_api_fetch_leaderboard_info_response_t {
/* The unique identifier of the leaderboard */
unsigned id;
uint32_t id;
/* The format to pass to rc_format_value to format the leaderboard value */
int format;
/* If non-zero, indicates that lower scores appear first */
int lower_is_better;
uint32_t lower_is_better;
/* The title of the leaderboard */
const char* title;
/* The description of the leaderboard */
@ -115,7 +117,7 @@ typedef struct rc_api_fetch_leaderboard_info_response_t {
/* The definition of the leaderboard to be passed to rc_runtime_activate_lboard */
const char* definition;
/* The unique identifier of the game to which the leaderboard is associated */
unsigned game_id;
uint32_t game_id;
/* The author of the leaderboard */
const char* author;
/* When the leaderboard was first uploaded to the server */
@ -126,7 +128,7 @@ typedef struct rc_api_fetch_leaderboard_info_response_t {
/* An array of requested entries */
rc_api_lboard_info_entry_t* entries;
/* The number of items in the entries array */
unsigned num_entries;
uint32_t num_entries;
/* Common server-provided response information */
rc_api_response_t response;
@ -135,6 +137,7 @@ rc_api_fetch_leaderboard_info_response_t;
int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const rc_api_fetch_leaderboard_info_request_t* api_params);
int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response);
int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboard_info_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response);
/* --- Fetch Games List --- */
@ -144,14 +147,14 @@ void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_inf
*/
typedef struct rc_api_fetch_games_list_request_t {
/* The unique identifier of the console to query */
unsigned console_id;
uint32_t console_id;
}
rc_api_fetch_games_list_request_t;
/* A game list entry */
typedef struct rc_api_game_list_entry_t {
/* The unique identifier of the game */
unsigned id;
uint32_t id;
/* The name of the game */
const char* name;
}
@ -164,7 +167,7 @@ typedef struct rc_api_fetch_games_list_response_t {
/* An array of requested entries */
rc_api_game_list_entry_t* entries;
/* The number of items in the entries array */
unsigned num_entries;
uint32_t num_entries;
/* Common server-provided response information */
rc_api_response_t response;
@ -173,6 +176,7 @@ rc_api_fetch_games_list_response_t;
int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api_fetch_games_list_request_t* api_params);
int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response);
int rc_api_process_fetch_games_list_server_response(rc_api_fetch_games_list_response_t* response, const rc_api_server_response_t* server_response);
void rc_api_destroy_fetch_games_list_response(rc_api_fetch_games_list_response_t* response);
#ifdef __cplusplus

View file

@ -2,37 +2,14 @@
#define RC_API_REQUEST_H
#include "rc_error.h"
#include "../src/rc_util.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* A block of memory for variable length data (like strings and arrays).
*/
typedef struct rc_api_buffer_chunk_t {
/* The current location where data is being written */
char* write;
/* The first byte past the end of data where writing cannot occur */
char* end;
/* The first byte of the data */
char* start;
/* The next block in the allocated memory chain */
struct rc_api_buffer_chunk_t* next;
}
rc_api_buffer_chunk_t;
/**
* A preallocated block of memory for variable length data (like strings and arrays).
*/
typedef struct rc_api_buffer_t {
/* The chunk data (will point at the local data member) */
struct rc_api_buffer_chunk_t chunk;
/* Small chunk of memory pre-allocated for the chunk */
char data[256];
}
rc_api_buffer_t;
/**
* A constructed request to send to the retroachievements server.
*/
@ -41,9 +18,11 @@ typedef struct rc_api_request_t {
const char* url;
/* Additional query args that should be sent via a POST command. If null, GET may be used */
const char* post_data;
/* The HTTP Content-Type of the POST data. */
const char* content_type;
/* Storage for the url and post_data */
rc_api_buffer_t buffer;
rc_buffer_t buffer;
}
rc_api_request_t;
@ -55,9 +34,11 @@ typedef struct rc_api_response_t {
int succeeded;
/* Server-provided message associated to the failure */
const char* error_message;
/* Server-provided error code associated to the failure */
const char* error_code;
/* Storage for the response data */
rc_api_buffer_t buffer;
rc_buffer_t buffer;
}
rc_api_response_t;
@ -66,6 +47,20 @@ void 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);
typedef struct rc_api_server_response_t {
/* Pointer to the data returned from the server */
const char* body;
/* Length of data returned from the server (Content-Length) */
size_t body_length;
/* HTTP status code returned from the server */
int http_status_code;
} rc_api_server_response_t;
enum {
RC_API_SERVER_RESPONSE_CLIENT_ERROR = -1,
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR = -2
};
#ifdef __cplusplus
}
#endif

View file

@ -3,6 +3,7 @@
#include "rc_api_request.h"
#include <stdint.h>
#include <time.h>
#ifdef __cplusplus
@ -19,7 +20,7 @@ typedef struct rc_api_fetch_image_request_t {
/* The name of the image to fetch */
const char* image_name;
/* The type of image to fetch */
int image_type;
uint32_t image_type;
}
rc_api_fetch_image_request_t;
@ -50,7 +51,7 @@ rc_api_resolve_hash_request_t;
*/
typedef struct rc_api_resolve_hash_response_t {
/* The unique identifier of the game, 0 if no match was found */
unsigned game_id;
uint32_t game_id;
/* Common server-provided response information */
rc_api_response_t response;
@ -59,6 +60,7 @@ 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);
/* --- Fetch Game Data --- */
@ -72,14 +74,14 @@ typedef struct rc_api_fetch_game_data_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
uint32_t game_id;
}
rc_api_fetch_game_data_request_t;
/* A leaderboard definition */
typedef struct rc_api_leaderboard_definition_t {
/* The unique identifier of the leaderboard */
unsigned id;
uint32_t id;
/* The format to pass to rc_format_value to format the leaderboard value */
int format;
/* The title of the leaderboard */
@ -89,20 +91,20 @@ typedef struct rc_api_leaderboard_definition_t {
/* The definition of the leaderboard to be passed to rc_runtime_activate_lboard */
const char* definition;
/* Non-zero if lower values are better for this leaderboard */
int lower_is_better;
uint8_t lower_is_better;
/* Non-zero if the leaderboard should not be displayed in a list of leaderboards */
int hidden;
uint8_t hidden;
}
rc_api_leaderboard_definition_t;
/* An achievement definition */
typedef struct rc_api_achievement_definition_t {
/* The unique identifier of the achievement */
unsigned id;
uint32_t id;
/* The number of points the achievement is worth */
unsigned points;
uint32_t points;
/* The achievement category (core, unofficial) */
unsigned category;
uint32_t category;
/* The title of the achievement */
const char* title;
/* The dscription of the achievement */
@ -128,9 +130,9 @@ rc_api_achievement_definition_t;
*/
typedef struct rc_api_fetch_game_data_response_t {
/* The unique identifier of the game */
unsigned id;
uint32_t id;
/* The console associated to the game */
unsigned console_id;
uint32_t console_id;
/* The title of the game */
const char* title;
/* The image name for the game badge */
@ -141,12 +143,12 @@ typedef struct rc_api_fetch_game_data_response_t {
/* An array of achievements for the game */
rc_api_achievement_definition_t* achievements;
/* The number of items in the achievements array */
unsigned num_achievements;
uint32_t num_achievements;
/* An array of leaderboards for the game */
rc_api_leaderboard_definition_t* leaderboards;
/* The number of items in the leaderboards array */
unsigned num_leaderboards;
uint32_t num_leaderboards;
/* Common server-provided response information */
rc_api_response_t response;
@ -155,6 +157,7 @@ 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);
/* --- Ping --- */
@ -168,7 +171,7 @@ typedef struct rc_api_ping_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
uint32_t game_id;
/* (optional) The current rich presence evaluation for the user */
const char* rich_presence;
}
@ -185,6 +188,7 @@ 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);
/* --- Award Achievement --- */
@ -198,9 +202,9 @@ typedef struct rc_api_award_achievement_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the achievement */
unsigned achievement_id;
uint32_t achievement_id;
/* Non-zero if the achievement was earned in hardcore */
int hardcore;
uint32_t hardcore;
/* The hash associated to the game being played */
const char* game_hash;
}
@ -211,12 +215,14 @@ rc_api_award_achievement_request_t;
*/
typedef struct rc_api_award_achievement_response_t {
/* The unique identifier of the achievement that was awarded */
unsigned awarded_achievement_id;
uint32_t awarded_achievement_id;
/* The updated player score */
unsigned new_player_score;
uint32_t new_player_score;
/* The updated player softcore score */
uint32_t new_player_score_softcore;
/* The number of achievements the user has not yet unlocked for this game
* (in hardcore/non-hardcore per hardcore flag in request) */
unsigned achievements_remaining;
uint32_t achievements_remaining;
/* Common server-provided response information */
rc_api_response_t response;
@ -225,6 +231,7 @@ 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);
/* --- Submit Leaderboard Entry --- */
@ -238,9 +245,9 @@ typedef struct rc_api_submit_lboard_entry_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the leaderboard */
unsigned leaderboard_id;
uint32_t leaderboard_id;
/* The value being submitted */
int score;
int32_t score;
/* The hash associated to the game being played */
const char* game_hash;
}
@ -251,9 +258,9 @@ typedef struct rc_api_lboard_entry_t {
/* The user associated to the entry */
const char* username;
/* The rank of the entry */
unsigned rank;
uint32_t rank;
/* The value of the entry */
int score;
int32_t score;
}
rc_api_lboard_entry_t;
@ -262,18 +269,18 @@ rc_api_lboard_entry_t;
*/
typedef struct rc_api_submit_lboard_entry_response_t {
/* The value that was submitted */
int submitted_score;
int32_t submitted_score;
/* The player's best submitted value */
int best_score;
int32_t best_score;
/* The player's new rank within the leaderboard */
unsigned new_rank;
uint32_t new_rank;
/* The total number of entries in the leaderboard */
unsigned num_entries;
uint32_t num_entries;
/* An array of the top entries for the leaderboard */
rc_api_lboard_entry_t* top_entries;
/* The number of items in the top_entries array */
unsigned num_top_entries;
uint32_t num_top_entries;
/* Common server-provided response information */
rc_api_response_t response;
@ -282,6 +289,7 @@ 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);
#ifdef __cplusplus

View file

@ -3,6 +3,9 @@
#include "rc_api_request.h"
#include <stdint.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -32,9 +35,11 @@ typedef struct rc_api_login_response_t {
/* The API token to use for all future requests */
const char* api_token;
/* The current score of the player */
unsigned score;
uint32_t score;
/* The current softcore score of the player */
uint32_t score_softcore;
/* The number of unread messages waiting for the player on the web site */
unsigned num_unread_messages;
uint32_t num_unread_messages;
/* The preferred name to display for the player */
const char* display_name;
@ -45,6 +50,7 @@ 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);
/* --- Start Session --- */
@ -58,14 +64,38 @@ typedef struct rc_api_start_session_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
uint32_t game_id;
}
rc_api_start_session_request_t;
/**
* Response data for an achievement unlock.
*/
typedef struct rc_api_unlock_entry_t {
/* The unique identifier of the unlocked achievement */
uint32_t achievement_id;
/* When the achievement was unlocked */
time_t when;
}
rc_api_unlock_entry_t;
/**
* Response data for a start session request.
*/
typedef struct rc_api_start_session_response_t {
/* An array of hardcore user unlocks */
rc_api_unlock_entry_t* hardcore_unlocks;
/* An array of user unlocks */
rc_api_unlock_entry_t* unlocks;
/* The number of items in the hardcore_unlocks array */
uint32_t num_hardcore_unlocks;
/* The number of items in the unlocks array */
uint32_t num_unlocks;
/* The server timestamp when the response was generated */
time_t server_now;
/* Common server-provided response information */
rc_api_response_t response;
}
@ -73,6 +103,7 @@ 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);
/* --- Fetch User Unlocks --- */
@ -86,9 +117,9 @@ typedef struct rc_api_fetch_user_unlocks_request_t {
/* The API token from the login request */
const char* api_token;
/* The unique identifier of the game */
unsigned game_id;
uint32_t game_id;
/* Non-zero to fetch hardcore unlocks, 0 to fetch non-hardcore unlocks */
int hardcore;
uint32_t hardcore;
}
rc_api_fetch_user_unlocks_request_t;
@ -97,9 +128,9 @@ rc_api_fetch_user_unlocks_request_t;
*/
typedef struct rc_api_fetch_user_unlocks_response_t {
/* An array of achievement IDs previously unlocked by the user */
unsigned* achievement_ids;
uint32_t* achievement_ids;
/* The number of items in the achievement_ids array */
unsigned num_achievement_ids;
uint32_t num_achievement_ids;
/* Common server-provided response information */
rc_api_response_t response;
@ -108,6 +139,7 @@ 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);
#ifdef __cplusplus

659
deps/rcheevos/include/rc_client.h vendored Normal file
View file

@ -0,0 +1,659 @@
#ifndef RC_CLIENT_H
#define RC_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_api_request.h"
#include "rc_error.h"
#include <stddef.h>
#include <stdint.h>
#include <time.h>
/* 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;
/*****************************************************************************\
| Callbacks |
\*****************************************************************************/
/**
* 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);
/**
* 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);
/**
* 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);
/**
* Generic callback for asynchronous eventing.
*/
typedef void (*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);
/*****************************************************************************\
| Runtime |
\*****************************************************************************/
/**
* 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);
/**
* 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);
/**
* Sets whether hardcore is enabled (on by default).
* Can be called with a game loaded.
* 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);
/**
* Gets whether hardcore is enabled (on by default).
*/
int 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);
/**
* Gets whether encore mode is enabled (off by default).
*/
int 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);
/**
* Gets whether unofficial achievements should be loaded.
*/
int rc_client_get_unofficial_enabled(const rc_client_t* client);
/**
* Sets whether spectator mode is enabled (off by default).
* If enabled, events for achievement unlocks and leaderboard submissions will be
* raised, but server calls to actually perform the unlock/submit will not occur.
* 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);
/**
* Gets whether spectator mode is enabled (off by default).
*/
int 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);
/**
* Gets the client-specific data attached to the runtime.
*/
void* 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);
typedef uint64_t rc_clock_t;
typedef rc_clock_t (*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);
/**
* 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);
/*****************************************************************************\
| Logging |
\*****************************************************************************/
/**
* 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);
enum {
RC_CLIENT_LOG_LEVEL_NONE = 0,
RC_CLIENT_LOG_LEVEL_ERROR = 1,
RC_CLIENT_LOG_LEVEL_WARN = 2,
RC_CLIENT_LOG_LEVEL_INFO = 3,
RC_CLIENT_LOG_LEVEL_VERBOSE = 4,
NUM_RC_CLIENT_LOG_LEVELS = 5
};
/*****************************************************************************\
| User |
\*****************************************************************************/
/**
* Attempt to login a user.
*/
rc_client_async_handle_t* 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,
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);
typedef struct rc_client_user_t {
const char* display_name;
const char* username;
const char* token;
uint32_t score;
uint32_t score_softcore;
uint32_t num_unread_messages;
} 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);
/**
* 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);
typedef struct rc_client_user_game_summary_t
{
uint32_t num_core_achievements;
uint32_t num_unofficial_achievements;
uint32_t num_unlocked_achievements;
uint32_t num_unsupported_achievements;
uint32_t points_core;
uint32_t points_unlocked;
} 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);
/*****************************************************************************\
| Game |
\*****************************************************************************/
/**
* Start loading an unidentified game.
*/
rc_client_async_handle_t* 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);
/**
* Start loading a game.
*/
rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const char* hash,
rc_client_callback_t callback, void* callback_userdata);
/**
* Unloads the current game.
*/
void rc_client_unload_game(rc_client_t* client);
typedef struct rc_client_game_t {
uint32_t id;
uint32_t console_id;
const char* title;
const char* hash;
const char* badge_name;
} 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);
/**
* 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);
/**
* 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,
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
/*****************************************************************************\
| Subsets |
\*****************************************************************************/
typedef struct rc_client_subset_t {
uint32_t id;
const char* title;
char badge_name[16];
uint32_t num_achievements;
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);
/*****************************************************************************\
| Achievements |
\*****************************************************************************/
enum {
RC_CLIENT_ACHIEVEMENT_STATE_INACTIVE = 0, /* unprocessed */
RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE = 1, /* eligible to trigger */
RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED = 2, /* earned by user */
RC_CLIENT_ACHIEVEMENT_STATE_DISABLED = 3, /* not supported by this version of the runtime */
NUM_RC_CLIENT_ACHIEVEMENT_STATES = 4
};
enum {
RC_CLIENT_ACHIEVEMENT_CATEGORY_NONE = 0,
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE = (1 << 0),
RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL = (1 << 1),
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE | RC_CLIENT_ACHIEVEMENT_CATEGORY_UNOFFICIAL
};
enum {
RC_CLIENT_ACHIEVEMENT_BUCKET_UNKNOWN = 0,
RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED = 1,
RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED = 2,
RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED = 3,
RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL = 4,
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
};
enum {
RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE = 0,
RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE = (1 << 0),
RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE = (1 << 1),
RC_CLIENT_ACHIEVEMENT_UNLOCKED_BOTH = RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE | RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE
};
typedef struct rc_client_achievement_t {
const char* title;
const char* description;
char badge_name[8];
char measured_progress[24];
float measured_percent;
uint32_t id;
uint32_t points;
time_t unlock_time;
uint8_t state;
uint8_t category;
uint8_t bucket;
uint8_t unlocked;
} 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);
/**
* 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);
typedef struct rc_client_achievement_bucket_t {
rc_client_achievement_t** achievements;
uint32_t num_achievements;
const char* label;
uint32_t subset_id;
uint8_t bucket_type;
} rc_client_achievement_bucket_t;
typedef struct rc_client_achievement_list_t {
rc_client_achievement_bucket_t* buckets;
uint32_t num_buckets;
} rc_client_achievement_list_t;
enum {
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE = 0,
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS = 1
};
/**
* 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);
/**
* Destroys a list allocated by rc_client_get_achievement_list.
*/
void 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);
/*****************************************************************************\
| Leaderboards |
\*****************************************************************************/
enum {
RC_CLIENT_LEADERBOARD_STATE_INACTIVE = 0,
RC_CLIENT_LEADERBOARD_STATE_ACTIVE = 1,
RC_CLIENT_LEADERBOARD_STATE_TRACKING = 2,
RC_CLIENT_LEADERBOARD_STATE_DISABLED = 3,
NUM_RC_CLIENT_LEADERBOARD_STATES = 4
};
enum {
RC_CLIENT_LEADERBOARD_FORMAT_TIME = 0,
RC_CLIENT_LEADERBOARD_FORMAT_SCORE = 1,
RC_CLIENT_LEADERBOARD_FORMAT_VALUE = 2,
NUM_RC_CLIENT_LEADERBOARD_FORMATS = 3
};
#define RC_CLIENT_LEADERBOARD_DISPLAY_SIZE 24
typedef struct rc_client_leaderboard_t {
const char* title;
const char* description;
const char* tracker_value;
uint32_t id;
uint8_t state;
uint8_t format;
uint8_t lower_is_better;
} 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);
typedef struct rc_client_leaderboard_tracker_t {
char display[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
uint32_t id;
} rc_client_leaderboard_tracker_t;
typedef struct rc_client_leaderboard_bucket_t {
rc_client_leaderboard_t** leaderboards;
uint32_t num_leaderboards;
const char* label;
uint32_t subset_id;
uint8_t bucket_type;
} rc_client_leaderboard_bucket_t;
typedef struct rc_client_leaderboard_list_t {
rc_client_leaderboard_bucket_t* buckets;
uint32_t num_buckets;
} rc_client_leaderboard_list_t;
enum {
RC_CLIENT_LEADERBOARD_BUCKET_UNKNOWN = 0,
RC_CLIENT_LEADERBOARD_BUCKET_INACTIVE = 1,
RC_CLIENT_LEADERBOARD_BUCKET_ACTIVE = 2,
RC_CLIENT_LEADERBOARD_BUCKET_UNSUPPORTED = 3,
RC_CLIENT_LEADERBOARD_BUCKET_ALL = 4,
NUM_RC_CLIENT_LEADERBOARD_BUCKETS = 5
};
enum {
RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE = 0,
RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING = 1
};
/**
* 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);
/**
* Destroys a list allocated by rc_client_get_leaderboard_list.
*/
void 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);
typedef struct rc_client_leaderboard_entry_t {
const char* user;
char display[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
time_t submitted;
uint32_t rank;
uint32_t index;
} rc_client_leaderboard_entry_t;
typedef struct rc_client_leaderboard_entry_list_t {
rc_client_leaderboard_entry_t* entries;
uint32_t num_entries;
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,
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,
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,
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);
/**
* 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);
/**
* Used for scoreboard events. Contains the response from the server when a leaderboard entry is submitted.
* NOTE: This structure is only valid within the event callback. If you want to make use of the data outside
* of the callback, you should create copies of both the top entries and usernames within.
*/
typedef struct rc_client_leaderboard_scoreboard_entry_t {
/* The user associated to the entry */
const char* username;
/* The rank of the entry */
uint32_t rank;
/* The value of the entry */
char score[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
} rc_client_leaderboard_scoreboard_entry_t;
typedef struct rc_client_leaderboard_scoreboard_t {
/* The ID of the leaderboard which was submitted */
uint32_t leaderboard_id;
/* The value that was submitted */
char submitted_score[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
/* The player's best submitted value */
char best_score[RC_CLIENT_LEADERBOARD_DISPLAY_SIZE];
/* The player's new rank within the leaderboard */
uint32_t new_rank;
/* The total number of entries in the leaderboard */
uint32_t num_entries;
/* An array of the top entries for the leaderboard */
rc_client_leaderboard_scoreboard_entry_t* top_entries;
/* The number of items in the top_entries array */
uint32_t num_top_entries;
} rc_client_leaderboard_scoreboard_t;
/*****************************************************************************\
| Rich Presence |
\*****************************************************************************/
/**
* Returns non-zero if the current game supports rich presence.
*/
int 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);
/*****************************************************************************\
| Processing |
\*****************************************************************************/
enum {
RC_CLIENT_EVENT_TYPE_NONE = 0,
RC_CLIENT_EVENT_ACHIEVEMENT_TRIGGERED = 1, /* [achievement] was earned by the player */
RC_CLIENT_EVENT_LEADERBOARD_STARTED = 2, /* [leaderboard] attempt has started */
RC_CLIENT_EVENT_LEADERBOARD_FAILED = 3, /* [leaderboard] attempt failed */
RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED = 4, /* [leaderboard] attempt submitted */
RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_SHOW = 5, /* [achievement] challenge indicator should be shown */
RC_CLIENT_EVENT_ACHIEVEMENT_CHALLENGE_INDICATOR_HIDE = 6, /* [achievement] challenge indicator should be hidden */
RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_SHOW = 7, /* progress indicator should be shown for [achievement] */
RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_HIDE = 8, /* progress indicator should be hidden */
RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE = 9, /* progress indicator should be updated to reflect new badge/progress for [achievement] */
RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW = 10, /* [leaderboard_tracker] should be shown */
RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE = 11, /* [leaderboard_tracker] should be hidden */
RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE = 12, /* [leaderboard_tracker] updated */
RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD = 13, /* [leaderboard_scoreboard] possibly-new ranking received for [leaderboard] */
RC_CLIENT_EVENT_RESET = 14, /* emulated system should be reset (as the result of enabling hardcore) */
RC_CLIENT_EVENT_GAME_COMPLETED = 15, /* all achievements for the game have been earned */
RC_CLIENT_EVENT_SERVER_ERROR = 16, /* an API response returned a [server_error] and will not be retried */
RC_CLIENT_EVENT_DISCONNECTED = 17, /* an unlock request could not be completed and is pending */
RC_CLIENT_EVENT_RECONNECTED = 18 /* all pending unlocks have been completed */
};
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
{
uint32_t type;
rc_client_achievement_t* achievement;
rc_client_leaderboard_t* leaderboard;
rc_client_leaderboard_tracker_t* leaderboard_tracker;
rc_client_leaderboard_scoreboard_t* leaderboard_scoreboard;
rc_client_server_error_t* server_error;
} 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);
/**
* Provides a callback for event handling.
*/
void 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);
/**
* Determines if there are any active achievements/leaderboards/rich presence that need processing.
*/
int rc_client_is_processing_required(rc_client_t* client);
/**
* Processes achievements for the current frame.
*/
void 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);
/**
* 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);
/**
* Gets the number of bytes needed to serialized the runtime state.
*/
size_t 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);
/**
* 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);
#ifdef __cplusplus
}
#endif
#endif /* RC_RUNTIME_H */

View file

@ -5,11 +5,14 @@
extern "C" {
#endif
#include <stdint.h>
/*****************************************************************************\
| Console identifiers |
\*****************************************************************************/
enum {
RC_CONSOLE_UNKNOWN = 0,
RC_CONSOLE_MEGA_DRIVE = 1,
RC_CONSOLE_NINTENDO_64 = 2,
RC_CONSOLE_SUPER_NINTENDO = 3,
@ -112,21 +115,21 @@ enum {
};
typedef struct rc_memory_region_t {
unsigned start_address; /* first address of block as queried by RetroAchievements */
unsigned end_address; /* last address of block as queried by RetroAchievements */
unsigned real_address; /* real address for first address of block */
char type; /* RC_MEMORY_TYPE_ for block */
uint32_t start_address; /* first address of block as queried by RetroAchievements */
uint32_t end_address; /* last address of block as queried by RetroAchievements */
uint32_t real_address; /* real address for first address of block */
uint8_t type; /* RC_MEMORY_TYPE_ for block */
const char* description; /* short description of block */
}
rc_memory_region_t;
typedef struct rc_memory_regions_t {
const rc_memory_region_t* region;
unsigned num_regions;
uint32_t num_regions;
}
rc_memory_regions_t;
const rc_memory_regions_t* rc_console_memory_regions(int console_id);
const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id);
#ifdef __cplusplus

View file

@ -36,7 +36,16 @@ enum {
RC_INVALID_MEASURED_TARGET = -23,
RC_INVALID_COMPARISON = -24,
RC_INVALID_STATE = -25,
RC_INVALID_JSON = -26
RC_INVALID_JSON = -26,
RC_API_FAILURE = -27,
RC_LOGIN_REQUIRED = -28,
RC_NO_GAME_LOADED = -29,
RC_HARDCORE_DISABLED = -30,
RC_ABORTED = -31,
RC_NO_RESPONSE = -32,
RC_ACCESS_DENIED = -33,
RC_INVALID_CREDENTIALS = -34,
RC_EXPIRED_TOKEN = -35
};
const char* rc_error_str(int ret);

View file

@ -27,20 +27,20 @@ extern "C" {
/* data for rc_hash_iterate
*/
struct rc_hash_iterator
typedef struct rc_hash_iterator
{
uint8_t* buffer;
const uint8_t* buffer;
size_t buffer_size;
uint8_t consoles[12];
int index;
const char* path;
};
} rc_hash_iterator_t;
/* initializes a rc_hash_iterator
* - 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, uint8_t* buffer, size_t buffer_size);
void 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
*/
@ -92,12 +92,12 @@ extern "C" {
/* ===================================================== */
#define RC_HASH_CDTRACK_FIRST_DATA ((uint32_t)-1)
#define RC_HASH_CDTRACK_LAST ((uint32_t)-2)
#define RC_HASH_CDTRACK_LARGEST ((uint32_t)-3)
#define RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION ((uint32_t)-4)
#define RC_HASH_CDTRACK_FIRST_DATA ((uint32_t)-1) /* the first data track (skip audio tracks) */
#define RC_HASH_CDTRACK_LAST ((uint32_t)-2) /* the last data/audio track */
#define RC_HASH_CDTRACK_LARGEST ((uint32_t)-3) /* the largest data/audio track */
#define RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION ((uint32_t)-4) /* the first data/audio track of the second session */
/* opens a track from the specified file. track 0 indicates the largest data track should be opened.
/* 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);

View file

@ -8,6 +8,7 @@ extern "C" {
#include "rc_error.h"
#include <stddef.h>
#include <stdint.h>
/*****************************************************************************\
| Forward Declarations (defined in rc_runtime_types.h) |
@ -34,32 +35,32 @@ typedef struct rc_value_t rc_value_t;
* num_bytes is greater than 1, the value is read in little-endian from
* memory.
*/
typedef unsigned (*rc_runtime_peek_t)(unsigned address, unsigned num_bytes, void* ud);
typedef uint32_t(*rc_runtime_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
/*****************************************************************************\
| Runtime |
\*****************************************************************************/
typedef struct rc_runtime_trigger_t {
unsigned id;
uint32_t id;
rc_trigger_t* trigger;
void* buffer;
rc_memref_t* invalid_memref;
unsigned char md5[16];
int serialized_size;
char owns_memrefs;
uint8_t md5[16];
int32_t serialized_size;
uint8_t owns_memrefs;
}
rc_runtime_trigger_t;
typedef struct rc_runtime_lboard_t {
unsigned id;
int value;
uint32_t id;
int32_t value;
rc_lboard_t* lboard;
void* buffer;
rc_memref_t* invalid_memref;
unsigned char md5[16];
int serialized_size;
char owns_memrefs;
uint8_t md5[16];
uint32_t serialized_size;
uint8_t owns_memrefs;
}
rc_runtime_lboard_t;
@ -67,19 +68,19 @@ typedef struct rc_runtime_richpresence_t {
rc_richpresence_t* richpresence;
void* buffer;
struct rc_runtime_richpresence_t* previous;
unsigned char md5[16];
char owns_memrefs;
uint8_t md5[16];
uint8_t owns_memrefs;
}
rc_runtime_richpresence_t;
typedef struct rc_runtime_t {
rc_runtime_trigger_t* triggers;
unsigned trigger_count;
unsigned trigger_capacity;
uint32_t trigger_count;
uint32_t trigger_capacity;
rc_runtime_lboard_t* lboards;
unsigned lboard_count;
unsigned lboard_capacity;
uint32_t lboard_count;
uint32_t lboard_capacity;
rc_runtime_richpresence_t* richpresence;
@ -88,26 +89,29 @@ typedef struct rc_runtime_t {
rc_value_t* variables;
rc_value_t** next_variable;
uint8_t owns_self;
}
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);
int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target);
int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, unsigned id, char *buffer, size_t buffer_size);
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);
int rc_runtime_activate_lboard(rc_runtime_t* runtime, unsigned id, const char* memaddr, lua_State* L, int funcs_idx);
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* runtime, unsigned id);
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format);
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);
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, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
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);
enum {
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
@ -126,9 +130,9 @@ enum {
};
typedef struct rc_runtime_event_t {
unsigned id;
int value;
char type;
uint32_t id;
int32_t value;
uint8_t type;
}
rc_runtime_event_t;
@ -137,13 +141,13 @@ typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_eve
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);
typedef int (*rc_runtime_validate_address_t)(unsigned address);
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, unsigned address);
void 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 unsigned char* serialized, lua_State* L);
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const uint8_t* serialized, lua_State* L);
#ifdef __cplusplus
}

View file

@ -7,6 +7,9 @@ extern "C" {
#include "rc_error.h"
#include <stddef.h>
#include <stdint.h>
#ifndef RC_RUNTIME_H /* prevents pedantic redefiniton error */
typedef struct lua_State lua_State;
@ -28,7 +31,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 unsigned (*rc_peek_t)(unsigned address, unsigned num_bytes, void* ud);
typedef uint32_t(*rc_peek_t)(uint32_t address, uint32_t num_bytes, void* ud);
/*****************************************************************************\
| Memory References |
@ -57,24 +60,25 @@ enum {
RC_MEMSIZE_FLOAT,
RC_MEMSIZE_MBF32,
RC_MEMSIZE_MBF32_LE,
RC_MEMSIZE_FLOAT_BE,
RC_MEMSIZE_VARIABLE
};
typedef struct rc_memref_value_t {
/* The current value of this memory reference. */
unsigned value;
uint32_t value;
/* The last differing value of this memory reference. */
unsigned prior;
uint32_t prior;
/* The size of the value. */
char size;
uint8_t size;
/* True if the value changed this frame. */
char changed;
uint8_t changed;
/* The value type of the value (for variables) */
char type;
uint8_t type;
/* True if the reference will be used in indirection.
* NOTE: This is actually a property of the rc_memref_t, but we put it here to save space */
char is_indirect;
uint8_t is_indirect;
}
rc_memref_value_t;
@ -83,7 +87,7 @@ struct rc_memref_t {
rc_memref_value_t value;
/* The memory address of this variable. */
unsigned address;
uint32_t address;
/* The next memory reference in the chain. */
rc_memref_t* next;
@ -111,7 +115,7 @@ typedef struct rc_operand_t {
rc_memref_t* memref;
/* An integer value. */
unsigned num;
uint32_t num;
/* A floating point value. */
double dbl;
@ -121,10 +125,10 @@ typedef struct rc_operand_t {
} value;
/* specifies which member of the value union is being used */
char type;
uint8_t type;
/* the actual RC_MEMSIZE of the operand - memref.size may differ */
char size;
uint8_t size;
}
rc_operand_t;
@ -182,27 +186,27 @@ struct rc_condition_t {
rc_operand_t operand2;
/* Required hits to fire this condition. */
unsigned required_hits;
uint32_t required_hits;
/* Number of hits so far. */
unsigned current_hits;
uint32_t current_hits;
/* The next condition in the chain. */
rc_condition_t* next;
/* The type of the condition. */
char type;
/* The type of the condition. (RC_CONDITION_*) */
uint8_t type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* The comparison operator to use. (RC_OPERATOR_*) */
uint8_t oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Set if the condition needs to processed as part of the "check if paused" pass. (bool) */
uint8_t pause;
/* Whether or not the condition evaluated true on the last check */
char is_true;
/* Whether or not the condition evaluated true on the last check. (bool) */
uint8_t is_true;
/* Unique identifier of optimized comparator to use */
char optimized_comparator;
/* Unique identifier of optimized comparator to use. (RC_PROCESSING_COMPARE_*) */
uint8_t optimized_comparator;
};
/*****************************************************************************\
@ -219,13 +223,13 @@ struct rc_condset_t {
rc_condition_t* conditions;
/* True if any condition in the set is a pause condition. */
char has_pause;
uint8_t has_pause;
/* True if the set is currently paused. */
char is_paused;
uint8_t is_paused;
/* True if the set has indirect memory references. */
char has_indirect_memrefs;
uint8_t has_indirect_memrefs;
};
/*****************************************************************************\
@ -254,22 +258,22 @@ struct rc_trigger_t {
rc_memref_t* memrefs;
/* The current state of the MEASURED condition. */
unsigned measured_value;
uint32_t measured_value;
/* The target state of the MEASURED condition */
unsigned measured_target;
uint32_t measured_target;
/* The current state of the trigger */
char state;
uint8_t state;
/* True if at least one condition has a non-zero hit count */
char has_hits;
uint8_t has_hits;
/* True if at least one condition has a non-zero required hit count */
char has_required_hits;
uint8_t has_required_hits;
/* True if the measured value should be displayed as a percentage */
char measured_as_percent;
uint8_t measured_as_percent;
};
int rc_trigger_size(const char* memaddr);
@ -301,7 +305,7 @@ struct rc_value_t {
int rc_value_size(const char* memaddr);
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
int32_t rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
/*****************************************************************************\
| Leaderboards |
@ -326,12 +330,12 @@ struct rc_lboard_t {
rc_value_t* progress;
rc_memref_t* memrefs;
char state;
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, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
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);
/*****************************************************************************\
@ -356,7 +360,7 @@ enum {
};
int rc_parse_format(const char* format_str);
int rc_format_value(char* buffer, int size, int value, int format);
int rc_format_value(char* buffer, int size, int32_t value, int format);
/*****************************************************************************\
| Rich Presence |
@ -365,8 +369,8 @@ int rc_format_value(char* buffer, int size, int value, int format);
typedef struct rc_richpresence_lookup_item_t rc_richpresence_lookup_item_t;
struct rc_richpresence_lookup_item_t {
unsigned first;
unsigned last;
uint32_t first;
uint32_t last;
rc_richpresence_lookup_item_t* left;
rc_richpresence_lookup_item_t* right;
const char* label;
@ -379,7 +383,7 @@ struct rc_richpresence_lookup_t {
rc_richpresence_lookup_t* next;
const char* name;
const char* default_label;
unsigned short format;
uint8_t format;
};
typedef struct rc_richpresence_display_part_t rc_richpresence_display_part_t;
@ -389,7 +393,7 @@ struct rc_richpresence_display_part_t {
const char* text;
rc_richpresence_lookup_t* lookup;
rc_memref_value_t *value;
unsigned short display_type;
uint8_t display_type;
};
typedef struct rc_richpresence_display_t rc_richpresence_display_t;
@ -410,9 +414,9 @@ struct rc_richpresence_t {
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, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L);
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, unsigned buffersize, 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);
#ifdef __cplusplus

View file

@ -2,7 +2,7 @@
#include "rc_api_request.h"
#include "rc_api_runtime.h"
#include "../rcheevos/rc_compat.h"
#include "../rc_compat.h"
#include <ctype.h>
#include <stdio.h>
@ -16,161 +16,176 @@
static char* g_host = NULL;
static char* g_imagehost = NULL;
#undef DEBUG_BUFFERS
/* --- rc_json --- */
static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen);
static int rc_json_parse_array(const char** json_ptr, rc_json_field_t* field);
static int rc_json_parse_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen);
static int rc_json_parse_array(rc_json_iterator_t* iterator, rc_json_field_t* field);
static int rc_json_parse_field(const char** json_ptr, rc_json_field_t* field) {
static int rc_json_match_char(rc_json_iterator_t* iterator, char c)
{
if (iterator->json < iterator->end && *iterator->json == c) {
++iterator->json;
return 1;
}
return 0;
}
static void rc_json_skip_whitespace(rc_json_iterator_t* iterator)
{
while (iterator->json < iterator->end && isspace((unsigned char)*iterator->json))
++iterator->json;
}
static int rc_json_find_closing_quote(rc_json_iterator_t* iterator)
{
while (iterator->json < iterator->end) {
if (*iterator->json == '"')
return 1;
if (*iterator->json == '\\') {
++iterator->json;
if (iterator->json == iterator->end)
return 0;
}
if (*iterator->json == '\0')
return 0;
++iterator->json;
}
return 0;
}
static int rc_json_parse_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
int result;
field->value_start = *json_ptr;
if (iterator->json >= iterator->end)
return RC_INVALID_JSON;
switch (**json_ptr)
field->value_start = iterator->json;
switch (*iterator->json)
{
case '"': /* quoted string */
++(*json_ptr);
while (**json_ptr != '"') {
if (**json_ptr == '\\')
++(*json_ptr);
if (**json_ptr == '\0')
return RC_INVALID_JSON;
++(*json_ptr);
}
++(*json_ptr);
++iterator->json;
if (!rc_json_find_closing_quote(iterator))
return RC_INVALID_JSON;
++iterator->json;
break;
case '-':
case '+': /* signed number */
++(*json_ptr);
++iterator->json;
/* fallthrough to number */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': /* number */
do {
++(*json_ptr);
} while (**json_ptr >= '0' && **json_ptr <= '9');
if (**json_ptr == '.') {
do {
++(*json_ptr);
} while (**json_ptr >= '0' && **json_ptr <= '9');
while (iterator->json < iterator->end && *iterator->json >= '0' && *iterator->json <= '9')
++iterator->json;
if (rc_json_match_char(iterator, '.')) {
while (iterator->json < iterator->end && *iterator->json >= '0' && *iterator->json <= '9')
++iterator->json;
}
break;
case '[': /* array */
result = rc_json_parse_array(json_ptr, field);
result = rc_json_parse_array(iterator, field);
if (result != RC_OK)
return result;
return result;
break;
case '{': /* object */
result = rc_json_parse_object(json_ptr, NULL, 0, &field->array_size);
result = rc_json_parse_object(iterator, NULL, 0, &field->array_size);
if (result != RC_OK)
return result;
break;
default: /* non-quoted text [true,false,null] */
if (!isalpha((unsigned char)**json_ptr))
if (!isalpha((unsigned char)*iterator->json))
return RC_INVALID_JSON;
do {
++(*json_ptr);
} while (isalnum((unsigned char)**json_ptr));
while (iterator->json < iterator->end && isalnum((unsigned char)*iterator->json))
++iterator->json;
break;
}
field->value_end = *json_ptr;
field->value_end = iterator->json;
return RC_OK;
}
static int rc_json_parse_array(const char** json_ptr, rc_json_field_t* field) {
static int rc_json_parse_array(rc_json_iterator_t* iterator, rc_json_field_t* field) {
rc_json_field_t unused_field;
const char* json = *json_ptr;
int result;
if (*json != '[')
if (!rc_json_match_char(iterator, '['))
return RC_INVALID_JSON;
++json;
field->array_size = 0;
if (*json != ']') {
do
{
while (isspace((unsigned char)*json))
++json;
result = rc_json_parse_field(&json, &unused_field);
if (result != RC_OK)
return result;
if (rc_json_match_char(iterator, ']')) /* empty array */
return RC_OK;
++field->array_size;
do
{
rc_json_skip_whitespace(iterator);
while (isspace((unsigned char)*json))
++json;
result = rc_json_parse_field(iterator, &unused_field);
if (result != RC_OK)
return result;
if (*json != ',')
break;
++field->array_size;
++json;
} while (1);
rc_json_skip_whitespace(iterator);
} while (rc_json_match_char(iterator, ','));
if (*json != ']')
return RC_INVALID_JSON;
}
if (!rc_json_match_char(iterator, ']'))
return RC_INVALID_JSON;
*json_ptr = ++json;
return RC_OK;
}
static int rc_json_get_next_field(rc_json_object_field_iterator_t* iterator) {
const char* json = iterator->json;
static int rc_json_get_next_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
rc_json_skip_whitespace(iterator);
while (isspace((unsigned char)*json))
++json;
if (*json != '"')
if (!rc_json_match_char(iterator, '"'))
return RC_INVALID_JSON;
iterator->field.name = ++json;
while (*json != '"') {
if (!*json)
field->name = iterator->json;
while (iterator->json < iterator->end && *iterator->json != '"') {
if (!*iterator->json)
return RC_INVALID_JSON;
++json;
++iterator->json;
}
iterator->name_len = json - iterator->field.name;
++json;
while (isspace((unsigned char)*json))
++json;
if (*json != ':')
if (iterator->json == iterator->end)
return RC_INVALID_JSON;
++json;
field->name_len = iterator->json - field->name;
++iterator->json;
while (isspace((unsigned char)*json))
++json;
rc_json_skip_whitespace(iterator);
if (rc_json_parse_field(&json, &iterator->field) < 0)
if (!rc_json_match_char(iterator, ':'))
return RC_INVALID_JSON;
while (isspace((unsigned char)*json))
++json;
rc_json_skip_whitespace(iterator);
if (rc_json_parse_field(iterator, field) < 0)
return RC_INVALID_JSON;
rc_json_skip_whitespace(iterator);
iterator->json = json;
return RC_OK;
}
static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen) {
rc_json_object_field_iterator_t iterator;
const char* json = *json_ptr;
static int rc_json_parse_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen) {
size_t i;
unsigned num_fields = 0;
uint32_t num_fields = 0;
rc_json_field_t field;
int result;
if (fields_seen)
@ -179,60 +194,119 @@ static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields,
for (i = 0; i < field_count; ++i)
fields[i].value_start = fields[i].value_end = NULL;
if (*json != '{')
if (!rc_json_match_char(iterator, '{'))
return RC_INVALID_JSON;
++json;
if (*json == '}') {
*json_ptr = ++json;
if (rc_json_match_char(iterator, '}')) /* empty object */
return RC_OK;
}
memset(&iterator, 0, sizeof(iterator));
iterator.json = json;
do
{
result = rc_json_get_next_field(&iterator);
result = rc_json_get_next_field(iterator, &field);
if (result != RC_OK)
return result;
for (i = 0; i < field_count; ++i) {
if (!fields[i].value_start && strncmp(fields[i].name, iterator.field.name, iterator.name_len) == 0 &&
fields[i].name[iterator.name_len] == '\0') {
fields[i].value_start = iterator.field.value_start;
fields[i].value_end = iterator.field.value_end;
fields[i].array_size = iterator.field.array_size;
if (!fields[i].value_start && fields[i].name_len == field.name_len &&
memcmp(fields[i].name, field.name, field.name_len) == 0) {
fields[i].value_start = field.value_start;
fields[i].value_end = field.value_end;
fields[i].array_size = field.array_size;
break;
}
}
++num_fields;
if (*iterator.json != ',')
break;
++iterator.json;
} while (1);
} while (rc_json_match_char(iterator, ','));
if (*iterator.json != '}')
if (!rc_json_match_char(iterator, '}'))
return RC_INVALID_JSON;
if (fields_seen)
*fields_seen = num_fields;
*json_ptr = ++iterator.json;
return RC_OK;
}
int rc_json_get_next_object_field(rc_json_object_field_iterator_t* iterator) {
if (*iterator->json != ',' && *iterator->json != '{')
int rc_json_get_next_object_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
if (!rc_json_match_char(iterator, ',') && !rc_json_match_char(iterator, '{'))
return 0;
++iterator->json;
return (rc_json_get_next_field(iterator) == RC_OK);
return (rc_json_get_next_field(iterator, field) == RC_OK);
}
int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_json_field_t* fields, size_t field_count) {
int rc_json_get_object_string_length(const char* json) {
const char* json_start = json;
rc_json_iterator_t iterator;
memset(&iterator, 0, sizeof(iterator));
iterator.json = json;
iterator.end = json + (1024 * 1024 * 1024); /* arbitrary 1GB limit on JSON response */
rc_json_parse_object(&iterator, NULL, 0, NULL);
return (int)(iterator.json - json_start);
}
static int rc_json_extract_html_error(rc_api_response_t* response, const rc_api_server_response_t* server_response) {
const char* json = server_response->body;
const char* end = json;
const char* title_start = strstr(json, "<title>");
if (title_start) {
title_start += 7;
if (isdigit((int)*title_start)) {
const char* title_end = strstr(title_start + 7, "</title>");
if (title_end) {
response->error_message = rc_buffer_strncpy(&response->buffer, title_start, title_end - title_start);
response->succeeded = 0;
return RC_INVALID_JSON;
}
}
}
while (*end && *end != '\n' && end - json < 200)
++end;
if (end > json && end[-1] == '\r')
--end;
if (end > json)
response->error_message = rc_buffer_strncpy(&response->buffer, json, end - json);
response->succeeded = 0;
return RC_INVALID_JSON;
}
static int rc_json_convert_error_code(const char* server_error_code)
{
switch (server_error_code[0]) {
case 'a':
if (strcmp(server_error_code, "access_denied") == 0)
return RC_ACCESS_DENIED;
break;
case 'e':
if (strcmp(server_error_code, "expired_token") == 0)
return RC_EXPIRED_TOKEN;
break;
case 'i':
if (strcmp(server_error_code, "invalid_credentials") == 0)
return RC_INVALID_CREDENTIALS;
break;
default:
break;
}
return RC_API_FAILURE;
}
int rc_json_parse_server_response(rc_api_response_t* response, const rc_api_server_response_t* server_response, rc_json_field_t* fields, size_t field_count) {
int result;
#ifndef NDEBUG
if (field_count < 2)
return RC_INVALID_STATE;
@ -242,37 +316,66 @@ int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_jso
return RC_INVALID_STATE;
#endif
if (*json == '{') {
int result = rc_json_parse_object(&json, fields, field_count, NULL);
response->error_message = NULL;
if (!server_response) {
response->succeeded = 0;
return RC_NO_RESPONSE;
}
if (server_response->http_status_code == RC_API_SERVER_RESPONSE_CLIENT_ERROR ||
server_response->http_status_code == RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR) {
/* client provided error message is passed as the response body */
response->error_message = server_response->body;
response->succeeded = 0;
return RC_NO_RESPONSE;
}
if (!server_response->body || !*server_response->body) {
/* expect valid HTTP status codes to have bodies that we can extract the message from,
* but provide some default messages in case they don't. */
switch (server_response->http_status_code) {
case 504: /* 504 Gateway Timeout */
case 522: /* 522 Connection Timed Out */
case 524: /* 524 A Timeout Occurred */
response->error_message = "Request has timed out.";
break;
case 521: /* 521 Web Server is Down */
case 523: /* 523 Origin is Unreachable */
response->error_message = "Could not connect to server.";
break;
default:
break;
}
response->succeeded = 0;
return RC_NO_RESPONSE;
}
if (*server_response->body != '{') {
result = rc_json_extract_html_error(response, server_response);
}
else {
rc_json_iterator_t iterator;
memset(&iterator, 0, sizeof(iterator));
iterator.json = server_response->body;
iterator.end = server_response->body + server_response->body_length;
result = rc_json_parse_object(&iterator, fields, field_count, NULL);
rc_json_get_optional_string(&response->error_message, response, &fields[1], "Error", NULL);
rc_json_get_optional_bool(&response->succeeded, &fields[0], "Success", 1);
return result;
}
response->error_message = NULL;
if (*json) {
const char* end = json;
while (*end && *end != '\n' && end - json < 200)
++end;
if (end > json && end[-1] == '\r')
--end;
if (end > json) {
char* dst = rc_buf_reserve(&response->buffer, (end - json) + 1);
response->error_message = dst;
memcpy(dst, json, end - json);
dst += (end - json);
*dst++ = '\0';
rc_buf_consume(&response->buffer, response->error_message, dst);
/* Code will be the third field in the fields array, but may not always be present */
if (field_count > 2 && strcmp(fields[2].name, "Code") == 0) {
rc_json_get_optional_string(&response->error_code, response, &fields[2], "Code", NULL);
if (response->error_code != NULL)
result = rc_json_convert_error_code(response->error_code);
}
}
response->succeeded = 0;
return RC_INVALID_JSON;
return result;
}
static int rc_json_missing_field(rc_api_response_t* response, const rc_json_field_t* field) {
@ -280,14 +383,14 @@ static int rc_json_missing_field(rc_api_response_t* response, const rc_json_fiel
const size_t not_found_len = strlen(not_found);
const size_t field_len = strlen(field->name);
char* write = rc_buf_reserve(&response->buffer, field_len + not_found_len + 1);
uint8_t* write = rc_buffer_reserve(&response->buffer, field_len + not_found_len + 1);
if (write) {
response->error_message = write;
response->error_message = (char*)write;
memcpy(write, field->name, field_len);
write += field_len;
memcpy(write, not_found, not_found_len + 1);
write += not_found_len + 1;
rc_buf_consume(&response->buffer, response->error_message, write);
rc_buffer_consume(&response->buffer, (uint8_t*)response->error_message, write);
}
response->succeeded = 0;
@ -295,7 +398,8 @@ static int rc_json_missing_field(rc_api_response_t* response, const rc_json_fiel
}
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) {
const char* json = field->value_start;
rc_json_iterator_t iterator;
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
return 0;
@ -303,45 +407,53 @@ int rc_json_get_required_object(rc_json_field_t* fields, size_t field_count, rc_
(void)field_name;
#endif
if (!json)
if (!field->value_start)
return rc_json_missing_field(response, field);
return (rc_json_parse_object(&json, fields, field_count, &field->array_size) == RC_OK);
memset(&iterator, 0, sizeof(iterator));
iterator.json = field->value_start;
iterator.end = field->value_end;
return (rc_json_parse_object(&iterator, fields, field_count, &field->array_size) == RC_OK);
}
static int rc_json_get_array_entry_value(rc_json_field_t* field, rc_json_field_t* iterator) {
if (!iterator->array_size)
static int rc_json_get_array_entry_value(rc_json_field_t* field, rc_json_iterator_t* iterator) {
rc_json_skip_whitespace(iterator);
if (iterator->json >= iterator->end)
return 0;
while (isspace((unsigned char)*iterator->value_start))
++iterator->value_start;
if (rc_json_parse_field(iterator, field) != RC_OK)
return 0;
rc_json_parse_field(&iterator->value_start, field);
rc_json_skip_whitespace(iterator);
while (isspace((unsigned char)*iterator->value_start))
++iterator->value_start;
if (!rc_json_match_char(iterator, ','))
rc_json_match_char(iterator, ']');
++iterator->value_start; /* skip , or ] */
--iterator->array_size;
return 1;
}
int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
rc_json_field_t iterator;
int rc_json_get_required_unum_array(uint32_t** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
rc_json_iterator_t iterator;
rc_json_field_t array;
rc_json_field_t value;
unsigned* entry;
if (!rc_json_get_required_array(num_entries, &iterator, response, field, field_name))
memset(&array, 0, sizeof(array));
if (!rc_json_get_required_array(num_entries, &array, response, field, field_name))
return RC_MISSING_VALUE;
if (*num_entries) {
*entries = (unsigned*)rc_buf_alloc(&response->buffer, *num_entries * sizeof(unsigned));
*entries = (unsigned*)rc_buffer_alloc(&response->buffer, *num_entries * sizeof(unsigned));
if (!*entries)
return RC_OUT_OF_MEMORY;
value.name = field_name;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array.value_start;
iterator.end = array.value_end;
entry = *entries;
while (rc_json_get_array_entry_value(&value, &iterator)) {
if (!rc_json_get_unum(entry, &value, field_name))
@ -357,7 +469,19 @@ int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, r
return RC_OK;
}
int rc_json_get_required_array(unsigned* 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_required_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) {
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
return 0;
#endif
if (!rc_json_get_optional_array(num_entries, array_field, response, 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) {
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
return 0;
@ -367,44 +491,43 @@ int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator,
if (!field->value_start || *field->value_start != '[') {
*num_entries = 0;
return rc_json_missing_field(response, field);
return 0;
}
memcpy(iterator, field, sizeof(*iterator));
++iterator->value_start; /* skip [ */
memcpy(array_field, field, sizeof(*array_field));
++array_field->value_start; /* skip [ */
*num_entries = field->array_size;
return 1;
}
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_field_t* iterator) {
if (!iterator->array_size)
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_iterator_t* iterator) {
rc_json_skip_whitespace(iterator);
if (iterator->json >= iterator->end)
return 0;
while (isspace((unsigned char)*iterator->value_start))
++iterator->value_start;
if (rc_json_parse_object(iterator, fields, field_count, NULL) != RC_OK)
return 0;
rc_json_parse_object(&iterator->value_start, fields, field_count, NULL);
rc_json_skip_whitespace(iterator);
while (isspace((unsigned char)*iterator->value_start))
++iterator->value_start;
if (!rc_json_match_char(iterator, ','))
rc_json_match_char(iterator, ']');
++iterator->value_start; /* skip , or ] */
--iterator->array_size;
return 1;
}
static unsigned rc_json_decode_hex4(const char* input) {
static uint32_t rc_json_decode_hex4(const char* input) {
char hex[5];
memcpy(hex, input, 4);
hex[4] = '\0';
return (unsigned)strtoul(hex, NULL, 16);
return (uint32_t)strtoul(hex, NULL, 16);
}
static int rc_json_ucs32_to_utf8(unsigned char* dst, unsigned ucs32_char) {
static int rc_json_ucs32_to_utf8(uint8_t* dst, uint32_t ucs32_char) {
if (ucs32_char < 0x80) {
dst[0] = (ucs32_char & 0x7F);
return 1;
@ -449,7 +572,7 @@ static int rc_json_ucs32_to_utf8(unsigned char* dst, unsigned ucs32_char) {
return 6;
}
int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_field_t* field, const char* field_name) {
int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_field_t* field, const char* field_name) {
const char* src = field->value_start;
size_t len = field->value_end - field->value_start;
char* dst;
@ -480,7 +603,7 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
return 1;
}
*out = dst = rc_buf_reserve(buffer, len - 1); /* -2 for quotes, +1 for null terminator */
*out = dst = (char*)rc_buffer_reserve(buffer, len - 1); /* -2 for quotes, +1 for null terminator */
do {
if (*src == '\\') {
@ -501,13 +624,13 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
if (*src == 'u') {
/* unicode character */
unsigned ucs32_char = rc_json_decode_hex4(src + 1);
uint32_t ucs32_char = rc_json_decode_hex4(src + 1);
src += 5;
if (ucs32_char >= 0xD800 && ucs32_char < 0xE000) {
/* surrogate lead - look for surrogate tail */
if (ucs32_char < 0xDC00 && src[0] == '\\' && src[1] == 'u') {
const unsigned surrogate = rc_json_decode_hex4(src + 2);
const uint32_t surrogate = rc_json_decode_hex4(src + 2);
src += 6;
if (surrogate >= 0xDC00 && surrogate < 0xE000) {
@ -540,13 +663,13 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
} while (*src != '\"');
} else {
*out = dst = rc_buf_reserve(buffer, len + 1); /* +1 for null terminator */
*out = dst = (char*)rc_buffer_reserve(buffer, len + 1); /* +1 for null terminator */
memcpy(dst, src, len);
dst += len;
}
*dst++ = '\0';
rc_buf_consume(buffer, *out, dst);
rc_buffer_consume(buffer, (uint8_t*)(*out), (uint8_t*)dst);
return 1;
}
@ -562,9 +685,9 @@ int rc_json_get_required_string(const char** out, rc_api_response_t* response, c
return rc_json_missing_field(response, field);
}
int rc_json_get_num(int* out, 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) {
const char* src = field->value_start;
int value = 0;
int32_t value = 0;
int negative = 0;
#ifndef NDEBUG
@ -604,21 +727,21 @@ int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_na
return 1;
}
void rc_json_get_optional_num(int* out, const rc_json_field_t* field, const char* field_name, int default_value) {
void rc_json_get_optional_num(int32_t* out, const rc_json_field_t* field, const char* field_name, int default_value) {
if (!rc_json_get_num(out, field, field_name))
*out = default_value;
}
int rc_json_get_required_num(int* 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) {
if (rc_json_get_num(out, field, field_name))
return 1;
return rc_json_missing_field(response, field);
}
int rc_json_get_unum(unsigned* 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) {
const char* src = field->value_start;
int value = 0;
uint32_t value = 0;
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
@ -648,12 +771,12 @@ int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* fi
return 1;
}
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, unsigned 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) {
if (!rc_json_get_unum(out, field, field_name))
*out = default_value;
}
int rc_json_get_required_unum(unsigned* 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) {
if (rc_json_get_unum(out, field, field_name))
return 1;
@ -746,121 +869,27 @@ int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_js
return rc_json_missing_field(response, field);
}
/* --- rc_buf --- */
/* --- rc_api_request --- */
void rc_buf_init(rc_api_buffer_t* buffer) {
buffer->chunk.write = buffer->chunk.start = &buffer->data[0];
buffer->chunk.end = &buffer->data[sizeof(buffer->data)];
buffer->chunk.next = NULL;
}
void rc_buf_destroy(rc_api_buffer_t* buffer) {
rc_api_buffer_chunk_t *chunk;
#ifdef DEBUG_BUFFERS
int count = 0;
int wasted = 0;
int total = 0;
#endif
/* first chunk is not allocated. skip it. */
chunk = buffer->chunk.next;
/* deallocate any additional buffers */
while (chunk) {
rc_api_buffer_chunk_t* next = chunk->next;
#ifdef DEBUG_BUFFERS
total += (int)(chunk->end - chunk->data);
wasted += (int)(chunk->end - chunk->write);
++count;
#endif
free(chunk);
chunk = next;
}
#ifdef DEBUG_BUFFERS
printf("-- %d allocated buffers (%d/%d used, %d wasted, %0.2f%% efficiency)\n", count,
total - wasted, total, wasted, (float)(100.0 - (wasted * 100.0) / total));
#endif
}
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount) {
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
size_t remaining;
while (chunk) {
remaining = chunk->end - chunk->write;
if (remaining >= amount)
return chunk->write;
if (!chunk->next) {
/* allocate a chunk of memory that is a multiple of 256-bytes. the first 32 bytes will be associated
* to the chunk header, and the remaining will be used for data.
*/
const size_t chunk_header_size = sizeof(rc_api_buffer_chunk_t);
const size_t alloc_size = (chunk_header_size + amount + 0xFF) & ~0xFF;
chunk->next = (rc_api_buffer_chunk_t*)malloc(alloc_size);
if (!chunk->next)
break;
chunk->next->start = (char*)chunk->next + chunk_header_size;
chunk->next->write = chunk->next->start;
chunk->next->end = (char*)chunk->next + alloc_size;
chunk->next->next = NULL;
}
chunk = chunk->next;
}
return NULL;
}
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end) {
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
do {
if (chunk->write == start) {
size_t offset = (end - chunk->start);
offset = (offset + 7) & ~7;
chunk->write = &chunk->start[offset];
if (chunk->write > chunk->end)
chunk->write = chunk->end;
break;
}
chunk = chunk->next;
} while (chunk);
}
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount) {
char* ptr = rc_buf_reserve(buffer, amount);
rc_buf_consume(buffer, ptr, ptr + amount);
return (void*)ptr;
}
void rc_api_destroy_request(rc_api_request_t* request) {
rc_buf_destroy(&request->buffer);
}
void rc_api_format_md5(char checksum[33], const unsigned char digest[16]) {
snprintf(checksum, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
);
void rc_api_destroy_request(rc_api_request_t* request)
{
rc_buffer_destroy(&request->buffer);
}
/* --- rc_url_builder --- */
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer, size_t estimated_size) {
rc_api_buffer_chunk_t* used_buffer;
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_buffer_t* buffer, size_t estimated_size) {
rc_buffer_chunk_t* used_buffer;
memset(builder, 0, sizeof(*builder));
builder->buffer = buffer;
builder->write = builder->start = rc_buf_reserve(buffer, estimated_size);
builder->write = builder->start = (char*)rc_buffer_reserve(buffer, estimated_size);
used_buffer = &buffer->chunk;
while (used_buffer && used_buffer->write != builder->write)
while (used_buffer && used_buffer->write != (uint8_t*)builder->write)
used_buffer = used_buffer->next;
builder->end = (used_buffer) ? used_buffer->end : builder->start + estimated_size;
builder->end = (used_buffer) ? (char*)used_buffer->end : builder->start + estimated_size;
}
const char* rc_url_builder_finalize(rc_api_url_builder_t* builder) {
@ -869,7 +898,7 @@ const char* rc_url_builder_finalize(rc_api_url_builder_t* builder) {
if (builder->result != RC_OK)
return NULL;
rc_buf_consume(builder->buffer, builder->start, builder->write);
rc_buffer_consume(builder->buffer, (uint8_t*)builder->start, (uint8_t*)builder->write);
return builder->start;
}
@ -879,7 +908,7 @@ static int rc_url_builder_reserve(rc_api_url_builder_t* builder, size_t amount)
if (remaining < amount) {
const size_t used = builder->write - builder->start;
const size_t current_size = builder->end - builder->start;
const size_t buffer_prefix_size = sizeof(rc_api_buffer_chunk_t);
const size_t buffer_prefix_size = sizeof(rc_buffer_chunk_t);
char* new_start;
size_t new_size = (current_size < 256) ? 256 : current_size * 2;
do {
@ -890,11 +919,11 @@ static int rc_url_builder_reserve(rc_api_url_builder_t* builder, size_t amount)
new_size *= 2;
} while (1);
/* rc_buf_reserve will align to 256 bytes after including the buffer prefix. attempt to account for that */
/* rc_buffer_reserve will align to 256 bytes after including the buffer prefix. attempt to account for that */
if ((remaining - amount) > buffer_prefix_size)
new_size -= buffer_prefix_size;
new_start = rc_buf_reserve(builder->buffer, new_size);
new_start = (char*)rc_buffer_reserve(builder->buffer, new_size);
if (!new_start) {
builder->result = RC_OUT_OF_MEMORY;
return RC_OUT_OF_MEMORY;
@ -985,7 +1014,7 @@ static int rc_url_builder_append_param_equals(rc_api_url_builder_t* builder, con
return builder->result;
}
void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char* param, unsigned value) {
void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char* param, uint32_t value) {
if (rc_url_builder_append_param_equals(builder, param) == RC_OK) {
char num[16];
int chars = snprintf(num, sizeof(num), "%u", value);
@ -993,7 +1022,7 @@ void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char*
}
}
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int value) {
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int32_t value) {
if (rc_url_builder_append_param_equals(builder, param) == RC_OK) {
char num[16];
int chars = snprintf(num, sizeof(num), "%d", value);
@ -1008,7 +1037,7 @@ 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) {
#define DOREQUEST_ENDPOINT "/dorequest.php"
rc_buf_init(&request->buffer);
rc_buffer_init(&request->buffer);
if (!g_host) {
request->url = RETROACHIEVEMENTS_HOST DOREQUEST_ENDPOINT;
@ -1017,13 +1046,13 @@ void rc_api_url_build_dorequest_url(rc_api_request_t* request) {
const size_t endpoint_len = sizeof(DOREQUEST_ENDPOINT);
const size_t host_len = strlen(g_host);
const size_t url_len = host_len + endpoint_len;
char* url = rc_buf_reserve(&request->buffer, url_len);
uint8_t* url = rc_buffer_reserve(&request->buffer, url_len);
memcpy(url, g_host, host_len);
memcpy(url + host_len, DOREQUEST_ENDPOINT, endpoint_len);
rc_buf_consume(&request->buffer, url, url + url_len);
rc_buffer_consume(&request->buffer, url, url + url_len);
request->url = url;
request->url = (char*)url;
}
#undef DOREQUEST_ENDPOINT
}
@ -1097,7 +1126,7 @@ void rc_api_set_image_host(const char* hostname) {
int rc_api_init_fetch_image_request(rc_api_request_t* request, const rc_api_fetch_image_request_t* api_params) {
rc_api_url_builder_t builder;
rc_buf_init(&request->buffer);
rc_buffer_init(&request->buffer);
rc_url_builder_init(&builder, &request->buffer, 64);
if (g_imagehost) {

View file

@ -10,72 +10,69 @@
extern "C" {
#endif
#define RC_CONTENT_TYPE_URLENCODED "application/x-www-form-urlencoded"
typedef struct rc_api_url_builder_t {
char* write;
char* start;
char* end;
/* pointer to a preallocated rc_api_buffer_t */
rc_api_buffer_t* buffer;
/* pointer to a preallocated rc_buffer_t */
rc_buffer_t* buffer;
int result;
}
rc_api_url_builder_t;
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer, size_t estimated_size);
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_buffer_t* buffer, size_t estimated_size);
void rc_url_builder_append(rc_api_url_builder_t* builder, const char* data, size_t len);
const char* rc_url_builder_finalize(rc_api_url_builder_t* builder);
#define RC_JSON_NEW_FIELD(n) {n,0,0,0}
#define RC_JSON_NEW_FIELD(n) {NULL,NULL,n,sizeof(n)-1,0}
typedef struct rc_json_field_t {
const char* name;
const char* value_start;
const char* value_end;
unsigned array_size;
const char* name;
size_t name_len;
uint32_t array_size;
}
rc_json_field_t;
typedef struct rc_json_object_field_iterator_t {
rc_json_field_t field;
typedef struct rc_json_iterator_t {
const char* json;
size_t name_len;
const char* end;
}
rc_json_object_field_iterator_t;
rc_json_iterator_t;
int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_json_field_t* fields, size_t field_count);
int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_field_t* field, const char* field_name);
int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_name);
int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* field_name);
int rc_json_parse_server_response(rc_api_response_t* response, const rc_api_server_response_t* server_response, rc_json_field_t* fields, size_t field_count);
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_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(int* out, const rc_json_field_t* field, const char* field_name, int default_value);
void rc_json_get_optional_unum(unsigned* out, const rc_json_field_t* field, const char* field_name, unsigned 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_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_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(int* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_unum(unsigned* 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_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);
int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_array(unsigned* 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_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_field_t* iterator);
int rc_json_get_next_object_field(rc_json_object_field_iterator_t* iterator);
void rc_buf_init(rc_api_buffer_t* buffer);
void rc_buf_destroy(rc_api_buffer_t* buffer);
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount);
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end);
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount);
int rc_json_get_required_unum_array(uint32_t** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_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_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_iterator_t* iterator);
int rc_json_get_next_object_field(rc_json_iterator_t* iterator, rc_json_field_t* field);
int rc_json_get_object_string_length(const char* json);
void rc_url_builder_append_encoded_str(rc_api_url_builder_t* builder, const char* str);
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int value);
void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char* param, unsigned value);
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int32_t value);
void rc_url_builder_append_unum_param(rc_api_url_builder_t* builder, const char* param, uint32_t value);
void rc_url_builder_append_str_param(rc_api_url_builder_t* builder, const char* param, const char* value);
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);
void rc_api_format_md5(char checksum[33], const unsigned char digest[16]);
#ifdef __cplusplus
}

View file

@ -27,15 +27,27 @@ int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_fetch_achievement_info_server_response(response, &response_obj);
}
int rc_api_process_fetch_achievement_info_server_response(rc_api_fetch_achievement_info_response_t* response, const rc_api_server_response_t* server_response) {
rc_api_achievement_awarded_entry_t* entry;
rc_json_field_t iterator;
unsigned timet;
rc_json_field_t array_field;
rc_json_iterator_t iterator;
uint32_t timet;
int result;
rc_json_field_t fields[] = {
@ -63,9 +75,9 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;
@ -81,14 +93,18 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
if (!rc_json_get_required_unum(&response->game_id, &response->response, &response_fields[2], "GameID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_array(&response->num_recently_awarded, &iterator, &response->response, &response_fields[3], "RecentWinner"))
if (!rc_json_get_required_array(&response->num_recently_awarded, &array_field, &response->response, &response_fields[3], "RecentWinner"))
return RC_MISSING_VALUE;
if (response->num_recently_awarded) {
response->recently_awarded = (rc_api_achievement_awarded_entry_t*)rc_buf_alloc(&response->response.buffer, response->num_recently_awarded * sizeof(rc_api_achievement_awarded_entry_t));
response->recently_awarded = (rc_api_achievement_awarded_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_recently_awarded * sizeof(rc_api_achievement_awarded_entry_t));
if (!response->recently_awarded)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
entry = response->recently_awarded;
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
@ -106,7 +122,7 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
}
void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Fetch Leaderboard Info --- */
@ -130,14 +146,26 @@ int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
return builder.result;
}
int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_fetch_leaderboard_info_server_response(response, &response_obj);
}
int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboard_info_response_t* response, const rc_api_server_response_t* server_response) {
rc_api_lboard_info_entry_t* entry;
rc_json_field_t iterator;
unsigned timet;
rc_json_field_t array_field;
rc_json_iterator_t iterator;
uint32_t timet;
int result;
size_t len;
char format[16];
@ -178,9 +206,9 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;
@ -189,7 +217,7 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
if (!rc_json_get_required_unum(&response->id, &response->response, &leaderboarddata_fields[0], "LBID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_num(&response->lower_is_better, &response->response, &leaderboarddata_fields[2], "LowerIsBetter"))
if (!rc_json_get_required_unum(&response->lower_is_better, &response->response, &leaderboarddata_fields[2], "LowerIsBetter"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&response->title, &response->response, &leaderboarddata_fields[3], "LBTitle"))
return RC_MISSING_VALUE;
@ -218,14 +246,18 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
response->format = RC_FORMAT_VALUE;
}
if (!rc_json_get_required_array(&response->num_entries, &iterator, &response->response, &leaderboarddata_fields[10], "Entries"))
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &leaderboarddata_fields[10], "Entries"))
return RC_MISSING_VALUE;
if (response->num_entries) {
response->entries = (rc_api_lboard_info_entry_t*)rc_buf_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_lboard_info_entry_t));
response->entries = (rc_api_lboard_info_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_lboard_info_entry_t));
if (!response->entries)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
entry = response->entries;
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
@ -252,7 +284,7 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
}
void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Fetch Games List --- */
@ -270,13 +302,25 @@ int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
return builder.result;
}
int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_fetch_games_list_server_response(response, &response_obj);
}
int rc_api_process_fetch_games_list_server_response(rc_api_fetch_games_list_response_t* response, const rc_api_server_response_t* server_response) {
rc_api_game_list_entry_t* entry;
rc_json_object_field_iterator_t iterator;
rc_json_iterator_t iterator;
rc_json_field_t field;
int result;
char* end;
@ -287,9 +331,9 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;
@ -300,21 +344,22 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
}
response->num_entries = fields[2].array_size;
rc_buf_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t)));
rc_buffer_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t)));
response->entries = (rc_api_game_list_entry_t*)rc_buf_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
if (!response->entries)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = fields[2].value_start;
iterator.end = fields[2].value_end;
entry = response->entries;
while (rc_json_get_next_object_field(&iterator)) {
entry->id = strtol(iterator.field.name, &end, 10);
while (rc_json_get_next_object_field(&iterator, &field)) {
entry->id = strtol(field.name, &end, 10);
iterator.field.name = "";
if (!rc_json_get_string(&entry->name, &response->response.buffer, &iterator.field, ""))
field.name = "";
if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, ""))
return RC_MISSING_VALUE;
++entry;
@ -324,5 +369,5 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
}
void rc_api_destroy_fetch_games_list_response(rc_api_fetch_games_list_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}

View file

@ -3,7 +3,7 @@
#include "rc_runtime.h"
#include "rc_runtime_types.h"
#include "../rcheevos/rc_compat.h"
#include "../rc_compat.h"
#include "../rhash/md5.h"
#include <stdlib.h>
@ -24,11 +24,22 @@ int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_res
rc_url_builder_append_str_param(&builder, "r", "gameid");
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
return builder.result;
}
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_resolve_hash_server_response(response, &response_obj);
}
int rc_api_process_resolve_hash_server_response(rc_api_resolve_hash_response_t* response, const rc_api_server_response_t* server_response) {
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
@ -37,9 +48,9 @@ int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* respons
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;
@ -48,7 +59,7 @@ int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* respons
}
void rc_api_destroy_resolve_hash_response(rc_api_resolve_hash_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Fetch Game Data --- */
@ -65,21 +76,33 @@ int rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_
if (rc_api_url_build_dorequest(&builder, "patch", api_params->username, api_params->api_token)) {
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_fetch_game_data_server_response(response, &response_obj);
}
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;
rc_json_field_t iterator;
rc_json_field_t array_field;
rc_json_iterator_t iterator;
const char* str;
const char* last_author = "";
const char* last_author_field = "";
size_t last_author_len = 0;
size_t len;
unsigned timet;
uint32_t timet;
int result;
char format[16];
@ -127,9 +150,9 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK || !response->response.succeeded)
return result;
@ -168,21 +191,25 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
len += (patchdata_fields[6].value_end - patchdata_fields[6].value_start) - /* leaderboards */
patchdata_fields[6].array_size * (60 - sizeof(rc_api_leaderboard_definition_t));
rc_buf_reserve(&response->response.buffer, len);
rc_buffer_reserve(&response->response.buffer, len);
/* end estimation */
rc_json_get_optional_string(&response->rich_presence_script, &response->response, &patchdata_fields[4], "RichPresencePatch", "");
if (!response->rich_presence_script)
response->rich_presence_script = "";
if (!rc_json_get_required_array(&response->num_achievements, &iterator, &response->response, &patchdata_fields[5], "Achievements"))
if (!rc_json_get_required_array(&response->num_achievements, &array_field, &response->response, &patchdata_fields[5], "Achievements"))
return RC_MISSING_VALUE;
if (response->num_achievements) {
response->achievements = (rc_api_achievement_definition_t*)rc_buf_alloc(&response->response.buffer, response->num_achievements * sizeof(rc_api_achievement_definition_t));
response->achievements = (rc_api_achievement_definition_t*)rc_buffer_alloc(&response->response.buffer, response->num_achievements * sizeof(rc_api_achievement_definition_t));
if (!response->achievements)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
achievement = response->achievements;
while (rc_json_get_array_entry_object(achievement_fields, sizeof(achievement_fields) / sizeof(achievement_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&achievement->id, &response->response, &achievement_fields[0], "ID"))
@ -224,14 +251,18 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
}
}
if (!rc_json_get_required_array(&response->num_leaderboards, &iterator, &response->response, &patchdata_fields[6], "Leaderboards"))
if (!rc_json_get_required_array(&response->num_leaderboards, &array_field, &response->response, &patchdata_fields[6], "Leaderboards"))
return RC_MISSING_VALUE;
if (response->num_leaderboards) {
response->leaderboards = (rc_api_leaderboard_definition_t*)rc_buf_alloc(&response->response.buffer, response->num_leaderboards * sizeof(rc_api_leaderboard_definition_t));
response->leaderboards = (rc_api_leaderboard_definition_t*)rc_buffer_alloc(&response->response.buffer, response->num_leaderboards * sizeof(rc_api_leaderboard_definition_t));
if (!response->leaderboards)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
leaderboard = response->leaderboards;
while (rc_json_get_array_entry_object(leaderboard_fields, sizeof(leaderboard_fields) / sizeof(leaderboard_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&leaderboard->id, &response->response, &leaderboard_fields[0], "ID"))
@ -242,8 +273,10 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&leaderboard->definition, &response->response, &leaderboard_fields[3], "Mem"))
return RC_MISSING_VALUE;
rc_json_get_optional_bool(&leaderboard->lower_is_better, &leaderboard_fields[5], "LowerIsBetter", 0);
rc_json_get_optional_bool(&leaderboard->hidden, &leaderboard_fields[6], "Hidden", 0);
rc_json_get_optional_bool(&result, &leaderboard_fields[5], "LowerIsBetter", 0);
leaderboard->lower_is_better = (uint8_t)result;
rc_json_get_optional_bool(&result, &leaderboard_fields[6], "Hidden", 0);
leaderboard->hidden = (uint8_t)result;
if (!leaderboard_fields[4].value_end)
return RC_MISSING_VALUE;
@ -265,7 +298,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
}
void rc_api_destroy_fetch_game_data_response(rc_api_fetch_game_data_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Ping --- */
@ -286,25 +319,36 @@ int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_reques
rc_url_builder_append_str_param(&builder, "m", api_params->rich_presence);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_ping_server_response(response, &response_obj);
}
int rc_api_process_ping_server_response(rc_api_ping_response_t* response, const rc_api_server_response_t* server_response) {
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error")
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
return rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
}
void rc_api_destroy_ping_response(rc_api_ping_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Award Achievement --- */
@ -335,29 +379,41 @@ int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_ap
snprintf(buffer, sizeof(buffer), "%d", api_params->hardcore ? 1 : 0);
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
md5_finish(&md5, digest);
rc_api_format_md5(buffer, digest);
rc_format_md5(buffer, digest);
rc_url_builder_append_str_param(&builder, "v", buffer);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_award_achievement_server_response(response, &response_obj);
}
int rc_api_process_award_achievement_server_response(rc_api_award_achievement_response_t* response, const rc_api_server_response_t* server_response) {
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("Score"),
RC_JSON_NEW_FIELD("SoftcoreScore"),
RC_JSON_NEW_FIELD("AchievementID"),
RC_JSON_NEW_FIELD("AchievementsRemaining")
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;
@ -375,14 +431,15 @@ int rc_api_process_award_achievement_response(rc_api_award_achievement_response_
}
rc_json_get_optional_unum(&response->new_player_score, &fields[2], "Score", 0);
rc_json_get_optional_unum(&response->awarded_achievement_id, &fields[3], "AchievementID", 0);
rc_json_get_optional_unum(&response->achievements_remaining, &fields[4], "AchievementsRemaining", (unsigned)-1);
rc_json_get_optional_unum(&response->new_player_score_softcore, &fields[3], "SoftcoreScore", 0);
rc_json_get_optional_unum(&response->awarded_achievement_id, &fields[4], "AchievementID", 0);
rc_json_get_optional_unum(&response->achievements_remaining, &fields[5], "AchievementsRemaining", (unsigned)-1);
return RC_OK;
}
void rc_api_destroy_award_achievement_response(rc_api_award_achievement_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Submit Leaderboard Entry --- */
@ -414,18 +471,30 @@ int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_
snprintf(buffer, sizeof(buffer), "%d", api_params->score);
md5_append(&md5, (md5_byte_t*)buffer, (int)strlen(buffer));
md5_finish(&md5, digest);
rc_api_format_md5(buffer, digest);
rc_format_md5(buffer, digest);
rc_url_builder_append_str_param(&builder, "v", buffer);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_submit_lboard_entry_server_response(response, &response_obj);
}
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) {
rc_api_lboard_entry_t* entry;
rc_json_field_t iterator;
rc_json_field_t array_field;
rc_json_iterator_t iterator;
const char* str;
int result;
@ -476,9 +545,9 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK || !response->response.succeeded)
return result;
@ -497,14 +566,18 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
return RC_MISSING_VALUE;
response->num_entries = (unsigned)atoi(str);
if (!rc_json_get_required_array(&response->num_top_entries, &iterator, &response->response, &response_fields[3], "TopEntries"))
if (!rc_json_get_required_array(&response->num_top_entries, &array_field, &response->response, &response_fields[3], "TopEntries"))
return RC_MISSING_VALUE;
if (response->num_top_entries) {
response->top_entries = (rc_api_lboard_entry_t*)rc_buf_alloc(&response->response.buffer, response->num_top_entries * sizeof(rc_api_lboard_entry_t));
response->top_entries = (rc_api_lboard_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_top_entries * sizeof(rc_api_lboard_entry_t));
if (!response->top_entries)
return RC_OUT_OF_MEMORY;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
entry = response->top_entries;
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
@ -524,5 +597,5 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
}
void rc_api_destroy_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}

View file

@ -1,7 +1,7 @@
#include "rc_api_user.h"
#include "rc_api_common.h"
#include "../rcheevos/rc_version.h"
#include "../rc_version.h"
#include <string.h>
@ -16,7 +16,7 @@ int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_requ
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
rc_url_builder_append_str_param(&builder, "r", "login");
rc_url_builder_append_str_param(&builder, "r", "login2");
rc_url_builder_append_str_param(&builder, "u", api_params->username);
if (api_params->password && api_params->password[0])
@ -27,44 +27,58 @@ int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_requ
return RC_INVALID_STATE;
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
return builder.result;
}
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_login_server_response(response, &response_obj);
}
int rc_api_process_login_server_response(rc_api_login_response_t* response, const rc_api_server_response_t* server_response) {
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("Code"),
RC_JSON_NEW_FIELD("User"),
RC_JSON_NEW_FIELD("Token"),
RC_JSON_NEW_FIELD("Score"),
RC_JSON_NEW_FIELD("SoftcoreScore"),
RC_JSON_NEW_FIELD("Messages"),
RC_JSON_NEW_FIELD("DisplayName")
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK || !response->response.succeeded)
return result;
if (!rc_json_get_required_string(&response->username, &response->response, &fields[2], "User"))
if (!rc_json_get_required_string(&response->username, &response->response, &fields[3], "User"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&response->api_token, &response->response, &fields[3], "Token"))
if (!rc_json_get_required_string(&response->api_token, &response->response, &fields[4], "Token"))
return RC_MISSING_VALUE;
rc_json_get_optional_unum(&response->score, &fields[4], "Score", 0);
rc_json_get_optional_unum(&response->num_unread_messages, &fields[5], "Messages", 0);
rc_json_get_optional_unum(&response->score, &fields[5], "Score", 0);
rc_json_get_optional_unum(&response->score_softcore, &fields[6], "SoftcoreScore", 0);
rc_json_get_optional_unum(&response->num_unread_messages, &fields[7], "Messages", 0);
rc_json_get_optional_string(&response->display_name, &response->response, &fields[6], "DisplayName", response->username);
rc_json_get_optional_string(&response->display_name, &response->response, &fields[8], "DisplayName", response->username);
return RC_OK;
}
void rc_api_destroy_login_response(rc_api_login_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Start Session --- */
@ -78,37 +92,103 @@ int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_st
return RC_INVALID_STATE;
rc_url_builder_init(&builder, &request->buffer, 48);
if (rc_api_url_build_dorequest(&builder, "postactivity", api_params->username, api_params->api_token)) {
/* activity type enum (only 3 is used )
* 1 = earned achievement - handled by awardachievement
* 2 = logged in - handled by login
* 3 = started playing
* 4 = uploaded achievement - handled by uploadachievement
* 5 = modified achievement - handled by uploadachievement
*/
rc_url_builder_append_unum_param(&builder, "a", 3);
rc_url_builder_append_unum_param(&builder, "m", api_params->game_id);
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);
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;
}
return builder.result;
}
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_start_session_server_response(response, &response_obj);
}
int rc_api_process_start_session_server_response(rc_api_start_session_response_t* response, const rc_api_server_response_t* server_response) {
rc_api_unlock_entry_t* unlock;
rc_json_field_t array_field;
rc_json_iterator_t iterator;
uint32_t timet;
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error")
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("Unlocks"),
RC_JSON_NEW_FIELD("HardcoreUnlocks"),
RC_JSON_NEW_FIELD("ServerNow")
};
rc_json_field_t unlock_entry_fields[] = {
RC_JSON_NEW_FIELD("ID"),
RC_JSON_NEW_FIELD("When")
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
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) {
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;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
unlock = response->unlocks;
while (rc_json_get_array_entry_object(unlock_entry_fields, sizeof(unlock_entry_fields) / sizeof(unlock_entry_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&unlock->achievement_id, &response->response, &unlock_entry_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&timet, &response->response, &unlock_entry_fields[1], "When"))
return RC_MISSING_VALUE;
unlock->when = (time_t)timet;
++unlock;
}
}
if (rc_json_get_optional_array(&response->num_hardcore_unlocks, &array_field, &response->response, &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;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;
unlock = response->hardcore_unlocks;
while (rc_json_get_array_entry_object(unlock_entry_fields, sizeof(unlock_entry_fields) / sizeof(unlock_entry_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&unlock->achievement_id, &response->response, &unlock_entry_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&timet, &response->response, &unlock_entry_fields[1], "When"))
return RC_MISSING_VALUE;
unlock->when = (time_t)timet;
++unlock;
}
}
rc_json_get_optional_unum(&timet, &fields[4], "ServerNow", 0);
response->server_now = (time_t)timet;
return RC_OK;
}
void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}
/* --- Fetch User Unlocks --- */
@ -123,12 +203,23 @@ int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_a
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore ? 1 : 0);
request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
}
return builder.result;
}
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response) {
rc_api_server_response_t response_obj;
memset(&response_obj, 0, sizeof(response_obj));
response_obj.body = server_response;
response_obj.body_length = rc_json_get_object_string_length(server_response);
return rc_api_process_fetch_user_unlocks_server_response(response, &response_obj);
}
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) {
int result;
rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
@ -141,9 +232,9 @@ int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_respons
};
memset(response, 0, sizeof(*response));
rc_buf_init(&response->response.buffer);
rc_buffer_init(&response->response.buffer);
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK || !response->response.succeeded)
return result;
@ -152,5 +243,5 @@ int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_respons
}
void rc_api_destroy_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response) {
rc_buf_destroy(&response->response.buffer);
rc_buffer_destroy(&response->response.buffer);
}

5097
deps/rcheevos/src/rc_client.c vendored Normal file

File diff suppressed because it is too large Load diff

350
deps/rcheevos/src/rc_client_internal.h vendored Normal file
View file

@ -0,0 +1,350 @@
#ifndef RC_CLIENT_INTERNAL_H
#define RC_CLIENT_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rc_client.h"
#include "rc_compat.h"
#include "rc_runtime.h"
#include "rc_runtime_types.h"
/*****************************************************************************\
| 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,
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 struct rc_client_callbacks_t {
rc_client_read_memory_func_t read_memory;
rc_client_event_handler_t event_handler;
rc_client_server_call_t server_call;
rc_client_message_callback_t log_call;
rc_get_time_millisecs_func_t get_time_millisecs;
rc_client_post_process_game_data_response_t post_process_game_data_response;
rc_client_can_submit_achievement_unlock_t can_submit_achievement_unlock;
rc_client_can_submit_leaderboard_entry_t can_submit_leaderboard_entry;
rc_client_rich_presence_override_t rich_presence_override;
void* client_data;
} 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 struct rc_client_scheduled_callback_data_t
{
rc_clock_t when;
uint32_t related_id;
rc_client_scheduled_callback_t callback;
void* data;
struct rc_client_scheduled_callback_data_t* next;
} rc_client_scheduled_callback_data_t;
void rc_client_schedule_callback(rc_client_t* client, rc_client_scheduled_callback_data_t* scheduled_callback);
/*****************************************************************************\
| Achievements |
\*****************************************************************************/
enum {
RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_NONE = 0,
RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_TRIGGERED = (1 << 1),
RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_CHALLENGE_INDICATOR_SHOW = (1 << 2),
RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_CHALLENGE_INDICATOR_HIDE = (1 << 3),
RC_CLIENT_ACHIEVEMENT_PENDING_EVENT_UPDATE = (1 << 4) /* not a real event, just triggers update */
};
typedef struct rc_client_achievement_info_t {
rc_client_achievement_t public_;
rc_trigger_t* trigger;
uint8_t md5[16];
time_t unlock_time_hardcore;
time_t unlock_time_softcore;
uint8_t pending_events;
const char* author;
time_t created_time;
time_t updated_time;
} rc_client_achievement_info_t;
enum {
RC_CLIENT_PROGRESS_TRACKER_ACTION_NONE,
RC_CLIENT_PROGRESS_TRACKER_ACTION_SHOW,
RC_CLIENT_PROGRESS_TRACKER_ACTION_UPDATE,
RC_CLIENT_PROGRESS_TRACKER_ACTION_HIDE
};
typedef struct rc_client_progress_tracker_t {
rc_client_achievement_info_t* achievement;
float progress;
rc_client_scheduled_callback_data_t* hide_callback;
uint8_t action;
} rc_client_progress_tracker_t;
/*****************************************************************************\
| Leaderboard Trackers |
\*****************************************************************************/
enum {
RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_NONE = 0,
RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_UPDATE = (1 << 1),
RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_SHOW = (1 << 2),
RC_CLIENT_LEADERBOARD_TRACKER_PENDING_EVENT_HIDE = (1 << 3)
};
typedef struct rc_client_leaderboard_tracker_info_t {
rc_client_leaderboard_tracker_t public_;
struct rc_client_leaderboard_tracker_info_t* next;
int32_t raw_value;
uint32_t value_djb2;
uint8_t format;
uint8_t pending_events;
uint8_t reference_count;
uint8_t value_from_hits;
} rc_client_leaderboard_tracker_info_t;
/*****************************************************************************\
| Leaderboards |
\*****************************************************************************/
enum {
RC_CLIENT_LEADERBOARD_PENDING_EVENT_NONE = 0,
RC_CLIENT_LEADERBOARD_PENDING_EVENT_STARTED = (1 << 1),
RC_CLIENT_LEADERBOARD_PENDING_EVENT_FAILED = (1 << 2),
RC_CLIENT_LEADERBOARD_PENDING_EVENT_SUBMITTED = (1 << 3)
};
typedef struct rc_client_leaderboard_info_t {
rc_client_leaderboard_t public_;
rc_lboard_t* lboard;
uint8_t md5[16];
rc_client_leaderboard_tracker_info_t* tracker;
uint32_t value_djb2;
int32_t value;
uint8_t format;
uint8_t pending_events;
uint8_t bucket;
uint8_t hidden;
} rc_client_leaderboard_info_t;
uint8_t rc_client_map_leaderboard_format(int format);
/*****************************************************************************\
| Subsets |
\*****************************************************************************/
enum {
RC_CLIENT_SUBSET_PENDING_EVENT_NONE = 0,
RC_CLIENT_SUBSET_PENDING_EVENT_ACHIEVEMENT = (1 << 1),
RC_CLIENT_SUBSET_PENDING_EVENT_LEADERBOARD = (1 << 2)
};
typedef struct rc_client_subset_info_t {
rc_client_subset_t public_;
rc_client_achievement_info_t* achievements;
rc_client_leaderboard_info_t* leaderboards;
struct rc_client_subset_info_t* next;
const char* all_label;
const char* inactive_label;
const char* locked_label;
const char* unlocked_label;
const char* unofficial_label;
const char* unsupported_label;
uint8_t active;
uint8_t mastery;
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);
/*****************************************************************************\
| Game |
\*****************************************************************************/
typedef struct rc_client_game_hash_t {
char hash[33];
uint32_t game_id;
struct rc_client_game_hash_t* next;
} rc_client_game_hash_t;
rc_client_game_hash_t* rc_client_find_game_hash(rc_client_t* client, const char* hash);
typedef struct rc_client_media_hash_t {
rc_client_game_hash_t* game_hash;
struct rc_client_media_hash_t* next;
uint32_t path_djb2;
} rc_client_media_hash_t;
enum {
RC_CLIENT_GAME_PENDING_EVENT_NONE = 0,
RC_CLIENT_GAME_PENDING_EVENT_LEADERBOARD_TRACKER = (1 << 1),
RC_CLIENT_GAME_PENDING_EVENT_UPDATE_ACTIVE_ACHIEVEMENTS = (1 << 2),
RC_CLIENT_GAME_PENDING_EVENT_PROGRESS_TRACKER = (1 << 3)
};
typedef struct rc_client_game_info_t {
rc_client_game_t public_;
rc_client_leaderboard_tracker_info_t* leaderboard_trackers;
rc_client_progress_tracker_t progress_tracker;
rc_client_subset_info_t* subsets;
rc_client_media_hash_t* media_hash;
rc_runtime_t runtime;
uint32_t max_valid_address;
uint8_t waiting_for_reset;
uint8_t pending_events;
rc_buffer_t buffer;
} rc_client_game_info_t;
void rc_client_update_active_achievements(rc_client_game_info_t* game);
void rc_client_update_active_leaderboards(rc_client_game_info_t* game);
/*****************************************************************************\
| Client |
\*****************************************************************************/
enum {
RC_CLIENT_LOAD_STATE_NONE,
RC_CLIENT_LOAD_STATE_IDENTIFYING_GAME,
RC_CLIENT_LOAD_STATE_AWAIT_LOGIN,
RC_CLIENT_LOAD_STATE_FETCHING_GAME_DATA,
RC_CLIENT_LOAD_STATE_STARTING_SESSION,
RC_CLIENT_LOAD_STATE_DONE,
RC_CLIENT_LOAD_STATE_UNKNOWN_GAME
};
enum {
RC_CLIENT_USER_STATE_NONE,
RC_CLIENT_USER_STATE_LOGIN_REQUESTED,
RC_CLIENT_USER_STATE_LOGGED_IN
};
enum {
RC_CLIENT_MASTERY_STATE_NONE,
RC_CLIENT_MASTERY_STATE_PENDING,
RC_CLIENT_MASTERY_STATE_SHOWN
};
enum {
RC_CLIENT_SPECTATOR_MODE_OFF,
RC_CLIENT_SPECTATOR_MODE_ON,
RC_CLIENT_SPECTATOR_MODE_LOCKED
};
enum {
RC_CLIENT_DISCONNECT_HIDDEN = 0,
RC_CLIENT_DISCONNECT_VISIBLE = (1 << 0),
RC_CLIENT_DISCONNECT_SHOW_PENDING = (1 << 1),
RC_CLIENT_DISCONNECT_HIDE_PENDING = (1 << 2)
};
struct rc_client_load_state_t;
typedef struct rc_client_state_t {
rc_mutex_t mutex;
rc_buffer_t buffer;
rc_client_scheduled_callback_data_t* scheduled_callbacks;
uint8_t hardcore;
uint8_t encore_mode;
uint8_t spectator_mode;
uint8_t unofficial_enabled;
uint8_t log_level;
uint8_t user;
uint8_t disconnect;
struct rc_client_load_state_t* load;
struct rc_client_async_handle_t* async_handles[4];
rc_memref_t* processing_memref;
rc_peek_t legacy_peek;
} rc_client_state_t;
struct rc_client_t {
rc_client_game_info_t* game;
rc_client_game_hash_t* hashes;
rc_client_user_t user;
rc_client_callbacks_t callbacks;
rc_client_state_t state;
};
/*****************************************************************************\
| Helpers |
\*****************************************************************************/
#ifdef RC_NO_VARIADIC_MACROS
void RC_CLIENT_LOG_ERR_FORMATTED(const rc_client_t* client, const char* format, ...);
void RC_CLIENT_LOG_WARN_FORMATTED(const rc_client_t* client, const char* format, ...);
void RC_CLIENT_LOG_INFO_FORMATTED(const rc_client_t* client, const char* format, ...);
void RC_CLIENT_LOG_VERBOSE_FORMATTED(const rc_client_t* client, const char* format, ...);
#else
void rc_client_log_message_formatted(const rc_client_t* client, const char* format, ...);
#define RC_CLIENT_LOG_ERR_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_ERROR) rc_client_log_message_formatted(client, format, __VA_ARGS__); }
#define RC_CLIENT_LOG_WARN_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_WARN) rc_client_log_message_formatted(client, format, __VA_ARGS__); }
#define RC_CLIENT_LOG_INFO_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO) rc_client_log_message_formatted(client, format, __VA_ARGS__); }
#define RC_CLIENT_LOG_VERBOSE_FORMATTED(client, format, ...) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_VERBOSE) rc_client_log_message_formatted(client, format, __VA_ARGS__); }
#endif
void rc_client_log_message(const rc_client_t* client, const char* message);
#define RC_CLIENT_LOG_ERR(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_ERROR) rc_client_log_message(client, message); }
#define RC_CLIENT_LOG_WARN(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_WARN) rc_client_log_message(client, message); }
#define RC_CLIENT_LOG_INFO(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO) rc_client_log_message(client, message); }
#define RC_CLIENT_LOG_VERBOSE(client, message) { if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_VERBOSE) rc_client_log_message(client, message); }
/* internals pulled from runtime.c */
void rc_runtime_checksum(const char* memaddr, uint8_t* md5);
int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref);
int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref);
/* end runtime.c internals */
/* helper functions for unit tests */
struct rc_hash_iterator;
struct rc_hash_iterator* rc_client_get_load_state_hash_iterator(rc_client_t* client);
/* end helper functions for unit tests */
enum {
RC_CLIENT_LEGACY_PEEK_AUTO,
RC_CLIENT_LEGACY_PEEK_CONSTRUCTED,
RC_CLIENT_LEGACY_PEEK_LITTLE_ENDIAN_READS
};
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
#endif /* RC_CLIENT_INTERNAL_H */

View file

@ -83,3 +83,57 @@ struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer)
}
#endif
#ifndef RC_NO_THREADS
#ifdef _WIN32
/* https://gist.github.com/roxlu/1c1af99f92bafff9d8d9 */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void rc_mutex_init(rc_mutex_t* mutex)
{
/* default security, not owned by calling thread, unnamed */
mutex->handle = CreateMutex(NULL, FALSE, NULL);
}
void rc_mutex_destroy(rc_mutex_t* mutex)
{
CloseHandle(mutex->handle);
}
void rc_mutex_lock(rc_mutex_t* mutex)
{
WaitForSingleObject(mutex->handle, 0xFFFFFFFF);
}
void rc_mutex_unlock(rc_mutex_t* mutex)
{
ReleaseMutex(mutex->handle);
}
#else
void rc_mutex_init(rc_mutex_t* mutex)
{
pthread_mutex_init(mutex, NULL);
}
void rc_mutex_destroy(rc_mutex_t* mutex)
{
pthread_mutex_destroy(mutex);
}
void rc_mutex_lock(rc_mutex_t* mutex)
{
pthread_mutex_lock(mutex);
}
void rc_mutex_unlock(rc_mutex_t* mutex)
{
pthread_mutex_unlock(mutex);
}
#endif
#endif /* RC_NO_THREADS */

View file

@ -13,6 +13,8 @@ extern "C" {
/* MinGW redefinitions */
#define RC_NO_VARIADIC_MACROS 1
#elif defined(_MSC_VER)
/* Visual Studio redefinitions */
@ -32,6 +34,8 @@ extern "C" {
/* C89 redefinitions */
#define RC_C89_HELPERS 1
#define RC_NO_VARIADIC_MACROS 1
#ifndef snprintf
extern int rc_snprintf(char* buffer, size_t size, const char* format, ...);
#define snprintf rc_snprintf
@ -65,6 +69,29 @@ extern "C" {
#define gmtime_s rc_gmtime_s
#endif
#ifdef RC_NO_THREADS
typedef int rc_mutex_t;
#define rc_mutex_init(mutex)
#define rc_mutex_destroy(mutex)
#define rc_mutex_lock(mutex)
#define rc_mutex_unlock(mutex)
#else
#ifdef _WIN32
typedef struct rc_mutex_t {
void* handle; /* HANDLE is defined as "void*" */
} rc_mutex_t;
#else
#include <pthread.h>
typedef pthread_mutex_t rc_mutex_t;
#endif
void rc_mutex_init(rc_mutex_t* mutex);
void rc_mutex_destroy(rc_mutex_t* mutex);
void rc_mutex_lock(rc_mutex_t* mutex);
void rc_mutex_unlock(rc_mutex_t* mutex);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -255,7 +255,7 @@ const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* l
typedef struct rc_disallowed_core_systems_t
{
const char* library_name;
const int disallowed_consoles[4];
const uint32_t disallowed_consoles[4];
} rc_disallowed_core_systems_t;
static const rc_disallowed_core_systems_t rc_disallowed_core_systems[] = {
@ -264,7 +264,7 @@ static const rc_disallowed_core_systems_t rc_disallowed_core_systems[] = {
{ NULL, { 0 } }
};
int rc_libretro_is_system_allowed(const char* library_name, int console_id) {
int rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id) {
const rc_disallowed_core_systems_t* core_filter = rc_disallowed_core_systems;
size_t library_name_length;
size_t i;
@ -288,8 +288,8 @@ int rc_libretro_is_system_allowed(const char* library_name, int console_id) {
return 1;
}
unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, unsigned address, unsigned* avail) {
unsigned i;
uint8_t* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, uint32_t address, uint32_t* avail) {
uint32_t i;
for (i = 0; i < regions->count; ++i) {
const size_t size = regions->size[i];
@ -298,12 +298,12 @@ unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t*
break;
if (avail)
*avail = (unsigned)(size - address);
*avail = (uint32_t)(size - address);
return &regions->data[i][address];
}
address -= (unsigned)size;
address -= (uint32_t)size;
}
if (avail)
@ -312,10 +312,35 @@ unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t*
return NULL;
}
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
uint8_t* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, uint32_t address) {
return rc_libretro_memory_find_avail(regions, address, NULL);
}
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 avail;
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;
}
address -= (unsigned)size;
}
return 0;
}
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
rc_libretro_verbose_message_callback = callback;
}
@ -342,7 +367,7 @@ static const char* rc_memory_type_str(int type) {
}
static void rc_libretro_memory_register_region(rc_libretro_memory_regions_t* regions, int type,
unsigned char* data, size_t size, const char* description) {
uint8_t* data, size_t size, const char* description) {
if (size == 0)
return;
@ -394,7 +419,7 @@ static void rc_libretro_memory_init_without_regions(rc_libretro_memory_regions_t
rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, info.data, info.size, description);
}
static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(const struct retro_memory_map* mmap, unsigned real_address, size_t* offset)
static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(const struct retro_memory_map* mmap, uint32_t real_address, size_t* offset)
{
const struct retro_memory_descriptor* desc = mmap->descriptors;
const struct retro_memory_descriptor* end = desc + mmap->num_descriptors;
@ -412,14 +437,14 @@ static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(c
/* address is in the block if (addr & select) == (start & select) */
if (((desc->start ^ real_address) & desc->select) == 0) {
/* get the relative offset of the address from the start of the memory block */
unsigned reduced_address = real_address - (unsigned)desc->start;
uint32_t reduced_address = real_address - (unsigned)desc->start;
/* remove any bits from the reduced_address that correspond to the bits in the disconnect
* mask and collapse the remaining bits. this code was copied from the mmap_reduce function
* in RetroArch. i'm not exactly sure how it works, but it does. */
unsigned disconnect_mask = (unsigned)desc->disconnect;
uint32_t disconnect_mask = (unsigned)desc->disconnect;
while (disconnect_mask) {
const unsigned tmp = (disconnect_mask - 1) & ~disconnect_mask;
const uint32_t tmp = (disconnect_mask - 1) & ~disconnect_mask;
reduced_address = (reduced_address & tmp) | ((reduced_address >> 1) & ~tmp);
disconnect_mask = (disconnect_mask & (disconnect_mask - 1)) >> 1;
}
@ -441,24 +466,24 @@ static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(c
static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
const rc_memory_regions_t* console_regions) {
char description[64];
unsigned i;
unsigned char* region_start;
unsigned char* desc_start;
uint32_t i;
uint8_t* region_start;
uint8_t* desc_start;
size_t desc_size;
size_t offset;
for (i = 0; i < console_regions->num_regions; ++i) {
const rc_memory_region_t* console_region = &console_regions->region[i];
size_t console_region_size = console_region->end_address - console_region->start_address + 1;
unsigned real_address = console_region->real_address;
unsigned disconnect_size = 0;
uint32_t real_address = console_region->real_address;
uint32_t disconnect_size = 0;
while (console_region_size > 0) {
const struct retro_memory_descriptor* desc = rc_libretro_memory_get_descriptor(mmap, real_address, &offset);
if (!desc) {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
real_address - console_region->real_address + console_region->start_address);
(unsigned)(real_address - console_region->real_address + console_region->start_address));
rc_libretro_verbose(description);
}
@ -498,7 +523,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
if (desc_size == 0) {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X",
real_address - console_region->real_address + console_region->start_address);
(unsigned)(real_address - console_region->real_address + console_region->start_address));
rc_libretro_verbose(description);
}
@ -519,7 +544,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
}
}
static unsigned rc_libretro_memory_console_region_to_ram_type(int region_type) {
static uint32_t rc_libretro_memory_console_region_to_ram_type(uint8_t region_type) {
switch (region_type)
{
case RC_MEMORY_TYPE_SAVE_RAM:
@ -536,15 +561,15 @@ static unsigned rc_libretro_memory_console_region_to_ram_type(int region_type) {
static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regions_t* regions,
rc_libretro_get_core_memory_info_func get_core_memory_info, const rc_memory_regions_t* console_regions) {
char description[64];
unsigned i, j;
uint32_t i, j;
rc_libretro_core_memory_info_t info;
size_t offset;
for (i = 0; i < console_regions->num_regions; ++i) {
const rc_memory_region_t* console_region = &console_regions->region[i];
const size_t console_region_size = console_region->end_address - console_region->start_address + 1;
const unsigned type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
unsigned base_address = 0;
const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
uint32_t base_address = 0;
for (j = 0; j <= i; ++j) {
const rc_memory_region_t* console_region2 = &console_regions->region[j];
@ -570,7 +595,7 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
}
else {
if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
snprintf(description, sizeof(description), "Could not map region starting at $%06X", console_region->start_address);
snprintf(description, sizeof(description), "Could not map region starting at $%06X", (unsigned)console_region->start_address);
rc_libretro_verbose(description);
}
@ -595,7 +620,7 @@ int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct
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;
unsigned i;
uint32_t i;
if (!regions)
return 0;
@ -650,47 +675,50 @@ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
file_len = rc_file_tell(file_handle);
rc_file_seek(file_handle, 0, SEEK_SET);
m3u_contents = (char*)malloc(file_len + 1);
rc_file_read(file_handle, m3u_contents, (int)file_len);
m3u_contents[file_len] = '\0';
rc_file_close(file_handle);
ptr = m3u_contents;
do
m3u_contents = (char*)malloc((size_t)file_len + 1);
if (m3u_contents)
{
/* ignore whitespace */
while (isspace((int)*ptr))
++ptr;
rc_file_read(file_handle, m3u_contents, (int)file_len);
m3u_contents[file_len] = '\0';
if (*ptr == '#')
rc_file_close(file_handle);
ptr = m3u_contents;
do
{
/* ignore comment unless it's the special SAVEDISK extension */
if (memcmp(ptr, "#SAVEDISK:", 10) == 0)
/* ignore whitespace */
while (isspace((int)*ptr))
++ptr;
if (*ptr == '#')
{
/* 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)))
/* ignore comment unless it's the special SAVEDISK extension */
if (memcmp(ptr, "#SAVEDISK:", 10) == 0)
{
const char save_disk_hash[33] = "[SAVE DISK]";
rc_libretro_hash_set_add(hash_set, image_path, -1, save_disk_hash);
++index;
/* get the path to the save disk from the frontend, assign it a bogus hash so
* it doesn't get hashed later */
if (get_image_path(index, image_path, sizeof(image_path)))
{
const char save_disk_hash[33] = "[SAVE DISK]";
rc_libretro_hash_set_add(hash_set, image_path, -1, save_disk_hash);
++index;
}
}
}
}
else
{
/* non-empty line, tally a file */
++index;
}
else
{
/* non-empty line, tally a file */
++index;
}
/* find the end of the line */
while (*ptr && *ptr != '\n')
++ptr;
/* find the end of the line */
while (*ptr && *ptr != '\n')
++ptr;
} while (*ptr);
} while (*ptr);
free(m3u_contents);
free(m3u_contents);
}
if (hash_set->entries_count > 0)
{
@ -707,9 +735,9 @@ void rc_libretro_hash_set_destroy(struct rc_libretro_hash_set_t* hash_set) {
memset(hash_set, 0, sizeof(*hash_set));
}
static unsigned rc_libretro_djb2(const char* input)
static uint32_t rc_libretro_djb2(const char* input)
{
unsigned result = 5381;
uint32_t result = 5381;
char c;
while ((c = *input++) != '\0')
@ -719,8 +747,8 @@ static unsigned rc_libretro_djb2(const char* input)
}
void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* path, int game_id, const char hash[33]) {
const unsigned path_djb2 = (path != NULL) ? rc_libretro_djb2(path) : 0;
const char* path, uint32_t game_id, const char hash[33]) {
const uint32_t path_djb2 = (path != NULL) ? rc_libretro_djb2(path) : 0;
struct rc_libretro_hash_entry_t* entry = NULL;
struct rc_libretro_hash_entry_t* scan;
struct rc_libretro_hash_entry_t* stop = hash_set->entries + hash_set->entries_count;;
@ -765,7 +793,7 @@ void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* hash_set, const char* path)
{
const unsigned path_djb2 = rc_libretro_djb2(path);
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)

View file

@ -1,13 +1,16 @@
#ifndef RC_LIBRETRO_H
#define RC_LIBRETRO_H
#ifdef __cplusplus
extern "C" {
#endif
/* this file comes from the libretro repository, which is not an explicit submodule.
* the integration must set up paths appropriately to find it. */
#include <libretro.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/*****************************************************************************\
| Disallowed Settings |
@ -21,7 +24,7 @@ typedef struct rc_disallowed_setting_t
const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name);
int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value);
int rc_libretro_is_system_allowed(const char* library_name, int console_id);
int rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id);
/*****************************************************************************\
| Memory Mapping |
@ -34,26 +37,27 @@ void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback call
#define RC_LIBRETRO_MAX_MEMORY_REGIONS 32
typedef struct rc_libretro_memory_regions_t
{
unsigned char* data[RC_LIBRETRO_MAX_MEMORY_REGIONS];
uint8_t* data[RC_LIBRETRO_MAX_MEMORY_REGIONS];
size_t size[RC_LIBRETRO_MAX_MEMORY_REGIONS];
size_t total_size;
unsigned count;
uint32_t count;
} rc_libretro_memory_regions_t;
typedef struct rc_libretro_core_memory_info_t
{
unsigned char* data;
uint8_t* data;
size_t size;
} rc_libretro_core_memory_info_t;
typedef void (*rc_libretro_get_core_memory_info_func)(unsigned id, rc_libretro_core_memory_info_t* info);
typedef void (*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);
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address);
unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, unsigned address, unsigned* avail);
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);
/*****************************************************************************\
| Disk Identification |
@ -62,7 +66,7 @@ unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t*
typedef struct rc_libretro_hash_entry_t
{
uint32_t path_djb2;
int game_id;
uint32_t game_id;
char hash[33];
} rc_libretro_hash_entry_t;
@ -73,14 +77,14 @@ typedef struct rc_libretro_hash_set_t
uint16_t entries_size;
} rc_libretro_hash_set_t;
typedef int (*rc_libretro_get_image_path_func)(unsigned index, char* buffer, size_t buffer_size);
typedef int (*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,
const char* m3u_path, rc_libretro_get_image_path_func get_image_path);
void rc_libretro_hash_set_destroy(struct rc_libretro_hash_set_t* hash_set);
void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
const char* path, int game_id, const char hash[33]);
const char* 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);

188
deps/rcheevos/src/rc_util.c vendored Normal file
View file

@ -0,0 +1,188 @@
#include "rc_util.h"
#include "rc_compat.h"
#include "rc_error.h"
#include <stdlib.h>
#include <string.h>
#undef DEBUG_BUFFERS
/* --- rc_buffer --- */
void rc_buffer_init(rc_buffer_t* buffer)
{
buffer->chunk.write = buffer->chunk.start = &buffer->data[0];
buffer->chunk.end = &buffer->data[sizeof(buffer->data)];
buffer->chunk.next = NULL;
/* leave buffer->data uninitialized */
}
void rc_buffer_destroy(rc_buffer_t* buffer)
{
rc_buffer_chunk_t* chunk;
#ifdef DEBUG_BUFFERS
int count = 0;
int wasted = 0;
int total = 0;
#endif
/* first chunk is not allocated. skip it. */
chunk = buffer->chunk.next;
/* deallocate any additional buffers */
while (chunk)
{
rc_buffer_chunk_t* next = chunk->next;
#ifdef DEBUG_BUFFERS
total += (int)(chunk->end - chunk->data);
wasted += (int)(chunk->end - chunk->write);
++count;
#endif
free(chunk);
chunk = next;
}
#ifdef DEBUG_BUFFERS
printf("-- %d allocated buffers (%d/%d used, %d wasted, %0.2f%% efficiency)\n", count,
total - wasted, total, wasted, (float)(100.0 - (wasted * 100.0) / total));
#endif
}
uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount)
{
rc_buffer_chunk_t* chunk = &buffer->chunk;
size_t remaining;
while (chunk)
{
remaining = chunk->end - chunk->write;
if (remaining >= amount)
return chunk->write;
if (!chunk->next)
{
/* allocate a chunk of memory that is a multiple of 256-bytes. the first 32 bytes will be associated
* to the chunk header, and the remaining will be used for data.
*/
const size_t chunk_header_size = sizeof(rc_buffer_chunk_t);
const size_t alloc_size = (chunk_header_size + amount + 0xFF) & ~0xFF;
chunk->next = (rc_buffer_chunk_t*)malloc(alloc_size);
if (!chunk->next)
break;
chunk->next->start = (uint8_t*)chunk->next + chunk_header_size;
chunk->next->write = chunk->next->start;
chunk->next->end = (uint8_t*)chunk->next + alloc_size;
chunk->next->next = NULL;
}
chunk = chunk->next;
}
return NULL;
}
void rc_buffer_consume(rc_buffer_t* buffer, const uint8_t* start, uint8_t* end)
{
rc_buffer_chunk_t* chunk = &buffer->chunk;
do
{
if (chunk->write == start)
{
size_t offset = (end - chunk->start);
offset = (offset + 7) & ~7;
chunk->write = &chunk->start[offset];
if (chunk->write > chunk->end)
chunk->write = chunk->end;
break;
}
chunk = chunk->next;
} while (chunk);
}
void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount)
{
uint8_t* ptr = rc_buffer_reserve(buffer, amount);
rc_buffer_consume(buffer, ptr, ptr + amount);
return (void*)ptr;
}
char* rc_buffer_strncpy(rc_buffer_t* buffer, const char* src, size_t len)
{
uint8_t* dst = rc_buffer_reserve(buffer, len + 1);
memcpy(dst, src, len);
dst[len] = '\0';
rc_buffer_consume(buffer, dst, dst + len + 2);
return (char*)dst;
}
char* rc_buffer_strcpy(rc_buffer_t* buffer, const char* src)
{
return rc_buffer_strncpy(buffer, src, strlen(src));
}
/* --- other --- */
void rc_format_md5(char checksum[33], const uint8_t digest[16])
{
snprintf(checksum, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
);
}
uint32_t rc_djb2(const char* input)
{
uint32_t result = 5381;
char c;
while ((c = *input++) != '\0')
result = ((result << 5) + result) + c; /* result = result * 33 + c */
return result;
}
const char* rc_error_str(int ret)
{
switch (ret) {
case RC_OK: return "OK";
case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
case RC_INVALID_OPERATOR: return "Invalid operator";
case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
case RC_DUPLICATED_START: return "Duplicated start condition";
case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
case RC_DUPLICATED_VALUE: return "Duplicated value expression";
case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
case RC_MISSING_START: return "Missing start condition";
case RC_MISSING_CANCEL: return "Missing cancel condition";
case RC_MISSING_SUBMIT: return "Missing submit condition";
case RC_MISSING_VALUE: return "Missing value expression";
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
case RC_OUT_OF_MEMORY: return "Out of memory";
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
case RC_MISSING_VALUE_MEASURED: return "Missing measured flag in value expression";
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
case RC_INVALID_COMPARISON: return "Invalid comparison";
case RC_INVALID_STATE: return "Invalid state";
case RC_INVALID_JSON: return "Invalid JSON";
case RC_API_FAILURE: return "API call failed";
case RC_LOGIN_REQUIRED: return "Login required";
case RC_NO_GAME_LOADED: return "No game loaded";
case RC_HARDCORE_DISABLED: return "Hardcore disabled";
case RC_ABORTED: return "Aborted";
case RC_NO_RESPONSE: return "No response";
case RC_ACCESS_DENIED: return "Access denied";
case RC_INVALID_CREDENTIALS: return "Invalid credentials";
case RC_EXPIRED_TOKEN: return "Expired token";
default: return "Unknown error";
}
}

53
deps/rcheevos/src/rc_util.h vendored Normal file
View file

@ -0,0 +1,53 @@
#ifndef RC_UTIL_H
#define RC_UTIL_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* A block of memory for variable length data (like strings and arrays).
*/
typedef struct rc_buffer_chunk_t {
/* The current location where data is being written */
uint8_t* write;
/* The first byte past the end of data where writing cannot occur */
uint8_t* end;
/* The first byte of the data */
uint8_t* start;
/* The next block in the allocated memory chain */
struct rc_buffer_chunk_t* next;
}
rc_buffer_chunk_t;
/**
* A preallocated block of memory for variable length data (like strings and arrays).
*/
typedef struct rc_buffer_t {
/* The chunk data (will point at the local data member) */
struct rc_buffer_chunk_t chunk;
/* Small chunk of memory pre-allocated for the chunk */
uint8_t data[256];
}
rc_buffer_t;
void rc_buffer_init(rc_buffer_t* buffer);
void rc_buffer_destroy(rc_buffer_t* buffer);
uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount);
void rc_buffer_consume(rc_buffer_t* buffer, const uint8_t* start, uint8_t* end);
void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount);
char* rc_buffer_strcpy(rc_buffer_t* buffer, const char* src);
char* rc_buffer_strncpy(rc_buffer_t* buffer, const char* src, size_t len);
uint32_t rc_djb2(const char* input);
void rc_format_md5(char checksum[33], const uint8_t digest[16]);
#ifdef __cplusplus
}
#endif
#endif /* RC_UTIL_H */

29
deps/rcheevos/src/rc_version.h vendored Normal file
View file

@ -0,0 +1,29 @@
#ifndef RC_VERSION_H
#define RC_VERSION_H
#ifdef __cplusplus
extern "C" {
#endif
#define RCHEEVOS_VERSION_MAJOR 11
#define RCHEEVOS_VERSION_MINOR 0
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
#define RCHEEVOS_VERSION RCHEEVOS_MAKE_VERSION(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR, RCHEEVOS_VERSION_PATCH)
#define RCHEEVOS_MAKE_STRING(num) #num
#define RCHEEVOS_MAKE_VERSION_STRING(major, minor, patch) RCHEEVOS_MAKE_STRING(major) "." RCHEEVOS_MAKE_STRING(minor) "." RCHEEVOS_MAKE_STRING(patch)
#define RCHEEVOS_MAKE_VERSION_STRING_SHORT(major, minor) RCHEEVOS_MAKE_STRING(major) "." RCHEEVOS_MAKE_STRING(minor)
#if RCHEEVOS_VERSION_PATCH > 0
#define RCHEEVOS_VERSION_STRING RCHEEVOS_MAKE_VERSION_STRING(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR, RCHEEVOS_VERSION_PATCH)
#else
#define RCHEEVOS_VERSION_STRING RCHEEVOS_MAKE_VERSION_STRING_SHORT(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR)
#endif
#ifdef __cplusplus
}
#endif
#endif /* RC_VERSION_H */

View file

@ -3,7 +3,7 @@
#include <stdlib.h>
#include <string.h>
void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset)
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;
@ -13,7 +13,7 @@ void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_s
/* update how much space will be required in the real buffer */
{
const int aligned_offset = (*offset + alignment - 1) & ~(alignment - 1);
const int32_t aligned_offset = (*offset + alignment - 1) & ~(alignment - 1);
*offset += (aligned_offset - *offset);
*offset += size;
}
@ -21,8 +21,8 @@ void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_s
/* find a scratch buffer to hold the temporary data */
buffer = &scratch->buffer;
do {
const int aligned_buffer_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
const int remaining = sizeof(buffer->buffer) - aligned_buffer_offset;
const uint32_t aligned_buffer_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
const uint32_t remaining = sizeof(buffer->buffer) - aligned_buffer_offset;
if (remaining >= size) {
/* claim the required space from an existing buffer */
@ -36,13 +36,13 @@ void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_s
} while (1);
/* not enough space in any existing buffer, allocate more */
if (size > (int)sizeof(buffer->buffer)) {
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 int needed = sizeof(rc_scratch_buffer_t) - sizeof(buffer->buffer) + size;
const size_t needed = sizeof(rc_scratch_buffer_t) - sizeof(buffer->buffer) + size;
buffer->next = (rc_scratch_buffer_t*)malloc(needed);
}
else {
@ -62,7 +62,7 @@ void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_s
return rc_alloc(buffer->buffer, &buffer->offset, size, alignment, NULL, -1);
}
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset) {
void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset) {
void* ptr;
*offset = (*offset + alignment - 1) & ~(alignment - 1);
@ -89,8 +89,8 @@ void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t
return ptr;
}
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length) {
int used = 0;
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, size_t length) {
int32_t used = 0;
char* ptr;
rc_scratch_string_t** next = &parse->scratch.strings;
@ -109,7 +109,7 @@ char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length) {
}
*next = (rc_scratch_string_t*)rc_alloc_scratch(NULL, &used, sizeof(rc_scratch_string_t), RC_ALIGNOF(rc_scratch_string_t), &parse->scratch, RC_OFFSETOF(parse->scratch.objs, __rc_scratch_string_t));
ptr = (char*)rc_alloc_scratch(parse->buffer, &parse->offset, length + 1, RC_ALIGNOF(char), &parse->scratch, -1);
ptr = (char*)rc_alloc_scratch(parse->buffer, &parse->offset, (uint32_t)length + 1, RC_ALIGNOF(char), &parse->scratch, -1);
if (!ptr || !*next) {
if (parse->offset >= 0)
@ -158,38 +158,3 @@ void rc_destroy_parse_state(rc_parse_state_t* parse)
buffer = next;
}
}
const char* rc_error_str(int ret)
{
switch (ret) {
case RC_OK: return "OK";
case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
case RC_INVALID_OPERATOR: return "Invalid operator";
case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
case RC_DUPLICATED_START: return "Duplicated start condition";
case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
case RC_DUPLICATED_VALUE: return "Duplicated value expression";
case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
case RC_MISSING_START: return "Missing start condition";
case RC_MISSING_CANCEL: return "Missing cancel condition";
case RC_MISSING_SUBMIT: return "Missing submit condition";
case RC_MISSING_VALUE: return "Missing value expression";
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
case RC_OUT_OF_MEMORY: return "Out of memory";
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
case RC_MISSING_VALUE_MEASURED: return "Missing measured flag in value expression";
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
case RC_INVALID_COMPARISON: return "Invalid comparison";
case RC_INVALID_STATE: return "Invalid state";
case RC_INVALID_JSON: return "Invalid JSON";
default: return "Unknown error";
}
}

View file

@ -3,7 +3,7 @@
#include <stdlib.h>
#include <assert.h>
static int rc_test_condition_compare(unsigned value1, unsigned value2, char oper) {
static int rc_test_condition_compare(uint32_t value1, uint32_t value2, uint8_t oper) {
switch (oper) {
case RC_OPERATOR_EQ: return value1 == value2;
case RC_OPERATOR_NE: return value1 != value2;
@ -149,7 +149,7 @@ static int rc_parse_operator(const char** memaddr) {
}
}
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect) {
rc_condition_t* self;
const char* aux;
int result;
@ -323,23 +323,23 @@ int rc_condition_is_combining(const rc_condition_t* self) {
}
static int rc_test_condition_compare_memref_to_const(rc_condition_t* self) {
const unsigned value1 = self->operand1.value.memref->value.value;
const unsigned value2 = self->operand2.value.num;
const uint32_t value1 = self->operand1.value.memref->value.value;
const uint32_t value2 = self->operand2.value.num;
assert(self->operand1.size == self->operand1.value.memref->value.size);
return rc_test_condition_compare(value1, value2, self->oper);
}
static int rc_test_condition_compare_delta_to_const(rc_condition_t* self) {
const rc_memref_value_t* memref1 = &self->operand1.value.memref->value;
const unsigned value1 = (memref1->changed) ? memref1->prior : memref1->value;
const unsigned value2 = self->operand2.value.num;
const uint32_t value1 = (memref1->changed) ? memref1->prior : memref1->value;
const uint32_t value2 = self->operand2.value.num;
assert(self->operand1.size == self->operand1.value.memref->value.size);
return rc_test_condition_compare(value1, value2, self->oper);
}
static int rc_test_condition_compare_memref_to_memref(rc_condition_t* self) {
const unsigned value1 = self->operand1.value.memref->value.value;
const unsigned value2 = self->operand2.value.memref->value.value;
const uint32_t value1 = self->operand1.value.memref->value.value;
const uint32_t value2 = self->operand2.value.memref->value.value;
assert(self->operand1.size == self->operand1.value.memref->value.size);
assert(self->operand2.size == self->operand2.value.memref->value.size);
return rc_test_condition_compare(value1, value2, self->oper);
@ -387,7 +387,7 @@ static int rc_test_condition_compare_delta_to_memref(rc_condition_t* self) {
static int rc_test_condition_compare_memref_to_const_transformed(rc_condition_t* self) {
rc_typed_value_t value1;
const unsigned value2 = self->operand2.value.num;
const uint32_t value2 = self->operand2.value.num;
value1.type = RC_VALUE_TYPE_UNSIGNED;
value1.value.u32 = self->operand1.value.memref->value.value;
@ -399,7 +399,7 @@ static int rc_test_condition_compare_memref_to_const_transformed(rc_condition_t*
static int rc_test_condition_compare_delta_to_const_transformed(rc_condition_t* self) {
rc_typed_value_t value1;
const rc_memref_value_t* memref1 = &self->operand1.value.memref->value;
const unsigned value2 = self->operand2.value.num;
const uint32_t value2 = self->operand2.value.num;
value1.type = RC_VALUE_TYPE_UNSIGNED;
value1.value.u32 = (memref1->changed) ? memref1->prior : memref1->value;

View file

@ -28,7 +28,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
rc_condset_t* self;
rc_condition_t** next;
int in_add_address;
unsigned measured_target = 0;
uint32_t measured_target = 0;
self = RC_ALLOC(rc_condset_t, parse);
self->has_pause = self->is_paused = self->has_indirect_memrefs = 0;
@ -181,7 +181,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
rc_typed_value_t value;
int set_valid, cond_valid, and_next, or_next, reset_next, measured_from_hits, can_measure;
rc_typed_value_t measured_value;
unsigned total_hits;
uint32_t total_hits;
measured_value.type = RC_VALUE_TYPE_NONE;
measured_from_hits = 0;

View file

@ -444,11 +444,13 @@ static const rc_memory_regions_t rc_memory_regions_gameboy = { _rc_memory_region
static const rc_memory_regions_t rc_memory_regions_gameboy_color = { _rc_memory_regions_gameboy, 17 };
/* ===== GameBoy Advance ===== */
/* http://problemkaputt.de/gbatek-gba-memory-map.htm */
static const rc_memory_region_t _rc_memory_regions_gameboy_advance[] = {
{ 0x000000U, 0x007FFFU, 0x03000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" },
{ 0x008000U, 0x047FFFU, 0x02000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
{ 0x000000U, 0x007FFFU, 0x03000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* 32KB Internal Work RAM */
{ 0x008000U, 0x047FFFU, 0x02000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* 256KB External Work RAM */
{ 0x048000U, 0x057FFFU, 0x0E000000U, RC_MEMORY_TYPE_SAVE_RAM, "Save RAM" } /* 64KB Game Pak SRAM */
};
static const rc_memory_regions_t rc_memory_regions_gameboy_advance = { _rc_memory_regions_gameboy_advance, 2 };
static const rc_memory_regions_t rc_memory_regions_gameboy_advance = { _rc_memory_regions_gameboy_advance, 3 };
/* ===== GameCube ===== */
/* https://wiibrew.org/wiki/Memory_map */
@ -541,11 +543,11 @@ static const rc_memory_region_t _rc_memory_regions_megadrive[] = {
static const rc_memory_regions_t rc_memory_regions_megadrive = { _rc_memory_regions_megadrive, 2 };
/* ===== MegaDrive 32X (Genesis 32X) ===== */
/* https://en.wikibooks.org/wiki/Genesis_Programming/68K_Memory_map/ */
/* http://devster.monkeeh.com/sega/32xguide1.txt */
static const rc_memory_region_t _rc_memory_regions_megadrive_32x[] = {
{ 0x000000U, 0x00FFFFU, 0xFF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x010000U, 0x04FFFFU, 0x200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "32X RAM"},
{ 0x050000U, 0x05FFFFU, 0x000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
{ 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* Main MegaDrive RAM */
{ 0x010000U, 0x04FFFFU, 0x06000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "32X RAM"}, /* Additional 32X RAM */
{ 0x050000U, 0x05FFFFU, 0x00000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
};
static const rc_memory_regions_t rc_memory_regions_megadrive_32x = { _rc_memory_regions_megadrive_32x, 3 };
@ -759,16 +761,19 @@ static const rc_memory_region_t _rc_memory_regions_sg1000[] = {
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 4 };
/* ===== Super Cassette Vision ===== */
/* https://github.com/mamedev/mame/blob/f32bb79e8541ba96d3a8144b220c48fb7536ba4b/src/mame/epoch/scv.cpp#L78-L86 */
/* SCV only has 128 bytes of system RAM, any additional memory is provided on the individual carts and is
* not backed up by battery. */
/* http://www.videogameconsolelibrary.com/pg80-super_cass_vis.htm#page=specs */
static const rc_memory_region_t _rc_memory_regions_scv[] = {
{ 0x000000U, 0x000FFFU, 0x000000U, RC_MEMORY_TYPE_READONLY, "System ROM" },
{ 0x000000U, 0x000FFFU, 0x000000U, RC_MEMORY_TYPE_READONLY, "System ROM" }, /* BIOS */
{ 0x001000U, 0x001FFFU, 0x001000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x002000U, 0x003FFFU, 0x002000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" },
{ 0x002000U, 0x003FFFU, 0x002000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" }, /* only really goes to $33FF? */
{ 0x004000U, 0x007FFFU, 0x004000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x008000U, 0x00DFFFU, 0x008000U, RC_MEMORY_TYPE_READONLY, "Cartridge ROM" },
{ 0x00E000U, 0x00FF7FU, 0x00E000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" },
{ 0x008000U, 0x00FF7FU, 0x008000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
{ 0x00FF80U, 0x00FFFFU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_scv = { _rc_memory_regions_scv, 7 };
static const rc_memory_regions_t rc_memory_regions_scv = { _rc_memory_regions_scv, 6 };
/* ===== Super Nintendo ===== */
/* https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map#LoROM */
@ -877,7 +882,7 @@ static const rc_memory_regions_t rc_memory_regions_wonderswan = { _rc_memory_reg
/* ===== default ===== */
static const rc_memory_regions_t rc_memory_regions_none = { 0, 0 };
const rc_memory_regions_t* rc_console_memory_regions(int console_id)
const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id)
{
switch (console_id)
{

View file

@ -1,6 +1,6 @@
#include "rc_internal.h"
#include "rc_compat.h"
#include "../rc_compat.h"
#include <string.h>
#include <stdio.h>
@ -75,16 +75,16 @@ int rc_parse_format(const char* format_str) {
return RC_FORMAT_VALUE;
}
static int rc_format_value_minutes(char* buffer, int size, unsigned minutes) {
unsigned hours;
static int rc_format_value_minutes(char* buffer, size_t size, uint32_t minutes) {
uint32_t hours;
hours = minutes / 60;
minutes -= hours * 60;
return snprintf(buffer, size, "%uh%02u", hours, minutes);
}
static int rc_format_value_seconds(char* buffer, int size, unsigned seconds) {
unsigned hours, minutes;
static int rc_format_value_seconds(char* buffer, size_t size, uint32_t seconds) {
uint32_t hours, minutes;
/* apply modulus math to split the seconds into hours/minutes/seconds */
minutes = seconds / 60;
@ -98,8 +98,8 @@ static int rc_format_value_seconds(char* buffer, int size, unsigned seconds) {
return snprintf(buffer, size, "%uh%02u:%02u", hours, minutes, seconds);
}
static int rc_format_value_centiseconds(char* buffer, int size, unsigned centiseconds) {
unsigned seconds;
static int rc_format_value_centiseconds(char* buffer, size_t size, uint32_t centiseconds) {
uint32_t seconds;
int chars, chars2;
/* modulus off the centiseconds */
@ -119,7 +119,7 @@ static int rc_format_value_centiseconds(char* buffer, int size, unsigned centise
return chars;
}
int rc_format_typed_value(char* buffer, int size, const rc_typed_value_t* value, int format) {
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;
@ -197,7 +197,7 @@ int rc_format_typed_value(char* buffer, int size, const rc_typed_value_t* value,
return chars;
}
int rc_format_value(char* buffer, int size, int value, int format) {
int rc_format_value(char* buffer, int size, int32_t value, int format) {
rc_typed_value_t typed_value;
typed_value.value.i32 = value;

View file

@ -164,7 +164,7 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
return (parse.offset >= 0) ? self : 0;
}
int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
int rc_evaluate_lboard(rc_lboard_t* self, int32_t* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
int start_ok, cancel_ok, submit_ok;
rc_update_memref_values(self->memrefs, peek, peek_ud);
@ -262,6 +262,9 @@ int rc_lboard_state_active(int state) {
}
void rc_reset_lboard(rc_lboard_t* self) {
if (!self)
return;
self->state = RC_LBOARD_STATE_WAITING;
rc_reset_trigger(&self->start);

View file

@ -6,7 +6,7 @@
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect) {
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size, uint8_t is_indirect) {
rc_memref_t** next_memref;
rc_memref_t* memref;
@ -41,7 +41,7 @@ rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char siz
return memref;
}
int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address) {
const char* aux = *memaddr;
char* end;
unsigned long value;
@ -94,6 +94,7 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
++aux;
switch (*aux++) {
case 'f': case 'F': *size = RC_MEMSIZE_FLOAT; break;
case 'b': case 'B': *size = RC_MEMSIZE_FLOAT_BE; break;
case 'm': case 'M': *size = RC_MEMSIZE_MBF32; break;
case 'l': case 'L': *size = RC_MEMSIZE_MBF32_LE; break;
@ -113,15 +114,15 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
if (value > 0xffffffffU)
value = 0xffffffffU;
*address = (unsigned)value;
*address = (uint32_t)value;
*memaddr = end;
return RC_OK;
}
static float rc_build_float(unsigned mantissa_bits, int exponent, int sign) {
static float rc_build_float(uint32_t mantissa_bits, int32_t exponent, int sign) {
/* 32-bit float has a 23-bit mantissa and 8-bit exponent */
const unsigned implied_bit = 1 << 23;
const unsigned mantissa = mantissa_bits | implied_bit;
const uint32_t implied_bit = 1 << 23;
const uint32_t mantissa = mantissa_bits | implied_bit;
double dbl = ((double)mantissa) / ((double)implied_bit);
if (exponent > 127) {
@ -178,20 +179,32 @@ static float rc_build_float(unsigned mantissa_bits, int exponent, int sign) {
static void rc_transform_memref_float(rc_typed_value_t* value) {
/* decodes an IEEE 754 float */
const unsigned mantissa = (value->value.u32 & 0x7FFFFF);
const int exponent = (int)((value->value.u32 >> 23) & 0xFF) - 127;
const uint32_t mantissa = (value->value.u32 & 0x7FFFFF);
const int32_t exponent = (int32_t)((value->value.u32 >> 23) & 0xFF) - 127;
const int sign = (value->value.u32 & 0x80000000);
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}
static void rc_transform_memref_float_be(rc_typed_value_t* value) {
/* decodes an IEEE 754 float in big endian format */
const uint32_t mantissa = ((value->value.u32 & 0xFF000000) >> 24) |
((value->value.u32 & 0x00FF0000) >> 8) |
((value->value.u32 & 0x00007F00) << 8);
const int32_t exponent = (int32_t)(((value->value.u32 & 0x0000007F) << 1) |
((value->value.u32 & 0x00008000) >> 15)) - 127;
const int sign = (value->value.u32 & 0x00000080);
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}
static void rc_transform_memref_mbf32(rc_typed_value_t* value) {
/* decodes a Microsoft Binary Format float */
/* NOTE: 32-bit MBF is stored in memory as big endian (at least for Apple II) */
const unsigned mantissa = ((value->value.u32 & 0xFF000000) >> 24) |
const uint32_t mantissa = ((value->value.u32 & 0xFF000000) >> 24) |
((value->value.u32 & 0x00FF0000) >> 8) |
((value->value.u32 & 0x00007F00) << 8);
const int exponent = (int)(value->value.u32 & 0xFF) - 129;
const int32_t exponent = (int32_t)(value->value.u32 & 0xFF) - 129;
const int sign = (value->value.u32 & 0x00008000);
if (mantissa == 0 && exponent == -129)
@ -205,8 +218,8 @@ static void rc_transform_memref_mbf32(rc_typed_value_t* value) {
static void rc_transform_memref_mbf32_le(rc_typed_value_t* value) {
/* decodes a Microsoft Binary Format float */
/* Locomotive BASIC (CPC) uses MBF40, but in little endian format */
const unsigned mantissa = value->value.u32 & 0x007FFFFF;
const int exponent = (int)(value->value.u32 >> 24) - 129;
const uint32_t mantissa = value->value.u32 & 0x007FFFFF;
const int32_t exponent = (int32_t)(value->value.u32 >> 24) - 129;
const int sign = (value->value.u32 & 0x00800000);
if (mantissa == 0 && exponent == -129)
@ -217,9 +230,9 @@ static void rc_transform_memref_mbf32_le(rc_typed_value_t* value) {
value->type = RC_VALUE_TYPE_FLOAT;
}
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
static const uint8_t rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
void rc_transform_memref_value(rc_typed_value_t* value, char size) {
void rc_transform_memref_value(rc_typed_value_t* value, uint8_t size) {
/* ASSERT: value->type == RC_VALUE_TYPE_UNSIGNED */
switch (size)
{
@ -305,6 +318,10 @@ void rc_transform_memref_value(rc_typed_value_t* value, char size) {
rc_transform_memref_float(value);
break;
case RC_MEMSIZE_FLOAT_BE:
rc_transform_memref_float_be(value);
break;
case RC_MEMSIZE_MBF32:
rc_transform_memref_mbf32(value);
break;
@ -318,7 +335,7 @@ void rc_transform_memref_value(rc_typed_value_t* value, char size) {
}
}
static const unsigned rc_memref_masks[] = {
static const uint32_t rc_memref_masks[] = {
0x000000ff, /* RC_MEMSIZE_8_BITS */
0x0000ffff, /* RC_MEMSIZE_16_BITS */
0x00ffffff, /* RC_MEMSIZE_24_BITS */
@ -340,10 +357,11 @@ static const unsigned rc_memref_masks[] = {
0xffffffff, /* RC_MEMSIZE_FLOAT */
0xffffffff, /* RC_MEMSIZE_MBF32 */
0xffffffff, /* RC_MEMSIZE_MBF32_LE */
0xffffffff, /* RC_MEMSIZE_FLOAT_BE */
0xffffffff /* RC_MEMSIZE_VARIABLE */
};
unsigned rc_memref_mask(char size) {
uint32_t rc_memref_mask(uint8_t size) {
const size_t index = (size_t)size;
if (index >= sizeof(rc_memref_masks) / sizeof(rc_memref_masks[0]))
return 0xffffffff;
@ -354,7 +372,7 @@ unsigned rc_memref_mask(char size) {
/* all sizes less than 8-bits (1 byte) are mapped to 8-bits. 24-bit is mapped to 32-bit
* as we don't expect the client to understand a request for 3 bytes. all other reads are
* mapped to the little-endian read of the same size. */
static const char rc_memref_shared_sizes[] = {
static const uint8_t rc_memref_shared_sizes[] = {
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_8_BITS */
RC_MEMSIZE_16_BITS, /* RC_MEMSIZE_16_BITS */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_24_BITS */
@ -376,10 +394,11 @@ static const char rc_memref_shared_sizes[] = {
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_FLOAT */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32 */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32_LE */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_FLOAT_BE */
RC_MEMSIZE_32_BITS /* RC_MEMSIZE_VARIABLE */
};
char rc_memref_shared_size(char size) {
uint8_t rc_memref_shared_size(uint8_t size) {
const size_t index = (size_t)size;
if (index >= sizeof(rc_memref_shared_sizes) / sizeof(rc_memref_shared_sizes[0]))
return size;
@ -387,7 +406,7 @@ char rc_memref_shared_size(char size) {
return rc_memref_shared_sizes[index];
}
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
uint32_t rc_peek_value(uint32_t address, uint8_t size, rc_peek_t peek, void* ud) {
if (!peek)
return 0;
@ -404,7 +423,7 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
default:
{
unsigned value;
uint32_t value;
const size_t index = (size_t)size;
if (index >= sizeof(rc_memref_shared_sizes) / sizeof(rc_memref_shared_sizes[0]))
return 0;
@ -418,7 +437,7 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
}
}
void rc_update_memref_value(rc_memref_value_t* memref, unsigned new_value) {
void rc_update_memref_value(rc_memref_value_t* memref, uint32_t new_value) {
if (memref->value == new_value) {
memref->changed = 0;
}
@ -444,7 +463,7 @@ void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs)
*memrefs = 0;
}
static unsigned rc_get_memref_value_value(const rc_memref_value_t* memref, int operand_type) {
static uint32_t rc_get_memref_value_value(const rc_memref_value_t* memref, int operand_type) {
switch (operand_type)
{
/* most common case explicitly first, even though it could be handled by default case.
@ -464,10 +483,10 @@ static unsigned rc_get_memref_value_value(const rc_memref_value_t* memref, int o
}
}
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) {
uint32_t rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) {
/* if this is an indirect reference, handle the indirection. */
if (memref->value.is_indirect) {
const unsigned new_address = memref->address + eval_state->add_address;
const uint32_t new_address = memref->address + eval_state->add_address;
rc_update_memref_value(&memref->value, rc_peek_value(new_address, memref->value.size, eval_state->peek, eval_state->peek_userdata));
}

View file

@ -68,10 +68,10 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
return RC_OK;
}
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect) {
const char* aux = *memaddr;
unsigned address;
char size;
uint32_t address;
uint8_t size;
int ret;
switch (*aux) {
@ -115,7 +115,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
size = self->size;
}
self->value.memref = rc_alloc_memref(parse, address, size, (char)is_indirect);
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
if (parse->offset < 0)
return parse->offset;
@ -123,7 +123,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
return RC_OK;
}
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indirect, rc_parse_state_t* parse) {
const char* aux = *memaddr;
char* end;
int ret;
@ -286,11 +286,11 @@ typedef struct {
rc_luapeek_t;
static int rc_luapeek(lua_State* L) {
unsigned address = (unsigned)luaL_checkinteger(L, 1);
unsigned num_bytes = (unsigned)luaL_checkinteger(L, 2);
uint32_t address = (uint32_t)luaL_checkinteger(L, 1);
uint32_t num_bytes = (uint32_t)luaL_checkinteger(L, 2);
rc_luapeek_t* luapeek = (rc_luapeek_t*)lua_touserdata(L, 3);
unsigned value = luapeek->peek(address, num_bytes, luapeek->ud);
uint32_t value = luapeek->peek(address, num_bytes, luapeek->ud);
lua_pushinteger(L, value);
return 1;
@ -301,6 +301,7 @@ static int rc_luapeek(lua_State* L) {
int rc_operand_is_float_memref(const rc_operand_t* self) {
switch (self->size) {
case RC_MEMSIZE_FLOAT:
case RC_MEMSIZE_FLOAT_BE:
case RC_MEMSIZE_MBF32:
case RC_MEMSIZE_MBF32_LE:
return 1;
@ -329,7 +330,7 @@ int rc_operand_is_float(const rc_operand_t* self) {
return rc_operand_is_float_memref(self);
}
unsigned rc_transform_operand_value(unsigned value, const rc_operand_t* self) {
uint32_t rc_transform_operand_value(uint32_t value, const rc_operand_t* self) {
switch (self->type)
{
case RC_OPERAND_BCD:
@ -450,10 +451,10 @@ void rc_evaluate_operand(rc_typed_value_t* result, rc_operand_t* self, rc_eval_s
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
if (lua_isboolean(eval_state->L, -1)) {
result->value.u32 = (unsigned)lua_toboolean(eval_state->L, -1);
result->value.u32 = (uint32_t)lua_toboolean(eval_state->L, -1);
}
else {
result->value.u32 = (unsigned)lua_tonumber(eval_state->L, -1);
result->value.u32 = (uint32_t)lua_tonumber(eval_state->L, -1);
}
}

View file

@ -1,5 +1,5 @@
#ifndef INTERNAL_H
#define INTERNAL_H
#ifndef RC_INTERNAL_H
#define RC_INTERNAL_H
#include "rc_runtime_types.h"
@ -36,10 +36,13 @@ RC_ALLOW_ALIGN(char)
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
#define RC_ALLOC_SCRATCH(t, p) ((t*)rc_alloc_scratch((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch, RC_OFFSETOF((p)->scratch.objs, __ ## t)))
/* 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;
int offset;
unsigned char buffer[512 - 16];
int32_t offset;
uint8_t buffer[512 - 16];
}
rc_scratch_buffer_t;
@ -74,8 +77,8 @@ enum {
typedef struct {
union {
unsigned u32;
int i32;
uint32_t u32;
int32_t i32;
float f32;
} value;
@ -87,24 +90,24 @@ rc_typed_value_t;
typedef struct {
rc_typed_value_t add_value;/* AddSource/SubSource */
int add_hits; /* AddHits */
unsigned add_address; /* AddAddress */
int32_t add_hits; /* AddHits */
uint32_t add_address; /* AddAddress */
rc_peek_t peek;
void* peek_userdata;
lua_State* L;
rc_typed_value_t measured_value; /* Measured */
char was_reset; /* ResetIf triggered */
char has_hits; /* one of more hit counts is non-zero */
char primed; /* true if all non-Trigger conditions are true */
char measured_from_hits; /* true if the measured_value came from a condition's hit count */
char was_cond_reset; /* ResetNextIf triggered */
uint8_t was_reset; /* ResetIf triggered */
uint8_t has_hits; /* one of more hit counts is non-zero */
uint8_t primed; /* true if all non-Trigger conditions are true */
uint8_t measured_from_hits; /* true if the measured_value came from a condition's hit count */
uint8_t was_cond_reset; /* ResetNextIf triggered */
}
rc_eval_state_t;
typedef struct {
int offset;
int32_t offset;
lua_State* L;
int funcs_ndx;
@ -115,11 +118,11 @@ typedef struct {
rc_memref_t** first_memref;
rc_value_t** variables;
unsigned measured_target;
uint32_t measured_target;
int lines_read;
char has_required_hits;
char measured_as_percent;
uint8_t has_required_hits;
uint8_t measured_as_percent;
}
rc_parse_state_t;
@ -128,18 +131,19 @@ void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs)
void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables);
void rc_destroy_parse_state(rc_parse_state_t* parse);
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
void* rc_alloc_scratch(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch, int scratch_object_pointer_offset);
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length);
void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment, rc_scratch_t* scratch, uint32_t scratch_object_pointer_offset);
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);
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, size_t length);
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, unsigned address, char size, char is_indirect);
int rc_parse_memref(const char** memaddr, char* size, unsigned* address);
rc_memref_t* rc_alloc_memref(rc_parse_state_t* parse, uint32_t address, uint8_t size, uint8_t is_indirect);
int rc_parse_memref(const char** memaddr, uint8_t* size, uint32_t* address);
void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
char rc_memref_shared_size(char size);
unsigned rc_memref_mask(char size);
void rc_transform_memref_value(rc_typed_value_t* value, char size);
void rc_update_memref_value(rc_memref_value_t* memref, uint32_t value);
uint32_t rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
uint8_t rc_memref_shared_size(uint8_t size);
uint32_t rc_memref_mask(uint8_t size);
void rc_transform_memref_value(rc_typed_value_t* value, uint8_t size);
uint32_t rc_peek_value(uint32_t address, uint8_t size, rc_peek_t peek, void* ud);
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
int rc_trigger_state_active(int state);
@ -164,12 +168,12 @@ enum {
RC_PROCESSING_COMPARE_ALWAYS_FALSE
};
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, uint8_t is_indirect);
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state);
int rc_condition_is_combining(const rc_condition_t* self);
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse);
int rc_parse_operand(rc_operand_t* self, const char** memaddr, uint8_t is_indirect, rc_parse_state_t* parse);
void rc_evaluate_operand(rc_typed_value_t* value, rc_operand_t* self, rc_eval_state_t* eval_state);
int rc_operand_is_float_memref(const rc_operand_t* self);
int rc_operand_is_float(const rc_operand_t* self);
@ -177,7 +181,8 @@ int rc_operand_is_float(const rc_operand_t* self);
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
void rc_reset_value(rc_value_t* self);
rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_parse_state_t* parse);
int rc_value_from_hits(rc_value_t* self);
rc_value_t* rc_alloc_helper_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse);
void rc_update_variables(rc_value_t* variable, rc_peek_t peek, void* ud, lua_State* L);
void rc_typed_value_convert(rc_typed_value_t* value, char new_type);
@ -188,7 +193,7 @@ void rc_typed_value_negate(rc_typed_value_t* value);
int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper);
void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref);
int rc_format_typed_value(char* buffer, int size, const rc_typed_value_t* value, int format);
int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* value, int format);
void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_state_t* parse);
int rc_lboard_state_active(int state);
@ -199,4 +204,4 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
#endif
#endif /* INTERNAL_H */
#endif /* RC_INTERNAL_H */

View file

@ -1,6 +1,6 @@
#include "rc_internal.h"
#include "rc_compat.h"
#include "../rc_compat.h"
#include <ctype.h>
@ -16,8 +16,8 @@ enum {
static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* memaddr, int memaddr_len, rc_parse_state_t* parse) {
const char* end;
rc_value_t* variable;
unsigned address;
char size;
uint32_t address;
uint8_t size;
/* single memory reference lookups without a modifier flag can be handled without a variable */
end = memaddr;
@ -78,7 +78,7 @@ static const char* rc_parse_line(const char* line, const char** end, rc_parse_st
typedef struct rc_richpresence_builtin_macro_t {
const char* name;
size_t name_len;
unsigned short display_type;
uint8_t display_type;
} rc_richpresence_builtin_macro_t;
static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const char* line, const char* endline, rc_parse_state_t* parse, rc_richpresence_lookup_t* first_lookup) {
@ -280,7 +280,6 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo
{
rc_richpresence_lookup_item_t** items;
rc_scratch_buffer_t* buffer;
const int alignment = sizeof(rc_richpresence_lookup_item_t*);
int index;
int size;
@ -293,7 +292,7 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo
size = count * sizeof(rc_richpresence_lookup_item_t*);
buffer = &parse->scratch.buffer;
do {
const int aligned_offset = (buffer->offset + alignment - 1) & ~(alignment - 1);
const int aligned_offset = RC_ALIGN(buffer->offset);
const int remaining = sizeof(buffer->buffer) - aligned_offset;
if (remaining >= size) {
@ -325,7 +324,7 @@ static void rc_rebalance_richpresence_lookup(rc_richpresence_lookup_item_t** roo
}
static void rc_insert_richpresence_lookup_item(rc_richpresence_lookup_t* lookup,
unsigned first, unsigned last, const char* label, int label_len, rc_parse_state_t* parse)
uint32_t first, uint32_t last, const char* label, size_t label_len, rc_parse_state_t* parse)
{
rc_richpresence_lookup_item_t** next;
rc_richpresence_lookup_item_t* item;
@ -371,7 +370,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
const char* endline;
const char* label;
char* endptr = 0;
unsigned first, last;
uint32_t first, last;
int base;
do
@ -526,7 +525,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
memcpy(format, line, chars);
format[chars] = '\0';
lookup->format = (unsigned short)rc_parse_format(format);
lookup->format = (uint8_t)rc_parse_format(format);
} else {
lookup->format = RC_FORMAT_VALUE;
}
@ -664,7 +663,7 @@ void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, voi
}
}
static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part, char* buffer, unsigned buffersize)
static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part, char* buffer, size_t buffersize)
{
rc_richpresence_lookup_item_t* item;
rc_typed_value_t value;
@ -808,7 +807,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part
return (int)(ptr - buffer);
}
int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, 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) {
rc_richpresence_display_t* display;
for (display = richpresence->first_display; display; display = display->next) {
@ -829,7 +828,7 @@ int rc_get_richpresence_display_string(rc_richpresence_t* richpresence, char* bu
return 0;
}
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
int rc_evaluate_richpresence(rc_richpresence_t* richpresence, char* buffer, size_t buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
rc_update_richpresence(richpresence, peek, peek_ud, L);
return rc_get_richpresence_display_string(richpresence, buffer, buffersize, peek, peek_ud, L);
}

View file

@ -1,7 +1,7 @@
#include "rc_runtime.h"
#include "rc_internal.h"
#include "rc_compat.h"
#include "../rc_compat.h"
#include "../rhash/md5.h"
#include <stdlib.h>
@ -9,6 +9,17 @@
#define RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE 256
rc_runtime_t* rc_runtime_alloc(void) {
rc_runtime_t* self = malloc(sizeof(rc_runtime_t));
if (self) {
rc_runtime_init(self);
self->owns_self = 1;
}
return self;
}
void rc_runtime_init(rc_runtime_t* self) {
memset(self, 0, sizeof(rc_runtime_t));
self->next_memref = &self->memrefs;
@ -16,7 +27,7 @@ void rc_runtime_init(rc_runtime_t* self) {
}
void rc_runtime_destroy(rc_runtime_t* self) {
unsigned i;
uint32_t i;
if (self->triggers) {
for (i = 0; i < self->trigger_count; ++i)
@ -48,9 +59,13 @@ void rc_runtime_destroy(rc_runtime_t* self) {
self->next_memref = 0;
self->memrefs = 0;
if (self->owns_self) {
free(self);
}
}
static void rc_runtime_checksum(const char* memaddr, unsigned char* md5) {
void rc_runtime_checksum(const char* memaddr, uint8_t* md5) {
md5_state_t state;
md5_init(&state);
md5_append(&state, (unsigned char*)memaddr, (int)strlen(memaddr));
@ -81,7 +96,7 @@ static char rc_runtime_allocated_memrefs(rc_runtime_t* self) {
return owns_memref;
}
static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, unsigned index) {
static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, uint32_t index) {
if (self->triggers[index].owns_memrefs) {
/* if the trigger has one or more memrefs in its buffer, we can't free the buffer.
* just null out the trigger so the runtime processor will skip it
@ -98,8 +113,8 @@ static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, unsigned
}
}
void rc_runtime_deactivate_achievement(rc_runtime_t* self, unsigned id) {
unsigned i;
void rc_runtime_deactivate_achievement(rc_runtime_t* self, uint32_t id) {
uint32_t i;
for (i = 0; i < self->trigger_count; ++i) {
if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
@ -107,14 +122,14 @@ void rc_runtime_deactivate_achievement(rc_runtime_t* self, unsigned id) {
}
}
int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char* memaddr, lua_State* L, int funcs_idx) {
int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx) {
void* trigger_buffer;
rc_trigger_t* trigger;
rc_runtime_trigger_t* runtime_trigger;
rc_parse_state_t parse;
unsigned char md5[16];
uint8_t md5[16];
int size;
unsigned i;
uint32_t i;
if (memaddr == NULL)
return RC_INVALID_MEMORY_OPERAND;
@ -207,9 +222,9 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, unsigned id, const char*
return RC_OK;
}
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, unsigned id)
rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, uint32_t id)
{
unsigned i;
uint32_t i;
for (i = 0; i < self->trigger_count; ++i) {
if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
@ -219,7 +234,7 @@ rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target)
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, uint32_t id, unsigned* measured_value, unsigned* measured_target)
{
const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
if (!measured_value || !measured_target)
@ -242,10 +257,10 @@ int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id
return 1;
}
int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, unsigned id, char* buffer, size_t buffer_size)
int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, uint32_t id, char* buffer, size_t buffer_size)
{
const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
unsigned value;
uint32_t value;
if (!buffer || !buffer_size)
return 0;
@ -262,14 +277,14 @@ int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, unsigned
value = trigger->measured_target;
if (trigger->measured_as_percent) {
unsigned percent = (unsigned)(((unsigned long long)value * 100) / trigger->measured_target);
const uint32_t percent = (uint32_t)(((unsigned long long)value * 100) / trigger->measured_target);
return snprintf(buffer, buffer_size, "%u%%", percent);
}
return snprintf(buffer, buffer_size, "%u/%u", value, trigger->measured_target);
}
static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, unsigned index) {
static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, uint32_t index) {
if (self->lboards[index].owns_memrefs) {
/* if the lboard has one or more memrefs in its buffer, we can't free the buffer.
* just null out the lboard so the runtime processor will skip it
@ -286,8 +301,8 @@ static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, unsigned i
}
}
void rc_runtime_deactivate_lboard(rc_runtime_t* self, unsigned id) {
unsigned i;
void rc_runtime_deactivate_lboard(rc_runtime_t* self, uint32_t id) {
uint32_t i;
for (i = 0; i < self->lboard_count; ++i) {
if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
@ -295,14 +310,14 @@ void rc_runtime_deactivate_lboard(rc_runtime_t* self, unsigned id) {
}
}
int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* memaddr, lua_State* L, int funcs_idx) {
int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* memaddr, lua_State* L, int funcs_idx) {
void* lboard_buffer;
unsigned char md5[16];
uint8_t md5[16];
rc_lboard_t* lboard;
rc_parse_state_t parse;
rc_runtime_lboard_t* runtime_lboard;
int size;
unsigned i;
uint32_t i;
if (memaddr == 0)
return RC_INVALID_MEMORY_OPERAND;
@ -395,9 +410,9 @@ int rc_runtime_activate_lboard(rc_runtime_t* self, unsigned id, const char* mema
return RC_OK;
}
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, unsigned id)
rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, uint32_t id)
{
unsigned i;
uint32_t i;
for (i = 0; i < self->lboard_count; ++i) {
if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
@ -407,7 +422,7 @@ rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format)
int rc_runtime_format_lboard_value(char* buffer, int size, int32_t value, int format)
{
return rc_format_value(buffer, size, value, format);
}
@ -417,7 +432,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
rc_runtime_richpresence_t* previous;
rc_runtime_richpresence_t** previous_ptr;
rc_parse_state_t parse;
unsigned char md5[16];
uint8_t md5[16];
int size;
if (script == NULL)
@ -514,7 +529,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
return RC_OK;
}
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L) {
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, size_t buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L) {
if (self->richpresence && self->richpresence->richpresence)
return rc_get_richpresence_display_string(self->richpresence->richpresence, buffer, buffersize, peek, peek_ud, L);
@ -534,7 +549,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
for (i = self->trigger_count - 1; i >= 0; --i) {
rc_trigger_t* trigger = self->triggers[i].trigger;
int old_state, new_state;
unsigned old_measured_value;
uint32_t old_measured_value;
if (!trigger)
continue;
@ -578,8 +593,8 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
if (trigger->measured_as_percent) {
/* if reporting measured value as a percentage, only send the notification if the percentage changes */
unsigned old_percent = (unsigned)(((unsigned long long)old_measured_value * 100) / trigger->measured_target);
unsigned new_percent = (unsigned)(((unsigned long long)trigger->measured_value * 100) / trigger->measured_target);
const int32_t old_percent = (int32_t)(((unsigned long long)old_measured_value * 100) / trigger->measured_target);
const int32_t new_percent = (int32_t)(((unsigned long long)trigger->measured_value * 100) / trigger->measured_target);
if (old_percent != new_percent) {
runtime_event.value = new_percent;
event_handler(&runtime_event);
@ -700,7 +715,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
void rc_runtime_reset(rc_runtime_t* self) {
rc_value_t* variable;
unsigned i;
uint32_t i;
for (i = 0; i < self->trigger_count; ++i) {
if (self->triggers[i].trigger)
@ -712,13 +727,8 @@ void rc_runtime_reset(rc_runtime_t* self) {
rc_reset_lboard(self->lboards[i].lboard);
}
if (self->richpresence && self->richpresence->richpresence) {
rc_richpresence_display_t* display = self->richpresence->richpresence->first_display;
while (display != 0) {
rc_reset_trigger(&display->trigger);
display = display->next;
}
}
if (self->richpresence && self->richpresence->richpresence)
rc_reset_richpresence(self->richpresence->richpresence);
for (variable = self->variables; variable; variable = variable->next)
rc_reset_value(variable);
@ -739,7 +749,7 @@ static int rc_condset_contains_memref(const rc_condset_t* condset, const rc_memr
return 0;
}
static int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref) {
int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref) {
rc_condset_t* condset;
if (!value)
return 0;
@ -752,7 +762,7 @@ static int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t*
return 0;
}
static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref) {
int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref) {
rc_condset_t* condset;
if (!trigger)
return 0;
@ -769,7 +779,7 @@ static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memr
}
static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
unsigned i;
uint32_t i;
/* disable any achievements dependent on the address */
for (i = 0; i < self->trigger_count; ++i) {
@ -804,7 +814,7 @@ static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref
}
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
void rc_runtime_invalidate_address(rc_runtime_t* self, uint32_t address) {
rc_memref_t** last_memref = &self->memrefs;
rc_memref_t* memref = self->memrefs;

View file

@ -1,6 +1,7 @@
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rc_util.h"
#include "../rhash/md5.h"
#include <stdlib.h>
@ -17,12 +18,12 @@
#define RC_RUNTIME_CHUNK_DONE 0x454E4F44 /* DONE */
typedef struct rc_runtime_progress_t {
rc_runtime_t* runtime;
const rc_runtime_t* runtime;
int offset;
unsigned char* buffer;
uint32_t offset;
uint8_t* buffer;
int chunk_size_offset;
uint32_t chunk_size_offset;
lua_State* L;
} rc_runtime_progress_t;
@ -39,7 +40,7 @@ typedef struct rc_runtime_progress_t {
#define RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF 0x00100000
#define RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME 0x00200000
static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, unsigned value)
static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, uint32_t value)
{
if (progress->buffer) {
progress->buffer[progress->offset + 0] = value & 0xFF; value >>= 8;
@ -51,9 +52,9 @@ static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, unsi
progress->offset += 4;
}
static unsigned rc_runtime_progress_read_uint(rc_runtime_progress_t* progress)
static uint32_t rc_runtime_progress_read_uint(rc_runtime_progress_t* progress)
{
unsigned value = progress->buffer[progress->offset + 0] |
uint32_t value = progress->buffer[progress->offset + 0] |
(progress->buffer[progress->offset + 1] << 8) |
(progress->buffer[progress->offset + 2] << 16) |
(progress->buffer[progress->offset + 3] << 24);
@ -62,7 +63,7 @@ static unsigned rc_runtime_progress_read_uint(rc_runtime_progress_t* progress)
return value;
}
static void rc_runtime_progress_write_md5(rc_runtime_progress_t* progress, unsigned char* md5)
static void rc_runtime_progress_write_md5(rc_runtime_progress_t* progress, uint8_t* md5)
{
if (progress->buffer)
memcpy(&progress->buffer[progress->offset], md5, 16);
@ -70,7 +71,7 @@ static void rc_runtime_progress_write_md5(rc_runtime_progress_t* progress, unsig
progress->offset += 16;
}
static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, unsigned char* md5)
static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, uint8_t* md5)
{
int result = 0;
if (progress->buffer)
@ -81,18 +82,7 @@ static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, unsign
return result;
}
static unsigned rc_runtime_progress_djb2(const char* input)
{
unsigned result = 5381;
char c;
while ((c = *input++) != '\0')
result = ((result << 5) + result) + c; /* result = result * 33 + c */
return result;
}
static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, unsigned chunk_id)
static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, uint32_t chunk_id)
{
rc_runtime_progress_write_uint(progress, chunk_id);
@ -103,14 +93,14 @@ static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, uns
static void rc_runtime_progress_end_chunk(rc_runtime_progress_t* progress)
{
unsigned length;
int offset;
uint32_t length;
uint32_t offset;
progress->offset = (progress->offset + 3) & ~0x03; /* align to 4 byte boundary */
if (progress->buffer) {
/* ignore chunk size field when calculating chunk size */
length = (unsigned)(progress->offset - progress->chunk_size_offset - 4);
length = (uint32_t)(progress->offset - progress->chunk_size_offset - 4);
/* temporarily update the write pointer to write the chunk size field */
offset = progress->offset;
@ -120,7 +110,7 @@ static void rc_runtime_progress_end_chunk(rc_runtime_progress_t* progress)
}
}
static void rc_runtime_progress_init(rc_runtime_progress_t* progress, rc_runtime_t* runtime, lua_State* L)
static void rc_runtime_progress_init(rc_runtime_progress_t* progress, const rc_runtime_t* runtime, lua_State* L)
{
memset(progress, 0, sizeof(rc_runtime_progress_t));
progress->runtime = runtime;
@ -130,7 +120,7 @@ static void rc_runtime_progress_init(rc_runtime_progress_t* progress, rc_runtime
static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
{
rc_memref_t* memref = progress->runtime->memrefs;
unsigned int flags = 0;
uint32_t flags = 0;
rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_MEMREFS);
@ -161,9 +151,9 @@ static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
{
unsigned entries;
unsigned address, flags, value, prior;
char size;
uint32_t entries;
uint32_t address, flags, value, prior;
uint8_t size;
rc_memref_t* memref;
rc_memref_t* first_unmatched_memref = progress->runtime->memrefs;
@ -218,7 +208,7 @@ static int rc_runtime_progress_is_indirect_memref(rc_operand_t* oper)
static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
{
rc_condition_t* cond;
unsigned flags;
uint32_t flags;
rc_runtime_progress_write_uint(progress, condset->is_paused);
@ -262,7 +252,7 @@ static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc
static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
{
rc_condition_t* cond;
unsigned flags;
uint32_t flags;
condset->is_paused = (char)rc_runtime_progress_read_uint(progress);
@ -297,7 +287,7 @@ static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_
return RC_OK;
}
static unsigned rc_runtime_progress_should_serialize_variable_condset(const rc_condset_t* conditions)
static uint32_t rc_runtime_progress_should_serialize_variable_condset(const rc_condset_t* conditions)
{
const rc_condition_t* condition;
@ -318,7 +308,7 @@ static unsigned rc_runtime_progress_should_serialize_variable_condset(const rc_c
static int rc_runtime_progress_write_variable(rc_runtime_progress_t* progress, const rc_value_t* variable)
{
unsigned flags;
uint32_t flags;
flags = rc_runtime_progress_should_serialize_variable_condset(variable->conditions);
if (variable->value.changed)
@ -339,7 +329,7 @@ static int rc_runtime_progress_write_variable(rc_runtime_progress_t* progress, c
static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress)
{
unsigned count = 0;
uint32_t count = 0;
const rc_value_t* variable;
for (variable = progress->runtime->variables; variable; variable = variable->next)
@ -352,7 +342,7 @@ static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress)
for (variable = progress->runtime->variables; variable; variable = variable->next)
{
unsigned djb2 = rc_runtime_progress_djb2(variable->name);
uint32_t djb2 = rc_djb2(variable->name);
rc_runtime_progress_write_uint(progress, djb2);
rc_runtime_progress_write_variable(progress, variable);
@ -364,7 +354,7 @@ static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress)
static int rc_runtime_progress_read_variable(rc_runtime_progress_t* progress, rc_value_t* variable)
{
unsigned flags = rc_runtime_progress_read_uint(progress);
uint32_t flags = rc_runtime_progress_read_uint(progress);
variable->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
variable->value.value = rc_runtime_progress_read_uint(progress);
variable->value.prior = rc_runtime_progress_read_uint(progress);
@ -386,14 +376,14 @@ static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress)
struct rc_pending_value_t
{
rc_value_t* variable;
unsigned djb2;
uint32_t djb2;
};
struct rc_pending_value_t local_pending_variables[32];
struct rc_pending_value_t* pending_variables;
rc_value_t* variable;
unsigned count, serialized_count;
uint32_t count, serialized_count;
int result;
unsigned i;
uint32_t i;
serialized_count = rc_runtime_progress_read_uint(progress);
if (serialized_count == 0)
@ -418,13 +408,13 @@ static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress)
count = 0;
for (variable = progress->runtime->variables; variable; variable = variable->next) {
pending_variables[count].variable = variable;
pending_variables[count].djb2 = rc_runtime_progress_djb2(variable->name);
pending_variables[count].djb2 = rc_djb2(variable->name);
++count;
}
result = RC_OK;
for (; serialized_count > 0 && result == RC_OK; --serialized_count) {
unsigned djb2 = rc_runtime_progress_read_uint(progress);
uint32_t djb2 = rc_runtime_progress_read_uint(progress);
for (i = 0; i < count; ++i) {
if (pending_variables[i].djb2 == djb2) {
variable = pending_variables[i].variable;
@ -502,7 +492,7 @@ static int rc_runtime_progress_read_trigger(rc_runtime_progress_t* progress, rc_
static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progress)
{
unsigned i;
uint32_t i;
int offset = 0;
int result;
@ -543,8 +533,8 @@ static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progres
static int rc_runtime_progress_read_achievement(rc_runtime_progress_t* progress)
{
unsigned id = rc_runtime_progress_read_uint(progress);
unsigned i;
uint32_t id = rc_runtime_progress_read_uint(progress);
uint32_t i;
for (i = 0; i < progress->runtime->trigger_count; ++i) {
rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
@ -564,8 +554,8 @@ static int rc_runtime_progress_read_achievement(rc_runtime_progress_t* progress)
static int rc_runtime_progress_write_leaderboards(rc_runtime_progress_t* progress)
{
unsigned i;
unsigned flags;
uint32_t i;
uint32_t flags;
int offset = 0;
int result;
@ -621,8 +611,8 @@ static int rc_runtime_progress_write_leaderboards(rc_runtime_progress_t* progres
static int rc_runtime_progress_read_leaderboard(rc_runtime_progress_t* progress)
{
unsigned id = rc_runtime_progress_read_uint(progress);
unsigned i;
uint32_t id = rc_runtime_progress_read_uint(progress);
uint32_t i;
int result;
for (i = 0; i < progress->runtime->lboard_count; ++i) {
@ -632,7 +622,7 @@ static int rc_runtime_progress_read_leaderboard(rc_runtime_progress_t* progress)
if (runtime_lboard->lboard->state == RC_TRIGGER_STATE_UNUPDATED) {
/* only update state if definition hasn't changed (md5 matches) */
if (rc_runtime_progress_match_md5(progress, runtime_lboard->md5)) {
unsigned flags = rc_runtime_progress_read_uint(progress);
uint32_t flags = rc_runtime_progress_read_uint(progress);
result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->start);
if (result != RC_OK)
@ -712,7 +702,7 @@ static int rc_runtime_progress_read_rich_presence(rc_runtime_progress_t* progres
static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progress)
{
md5_state_t state;
unsigned char md5[16];
uint8_t md5[16];
int result;
rc_runtime_progress_write_uint(progress, RC_RUNTIME_MARKER);
@ -751,7 +741,7 @@ int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L)
rc_runtime_progress_t progress;
int result;
rc_runtime_progress_init(&progress, (rc_runtime_t*)runtime, L);
rc_runtime_progress_init(&progress, runtime, L);
result = rc_runtime_progress_serialize_internal(&progress);
if (result != RC_OK)
@ -764,26 +754,34 @@ int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, lua
{
rc_runtime_progress_t progress;
rc_runtime_progress_init(&progress, (rc_runtime_t*)runtime, L);
progress.buffer = (unsigned char*)buffer;
if (!buffer)
return RC_INVALID_STATE;
rc_runtime_progress_init(&progress, runtime, L);
progress.buffer = (uint8_t*)buffer;
return rc_runtime_progress_serialize_internal(&progress);
}
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char* serialized, lua_State* L)
int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const uint8_t* serialized, lua_State* L)
{
rc_runtime_progress_t progress;
md5_state_t state;
unsigned char md5[16];
unsigned chunk_id;
unsigned chunk_size;
unsigned next_chunk_offset;
unsigned i;
uint8_t md5[16];
uint32_t chunk_id;
uint32_t chunk_size;
uint32_t next_chunk_offset;
uint32_t i;
int seen_rich_presence = 0;
int result = RC_OK;
if (!serialized) {
rc_runtime_reset(runtime);
return RC_INVALID_STATE;
}
rc_runtime_progress_init(&progress, runtime, L);
progress.buffer = (unsigned char*)serialized;
progress.buffer = (uint8_t*)serialized;
if (rc_runtime_progress_read_uint(&progress) != RC_RUNTIME_MARKER) {
rc_runtime_reset(runtime);

View file

@ -96,7 +96,7 @@ int rc_trigger_state_active(int state)
}
}
static int rc_condset_is_measured_from_hitcount(const rc_condset_t* condset, unsigned measured_value)
static int rc_condset_is_measured_from_hitcount(const rc_condset_t* condset, uint32_t measured_value)
{
const rc_condition_t* condition;
for (condition = condset->conditions; condition; condition = condition->next) {
@ -280,6 +280,9 @@ int rc_test_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L)
}
void rc_reset_trigger(rc_trigger_t* self) {
if (!self)
return;
rc_reset_trigger_hitcounts(self);
self->state = RC_TRIGGER_STATE_WAITING;

View file

@ -255,7 +255,7 @@ int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t
return valid;
}
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
int32_t rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
rc_typed_value_t result;
int valid = rc_evaluate_value_typed(self, &result, peek, ud, L);
@ -285,17 +285,31 @@ void rc_reset_value(rc_value_t* self) {
self->value.changed = 0;
}
int rc_value_from_hits(rc_value_t* self)
{
rc_condset_t* condset = self->conditions;
for (; condset != NULL; condset = condset->next) {
rc_condition_t* condition = condset->conditions;
for (; condition != NULL; condition = condition->next) {
if (condition->type == RC_CONDITION_MEASURED)
return (condition->required_hits != 0);
}
}
return 0;
}
void rc_init_parse_state_variables(rc_parse_state_t* parse, rc_value_t** variables) {
parse->variables = variables;
*variables = 0;
}
rc_value_t* rc_alloc_helper_variable(const char* memaddr, int memaddr_len, rc_parse_state_t* parse)
rc_value_t* rc_alloc_helper_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse)
{
rc_value_t** variables = parse->variables;
rc_value_t* value;
const char* name;
unsigned measured_target;
uint32_t measured_target;
while ((value = *variables) != NULL) {
if (strncmp(value->name, memaddr, memaddr_len) == 0 && value->name[memaddr_len] == 0)

View file

@ -1,6 +1,6 @@
#include "rc_hash.h"
#include "../rcheevos/rc_compat.h"
#include "../rc_compat.h"
#include <ctype.h>
#include <string.h>
@ -31,7 +31,7 @@ struct cdrom_t
#endif
};
static int cdreader_get_sector(unsigned char header[16])
static int cdreader_get_sector(uint8_t header[16])
{
int minutes = (header[12] >> 4) * 10 + (header[12] & 0x0F);
int seconds = (header[13] >> 4) * 10 + (header[13] & 0x0F);
@ -50,11 +50,11 @@ static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
* Then check for the presence of "CD001", which is gauranteed to be in either the
* boot record or primary volume descriptor, one of which is always at sector 16.
*/
const unsigned char sync_pattern[] = {
const uint8_t sync_pattern[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
};
unsigned char header[32];
uint8_t header[32];
const int64_t toc_sector = 16 + cdrom->track_pregap_sectors;
cdrom->sector_size = 0;

View file

@ -1,6 +1,6 @@
#include "rc_hash.h"
#include "../rcheevos/rc_compat.h"
#include "../rc_compat.h"
#include "md5.h"
@ -225,11 +225,11 @@ static void rc_cd_close_track(void* track_handle)
rc_hash_error("no hook registered for cdreader_close_track");
}
static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, unsigned* size)
static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uint32_t* size)
{
uint8_t buffer[2048], *tmp;
int sector;
unsigned num_sectors = 0;
uint32_t num_sectors = 0;
size_t filename_length;
const char* slash;
@ -258,7 +258,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
}
else
{
unsigned logical_block_size;
uint32_t logical_block_size;
/* find the cd information */
if (!rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle) + 16, buffer, 256))
@ -452,7 +452,7 @@ static int rc_hash_buffer(char hash[33], const uint8_t* buffer, size_t buffer_si
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector, const char* name, unsigned size, const char* description)
static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector, const char* name, uint32_t size, const char* description)
{
uint8_t buffer[2048];
size_t num_read;
@ -760,11 +760,11 @@ static int rc_hash_jaguar_cd(char hash[33], const char* path)
void* track_handle;
md5_state_t md5;
int byteswapped = 0;
unsigned size = 0;
unsigned offset = 0;
unsigned sector = 0;
unsigned remaining;
unsigned i;
uint32_t size = 0;
uint32_t offset = 0;
uint32_t sector = 0;
uint32_t remaining;
uint32_t i;
/* Jaguar CD header is in the first sector of the first data track OF THE SECOND SESSION.
* The first track must be an audio track, but may be a warning message or actual game audio */
@ -905,7 +905,7 @@ static int rc_hash_neogeo_cd(char hash[33], const char* path)
char buffer[1024], *ptr;
void* track_handle;
uint32_t sector;
unsigned size;
uint32_t size;
md5_state_t md5;
track_handle = rc_cd_open_track(path, 1);
@ -1091,7 +1091,7 @@ static int rc_hash_nintendo_ds(char hash[33], const char* path)
{
uint8_t header[512];
uint8_t* hash_buffer;
unsigned int hash_size, arm9_size, arm9_addr, arm7_size, arm7_addr, icon_addr;
uint32_t hash_size, arm9_size, arm9_addr, arm7_size, arm7_addr, icon_addr;
size_t num_read;
int64_t offset = 0;
md5_state_t md5;
@ -1215,6 +1215,8 @@ static int rc_hash_gamecube(char hash[33], const char* path)
uint32_t ix;
file_handle = rc_file_open(path);
if (!file_handle)
return rc_hash_error("Could not open file");
/* Verify Gamecube */
rc_file_seek(file_handle, 0x1c, SEEK_SET);
@ -1330,8 +1332,8 @@ static int rc_hash_pce_track(char hash[33], void* track_handle)
{
uint8_t buffer[2048];
md5_state_t md5;
int sector, num_sectors;
unsigned size;
uint32_t sector, num_sectors;
uint32_t size;
/* the PC-Engine uses the second sector to specify boot information and program name.
* the string "PC Engine CD-ROM SYSTEM" should exist at 32 bytes into the sector
@ -1516,7 +1518,7 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
uint8_t buffer[256] = "";
void* track_handle;
char exe_file[32] = "";
unsigned size;
uint32_t size;
uint32_t sector;
int result = 0;
md5_state_t md5;
@ -1610,10 +1612,10 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
}
static int rc_hash_find_playstation_executable(void* track_handle, const char* boot_key, const char* cdrom_prefix,
char exe_name[], unsigned exe_name_size, unsigned* exe_size)
char exe_name[], uint32_t exe_name_size, unsigned* exe_size)
{
uint8_t buffer[2048];
unsigned size;
uint32_t size;
char* ptr;
char* start;
const size_t boot_key_len = strlen(boot_key);
@ -1683,7 +1685,7 @@ static int rc_hash_psx(char hash[33], const char* path)
char exe_name[64] = "";
void* track_handle;
uint32_t sector;
unsigned size;
uint32_t size;
int result = 0;
md5_state_t md5;
@ -1747,7 +1749,7 @@ static int rc_hash_ps2(char hash[33], const char* path)
char exe_name[64] = "";
void* track_handle;
uint32_t sector;
unsigned size;
uint32_t size;
int result = 0;
md5_state_t md5;
@ -1795,7 +1797,7 @@ static int rc_hash_psp(char hash[33], const char* path)
{
void* track_handle;
uint32_t sector;
unsigned size;
uint32_t size;
md5_state_t md5;
track_handle = rc_cd_open_track(path, 1);
@ -1866,6 +1868,21 @@ static int rc_hash_sega_cd(char hash[33], const char* path)
return rc_hash_buffer(hash, buffer, sizeof(buffer));
}
static int rc_hash_scv(char hash[33], const uint8_t* buffer, size_t buffer_size)
{
/* if the file contains a header, ignore it */
/* https://gitlab.com/MaaaX-EmuSCV/libretro-emuscv/-/blob/master/readme.txt#L211 */
if (memcmp(buffer, "EmuSCV", 6) == 0)
{
rc_hash_verbose("Ignoring SCV header");
buffer += 32;
buffer_size -= 32;
}
return rc_hash_buffer(hash, buffer, buffer_size);
}
static int rc_hash_snes(char hash[33], const uint8_t* buffer, size_t buffer_size)
{
/* if the file contains a header, ignore it */
@ -2026,6 +2043,9 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
case RC_CONSOLE_PC_ENGINE: /* NOTE: does not support PCEngine CD */
return rc_hash_pce(hash, buffer, buffer_size);
case RC_CONSOLE_SUPER_CASSETTEVISION:
return rc_hash_scv(hash, buffer, buffer_size);
case RC_CONSOLE_SUPER_NINTENDO:
return rc_hash_snes(hash, buffer, buffer_size);
@ -2322,6 +2342,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_ATARI_LYNX:
case RC_CONSOLE_NINTENDO:
case RC_CONSOLE_PC_ENGINE:
case RC_CONSOLE_SUPER_CASSETTEVISION:
case RC_CONSOLE_SUPER_NINTENDO:
/* additional logic whole-file hash - buffer then call rc_hash_generate_from_buffer */
return rc_hash_buffered_file(hash, console_id, path);
@ -2463,7 +2484,7 @@ static void rc_hash_initialize_dsk_iterator(struct rc_hash_iterator* iterator, c
rc_hash_iterator_append_console(iterator, RC_CONSOLE_APPLE_II);
}
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, uint8_t* buffer, size_t buffer_size)
void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char* path, const uint8_t* buffer, size_t buffer_size)
{
int need_path = !buffer;
@ -2540,7 +2561,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
}
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, MegaDuck,
* Fairchild Channel F, Arcadia 2001, and Interton VC 4000.
* Fairchild Channel F, Arcadia 2001, Interton VC 4000, and Super Cassette Vision.
* Since they all use the same hashing algorithm, only specify one of them */
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
}
@ -2570,9 +2591,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[5] = RC_CONSOLE_3DO;
iterator->consoles[6] = RC_CONSOLE_PCFX;
iterator->consoles[4] = RC_CONSOLE_PSP;
iterator->consoles[5] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[6] = RC_CONSOLE_3DO;
iterator->consoles[7] = RC_CONSOLE_PCFX;
need_path = 1;
}
else if (rc_path_compare_extension(ext, "col"))
@ -2587,6 +2609,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_FAIRCHILD_CHANNEL_F;
}
else if (rc_path_compare_extension(ext, "cart"))
{
iterator->consoles[0] = RC_CONSOLE_SUPER_CASSETTEVISION;
}
break;
case 'd':
@ -2596,7 +2622,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
}
else if (rc_path_compare_extension(ext, "d64"))
{
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
}
else if (rc_path_compare_extension(ext, "d88"))
{
@ -2616,7 +2642,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
}
else if (rc_path_compare_extension(ext, "fd"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
}
break;

View file

@ -186,6 +186,15 @@ ACHIEVEMENTS
#include "../libretro-common/net/net_http.c"
#endif
/* rcheevos doesn't actually spawn and manage threads, RC_NO_THREADS
* simply disables the mutexes that provide thread safety. */
#if !defined(HAVE_THREADS)
#define RC_NO_THREADS 1
#elif defined(GEKKO) || defined(_3DS)
/* Gekko (Wii) and 3DS use custom pthread wrappers (see rthreads.c) */
#define RC_NO_THREADS 1
#endif
#include "../libretro-common/formats/cdfs/cdfs.c"
#include "../network/net_http_special.c"
@ -193,11 +202,15 @@ ACHIEVEMENTS
#include "../cheevos/cheevos_client.c"
#include "../cheevos/cheevos_menu.c"
#include "../deps/rcheevos/src/rc_client.c"
#include "../deps/rcheevos/src/rc_compat.c"
#include "../deps/rcheevos/src/rc_libretro.c"
#include "../deps/rcheevos/src/rc_util.c"
#include "../deps/rcheevos/src/rapi/rc_api_common.c"
#include "../deps/rcheevos/src/rapi/rc_api_info.c"
#include "../deps/rcheevos/src/rapi/rc_api_runtime.c"
#include "../deps/rcheevos/src/rapi/rc_api_user.c"
#include "../deps/rcheevos/src/rcheevos/alloc.c"
#include "../deps/rcheevos/src/rcheevos/compat.c"
#include "../deps/rcheevos/src/rcheevos/condition.c"
#include "../deps/rcheevos/src/rcheevos/condset.c"
#include "../deps/rcheevos/src/rcheevos/consoleinfo.c"
@ -205,7 +218,6 @@ ACHIEVEMENTS
#include "../deps/rcheevos/src/rcheevos/lboard.c"
#include "../deps/rcheevos/src/rcheevos/memref.c"
#include "../deps/rcheevos/src/rcheevos/operand.c"
#include "../deps/rcheevos/src/rcheevos/rc_libretro.c"
#include "../deps/rcheevos/src/rcheevos/richpresence.c"
#include "../deps/rcheevos/src/rcheevos/runtime.c"
#include "../deps/rcheevos/src/rcheevos/runtime_progress.c"