#ifndef RCHEEVOS_H #define RCHEEVOS_H #ifdef __cplusplus extern "C" { #endif typedef struct lua_State lua_State; /*****************************************************************************\ | Return values | \*****************************************************************************/ 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 }; const char* rc_error_str(int ret); /*****************************************************************************\ | Callbacks | \*****************************************************************************/ /** * Callback used to read num_bytes bytes from memory starting at address. If * 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); /*****************************************************************************\ | Memory References | \*****************************************************************************/ /* Sizes. */ enum { RC_MEMSIZE_8_BITS, RC_MEMSIZE_16_BITS, RC_MEMSIZE_24_BITS, RC_MEMSIZE_32_BITS, RC_MEMSIZE_LOW, RC_MEMSIZE_HIGH, RC_MEMSIZE_BIT_0, RC_MEMSIZE_BIT_1, RC_MEMSIZE_BIT_2, RC_MEMSIZE_BIT_3, RC_MEMSIZE_BIT_4, RC_MEMSIZE_BIT_5, RC_MEMSIZE_BIT_6, RC_MEMSIZE_BIT_7, RC_MEMSIZE_BITCOUNT, RC_MEMSIZE_VARIABLE }; typedef struct rc_memref_value_t { /* The current value of this memory reference. */ unsigned value; /* The last differing value of this memory reference. */ unsigned prior; /* The size of the value. */ char size; /* True if the value changed this frame. */ char changed; /* 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; } rc_memref_value_t; typedef struct rc_memref_t rc_memref_t; struct rc_memref_t { /* The current value at the specified memory address. */ rc_memref_value_t value; /* The memory address of this variable. */ unsigned address; /* The next memory reference in the chain. */ rc_memref_t* next; }; /*****************************************************************************\ | Operands | \*****************************************************************************/ /* types */ enum { RC_OPERAND_ADDRESS, /* The value of a live address in RAM. */ RC_OPERAND_DELTA, /* The value last known at this address. */ RC_OPERAND_CONST, /* A 32-bit unsigned integer. */ RC_OPERAND_FP, /* A floating point value. */ RC_OPERAND_LUA, /* A Lua function that provides the value. */ RC_OPERAND_PRIOR, /* The last differing value at this address. */ RC_OPERAND_BCD, /* The BCD-decoded value of a live address in RAM. */ RC_OPERAND_INVERTED /* The twos-complement value of a live address in RAM. */ }; typedef struct { union { /* A value read from memory. */ rc_memref_t* memref; /* An integer value. */ unsigned num; /* A floating point value. */ double dbl; /* A reference to the Lua function that provides the value. */ int luafunc; } value; /* specifies which member of the value union is being used */ char type; /* the actual RC_MEMSIZE of the operand - memref.size may differ */ char size; } rc_operand_t; /*****************************************************************************\ | Conditions | \*****************************************************************************/ /* types */ enum { RC_CONDITION_STANDARD, RC_CONDITION_PAUSE_IF, RC_CONDITION_RESET_IF, RC_CONDITION_ADD_SOURCE, RC_CONDITION_SUB_SOURCE, RC_CONDITION_ADD_HITS, RC_CONDITION_AND_NEXT, RC_CONDITION_MEASURED, RC_CONDITION_ADD_ADDRESS, RC_CONDITION_OR_NEXT, RC_CONDITION_TRIGGER, RC_CONDITION_MEASURED_IF, RC_CONDITION_RESET_NEXT_IF, RC_CONDITION_SUB_HITS }; /* operators */ enum { RC_OPERATOR_EQ, RC_OPERATOR_LT, RC_OPERATOR_LE, RC_OPERATOR_GT, RC_OPERATOR_GE, RC_OPERATOR_NE, RC_OPERATOR_NONE, RC_OPERATOR_MULT, RC_OPERATOR_DIV, RC_OPERATOR_AND }; typedef struct rc_condition_t rc_condition_t; struct rc_condition_t { /* The condition's operands. */ rc_operand_t operand1; rc_operand_t operand2; /* Required hits to fire this condition. */ unsigned required_hits; /* Number of hits so far. */ unsigned current_hits; /* The next condition in the chain. */ rc_condition_t* next; /* The type of the condition. */ char type; /* The comparison operator to use. */ char 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; /* Whether or not the condition evaluated true on the last check */ char is_true; }; /*****************************************************************************\ | Condition sets | \*****************************************************************************/ typedef struct rc_condset_t rc_condset_t; struct rc_condset_t { /* The next condition set in the chain. */ rc_condset_t* next; /* The list of conditions in this condition set. */ rc_condition_t* conditions; /* True if any condition in the set is a pause condition. */ char has_pause; /* True if the set is currently paused. */ char is_paused; }; /*****************************************************************************\ | Trigger | \*****************************************************************************/ enum { RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */ RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */ RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */ RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */ RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */ RC_TRIGGER_STATE_TRIGGERED, /* achievement has triggered */ RC_TRIGGER_STATE_PRIMED, /* all non-Trigger conditions are true */ RC_TRIGGER_STATE_DISABLED /* achievement cannot be processed at this time */ }; typedef struct { /* The main condition set. */ rc_condset_t* requirement; /* The list of sub condition sets in this test. */ rc_condset_t* alternative; /* The memory references required by the trigger. */ rc_memref_t* memrefs; /* The current state of the MEASURED condition. */ unsigned measured_value; /* The target state of the MEASURED condition */ unsigned measured_target; /* The current state of the trigger */ char state; /* True if at least one condition has a non-zero hit count */ char has_hits; /* True if at least one condition has a non-zero required hit count */ char has_required_hits; } rc_trigger_t; int rc_trigger_size(const char* memaddr); rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx); int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L); int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L); void rc_reset_trigger(rc_trigger_t* self); /*****************************************************************************\ | Values | \*****************************************************************************/ typedef struct rc_value_t rc_value_t; struct rc_value_t { /* The current value of the variable. */ rc_memref_value_t value; /* The list of conditions to evaluate. */ rc_condset_t* conditions; /* The memory references required by the value. */ rc_memref_t* memrefs; /* The name of the variable. */ const char* name; /* The next variable in the chain. */ rc_value_t* next; }; int rc_value_size(const char* memaddr); rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx); int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L); /*****************************************************************************\ | Leaderboards | \*****************************************************************************/ /* Return values for rc_evaluate_lboard. */ enum { RC_LBOARD_STATE_INACTIVE, /* leaderboard is not being processed */ RC_LBOARD_STATE_WAITING, /* leaderboard cannot activate until the start condition has been false for at least one frame */ RC_LBOARD_STATE_ACTIVE, /* leaderboard is active and may start */ RC_LBOARD_STATE_STARTED, /* leaderboard attempt in progress */ RC_LBOARD_STATE_CANCELED, /* leaderboard attempt canceled */ RC_LBOARD_STATE_TRIGGERED, /* leaderboard attempt complete, value should be submitted */ RC_LBOARD_STATE_DISABLED /* leaderboard cannot be processed at this time */ }; typedef struct { rc_trigger_t start; rc_trigger_t submit; rc_trigger_t cancel; rc_value_t value; rc_value_t* progress; rc_memref_t* memrefs; char state; } rc_lboard_t; 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); void rc_reset_lboard(rc_lboard_t* lboard); /*****************************************************************************\ | Value formatting | \*****************************************************************************/ /* Supported formats. */ enum { RC_FORMAT_FRAMES, RC_FORMAT_SECONDS, RC_FORMAT_CENTISECS, RC_FORMAT_SCORE, RC_FORMAT_VALUE, RC_FORMAT_MINUTES, RC_FORMAT_SECONDS_AS_MINUTES }; int rc_parse_format(const char* format_str); int rc_format_value(char* buffer, int size, int value, int format); /*****************************************************************************\ | Rich Presence | \*****************************************************************************/ typedef struct rc_richpresence_lookup_item_t rc_richpresence_lookup_item_t; struct rc_richpresence_lookup_item_t { unsigned first; unsigned last; rc_richpresence_lookup_item_t* left; rc_richpresence_lookup_item_t* right; const char* label; }; typedef struct rc_richpresence_lookup_t rc_richpresence_lookup_t; struct rc_richpresence_lookup_t { rc_richpresence_lookup_item_t* root; rc_richpresence_lookup_t* next; const char* name; const char* default_label; unsigned short format; }; typedef struct rc_richpresence_display_part_t rc_richpresence_display_part_t; struct rc_richpresence_display_part_t { rc_richpresence_display_part_t* next; const char* text; rc_richpresence_lookup_t* lookup; rc_memref_value_t *value; unsigned short display_type; }; typedef struct rc_richpresence_display_t rc_richpresence_display_t; struct rc_richpresence_display_t { rc_trigger_t trigger; rc_richpresence_display_t* next; rc_richpresence_display_part_t* display; }; typedef struct { rc_richpresence_display_t* first_display; rc_richpresence_lookup_t* first_lookup; rc_memref_t* memrefs; rc_value_t* variables; } rc_richpresence_t; int rc_richpresence_size(const char* script); 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); 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); /*****************************************************************************\ | Runtime | \*****************************************************************************/ typedef struct rc_runtime_trigger_t { unsigned id; rc_trigger_t* trigger; void* buffer; rc_memref_t* invalid_memref; unsigned char md5[16]; int serialized_size; char owns_memrefs; } rc_runtime_trigger_t; typedef struct rc_runtime_lboard_t { unsigned id; int value; rc_lboard_t* lboard; void* buffer; rc_memref_t* invalid_memref; unsigned char md5[16]; char owns_memrefs; } rc_runtime_lboard_t; typedef struct rc_runtime_richpresence_t { rc_richpresence_t* richpresence; void* buffer; struct rc_runtime_richpresence_t* previous; char owns_memrefs; } rc_runtime_richpresence_t; 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_t* memrefs; rc_memref_t** next_memref; rc_value_t* variables; rc_value_t** next_variable; } rc_runtime_t; 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_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_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx); int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L); enum { RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */ RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED, RC_RUNTIME_EVENT_ACHIEVEMENT_RESET, RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED, RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED, RC_RUNTIME_EVENT_LBOARD_STARTED, RC_RUNTIME_EVENT_LBOARD_CANCELED, RC_RUNTIME_EVENT_LBOARD_UPDATED, RC_RUNTIME_EVENT_LBOARD_TRIGGERED, RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED, RC_RUNTIME_EVENT_LBOARD_DISABLED }; 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); void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L); void rc_runtime_reset(rc_runtime_t* runtime); void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned 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); /*****************************************************************************\ | Memory mapping | \*****************************************************************************/ enum { RC_MEMORY_TYPE_SYSTEM_RAM, /* normal system memory */ RC_MEMORY_TYPE_SAVE_RAM, /* memory that persists between sessions */ RC_MEMORY_TYPE_VIDEO_RAM, /* memory reserved for graphical processing */ RC_MEMORY_TYPE_READONLY, /* memory that maps to read only data */ RC_MEMORY_TYPE_HARDWARE_CONTROLLER, /* memory for interacting with system components */ RC_MEMORY_TYPE_VIRTUAL_RAM, /* secondary address space that maps to real memory in system RAM */ RC_MEMORY_TYPE_UNUSED /* these addresses don't really exist */ }; 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 */ 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; } rc_memory_regions_t; const rc_memory_regions_t* rc_console_memory_regions(int console_id); #ifdef __cplusplus } #endif #endif /* RCHEEVOS_H */