mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
Merge pull request #2797 from CookiePLMonster/rcheevos-update
Rcheevos update
This commit is contained in:
commit
51041e47f7
|
@ -16,7 +16,7 @@ extern "C" {
|
||||||
/* generates a hash from a block of memory.
|
/* generates a hash from a block of memory.
|
||||||
* returns non-zero on success, or zero on failure.
|
* returns non-zero on success, or zero on failure.
|
||||||
*/
|
*/
|
||||||
int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer, size_t buffer_size);
|
int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* buffer, size_t buffer_size);
|
||||||
|
|
||||||
/* generates a hash from a file.
|
/* generates a hash from a file.
|
||||||
* returns non-zero on success, or zero on failure.
|
* returns non-zero on success, or zero on failure.
|
||||||
|
@ -120,7 +120,7 @@ extern "C" {
|
||||||
rc_hash_cdreader_absolute_sector_to_track_sector absolute_sector_to_track_sector;
|
rc_hash_cdreader_absolute_sector_to_track_sector absolute_sector_to_track_sector;
|
||||||
};
|
};
|
||||||
|
|
||||||
void rc_hash_init_default_cdreader();
|
void rc_hash_init_default_cdreader(void);
|
||||||
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
|
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
|
||||||
|
|
||||||
/* ===================================================== */
|
/* ===================================================== */
|
||||||
|
|
|
@ -7,6 +7,8 @@ extern "C" {
|
||||||
|
|
||||||
#include "rc_error.h"
|
#include "rc_error.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| Forward Declarations (defined in rc_runtime_types.h) |
|
| Forward Declarations (defined in rc_runtime_types.h) |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
@ -94,6 +96,7 @@ int rc_runtime_activate_achievement(rc_runtime_t* runtime, unsigned id, const ch
|
||||||
void rc_runtime_deactivate_achievement(rc_runtime_t* runtime, unsigned id);
|
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);
|
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_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_lboard(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);
|
||||||
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
|
void rc_runtime_deactivate_lboard(rc_runtime_t* runtime, unsigned id);
|
||||||
|
|
|
@ -51,6 +51,9 @@ enum {
|
||||||
RC_MEMSIZE_BIT_6,
|
RC_MEMSIZE_BIT_6,
|
||||||
RC_MEMSIZE_BIT_7,
|
RC_MEMSIZE_BIT_7,
|
||||||
RC_MEMSIZE_BITCOUNT,
|
RC_MEMSIZE_BITCOUNT,
|
||||||
|
RC_MEMSIZE_16_BITS_BE,
|
||||||
|
RC_MEMSIZE_24_BITS_BE,
|
||||||
|
RC_MEMSIZE_32_BITS_BE,
|
||||||
RC_MEMSIZE_VARIABLE
|
RC_MEMSIZE_VARIABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,6 +258,9 @@ struct rc_trigger_t {
|
||||||
|
|
||||||
/* True if at least one condition has a non-zero required hit count */
|
/* True if at least one condition has a non-zero required hit count */
|
||||||
char has_required_hits;
|
char has_required_hits;
|
||||||
|
|
||||||
|
/* True if the measured value should be displayed as a percentage */
|
||||||
|
char measured_as_percent;
|
||||||
};
|
};
|
||||||
|
|
||||||
int rc_trigger_size(const char* memaddr);
|
int rc_trigger_size(const char* memaddr);
|
||||||
|
|
|
@ -11,6 +11,9 @@ int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const
|
||||||
|
|
||||||
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
|
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value);
|
||||||
|
|
||||||
|
int rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count);
|
||||||
|
int rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count);
|
||||||
|
|
||||||
int rc_url_get_gameid(char* buffer, size_t size, const char* hash);
|
int rc_url_get_gameid(char* buffer, size_t size, const char* hash);
|
||||||
|
|
||||||
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
|
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid);
|
||||||
|
@ -28,13 +31,6 @@ int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const
|
||||||
int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
|
int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, size_t post_buffer_size,
|
||||||
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
|
const char* user_name, const char* login_token, unsigned gameid, const char* rich_presence);
|
||||||
|
|
||||||
// Custom exports, static in upstream rcheevos
|
|
||||||
int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buffer_offset, const char* api, const char* user_name);
|
|
||||||
|
|
||||||
int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, unsigned value);
|
|
||||||
|
|
||||||
int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, const char* value);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||||
<PreprocessorDefinitions>RC_DISABLE_LUA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>RC_DISABLE_LUA;RCHEEVOS_URL_SSL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
|
|
@ -144,6 +144,7 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in
|
||||||
parse->measured_target = 0;
|
parse->measured_target = 0;
|
||||||
parse->lines_read = 0;
|
parse->lines_read = 0;
|
||||||
parse->has_required_hits = 0;
|
parse->has_required_hits = 0;
|
||||||
|
parse->measured_as_percent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rc_destroy_parse_state(rc_parse_state_t* parse)
|
void rc_destroy_parse_state(rc_parse_state_t* parse)
|
||||||
|
|
|
@ -55,6 +55,7 @@ int rc_snprintf(char* buffer, size_t size, const char* format, ...)
|
||||||
|
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
/* assume buffer is large enough and ignore size */
|
/* assume buffer is large enough and ignore size */
|
||||||
|
(void)size;
|
||||||
result = vsprintf(buffer, format, args);
|
result = vsprintf(buffer, format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
char rc_parse_operator(const char** memaddr) {
|
static int rc_parse_operator(const char** memaddr) {
|
||||||
const char* oper = *memaddr;
|
const char* oper = *memaddr;
|
||||||
|
|
||||||
switch (*oper) {
|
switch (*oper) {
|
||||||
|
@ -63,7 +63,7 @@ char 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, int is_indirect) {
|
||||||
rc_condition_t* self;
|
rc_condition_t* self;
|
||||||
const char* aux;
|
const char* aux;
|
||||||
int ret2;
|
int result;
|
||||||
int can_modify = 0;
|
int can_modify = 0;
|
||||||
|
|
||||||
aux = *memaddr;
|
aux = *memaddr;
|
||||||
|
@ -86,6 +86,11 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; can_modify = 1; break;
|
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; can_modify = 1; break;
|
||||||
case 't': case 'T': self->type = RC_CONDITION_TRIGGER; break;
|
case 't': case 'T': self->type = RC_CONDITION_TRIGGER; break;
|
||||||
case 'z': case 'Z': self->type = RC_CONDITION_RESET_NEXT_IF; break;
|
case 'z': case 'Z': self->type = RC_CONDITION_RESET_NEXT_IF; break;
|
||||||
|
case 'g': case 'G':
|
||||||
|
parse->measured_as_percent = 1;
|
||||||
|
self->type = RC_CONDITION_MEASURED;
|
||||||
|
break;
|
||||||
|
/* e f h j k l s u v w x y */
|
||||||
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
|
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,10 +100,9 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
self->type = RC_CONDITION_STANDARD;
|
self->type = RC_CONDITION_STANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret2 = rc_parse_operand(&self->operand1, &aux, 1, is_indirect, parse);
|
result = rc_parse_operand(&self->operand1, &aux, is_indirect, parse);
|
||||||
|
if (result < 0) {
|
||||||
if (ret2 < 0) {
|
parse->offset = result;
|
||||||
parse->offset = ret2;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +111,13 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->oper = rc_parse_operator(&aux);
|
result = rc_parse_operator(&aux);
|
||||||
|
if (result < 0) {
|
||||||
|
parse->offset = result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->oper = (char)result;
|
||||||
switch (self->oper) {
|
switch (self->oper) {
|
||||||
case RC_OPERATOR_NONE:
|
case RC_OPERATOR_NONE:
|
||||||
/* non-modifying statements must have a second operand */
|
/* non-modifying statements must have a second operand */
|
||||||
|
@ -135,10 +144,6 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
break;
|
break;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case RC_INVALID_OPERATOR:
|
|
||||||
parse->offset = RC_INVALID_OPERATOR;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* comparison operators are not valid on modifying statements */
|
/* comparison operators are not valid on modifying statements */
|
||||||
if (can_modify) {
|
if (can_modify) {
|
||||||
|
@ -158,10 +163,9 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret2 = rc_parse_operand(&self->operand2, &aux, 1, is_indirect, parse);
|
result = rc_parse_operand(&self->operand2, &aux, is_indirect, parse);
|
||||||
|
if (result < 0) {
|
||||||
if (ret2 < 0) {
|
parse->offset = result;
|
||||||
parse->offset = ret2;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
|
||||||
case RC_CONDITION_OR_NEXT:
|
case RC_CONDITION_OR_NEXT:
|
||||||
case RC_CONDITION_ADD_ADDRESS:
|
case RC_CONDITION_ADD_ADDRESS:
|
||||||
case RC_CONDITION_RESET_NEXT_IF:
|
case RC_CONDITION_RESET_NEXT_IF:
|
||||||
condition->pause = *in_pause;
|
condition->pause = (char)*in_pause;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -140,7 +140,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rc_condset_update_indirect_memrefs(rc_condset_t* self, rc_condition_t* condition, int processing_pause, rc_eval_state_t* eval_state) {
|
static void rc_condset_update_indirect_memrefs(rc_condition_t* condition, int processing_pause, rc_eval_state_t* eval_state) {
|
||||||
for (; condition != 0; condition = condition->next) {
|
for (; condition != 0; condition = condition->next) {
|
||||||
if (condition->pause != processing_pause)
|
if (condition->pause != processing_pause)
|
||||||
continue;
|
continue;
|
||||||
|
@ -210,7 +210,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||||
}
|
}
|
||||||
|
|
||||||
/* STEP 2: evaluate the current condition */
|
/* STEP 2: evaluate the current condition */
|
||||||
condition->is_true = rc_test_condition(condition, eval_state);
|
condition->is_true = (char)rc_test_condition(condition, eval_state);
|
||||||
eval_state->add_value = 0;
|
eval_state->add_value = 0;
|
||||||
eval_state->add_address = 0;
|
eval_state->add_address = 0;
|
||||||
|
|
||||||
|
@ -311,10 +311,10 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||||
* if the set has any indirect memrefs, manually update them now so the deltas are correct */
|
* if the set has any indirect memrefs, manually update them now so the deltas are correct */
|
||||||
if (self->has_indirect_memrefs) {
|
if (self->has_indirect_memrefs) {
|
||||||
/* first, update any indirect memrefs in the remaining part of the pause subset */
|
/* first, update any indirect memrefs in the remaining part of the pause subset */
|
||||||
rc_condset_update_indirect_memrefs(self, condition->next, 1, eval_state);
|
rc_condset_update_indirect_memrefs(condition->next, 1, eval_state);
|
||||||
|
|
||||||
/* then, update all indirect memrefs in the non-pause subset */
|
/* then, update all indirect memrefs in the non-pause subset */
|
||||||
rc_condset_update_indirect_memrefs(self, self->conditions, 0, eval_state);
|
rc_condset_update_indirect_memrefs(self->conditions, 0, eval_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -371,7 +371,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||||
/* if not suppressed, update the measured value */
|
/* if not suppressed, update the measured value */
|
||||||
if (measured_value > eval_state->measured_value && can_measure) {
|
if (measured_value > eval_state->measured_value && can_measure) {
|
||||||
eval_state->measured_value = measured_value;
|
eval_state->measured_value = measured_value;
|
||||||
eval_state->measured_from_hits = measured_from_hits;
|
eval_state->measured_from_hits = (char)measured_from_hits;
|
||||||
}
|
}
|
||||||
|
|
||||||
return set_valid;
|
return set_valid;
|
||||||
|
@ -384,8 +384,9 @@ int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->has_pause) {
|
if (self->has_pause) {
|
||||||
if ((self->is_paused = rc_test_condset_internal(self, 1, eval_state))) {
|
|
||||||
/* one or more Pause conditions exists, if any of them are true, stop processing this group */
|
/* one or more Pause conditions exists, if any of them are true, stop processing this group */
|
||||||
|
self->is_paused = (char)rc_test_condset_internal(self, 1, eval_state);
|
||||||
|
if (self->is_paused) {
|
||||||
eval_state->primed = 0;
|
eval_state->primed = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,19 +323,45 @@ static const rc_memory_region_t _rc_memory_regions_game_gear[] = {
|
||||||
static const rc_memory_regions_t rc_memory_regions_game_gear = { _rc_memory_regions_game_gear, 1 };
|
static const rc_memory_regions_t rc_memory_regions_game_gear = { _rc_memory_regions_game_gear, 1 };
|
||||||
|
|
||||||
/* ===== Intellivision ===== */
|
/* ===== Intellivision ===== */
|
||||||
/* http://wiki.intellivision.us/index.php%3Ftitle%3DMemory_Map */
|
/* http://wiki.intellivision.us/index.php/Memory_Map */
|
||||||
|
/* NOTE: Intellivision memory addresses point at 16-bit values. FreeIntv exposes them as little-endian
|
||||||
|
* 32-bit values. As such, the addresses are off by a factor of 4 _and_ the data is only where we
|
||||||
|
* expect it on little-endian systems.
|
||||||
|
*/
|
||||||
static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
||||||
{ 0x000000U, 0x00007FU, 0x000000U, RC_MEMORY_TYPE_VIDEO_RAM, "STIC Registers" },
|
/* For backwards compatibility, register a 128-byte chunk of video RAM so the system memory
|
||||||
{ 0x000080U, 0x0000FFU, 0x000080U, RC_MEMORY_TYPE_UNUSED, "" },
|
* will start at $0080. $0000-$007F previously tried to map to the STIC video registers as
|
||||||
{ 0x000100U, 0x00035FU, 0x000100U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
* RETRO_MEMORY_VIDEO_RAM, and FreeIntv didn't expose any RETRO_MEMORY_VIDEO_RAM, so the first
|
||||||
{ 0x000360U, 0x0003FFU, 0x000360U, RC_MEMORY_TYPE_UNUSED, "" },
|
* byte of RETRO_MEMORY_SYSTEM_RAM was registered at $0080. The data at $0080 is actually the
|
||||||
{ 0x000400U, 0x000FFFU, 0x000400U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
* STIC registers (4 bytes each), so we need to provide an arbitrary 128-byte padding that
|
||||||
{ 0x001000U, 0x001FFFU, 0x001000U, RC_MEMORY_TYPE_UNUSED, "" },
|
* claims to be video RAM to ensure the system RAM ends up at the right address.
|
||||||
{ 0x002000U, 0x002FFFU, 0x002000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
*/
|
||||||
{ 0x003000U, 0x003FFFU, 0x003000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" },
|
{ 0x000000U, 0x00007FU, 0xFFFFFFU, RC_MEMORY_TYPE_VIDEO_RAM, "" },
|
||||||
{ 0x004000U, 0x00FFFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
|
||||||
|
/* RetroAchievements address = real address x4 + 0x80.
|
||||||
|
* These all have to map to RETRO_MEMORY_SYSTEM_RAM (even the video-related fields) as the
|
||||||
|
* entire block is exposed as a single entity by FreeIntv */
|
||||||
|
|
||||||
|
/* $0000-$007F: STIC registers, $0040-$007F are readonly */
|
||||||
|
{ 0x000080U, 0x00027FU, 0x000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "STIC Registers" },
|
||||||
|
/* $0080-$00FF: unused */
|
||||||
|
{ 0x000280U, 0x00047FU, 0x000080U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||||
|
/* $0100-$035F: system RAM, $0100-$01EF is scratch memory and only 8-bits per address */
|
||||||
|
{ 0x000480U, 0x000DFFU, 0x000100U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||||
|
/* $0360-$03FF: unused */
|
||||||
|
{ 0x000E00U, 0x00107FU, 0x000360U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||||
|
/* $0400-$0FFF: cartridge RAM */
|
||||||
|
{ 0x001080U, 0x00407FU, 0x000400U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||||
|
/* $1000-$1FFF: unused */
|
||||||
|
{ 0x004080U, 0x00807FU, 0x001000U, RC_MEMORY_TYPE_UNUSED, "" },
|
||||||
|
/* $2000-$2FFF: cartridge RAM */
|
||||||
|
{ 0x008080U, 0x00C07FU, 0x002000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||||
|
/* $3000-$3FFF: video RAM */
|
||||||
|
{ 0x00C080U, 0x01007FU, 0x003000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Video RAM" },
|
||||||
|
/* $4000-$FFFF: cartridge RAM */
|
||||||
|
{ 0x010080U, 0x04007FU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
|
||||||
};
|
};
|
||||||
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 10 };
|
||||||
|
|
||||||
/* ===== Magnavox Odyssey 2 ===== */
|
/* ===== Magnavox Odyssey 2 ===== */
|
||||||
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
|
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
|
||||||
|
@ -447,13 +473,13 @@ static const rc_memory_regions_t rc_memory_regions_pc8800 = { _rc_memory_regions
|
||||||
|
|
||||||
/* ===== PC Engine ===== */
|
/* ===== PC Engine ===== */
|
||||||
/* http://www.archaicpixels.com/Memory_Map */
|
/* http://www.archaicpixels.com/Memory_Map */
|
||||||
static const rc_memory_region_t _rc_memory_regions_pcengine[] = {
|
static const rc_memory_region_t _rc_memory_regions_pc_engine[] = {
|
||||||
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||||
{ 0x002000U, 0x011FFFU, 0x100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD RAM" },
|
{ 0x002000U, 0x011FFFU, 0x100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD RAM" },
|
||||||
{ 0x012000U, 0x041FFFU, 0x0D0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Super System Card RAM" },
|
{ 0x012000U, 0x041FFFU, 0x0D0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Super System Card RAM" },
|
||||||
{ 0x042000U, 0x0427FFU, 0x1EE000U, RC_MEMORY_TYPE_SAVE_RAM, "CD Battery-backed RAM" }
|
{ 0x042000U, 0x0427FFU, 0x1EE000U, RC_MEMORY_TYPE_SAVE_RAM, "CD Battery-backed RAM" }
|
||||||
};
|
};
|
||||||
static const rc_memory_regions_t rc_memory_regions_pcengine = { _rc_memory_regions_pcengine, 4 };
|
static const rc_memory_regions_t rc_memory_regions_pc_engine = { _rc_memory_regions_pc_engine, 4 };
|
||||||
|
|
||||||
/* ===== PC-FX ===== */
|
/* ===== PC-FX ===== */
|
||||||
/* http://daifukkat.su/pcfx/data/memmap.html */
|
/* http://daifukkat.su/pcfx/data/memmap.html */
|
||||||
|
@ -679,7 +705,7 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||||
return &rc_memory_regions_pc8800;
|
return &rc_memory_regions_pc8800;
|
||||||
|
|
||||||
case RC_CONSOLE_PC_ENGINE:
|
case RC_CONSOLE_PC_ENGINE:
|
||||||
return &rc_memory_regions_pcengine;
|
return &rc_memory_regions_pc_engine;
|
||||||
|
|
||||||
case RC_CONSOLE_PCFX:
|
case RC_CONSOLE_PCFX:
|
||||||
return &rc_memory_regions_pcfx;
|
return &rc_memory_regions_pcfx;
|
||||||
|
|
|
@ -29,10 +29,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||||
memaddr += 4;
|
memaddr += 4;
|
||||||
rc_parse_trigger_internal(&self->start, &memaddr, parse);
|
rc_parse_trigger_internal(&self->start, &memaddr, parse);
|
||||||
self->start.memrefs = 0;
|
self->start.memrefs = 0;
|
||||||
|
|
||||||
if (parse->offset < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ((memaddr[0] == 'c' || memaddr[0] == 'C') &&
|
else if ((memaddr[0] == 'c' || memaddr[0] == 'C') &&
|
||||||
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
||||||
|
@ -46,10 +42,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||||
memaddr += 4;
|
memaddr += 4;
|
||||||
rc_parse_trigger_internal(&self->cancel, &memaddr, parse);
|
rc_parse_trigger_internal(&self->cancel, &memaddr, parse);
|
||||||
self->cancel.memrefs = 0;
|
self->cancel.memrefs = 0;
|
||||||
|
|
||||||
if (parse->offset < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ((memaddr[0] == 's' || memaddr[0] == 'S') &&
|
else if ((memaddr[0] == 's' || memaddr[0] == 'S') &&
|
||||||
(memaddr[1] == 'u' || memaddr[1] == 'U') &&
|
(memaddr[1] == 'u' || memaddr[1] == 'U') &&
|
||||||
|
@ -63,10 +55,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||||
memaddr += 4;
|
memaddr += 4;
|
||||||
rc_parse_trigger_internal(&self->submit, &memaddr, parse);
|
rc_parse_trigger_internal(&self->submit, &memaddr, parse);
|
||||||
self->submit.memrefs = 0;
|
self->submit.memrefs = 0;
|
||||||
|
|
||||||
if (parse->offset < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ((memaddr[0] == 'v' || memaddr[0] == 'V') &&
|
else if ((memaddr[0] == 'v' || memaddr[0] == 'V') &&
|
||||||
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
(memaddr[1] == 'a' || memaddr[1] == 'A') &&
|
||||||
|
@ -80,10 +68,6 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||||
memaddr += 4;
|
memaddr += 4;
|
||||||
rc_parse_value_internal(&self->value, &memaddr, parse);
|
rc_parse_value_internal(&self->value, &memaddr, parse);
|
||||||
self->value.memrefs = 0;
|
self->value.memrefs = 0;
|
||||||
|
|
||||||
if (parse->offset < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ((memaddr[0] == 'p' || memaddr[0] == 'P') &&
|
else if ((memaddr[0] == 'p' || memaddr[0] == 'P') &&
|
||||||
(memaddr[1] == 'r' || memaddr[1] == 'R') &&
|
(memaddr[1] == 'r' || memaddr[1] == 'R') &&
|
||||||
|
@ -99,20 +83,22 @@ void rc_parse_lboard_internal(rc_lboard_t* self, const char* memaddr, rc_parse_s
|
||||||
self->progress = RC_ALLOC(rc_value_t, parse);
|
self->progress = RC_ALLOC(rc_value_t, parse);
|
||||||
rc_parse_value_internal(self->progress, &memaddr, parse);
|
rc_parse_value_internal(self->progress, &memaddr, parse);
|
||||||
self->progress->memrefs = 0;
|
self->progress->memrefs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (parse->offset < 0) {
|
/* encountered an error parsing one of the parts */
|
||||||
|
if (parse->offset < 0)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
/* end of string, or end of quoted string - stop processing */
|
||||||
else {
|
if (memaddr[0] == '\0' || memaddr[0] == '\"')
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* expect two colons between fields */
|
||||||
|
if (memaddr[0] != ':' || memaddr[1] != ':') {
|
||||||
parse->offset = RC_INVALID_LBOARD_FIELD;
|
parse->offset = RC_INVALID_LBOARD_FIELD;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memaddr[0] != ':' || memaddr[1] != ':') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
memaddr += 2;
|
memaddr += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,11 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||||
aux++;
|
aux++;
|
||||||
|
|
||||||
switch (*aux++) {
|
switch (*aux++) {
|
||||||
|
/* ordered by estimated frequency in case compiler doesn't build a jump table */
|
||||||
|
case 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
|
||||||
|
case ' ': *size = RC_MEMSIZE_16_BITS; break;
|
||||||
|
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
|
||||||
|
|
||||||
case 'm': case 'M': *size = RC_MEMSIZE_BIT_0; break;
|
case 'm': case 'M': *size = RC_MEMSIZE_BIT_0; break;
|
||||||
case 'n': case 'N': *size = RC_MEMSIZE_BIT_1; break;
|
case 'n': case 'N': *size = RC_MEMSIZE_BIT_1; break;
|
||||||
case 'o': case 'O': *size = RC_MEMSIZE_BIT_2; break;
|
case 'o': case 'O': *size = RC_MEMSIZE_BIT_2; break;
|
||||||
|
@ -64,17 +69,21 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||||
case 'l': case 'L': *size = RC_MEMSIZE_LOW; break;
|
case 'l': case 'L': *size = RC_MEMSIZE_LOW; break;
|
||||||
case 'u': case 'U': *size = RC_MEMSIZE_HIGH; break;
|
case 'u': case 'U': *size = RC_MEMSIZE_HIGH; break;
|
||||||
case 'k': case 'K': *size = RC_MEMSIZE_BITCOUNT; break;
|
case 'k': case 'K': *size = RC_MEMSIZE_BITCOUNT; break;
|
||||||
case 'h': case 'H': *size = RC_MEMSIZE_8_BITS; break;
|
|
||||||
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
|
case 'w': case 'W': *size = RC_MEMSIZE_24_BITS; break;
|
||||||
case 'x': case 'X': *size = RC_MEMSIZE_32_BITS; break;
|
case 'g': case 'G': *size = RC_MEMSIZE_32_BITS_BE; break;
|
||||||
|
case 'i': case 'I': *size = RC_MEMSIZE_16_BITS_BE; break;
|
||||||
|
case 'j': case 'J': *size = RC_MEMSIZE_24_BITS_BE; break;
|
||||||
|
|
||||||
|
/* case 'v': case 'V': */
|
||||||
|
/* case 'y': case 'Y': 64 bit? */
|
||||||
|
/* case 'z': case 'Z': 128 bit? */
|
||||||
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
|
/* legacy support - addresses without a size prefix are assumed to be 16-bit */
|
||||||
aux--;
|
aux--;
|
||||||
/* fallthrough */
|
|
||||||
case ' ':
|
|
||||||
*size = RC_MEMSIZE_16_BITS;
|
*size = RC_MEMSIZE_16_BITS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -97,10 +106,24 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||||
|
|
||||||
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 unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||||
|
|
||||||
unsigned rc_transform_memref_value(unsigned value, char size)
|
unsigned rc_transform_memref_value(unsigned value, char size) {
|
||||||
{
|
|
||||||
switch (size)
|
switch (size)
|
||||||
{
|
{
|
||||||
|
case RC_MEMSIZE_8_BITS:
|
||||||
|
value = (value & 0x000000ff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_16_BITS:
|
||||||
|
value = (value & 0x0000ffff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_24_BITS:
|
||||||
|
value = (value & 0x00ffffff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_32_BITS:
|
||||||
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_BIT_0:
|
case RC_MEMSIZE_BIT_0:
|
||||||
value = (value >> 0) & 1;
|
value = (value >> 0) & 1;
|
||||||
break;
|
break;
|
||||||
|
@ -146,6 +169,24 @@ unsigned rc_transform_memref_value(unsigned value, char size)
|
||||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_16_BITS_BE:
|
||||||
|
value = ((value & 0xFF00) >> 8) |
|
||||||
|
((value & 0x00FF) << 8);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_24_BITS_BE:
|
||||||
|
value = ((value & 0xFF0000) >> 16) |
|
||||||
|
(value & 0x00FF00) |
|
||||||
|
((value & 0x0000FF) << 16);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MEMSIZE_32_BITS_BE:
|
||||||
|
value = ((value & 0xFF000000) >> 24) |
|
||||||
|
((value & 0x00FF0000) >> 8) |
|
||||||
|
((value & 0x0000FF00) << 8) |
|
||||||
|
((value & 0x000000FF) << 24);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -153,35 +194,48 @@ unsigned rc_transform_memref_value(unsigned value, char size)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
char rc_memref_shared_size(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
|
||||||
switch (size) {
|
* mapped to the little-endian read of the same size. */
|
||||||
case RC_MEMSIZE_BIT_0:
|
static const char rc_memref_shared_sizes[] = {
|
||||||
case RC_MEMSIZE_BIT_1:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_8_BITS */
|
||||||
case RC_MEMSIZE_BIT_2:
|
RC_MEMSIZE_16_BITS, /* RC_MEMSIZE_16_BITS */
|
||||||
case RC_MEMSIZE_BIT_3:
|
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_24_BITS */
|
||||||
case RC_MEMSIZE_BIT_4:
|
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_32_BITS */
|
||||||
case RC_MEMSIZE_BIT_5:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_LOW */
|
||||||
case RC_MEMSIZE_BIT_6:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_HIGH */
|
||||||
case RC_MEMSIZE_BIT_7:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_0 */
|
||||||
case RC_MEMSIZE_LOW:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_1 */
|
||||||
case RC_MEMSIZE_HIGH:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_2 */
|
||||||
case RC_MEMSIZE_BITCOUNT:
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_3 */
|
||||||
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_transform_memref_value */
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_4 */
|
||||||
return RC_MEMSIZE_8_BITS;
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_5 */
|
||||||
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_6 */
|
||||||
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BIT_7 */
|
||||||
|
RC_MEMSIZE_8_BITS, /* RC_MEMSIZE_BITCOUNT */
|
||||||
|
RC_MEMSIZE_16_BITS, /* RC_MEMSIZE_16_BITS_BE */
|
||||||
|
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_24_BITS_BE */
|
||||||
|
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_32_BITS_BE */
|
||||||
|
RC_MEMSIZE_32_BITS /* RC_MEMSIZE_VARIABLE */
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
char rc_memref_shared_size(char size) {
|
||||||
|
const size_t index = (size_t)size;
|
||||||
|
if (index >= sizeof(rc_memref_shared_sizes) / sizeof(rc_memref_shared_sizes[0]))
|
||||||
return size;
|
return size;
|
||||||
}
|
|
||||||
|
return rc_memref_shared_sizes[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
||||||
unsigned value;
|
unsigned value;
|
||||||
|
char shared_size;
|
||||||
|
|
||||||
if (!peek)
|
if (!peek)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (size)
|
shared_size = rc_memref_shared_size(size);
|
||||||
|
switch (shared_size)
|
||||||
{
|
{
|
||||||
case RC_MEMSIZE_8_BITS:
|
case RC_MEMSIZE_8_BITS:
|
||||||
value = peek(address, 1, ud);
|
value = peek(address, 1, ud);
|
||||||
|
@ -191,27 +245,16 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
|
||||||
value = peek(address, 2, ud);
|
value = peek(address, 2, ud);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_24_BITS:
|
|
||||||
/* peek 4 bytes - don't expect the caller to understand 24-bit numbers */
|
|
||||||
value = peek(address, 4, ud) & 0x00FFFFFF;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RC_MEMSIZE_32_BITS:
|
case RC_MEMSIZE_32_BITS:
|
||||||
value = peek(address, 4, ud);
|
value = peek(address, 4, ud);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
|
return 0;
|
||||||
{
|
}
|
||||||
value = peek(address, 1, ud);
|
|
||||||
|
if (shared_size != size)
|
||||||
value = rc_transform_memref_value(value, size);
|
value = rc_transform_memref_value(value, size);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -242,17 +285,7 @@ void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_t** memrefs)
|
||||||
*memrefs = 0;
|
*memrefs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state) {
|
static unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type) {
|
||||||
/* if this is an indirect reference, handle the indirection. */
|
|
||||||
if (memref->value.is_indirect) {
|
|
||||||
const unsigned 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc_get_memref_value_value(&memref->value, operand_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type) {
|
|
||||||
switch (operand_type)
|
switch (operand_type)
|
||||||
{
|
{
|
||||||
/* most common case explicitly first, even though it could be handled by default case.
|
/* most common case explicitly first, even though it could be handled by default case.
|
||||||
|
@ -271,3 +304,13 @@ unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type)
|
||||||
return memref->prior;
|
return memref->prior;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned 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;
|
||||||
|
rc_update_memref_value(&memref->value, rc_peek_value(new_address, memref->value.size, eval_state->peek, eval_state->peek_userdata));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc_get_memref_value_value(&memref->value, operand_type);
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||||
return RC_INVALID_LUA_OPERAND;
|
return RC_INVALID_LUA_OPERAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isalpha(*aux)) {
|
if (!isalpha((unsigned char)*aux)) {
|
||||||
return RC_INVALID_LUA_OPERAND;
|
return RC_INVALID_LUA_OPERAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||||
id = aux;
|
id = aux;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (isalnum(*aux) || *aux == '_') {
|
while (isalnum((unsigned char)*aux) || *aux == '_') {
|
||||||
aux++;
|
aux++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
size = rc_memref_shared_size(self->size);
|
size = rc_memref_shared_size(self->size);
|
||||||
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
|
self->value.memref = rc_alloc_memref(parse, address, size, (char)is_indirect);
|
||||||
if (parse->offset < 0)
|
if (parse->offset < 0)
|
||||||
return parse->offset;
|
return parse->offset;
|
||||||
|
|
||||||
|
@ -111,12 +111,13 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||||
return RC_OK;
|
return RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse) {
|
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
|
||||||
const char* aux = *memaddr;
|
const char* aux = *memaddr;
|
||||||
char* end;
|
char* end;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
int negative;
|
int negative;
|
||||||
|
int allow_decimal = 0;
|
||||||
|
|
||||||
self->size = RC_MEMSIZE_32_BITS;
|
self->size = RC_MEMSIZE_32_BITS;
|
||||||
|
|
||||||
|
@ -128,14 +129,11 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
}
|
}
|
||||||
|
|
||||||
value = strtoul(++aux, &end, 16);
|
value = strtoul(++aux, &end, 16);
|
||||||
|
if (end == aux)
|
||||||
if (end == aux) {
|
|
||||||
return RC_INVALID_CONST_OPERAND;
|
return RC_INVALID_CONST_OPERAND;
|
||||||
}
|
|
||||||
|
|
||||||
if (value > 0xffffffffU) {
|
if (value > 0xffffffffU)
|
||||||
value = 0xffffffffU;
|
value = 0xffffffffU;
|
||||||
}
|
|
||||||
|
|
||||||
self->type = RC_OPERAND_CONST;
|
self->type = RC_OPERAND_CONST;
|
||||||
self->value.num = (unsigned)value;
|
self->value.num = (unsigned)value;
|
||||||
|
@ -144,47 +142,61 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'f': case 'F': /* floating point constant */
|
case 'f': case 'F': /* floating point constant */
|
||||||
self->value.dbl = strtod(++aux, &end);
|
allow_decimal = 1;
|
||||||
|
/* fall through */
|
||||||
if (end == aux) {
|
|
||||||
return RC_INVALID_FP_OPERAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (floor(self->value.dbl) == self->value.dbl) {
|
|
||||||
self->type = RC_OPERAND_CONST;
|
|
||||||
self->value.num = (unsigned)floor(self->value.dbl);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self->type = RC_OPERAND_FP;
|
|
||||||
}
|
|
||||||
|
|
||||||
aux = end;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'v': case 'V': /* signed integer constant */
|
case 'v': case 'V': /* signed integer constant */
|
||||||
++aux;
|
++aux;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case '+': case '-': /* signed integer constant */
|
case '+': case '-': /* signed integer constant */
|
||||||
negative = 0;
|
negative = 0;
|
||||||
if (*aux == '-')
|
if (*aux == '-') {
|
||||||
{
|
|
||||||
negative = 1;
|
negative = 1;
|
||||||
++aux;
|
++aux;
|
||||||
}
|
}
|
||||||
else if (*aux == '+')
|
else if (*aux == '+') {
|
||||||
{
|
|
||||||
++aux;
|
++aux;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = strtoul(aux, &end, 10);
|
value = strtoul(aux, &end, 10);
|
||||||
|
|
||||||
if (end == aux) {
|
if (*end == '.' && allow_decimal) {
|
||||||
return RC_INVALID_CONST_OPERAND;
|
/* custom parser for decimal values to ignore locale */
|
||||||
|
unsigned long shift = 1;
|
||||||
|
unsigned long fraction = 0;
|
||||||
|
|
||||||
|
aux = end + 1;
|
||||||
|
if (*aux < '0' || *aux > '9')
|
||||||
|
return RC_INVALID_FP_OPERAND;
|
||||||
|
|
||||||
|
do {
|
||||||
|
fraction *= 10;
|
||||||
|
fraction += (*aux - '0');
|
||||||
|
shift *= 10;
|
||||||
|
++aux;
|
||||||
|
} while (*aux >= '0' && *aux <= '9');
|
||||||
|
|
||||||
|
/* if fractional part is 0, convert to an integer constant */
|
||||||
|
if (fraction != 0) {
|
||||||
|
/* non-zero fractional part, convert to double and merge in integer portion */
|
||||||
|
const double dbl_fraction = ((double)fraction) / ((double)shift);
|
||||||
|
if (negative)
|
||||||
|
self->value.dbl = ((double)(-((long)value))) - dbl_fraction;
|
||||||
|
else
|
||||||
|
self->value.dbl = (double)value + dbl_fraction;
|
||||||
|
|
||||||
|
self->type = RC_OPERAND_FP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* not a floating point value, make sure something was read and advance the read pointer */
|
||||||
|
if (end == aux)
|
||||||
|
return allow_decimal ? RC_INVALID_FP_OPERAND : RC_INVALID_CONST_OPERAND;
|
||||||
|
|
||||||
|
aux = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value > 0x7fffffffU) {
|
if (value > 0x7fffffffU)
|
||||||
value = 0x7fffffffU;
|
value = 0x7fffffffU;
|
||||||
}
|
|
||||||
|
|
||||||
self->type = RC_OPERAND_CONST;
|
self->type = RC_OPERAND_CONST;
|
||||||
|
|
||||||
|
@ -193,7 +205,6 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
else
|
else
|
||||||
self->value.num = (unsigned)value;
|
self->value.num = (unsigned)value;
|
||||||
|
|
||||||
aux = end;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '0':
|
case '0':
|
||||||
|
@ -202,9 +213,8 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
default:
|
default:
|
||||||
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -213,14 +223,11 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
|
case '1': case '2': case '3': case '4': case '5': /* unsigned integer constant */
|
||||||
case '6': case '7': case '8': case '9':
|
case '6': case '7': case '8': case '9':
|
||||||
value = strtoul(aux, &end, 10);
|
value = strtoul(aux, &end, 10);
|
||||||
|
if (end == aux)
|
||||||
if (end == aux) {
|
|
||||||
return RC_INVALID_CONST_OPERAND;
|
return RC_INVALID_CONST_OPERAND;
|
||||||
}
|
|
||||||
|
|
||||||
if (value > 0xffffffffU) {
|
if (value > 0xffffffffU)
|
||||||
value = 0xffffffffU;
|
value = 0xffffffffU;
|
||||||
}
|
|
||||||
|
|
||||||
self->type = RC_OPERAND_CONST;
|
self->type = RC_OPERAND_CONST;
|
||||||
self->value.num = (unsigned)value;
|
self->value.num = (unsigned)value;
|
||||||
|
@ -231,9 +238,8 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, i
|
||||||
case '@':
|
case '@':
|
||||||
ret = rc_parse_operand_lua(self, &aux, parse);
|
ret = rc_parse_operand_lua(self, &aux, parse);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -340,6 +346,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_16_BITS:
|
case RC_MEMSIZE_16_BITS:
|
||||||
|
case RC_MEMSIZE_16_BITS_BE:
|
||||||
value = ((value >> 12) & 0x0f) * 1000
|
value = ((value >> 12) & 0x0f) * 1000
|
||||||
+ ((value >> 8) & 0x0f) * 100
|
+ ((value >> 8) & 0x0f) * 100
|
||||||
+ ((value >> 4) & 0x0f) * 10
|
+ ((value >> 4) & 0x0f) * 10
|
||||||
|
@ -347,6 +354,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_24_BITS:
|
case RC_MEMSIZE_24_BITS:
|
||||||
|
case RC_MEMSIZE_24_BITS_BE:
|
||||||
value = ((value >> 20) & 0x0f) * 100000
|
value = ((value >> 20) & 0x0f) * 100000
|
||||||
+ ((value >> 16) & 0x0f) * 10000
|
+ ((value >> 16) & 0x0f) * 10000
|
||||||
+ ((value >> 12) & 0x0f) * 1000
|
+ ((value >> 12) & 0x0f) * 1000
|
||||||
|
@ -356,6 +364,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_32_BITS:
|
case RC_MEMSIZE_32_BITS:
|
||||||
|
case RC_MEMSIZE_32_BITS_BE:
|
||||||
case RC_MEMSIZE_VARIABLE:
|
case RC_MEMSIZE_VARIABLE:
|
||||||
value = ((value >> 28) & 0x0f) * 10000000
|
value = ((value >> 28) & 0x0f) * 10000000
|
||||||
+ ((value >> 24) & 0x0f) * 1000000
|
+ ((value >> 24) & 0x0f) * 1000000
|
||||||
|
@ -385,14 +394,17 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_16_BITS:
|
case RC_MEMSIZE_16_BITS:
|
||||||
|
case RC_MEMSIZE_16_BITS_BE:
|
||||||
value ^= 0xffff;
|
value ^= 0xffff;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_24_BITS:
|
case RC_MEMSIZE_24_BITS:
|
||||||
|
case RC_MEMSIZE_24_BITS_BE:
|
||||||
value ^= 0xffffff;
|
value ^= 0xffffff;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RC_MEMSIZE_32_BITS:
|
case RC_MEMSIZE_32_BITS:
|
||||||
|
case RC_MEMSIZE_32_BITS_BE:
|
||||||
case RC_MEMSIZE_VARIABLE:
|
case RC_MEMSIZE_VARIABLE:
|
||||||
value ^= 0xffffffff;
|
value ^= 0xffffffff;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -99,6 +99,7 @@ typedef struct {
|
||||||
int lines_read;
|
int lines_read;
|
||||||
|
|
||||||
char has_required_hits;
|
char has_required_hits;
|
||||||
|
char measured_as_percent;
|
||||||
}
|
}
|
||||||
rc_parse_state_t;
|
rc_parse_state_t;
|
||||||
|
|
||||||
|
@ -116,7 +117,6 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address);
|
||||||
void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
|
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);
|
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);
|
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
||||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
|
|
||||||
char rc_memref_shared_size(char size);
|
char rc_memref_shared_size(char size);
|
||||||
unsigned rc_transform_memref_value(unsigned value, char size);
|
unsigned rc_transform_memref_value(unsigned value, char size);
|
||||||
|
|
||||||
|
@ -131,9 +131,8 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
|
||||||
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||||
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_state);
|
||||||
|
|
||||||
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse);
|
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse);
|
||||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
|
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
|
||||||
char rc_parse_operator(const char** memaddr);
|
|
||||||
|
|
||||||
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||||
void rc_reset_value(rc_value_t* self);
|
void rc_reset_value(rc_value_t* self);
|
||||||
|
|
|
@ -383,7 +383,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
|
||||||
base = 10;
|
base = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
first = strtoul(line, &endptr, base);
|
first = (unsigned)strtoul(line, &endptr, base);
|
||||||
|
|
||||||
/* check for a range */
|
/* check for a range */
|
||||||
if (*endptr != '-') {
|
if (*endptr != '-') {
|
||||||
|
@ -401,7 +401,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
|
||||||
base = 10;
|
base = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
last = strtoul(line, &endptr, base);
|
last = (unsigned)strtoul(line, &endptr, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ignore spaces after the number - was previously ignored as string was split on equals */
|
/* ignore spaces after the number - was previously ignored as string was split on equals */
|
||||||
|
@ -493,7 +493,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
|
||||||
memcpy(format, line, chars);
|
memcpy(format, line, chars);
|
||||||
format[chars] = '\0';
|
format[chars] = '\0';
|
||||||
|
|
||||||
lookup->format = rc_parse_format(format);
|
lookup->format = (unsigned short)rc_parse_format(format);
|
||||||
} else {
|
} else {
|
||||||
lookup->format = RC_FORMAT_VALUE;
|
lookup->format = RC_FORMAT_VALUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "rc_runtime.h"
|
#include "rc_runtime.h"
|
||||||
#include "rc_internal.h"
|
#include "rc_internal.h"
|
||||||
|
#include "rc_compat.h"
|
||||||
|
|
||||||
#include "../rhash/md5.h"
|
#include "../rhash/md5.h"
|
||||||
|
|
||||||
|
@ -241,6 +242,33 @@ int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, unsigned id, char* buffer, size_t buffer_size)
|
||||||
|
{
|
||||||
|
const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
|
||||||
|
unsigned value;
|
||||||
|
if (!buffer || !buffer_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!trigger || /* no trigger */
|
||||||
|
trigger->measured_target == 0 || /* not measured */
|
||||||
|
!rc_trigger_state_active(trigger->state)) { /* don't report measured value for inactive triggers */
|
||||||
|
*buffer = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cap the value at the target so we can count past the target: "107 >= 100" */
|
||||||
|
value = trigger->measured_value;
|
||||||
|
if (value > trigger->measured_target)
|
||||||
|
value = trigger->measured_target;
|
||||||
|
|
||||||
|
if (trigger->measured_as_percent) {
|
||||||
|
unsigned percent = (unsigned)(((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, unsigned index) {
|
||||||
if (self->lboards[index].owns_memrefs) {
|
if (self->lboards[index].owns_memrefs) {
|
||||||
/* if the lboard has one or more memrefs in its buffer, we can't free the buffer.
|
/* if the lboard has one or more memrefs in its buffer, we can't free the buffer.
|
||||||
|
@ -507,7 +535,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||||
if (new_state == old_state)
|
if (new_state == old_state)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* raise an UNPRIMED event when changing from UNPRIMED to anything else */
|
/* raise an UNPRIMED event when changing from PRIMED to anything else */
|
||||||
if (old_state == RC_TRIGGER_STATE_PRIMED) {
|
if (old_state == RC_TRIGGER_STATE_PRIMED) {
|
||||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
|
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
|
||||||
runtime_event.id = self->triggers[i].id;
|
runtime_event.id = self->triggers[i].id;
|
||||||
|
|
|
@ -247,7 +247,7 @@ static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_
|
||||||
rc_condition_t* cond;
|
rc_condition_t* cond;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
|
|
||||||
condset->is_paused = rc_runtime_progress_read_uint(progress);
|
condset->is_paused = (char)rc_runtime_progress_read_uint(progress);
|
||||||
|
|
||||||
cond = condset->conditions;
|
cond = condset->conditions;
|
||||||
while (cond) {
|
while (cond) {
|
||||||
|
@ -257,12 +257,18 @@ static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_
|
||||||
cond->is_true = (flags & RC_COND_FLAG_IS_TRUE) ? 1 : 0;
|
cond->is_true = (flags & RC_COND_FLAG_IS_TRUE) ? 1 : 0;
|
||||||
|
|
||||||
if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
|
if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
|
||||||
|
if (!rc_operand_is_memref(&cond->operand1)) /* this should never happen, but better safe than sorry */
|
||||||
|
return RC_INVALID_STATE;
|
||||||
|
|
||||||
cond->operand1.value.memref->value.value = rc_runtime_progress_read_uint(progress);
|
cond->operand1.value.memref->value.value = rc_runtime_progress_read_uint(progress);
|
||||||
cond->operand1.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
|
cond->operand1.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
|
||||||
cond->operand1.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
|
cond->operand1.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
|
if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
|
||||||
|
if (!rc_operand_is_memref(&cond->operand2)) /* this should never happen, but better safe than sorry */
|
||||||
|
return RC_INVALID_STATE;
|
||||||
|
|
||||||
cond->operand2.value.memref->value.value = rc_runtime_progress_read_uint(progress);
|
cond->operand2.value.memref->value.value = rc_runtime_progress_read_uint(progress);
|
||||||
cond->operand2.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
|
cond->operand2.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
|
||||||
cond->operand2.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
|
cond->operand2.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
|
||||||
|
@ -305,7 +311,7 @@ static int rc_runtime_progress_read_trigger(rc_runtime_progress_t* progress, rc_
|
||||||
rc_condset_t* condset;
|
rc_condset_t* condset;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
trigger->state = rc_runtime_progress_read_uint(progress);
|
trigger->state = (char)rc_runtime_progress_read_uint(progress);
|
||||||
trigger->measured_value = rc_runtime_progress_read_uint(progress);
|
trigger->measured_value = rc_runtime_progress_read_uint(progress);
|
||||||
|
|
||||||
if (trigger->requirement) {
|
if (trigger->requirement) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
|
||||||
/* reset in case multiple triggers are parsed by the same parse_state */
|
/* reset in case multiple triggers are parsed by the same parse_state */
|
||||||
parse->measured_target = 0;
|
parse->measured_target = 0;
|
||||||
parse->has_required_hits = 0;
|
parse->has_required_hits = 0;
|
||||||
|
parse->measured_as_percent = 0;
|
||||||
|
|
||||||
if (*aux == 's' || *aux == 'S') {
|
if (*aux == 's' || *aux == 'S') {
|
||||||
self->requirement = 0;
|
self->requirement = 0;
|
||||||
|
@ -43,6 +44,7 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
|
||||||
|
|
||||||
self->measured_value = 0;
|
self->measured_value = 0;
|
||||||
self->measured_target = parse->measured_target;
|
self->measured_target = parse->measured_target;
|
||||||
|
self->measured_as_percent = parse->measured_as_percent;
|
||||||
self->state = RC_TRIGGER_STATE_WAITING;
|
self->state = RC_TRIGGER_STATE_WAITING;
|
||||||
self->has_hits = 0;
|
self->has_hits = 0;
|
||||||
self->has_required_hits = parse->has_required_hits;
|
self->has_required_hits = parse->has_required_hits;
|
||||||
|
|
|
@ -72,17 +72,14 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
|
||||||
*ptr++ = '*';
|
*ptr++ = '*';
|
||||||
|
|
||||||
buffer_ptr = *memaddr + 1;
|
buffer_ptr = *memaddr + 1;
|
||||||
if (*buffer_ptr == '-') {
|
if (*buffer_ptr == '-' || *buffer_ptr == '+')
|
||||||
/* negative value automatically needs prefix, 'f' handles both float and digits, so use it */
|
++buffer_ptr; /* ignore sign */
|
||||||
*ptr++ = 'f';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* if it looks like a floating point number, add the 'f' prefix */
|
/* if it looks like a floating point number, add the 'f' prefix */
|
||||||
while (isdigit(*(unsigned char*)buffer_ptr))
|
while (isdigit((unsigned char)*buffer_ptr))
|
||||||
++buffer_ptr;
|
++buffer_ptr;
|
||||||
if (*buffer_ptr == '.')
|
if (*buffer_ptr == '.')
|
||||||
*ptr++ = 'f';
|
*ptr++ = 'f';
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -95,7 +92,12 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
|
||||||
|
|
||||||
buffer_ptr = buffer;
|
buffer_ptr = buffer;
|
||||||
cond = rc_parse_condition(&buffer_ptr, parse, 0);
|
cond = rc_parse_condition(&buffer_ptr, parse, 0);
|
||||||
if (parse->offset < 0) {
|
if (parse->offset < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (*buffer_ptr) {
|
||||||
|
/* whatever we copied as a single condition was not fully consumed */
|
||||||
|
parse->offset = RC_INVALID_COMPARISON;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -324,7 +324,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculate the true offset and update the counters for the next INDEX marker */
|
/* calculate the true offset and update the counters for the next INDEX marker */
|
||||||
offset += sector_offset * previous_sector_size;
|
offset += (int64_t)sector_offset * previous_sector_size;
|
||||||
previous_sector_size = sector_size;
|
previous_sector_size = sector_size;
|
||||||
previous_index_sector_offset += sector_offset;
|
previous_index_sector_offset += sector_offset;
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||||
{
|
{
|
||||||
char message[128];
|
char message[128];
|
||||||
char* scan = mode;
|
char* scan = mode;
|
||||||
while (*scan && !isspace(*scan))
|
while (*scan && !isspace((unsigned char)*scan))
|
||||||
++scan;
|
++scan;
|
||||||
*scan = '\0';
|
*scan = '\0';
|
||||||
|
|
||||||
|
@ -589,43 +589,43 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
/* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
|
/* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
current_track = (uint32_t)atoi(ptr);
|
current_track = (uint32_t)atoi(ptr);
|
||||||
if (track && current_track != track)
|
if (track && current_track != track)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
while (isdigit(*ptr))
|
while (isdigit((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
lba = atoi(ptr);
|
lba = atoi(ptr);
|
||||||
while (isdigit(*ptr))
|
while (isdigit((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
track_type = atoi(ptr);
|
track_type = atoi(ptr);
|
||||||
while (isdigit(*ptr))
|
while (isdigit((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
ptr2 = sector_size;
|
ptr2 = sector_size;
|
||||||
while (isdigit(*ptr))
|
while (isdigit((unsigned char)*ptr))
|
||||||
*ptr2++ = *ptr++;
|
*ptr2++ = *ptr++;
|
||||||
*ptr2 = '\0';
|
*ptr2 = '\0';
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
ptr2 = file;
|
ptr2 = file;
|
||||||
|
|
|
@ -58,7 +58,6 @@ static void filereader_seek(void* file_handle, int64_t offset, int origin)
|
||||||
#elif defined(_LARGEFILE64_SOURCE)
|
#elif defined(_LARGEFILE64_SOURCE)
|
||||||
fseeko64((FILE*)file_handle, offset, origin);
|
fseeko64((FILE*)file_handle, offset, origin);
|
||||||
#else
|
#else
|
||||||
#pragma message("Using generic fseek may fail for large files")
|
|
||||||
fseek((FILE*)file_handle, offset, origin);
|
fseek((FILE*)file_handle, offset, origin);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -119,7 +118,11 @@ void* rc_file_open(const char* path)
|
||||||
void* handle;
|
void* handle;
|
||||||
|
|
||||||
if (!filereader)
|
if (!filereader)
|
||||||
|
{
|
||||||
rc_hash_init_custom_filereader(NULL);
|
rc_hash_init_custom_filereader(NULL);
|
||||||
|
if (!filereader)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
handle = filereader->open(path);
|
handle = filereader->open(path);
|
||||||
if (handle && verbose_message_callback)
|
if (handle && verbose_message_callback)
|
||||||
|
@ -361,7 +364,7 @@ static int rc_hash_finalize(md5_state_t* md5, char hash[33])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_buffer(char hash[33], uint8_t* buffer, size_t buffer_size)
|
static int rc_hash_buffer(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
md5_state_t md5;
|
md5_state_t md5;
|
||||||
md5_init(&md5);
|
md5_init(&md5);
|
||||||
|
@ -411,9 +414,9 @@ static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector
|
||||||
{
|
{
|
||||||
md5_append(md5, buffer, (int)num_read);
|
md5_append(md5, buffer, (int)num_read);
|
||||||
|
|
||||||
size -= (unsigned)num_read;
|
if (size <= (unsigned)num_read)
|
||||||
if (size == 0)
|
|
||||||
break;
|
break;
|
||||||
|
size -= (unsigned)num_read;
|
||||||
|
|
||||||
++sector;
|
++sector;
|
||||||
if (size >= sizeof(buffer))
|
if (size >= sizeof(buffer))
|
||||||
|
@ -495,7 +498,7 @@ static int rc_hash_3do(char hash[33], const char* path)
|
||||||
block_location *= block_size;
|
block_location *= block_size;
|
||||||
|
|
||||||
/* the file size is at offset 0x10 (assume 0x10 is always 0) */
|
/* the file size is at offset 0x10 (assume 0x10 is always 0) */
|
||||||
size = buffer[offset + 0x11] * 65536 + buffer[offset + 0x12] * 256 + buffer[offset + 0x13];
|
size = (size_t)buffer[offset + 0x11] * 65536 + buffer[offset + 0x12] * 256 + buffer[offset + 0x13];
|
||||||
|
|
||||||
if (verbose_message_callback)
|
if (verbose_message_callback)
|
||||||
{
|
{
|
||||||
|
@ -558,7 +561,7 @@ static int rc_hash_3do(char hash[33], const char* path)
|
||||||
return rc_hash_finalize(&md5, hash);
|
return rc_hash_finalize(&md5, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_7800(char hash[33], uint8_t* buffer, size_t buffer_size)
|
static int rc_hash_7800(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
/* if the file contains a header, ignore it */
|
/* if the file contains a header, ignore it */
|
||||||
if (memcmp(&buffer[1], "ATARI7800", 9) == 0)
|
if (memcmp(&buffer[1], "ATARI7800", 9) == 0)
|
||||||
|
@ -645,7 +648,7 @@ static int rc_hash_arcade(char hash[33], const char* path)
|
||||||
return rc_hash_buffer(hash, (uint8_t*)filename, filename_length);
|
return rc_hash_buffer(hash, (uint8_t*)filename, filename_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_lynx(char hash[33], uint8_t* buffer, size_t buffer_size)
|
static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
/* if the file contains a header, ignore it */
|
/* if the file contains a header, ignore it */
|
||||||
if (buffer[0] == 'L' && buffer[1] == 'Y' && buffer[2] == 'N' && buffer[3] == 'X' && buffer[4] == 0)
|
if (buffer[0] == 'L' && buffer[1] == 'Y' && buffer[2] == 'N' && buffer[3] == 'X' && buffer[4] == 0)
|
||||||
|
@ -659,7 +662,7 @@ static int rc_hash_lynx(char hash[33], uint8_t* buffer, size_t buffer_size)
|
||||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_nes(char hash[33], uint8_t* buffer, size_t buffer_size)
|
static int rc_hash_nes(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
/* if the file contains a header, ignore it */
|
/* if the file contains a header, ignore it */
|
||||||
if (buffer[0] == 'N' && buffer[1] == 'E' && buffer[2] == 'S' && buffer[3] == 0x1A)
|
if (buffer[0] == 'N' && buffer[1] == 'E' && buffer[2] == 'S' && buffer[3] == 0x1A)
|
||||||
|
@ -680,6 +683,212 @@ static int rc_hash_nes(char hash[33], uint8_t* buffer, size_t buffer_size)
|
||||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rc_hash_v64_to_z64(uint8_t* buffer, const uint8_t* stop)
|
||||||
|
{
|
||||||
|
uint32_t* ptr = (uint32_t*)buffer;
|
||||||
|
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||||
|
while (ptr < stop32)
|
||||||
|
{
|
||||||
|
uint32_t temp = *ptr;
|
||||||
|
temp = (temp & 0xFF00FF00) >> 8 |
|
||||||
|
(temp & 0x00FF00FF) << 8;
|
||||||
|
*ptr++ = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rc_hash_n64_to_z64(uint8_t* buffer, const uint8_t* stop)
|
||||||
|
{
|
||||||
|
uint32_t* ptr = (uint32_t*)buffer;
|
||||||
|
const uint32_t* stop32 = (const uint32_t*)stop;
|
||||||
|
while (ptr < stop32)
|
||||||
|
{
|
||||||
|
uint32_t temp = *ptr;
|
||||||
|
temp = (temp & 0xFF000000) >> 24 |
|
||||||
|
(temp & 0x00FF0000) >> 8 |
|
||||||
|
(temp & 0x0000FF00) << 8 |
|
||||||
|
(temp & 0x000000FF) << 24;
|
||||||
|
*ptr++ = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rc_hash_n64(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
|
{
|
||||||
|
uint8_t* swapbuffer;
|
||||||
|
uint8_t* stop;
|
||||||
|
const size_t swapbuffer_size = 65536;
|
||||||
|
md5_state_t md5;
|
||||||
|
size_t remaining;
|
||||||
|
int is_v64;
|
||||||
|
|
||||||
|
if (buffer[0] == 0x80) /* z64 format (big endian [native]) */
|
||||||
|
{
|
||||||
|
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||||
|
}
|
||||||
|
else if (buffer[0] == 0x37) /* v64 format (byteswapped) */
|
||||||
|
{
|
||||||
|
rc_hash_verbose("converting v64 to z64");
|
||||||
|
is_v64 = 1;
|
||||||
|
}
|
||||||
|
else if (buffer[0] == 0x40) /* n64 format (little endian) */
|
||||||
|
{
|
||||||
|
rc_hash_verbose("converting n64 to z64");
|
||||||
|
is_v64 = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc_hash_verbose("Not a Nintendo 64 ROM");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
swapbuffer = (uint8_t*)malloc(swapbuffer_size);
|
||||||
|
if (!swapbuffer)
|
||||||
|
return rc_hash_error("Could not allocate temporary buffer");
|
||||||
|
stop = swapbuffer + swapbuffer_size;
|
||||||
|
|
||||||
|
md5_init(&md5);
|
||||||
|
|
||||||
|
if (buffer_size > MAX_BUFFER_SIZE)
|
||||||
|
remaining = MAX_BUFFER_SIZE;
|
||||||
|
else
|
||||||
|
remaining = (size_t)buffer_size;
|
||||||
|
|
||||||
|
if (verbose_message_callback)
|
||||||
|
{
|
||||||
|
char message[64];
|
||||||
|
snprintf(message, sizeof(message), "Hashing %u bytes", (unsigned)remaining);
|
||||||
|
verbose_message_callback(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (remaining >= swapbuffer_size)
|
||||||
|
{
|
||||||
|
memcpy(swapbuffer, buffer, swapbuffer_size);
|
||||||
|
|
||||||
|
if (is_v64)
|
||||||
|
rc_hash_v64_to_z64(swapbuffer, stop);
|
||||||
|
else
|
||||||
|
rc_hash_n64_to_z64(swapbuffer, stop);
|
||||||
|
|
||||||
|
md5_append(&md5, swapbuffer, (int)swapbuffer_size);
|
||||||
|
buffer += swapbuffer_size;
|
||||||
|
remaining -= swapbuffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining > 0)
|
||||||
|
{
|
||||||
|
memcpy(swapbuffer, buffer, remaining);
|
||||||
|
|
||||||
|
stop = swapbuffer + remaining;
|
||||||
|
if (is_v64)
|
||||||
|
rc_hash_v64_to_z64(swapbuffer, stop);
|
||||||
|
else
|
||||||
|
rc_hash_n64_to_z64(swapbuffer, stop);
|
||||||
|
|
||||||
|
md5_append(&md5, swapbuffer, (int)remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(swapbuffer);
|
||||||
|
return rc_hash_finalize(&md5, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rc_hash_n64_file(char hash[33], const char* path)
|
||||||
|
{
|
||||||
|
uint8_t* buffer;
|
||||||
|
uint8_t* stop;
|
||||||
|
const size_t buffer_size = 65536;
|
||||||
|
md5_state_t md5;
|
||||||
|
size_t remaining;
|
||||||
|
void* file_handle;
|
||||||
|
int is_v64 = 0;
|
||||||
|
int is_n64 = 0;
|
||||||
|
|
||||||
|
file_handle = rc_file_open(path);
|
||||||
|
if (!file_handle)
|
||||||
|
return rc_hash_error("Could not open file");
|
||||||
|
|
||||||
|
buffer = (uint8_t*)malloc(buffer_size);
|
||||||
|
if (!buffer)
|
||||||
|
{
|
||||||
|
rc_file_close(file_handle);
|
||||||
|
return rc_hash_error("Could not allocate temporary buffer");
|
||||||
|
}
|
||||||
|
stop = buffer + buffer_size;
|
||||||
|
|
||||||
|
/* read first byte so we can detect endianness */
|
||||||
|
rc_file_seek(file_handle, 0, SEEK_SET);
|
||||||
|
rc_file_read(file_handle, buffer, 1);
|
||||||
|
|
||||||
|
if (buffer[0] == 0x80) /* z64 format (big endian [native]) */
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (buffer[0] == 0x37) /* v64 format (byteswapped) */
|
||||||
|
{
|
||||||
|
rc_hash_verbose("converting v64 to z64");
|
||||||
|
is_v64 = 1;
|
||||||
|
}
|
||||||
|
else if (buffer[0] == 0x40) /* n64 format (little endian) */
|
||||||
|
{
|
||||||
|
rc_hash_verbose("converting n64 to z64");
|
||||||
|
is_n64 = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
rc_file_close(file_handle);
|
||||||
|
|
||||||
|
rc_hash_verbose("Not a Nintendo 64 ROM");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate total file size */
|
||||||
|
rc_file_seek(file_handle, 0, SEEK_END);
|
||||||
|
remaining = (size_t)rc_file_tell(file_handle);
|
||||||
|
if (remaining > MAX_BUFFER_SIZE)
|
||||||
|
remaining = MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
|
if (verbose_message_callback)
|
||||||
|
{
|
||||||
|
char message[64];
|
||||||
|
snprintf(message, sizeof(message), "Hashing %u bytes", (unsigned)remaining);
|
||||||
|
verbose_message_callback(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* begin hashing */
|
||||||
|
md5_init(&md5);
|
||||||
|
|
||||||
|
rc_file_seek(file_handle, 0, SEEK_SET);
|
||||||
|
while (remaining >= buffer_size)
|
||||||
|
{
|
||||||
|
rc_file_read(file_handle, buffer, (int)buffer_size);
|
||||||
|
|
||||||
|
if (is_v64)
|
||||||
|
rc_hash_v64_to_z64(buffer, stop);
|
||||||
|
else if (is_n64)
|
||||||
|
rc_hash_n64_to_z64(buffer, stop);
|
||||||
|
|
||||||
|
md5_append(&md5, buffer, (int)buffer_size);
|
||||||
|
remaining -= buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining > 0)
|
||||||
|
{
|
||||||
|
rc_file_read(file_handle, buffer, (int)remaining);
|
||||||
|
|
||||||
|
stop = buffer + remaining;
|
||||||
|
if (is_v64)
|
||||||
|
rc_hash_v64_to_z64(buffer, stop);
|
||||||
|
else if (is_n64)
|
||||||
|
rc_hash_n64_to_z64(buffer, stop);
|
||||||
|
|
||||||
|
md5_append(&md5, buffer, (int)remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
rc_file_close(file_handle);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return rc_hash_finalize(&md5, hash);
|
||||||
|
}
|
||||||
|
|
||||||
static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
||||||
{
|
{
|
||||||
uint8_t header[512];
|
uint8_t header[512];
|
||||||
|
@ -791,7 +1000,7 @@ static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
||||||
return rc_hash_finalize(&md5, hash);
|
return rc_hash_finalize(&md5, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_pce(char hash[33], uint8_t* buffer, size_t buffer_size)
|
static int rc_hash_pce(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
/* if the file contains a header, ignore it (expect ROM data to be multiple of 128KB) */
|
/* if the file contains a header, ignore it (expect ROM data to be multiple of 128KB) */
|
||||||
uint32_t calc_size = ((uint32_t)buffer_size / 0x20000) * 0x20000;
|
uint32_t calc_size = ((uint32_t)buffer_size / 0x20000) * 0x20000;
|
||||||
|
@ -1034,7 +1243,7 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
|
||||||
/* the boot filename is 96 bytes into the meta information (https://mc.pp.se/dc/ip0000.bin.html) */
|
/* the boot filename is 96 bytes into the meta information (https://mc.pp.se/dc/ip0000.bin.html) */
|
||||||
/* remove whitespace from bootfile */
|
/* remove whitespace from bootfile */
|
||||||
i = 0;
|
i = 0;
|
||||||
while (!isspace(buffer[96 + i]) && i < 16)
|
while (!isspace((unsigned char)buffer[96 + i]) && i < 16)
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
/* sometimes boot file isn't present on meta information.
|
/* sometimes boot file isn't present on meta information.
|
||||||
|
@ -1096,18 +1305,19 @@ static int rc_hash_find_playstation_executable(void* track_handle, const char* b
|
||||||
size = (unsigned)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
|
size = (unsigned)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
|
||||||
buffer[size] = '\0';
|
buffer[size] = '\0';
|
||||||
|
|
||||||
|
sector = 0;
|
||||||
for (ptr = (char*)buffer; *ptr; ++ptr)
|
for (ptr = (char*)buffer; *ptr; ++ptr)
|
||||||
{
|
{
|
||||||
if (strncmp(ptr, boot_key, boot_key_len) == 0)
|
if (strncmp(ptr, boot_key, boot_key_len) == 0)
|
||||||
{
|
{
|
||||||
ptr += boot_key_len;
|
ptr += boot_key_len;
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
if (*ptr == '=')
|
if (*ptr == '=')
|
||||||
{
|
{
|
||||||
++ptr;
|
++ptr;
|
||||||
while (isspace(*ptr))
|
while (isspace((unsigned char)*ptr))
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
if (strncmp(ptr, cdrom_prefix, cdrom_prefix_len) == 0)
|
if (strncmp(ptr, cdrom_prefix, cdrom_prefix_len) == 0)
|
||||||
|
@ -1116,7 +1326,7 @@ static int rc_hash_find_playstation_executable(void* track_handle, const char* b
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
start = ptr;
|
start = ptr;
|
||||||
while (!isspace(*ptr) && *ptr != ';')
|
while (!isspace((unsigned char)*ptr) && *ptr != ';')
|
||||||
++ptr;
|
++ptr;
|
||||||
|
|
||||||
size = (unsigned)(ptr - start);
|
size = (unsigned)(ptr - start);
|
||||||
|
@ -1152,7 +1362,6 @@ static int rc_hash_psx(char hash[33], const char* path)
|
||||||
void* track_handle;
|
void* track_handle;
|
||||||
uint32_t sector;
|
uint32_t sector;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
size_t num_read;
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
md5_state_t md5;
|
md5_state_t md5;
|
||||||
|
|
||||||
|
@ -1172,7 +1381,7 @@ static int rc_hash_psx(char hash[33], const char* path)
|
||||||
{
|
{
|
||||||
rc_hash_error("Could not locate primary executable");
|
rc_hash_error("Could not locate primary executable");
|
||||||
}
|
}
|
||||||
else if ((num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer))) < sizeof(buffer))
|
else if (rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer)) < sizeof(buffer))
|
||||||
{
|
{
|
||||||
rc_hash_error("Could not read primary executable");
|
rc_hash_error("Could not read primary executable");
|
||||||
}
|
}
|
||||||
|
@ -1217,7 +1426,6 @@ static int rc_hash_ps2(char hash[33], const char* path)
|
||||||
void* track_handle;
|
void* track_handle;
|
||||||
uint32_t sector;
|
uint32_t sector;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
size_t num_read;
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
md5_state_t md5;
|
md5_state_t md5;
|
||||||
|
|
||||||
|
@ -1230,7 +1438,7 @@ static int rc_hash_ps2(char hash[33], const char* path)
|
||||||
{
|
{
|
||||||
rc_hash_error("Could not locate primary executable");
|
rc_hash_error("Could not locate primary executable");
|
||||||
}
|
}
|
||||||
else if ((num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer))) < sizeof(buffer))
|
else if (rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer)) < sizeof(buffer))
|
||||||
{
|
{
|
||||||
rc_hash_error("Could not read primary executable");
|
rc_hash_error("Could not read primary executable");
|
||||||
}
|
}
|
||||||
|
@ -1261,6 +1469,40 @@ static int rc_hash_ps2(char hash[33], const char* path)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rc_hash_psp(char hash[33], const char* path)
|
||||||
|
{
|
||||||
|
void* track_handle;
|
||||||
|
uint32_t sector;
|
||||||
|
unsigned size;
|
||||||
|
md5_state_t md5;
|
||||||
|
|
||||||
|
track_handle = rc_cd_open_track(path, 1);
|
||||||
|
if (!track_handle)
|
||||||
|
return rc_hash_error("Could not open track");
|
||||||
|
|
||||||
|
/* http://www.romhacking.net/forum/index.php?topic=30899.0
|
||||||
|
* PSP_GAME/PARAM.SFO contains key/value pairs identifying the game for the system (i.e. serial number,
|
||||||
|
* name, version). PSP_GAME/SYSDIR/EBOOT.BIN is the encrypted primary executable.
|
||||||
|
*/
|
||||||
|
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\PARAM.SFO", &size);
|
||||||
|
if (!sector)
|
||||||
|
return rc_hash_error("Not a PSP game disc");
|
||||||
|
|
||||||
|
md5_init(&md5);
|
||||||
|
if (!rc_hash_cd_file(&md5, track_handle, sector, NULL, size, "PSP_GAME\\PARAM.SFO"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\SYSDIR\\EBOOT.BIN", &size);
|
||||||
|
if (!sector)
|
||||||
|
return rc_hash_error("Could not find primary executable");
|
||||||
|
|
||||||
|
if (!rc_hash_cd_file(&md5, track_handle, sector, NULL, size, "PSP_GAME\\SYSDIR\\EBOOT.BIN"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rc_cd_close_track(track_handle);
|
||||||
|
return rc_hash_finalize(&md5, hash);
|
||||||
|
}
|
||||||
|
|
||||||
static int rc_hash_sega_cd(char hash[33], const char* path)
|
static int rc_hash_sega_cd(char hash[33], const char* path)
|
||||||
{
|
{
|
||||||
uint8_t buffer[512];
|
uint8_t buffer[512];
|
||||||
|
@ -1290,7 +1532,7 @@ static int rc_hash_sega_cd(char hash[33], const char* path)
|
||||||
return rc_hash_buffer(hash, buffer, sizeof(buffer));
|
return rc_hash_buffer(hash, buffer, sizeof(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_snes(char hash[33], uint8_t* buffer, size_t 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 */
|
/* if the file contains a header, ignore it */
|
||||||
uint32_t calc_size = ((uint32_t)buffer_size / 0x2000) * 0x2000;
|
uint32_t calc_size = ((uint32_t)buffer_size / 0x2000) * 0x2000;
|
||||||
|
@ -1305,7 +1547,7 @@ static int rc_hash_snes(char hash[33], uint8_t* buffer, size_t buffer_size)
|
||||||
return rc_hash_buffer(hash, buffer, buffer_size);
|
return rc_hash_buffer(hash, buffer, buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer, size_t buffer_size)
|
int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
switch (console_id)
|
switch (console_id)
|
||||||
{
|
{
|
||||||
|
@ -1330,7 +1572,6 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer,
|
||||||
case RC_CONSOLE_MEGA_DRIVE:
|
case RC_CONSOLE_MEGA_DRIVE:
|
||||||
case RC_CONSOLE_MSX:
|
case RC_CONSOLE_MSX:
|
||||||
case RC_CONSOLE_NEOGEO_POCKET:
|
case RC_CONSOLE_NEOGEO_POCKET:
|
||||||
case RC_CONSOLE_NINTENDO_64:
|
|
||||||
case RC_CONSOLE_ORIC:
|
case RC_CONSOLE_ORIC:
|
||||||
case RC_CONSOLE_PC8800:
|
case RC_CONSOLE_PC8800:
|
||||||
case RC_CONSOLE_POKEMON_MINI:
|
case RC_CONSOLE_POKEMON_MINI:
|
||||||
|
@ -1352,6 +1593,9 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer,
|
||||||
case RC_CONSOLE_NINTENDO:
|
case RC_CONSOLE_NINTENDO:
|
||||||
return rc_hash_nes(hash, buffer, buffer_size);
|
return rc_hash_nes(hash, buffer, buffer_size);
|
||||||
|
|
||||||
|
case RC_CONSOLE_NINTENDO_64:
|
||||||
|
return rc_hash_n64(hash, buffer, buffer_size);
|
||||||
|
|
||||||
case RC_CONSOLE_PC_ENGINE: /* NOTE: does not support PCEngine CD */
|
case RC_CONSOLE_PC_ENGINE: /* NOTE: does not support PCEngine CD */
|
||||||
return rc_hash_pce(hash, buffer, buffer_size);
|
return rc_hash_pce(hash, buffer, buffer_size);
|
||||||
|
|
||||||
|
@ -1360,7 +1604,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rc_hash_whole_file(char hash[33], int console_id, const char* path)
|
static int rc_hash_whole_file(char hash[33], const char* path)
|
||||||
{
|
{
|
||||||
md5_state_t md5;
|
md5_state_t md5;
|
||||||
uint8_t* buffer;
|
uint8_t* buffer;
|
||||||
|
@ -1525,7 +1769,7 @@ static const char* rc_hash_get_first_item_from_playlist(const char* path)
|
||||||
next = ptr;
|
next = ptr;
|
||||||
|
|
||||||
/* remove trailing whitespace - especially '\r' */
|
/* remove trailing whitespace - especially '\r' */
|
||||||
while (ptr > start && isspace(ptr[-1]))
|
while (ptr > start && isspace((unsigned char)ptr[-1]))
|
||||||
--ptr;
|
--ptr;
|
||||||
|
|
||||||
/* if we found a non-empty line, break out of the loop to process it */
|
/* if we found a non-empty line, break out of the loop to process it */
|
||||||
|
@ -1611,7 +1855,6 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
case RC_CONSOLE_MASTER_SYSTEM:
|
case RC_CONSOLE_MASTER_SYSTEM:
|
||||||
case RC_CONSOLE_MEGA_DRIVE:
|
case RC_CONSOLE_MEGA_DRIVE:
|
||||||
case RC_CONSOLE_NEOGEO_POCKET:
|
case RC_CONSOLE_NEOGEO_POCKET:
|
||||||
case RC_CONSOLE_NINTENDO_64:
|
|
||||||
case RC_CONSOLE_ORIC:
|
case RC_CONSOLE_ORIC:
|
||||||
case RC_CONSOLE_POKEMON_MINI:
|
case RC_CONSOLE_POKEMON_MINI:
|
||||||
case RC_CONSOLE_SEGA_32X:
|
case RC_CONSOLE_SEGA_32X:
|
||||||
|
@ -1622,7 +1865,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
case RC_CONSOLE_VIRTUAL_BOY:
|
case RC_CONSOLE_VIRTUAL_BOY:
|
||||||
case RC_CONSOLE_WONDERSWAN:
|
case RC_CONSOLE_WONDERSWAN:
|
||||||
/* generic whole-file hash - don't buffer */
|
/* generic whole-file hash - don't buffer */
|
||||||
return rc_hash_whole_file(hash, console_id, path);
|
return rc_hash_whole_file(hash, path);
|
||||||
|
|
||||||
case RC_CONSOLE_MSX:
|
case RC_CONSOLE_MSX:
|
||||||
case RC_CONSOLE_PC8800:
|
case RC_CONSOLE_PC8800:
|
||||||
|
@ -1630,7 +1873,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
if (rc_path_compare_extension(path, "m3u"))
|
if (rc_path_compare_extension(path, "m3u"))
|
||||||
return rc_hash_generate_from_playlist(hash, console_id, path);
|
return rc_hash_generate_from_playlist(hash, console_id, path);
|
||||||
|
|
||||||
return rc_hash_whole_file(hash, console_id, path);
|
return rc_hash_whole_file(hash, path);
|
||||||
|
|
||||||
case RC_CONSOLE_ATARI_7800:
|
case RC_CONSOLE_ATARI_7800:
|
||||||
case RC_CONSOLE_ATARI_LYNX:
|
case RC_CONSOLE_ATARI_LYNX:
|
||||||
|
@ -1648,6 +1891,9 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
case RC_CONSOLE_ARCADE:
|
case RC_CONSOLE_ARCADE:
|
||||||
return rc_hash_arcade(hash, path);
|
return rc_hash_arcade(hash, path);
|
||||||
|
|
||||||
|
case RC_CONSOLE_NINTENDO_64:
|
||||||
|
return rc_hash_n64_file(hash, path);
|
||||||
|
|
||||||
case RC_CONSOLE_NINTENDO_DS:
|
case RC_CONSOLE_NINTENDO_DS:
|
||||||
return rc_hash_nintendo_ds(hash, path);
|
return rc_hash_nintendo_ds(hash, path);
|
||||||
|
|
||||||
|
@ -1678,6 +1924,9 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
|
|
||||||
return rc_hash_ps2(hash, path);
|
return rc_hash_ps2(hash, path);
|
||||||
|
|
||||||
|
case RC_CONSOLE_PSP:
|
||||||
|
return rc_hash_psp(hash, path);
|
||||||
|
|
||||||
case RC_CONSOLE_DREAMCAST:
|
case RC_CONSOLE_DREAMCAST:
|
||||||
if (rc_path_compare_extension(path, "m3u"))
|
if (rc_path_compare_extension(path, "m3u"))
|
||||||
return rc_hash_generate_from_playlist(hash, console_id, path);
|
return rc_hash_generate_from_playlist(hash, console_id, path);
|
||||||
|
@ -1693,7 +1942,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rc_hash_iterator_append_console(struct rc_hash_iterator* iterator, int console_id)
|
static void rc_hash_iterator_append_console(struct rc_hash_iterator* iterator, uint8_t console_id)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (iterator->consoles[i] != 0)
|
while (iterator->consoles[i] != 0)
|
||||||
|
@ -1919,8 +2168,9 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||||
if (rc_path_compare_extension(ext, "iso"))
|
if (rc_path_compare_extension(ext, "iso"))
|
||||||
{
|
{
|
||||||
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION_2;
|
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION_2;
|
||||||
iterator->consoles[1] = RC_CONSOLE_3DO;
|
iterator->consoles[1] = RC_CONSOLE_PSP;
|
||||||
iterator->consoles[2] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
iterator->consoles[2] = RC_CONSOLE_3DO;
|
||||||
|
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
||||||
need_path = 1;
|
need_path = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2064,6 +2314,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||||
{
|
{
|
||||||
iterator->consoles[0] = RC_CONSOLE_VIRTUAL_BOY;
|
iterator->consoles[0] = RC_CONSOLE_VIRTUAL_BOY;
|
||||||
}
|
}
|
||||||
|
else if (rc_path_compare_extension(ext, "v64"))
|
||||||
|
{
|
||||||
|
iterator->consoles[0] = RC_CONSOLE_NINTENDO_64;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
|
@ -2084,6 +2338,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||||
iterator->consoles[0] = RC_CONSOLE_ARCADE;
|
iterator->consoles[0] = RC_CONSOLE_ARCADE;
|
||||||
need_path = 1;
|
need_path = 1;
|
||||||
}
|
}
|
||||||
|
else if (rc_path_compare_extension(ext, "z64"))
|
||||||
|
{
|
||||||
|
iterator->consoles[0] = RC_CONSOLE_NINTENDO_64;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#if RCHEEVOS_URL_SSL
|
||||||
|
#define RCHEEVOS_URL_PROTOCOL "https"
|
||||||
|
#else
|
||||||
|
#define RCHEEVOS_URL_PROTOCOL "http"
|
||||||
|
#endif
|
||||||
|
|
||||||
static int rc_url_encode(char* encoded, size_t len, const char* str) {
|
static int rc_url_encode(char* encoded, size_t len, const char* str) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (*str) {
|
switch (*str) {
|
||||||
|
@ -67,7 +73,7 @@ int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=awardachievement&u=%s&t=%s&a=%u&h=%d",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=awardachievement&u=%s&t=%s&a=%u&h=%d",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token,
|
urle_login_token,
|
||||||
cheevo_id,
|
cheevo_id,
|
||||||
|
@ -106,7 +112,7 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token,
|
urle_login_token,
|
||||||
lboard_id,
|
lboard_id,
|
||||||
|
@ -122,7 +128,7 @@ int rc_url_get_gameid(char* buffer, size_t size, const char* hash) {
|
||||||
int written = snprintf(
|
int written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=gameid&m=%s",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=gameid&m=%s",
|
||||||
hash
|
hash
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -145,7 +151,7 @@ int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const cha
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=patch&u=%s&t=%s&g=%u",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=patch&u=%s&t=%s&g=%u",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token,
|
urle_login_token,
|
||||||
gameid
|
gameid
|
||||||
|
@ -181,7 +187,7 @@ int rc_url_login_with_password(char* buffer, size_t size, const char* user_name,
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=login&u=%s&p=%s",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&p=%s",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_password
|
urle_password
|
||||||
);
|
);
|
||||||
|
@ -205,7 +211,7 @@ int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, co
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=login&u=%s&t=%s",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&t=%s",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token
|
urle_login_token
|
||||||
);
|
);
|
||||||
|
@ -229,7 +235,7 @@ int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, con
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=%d",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=%d",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token,
|
urle_login_token,
|
||||||
gameid,
|
gameid,
|
||||||
|
@ -255,7 +261,7 @@ int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const
|
||||||
written = snprintf(
|
written = snprintf(
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
"https://retroachievements.org/dorequest.php?r=postactivity&u=%s&t=%s&a=3&m=%u",
|
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=postactivity&u=%s&t=%s&a=3&m=%u",
|
||||||
urle_user_name,
|
urle_user_name,
|
||||||
urle_login_token,
|
urle_login_token,
|
||||||
gameid
|
gameid
|
||||||
|
@ -293,15 +299,14 @@ static int rc_url_append_param_equals(char* buffer, size_t buffer_size, size_t b
|
||||||
return written + (int)buffer_offset;
|
return written + (int)buffer_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, unsigned value)
|
static int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, unsigned value)
|
||||||
{
|
{
|
||||||
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
||||||
if (written > 0) {
|
if (written > 0) {
|
||||||
char num[16];
|
char num[16];
|
||||||
int chars = snprintf(num, sizeof(num), "%u", value);
|
int chars = snprintf(num, sizeof(num), "%u", value);
|
||||||
|
|
||||||
if (chars + written < (int)buffer_size)
|
if (chars + written < (int)buffer_size) {
|
||||||
{
|
|
||||||
memcpy(&buffer[written], num, chars + 1);
|
memcpy(&buffer[written], num, chars + 1);
|
||||||
*buffer_offset = written + chars;
|
*buffer_offset = written + chars;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -311,16 +316,14 @@ int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, const char* value)
|
static int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, const char* param, const char* value)
|
||||||
{
|
{
|
||||||
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
|
||||||
if (written > 0)
|
if (written > 0) {
|
||||||
{
|
|
||||||
buffer += written;
|
buffer += written;
|
||||||
buffer_size -= written;
|
buffer_size -= written;
|
||||||
|
|
||||||
if (rc_url_encode(buffer, buffer_size, value) == 0)
|
if (rc_url_encode(buffer, buffer_size, value) == 0) {
|
||||||
{
|
|
||||||
written += (int)strlen(buffer);
|
written += (int)strlen(buffer);
|
||||||
*buffer_offset = written;
|
*buffer_offset = written;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -330,10 +333,10 @@ int rc_url_append_str(char* buffer, size_t buffer_size, size_t* buffer_offset, c
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buffer_offset,
|
static int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buffer_offset,
|
||||||
const char* api, const char* user_name)
|
const char* api, const char* user_name)
|
||||||
{
|
{
|
||||||
const char* base_url = "https://retroachievements.org/dorequest.php";
|
const char* base_url = RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php";
|
||||||
size_t written = strlen(base_url);
|
size_t written = strlen(base_url);
|
||||||
int failure = 0;
|
int failure = 0;
|
||||||
|
|
||||||
|
@ -343,6 +346,7 @@ int rc_url_build_dorequest(char* url_buffer, size_t url_buffer_size, size_t* buf
|
||||||
url_buffer[written++] = '?';
|
url_buffer[written++] = '?';
|
||||||
|
|
||||||
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "r", api);
|
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "r", api);
|
||||||
|
if (user_name)
|
||||||
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "u", user_name);
|
failure |= rc_url_append_str(url_buffer, url_buffer_size, &written, "u", user_name);
|
||||||
|
|
||||||
*buffer_offset += written;
|
*buffer_offset += written;
|
||||||
|
@ -371,3 +375,28 @@ int rc_url_ping(char* url_buffer, size_t url_buffer_size, char* post_buffer, siz
|
||||||
|
|
||||||
return failure;
|
return failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rc_url_get_lboard_entries(char* buffer, size_t size, unsigned lboard_id, unsigned first_index, unsigned count)
|
||||||
|
{
|
||||||
|
size_t written = 0;
|
||||||
|
int failure = rc_url_build_dorequest(buffer, size, &written, "lbinfo", NULL);
|
||||||
|
failure |= rc_url_append_unum(buffer, size, &written, "i", lboard_id);
|
||||||
|
if (first_index > 1)
|
||||||
|
failure |= rc_url_append_unum(buffer, size, &written, "o", first_index - 1);
|
||||||
|
failure |= rc_url_append_unum(buffer, size, &written, "c", count);
|
||||||
|
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc_url_get_lboard_entries_near_user(char* buffer, size_t size, unsigned lboard_id, const char* user_name, unsigned count)
|
||||||
|
{
|
||||||
|
size_t written = 0;
|
||||||
|
int failure = rc_url_build_dorequest(buffer, size, &written, "lbinfo", NULL);
|
||||||
|
failure |= rc_url_append_unum(buffer, size, &written, "i", lboard_id);
|
||||||
|
failure |= rc_url_append_str(buffer, size, &written, "u", user_name);
|
||||||
|
failure |= rc_url_append_unum(buffer, size, &written, "c", count);
|
||||||
|
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef RCHEEVOS_URL_PROTOCOL
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/md5_digest.h"
|
#include "common/md5_digest.h"
|
||||||
#include "common/platform.h"
|
#include "common/platform.h"
|
||||||
#include "common/string.h"
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/timestamp.h"
|
#include "common/timestamp.h"
|
||||||
#include "common_host_interface.h"
|
#include "common_host_interface.h"
|
||||||
|
@ -1206,18 +1205,14 @@ std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(co
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
s_last_queried_lboard = id;
|
||||||
|
s_lboard_entries.reset();
|
||||||
|
|
||||||
// TODO: Add paging? For now, stick to defaults
|
// TODO: Add paging? For now, stick to defaults
|
||||||
char url[512];
|
char url[512];
|
||||||
|
|
||||||
size_t written = 0;
|
// Just over what a single page can store, should be a reasonable amount for now
|
||||||
rc_url_build_dorequest(url, sizeof(url), &written, "lbinfo", s_username.c_str());
|
rc_url_get_lboard_entries_near_user(url, sizeof(url), id, s_username.c_str(), 15);
|
||||||
rc_url_append_unum(url, sizeof(url), &written, "i", id);
|
|
||||||
rc_url_append_unum(url, sizeof(url), &written, "c",
|
|
||||||
15); // Just over what a single page can store, should be a reasonable amount for now
|
|
||||||
// rc_url_append_unum(url, sizeof(url), &written, "o", 0);
|
|
||||||
|
|
||||||
s_last_queried_lboard = id;
|
|
||||||
s_lboard_entries.reset();
|
|
||||||
s_http_downloader->CreateRequest(url, GetLbInfoCallback);
|
s_http_downloader->CreateRequest(url, GetLbInfoCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1381,6 +1376,15 @@ std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TinyString GetAchievementProgressText(const Achievement& achievement)
|
||||||
|
{
|
||||||
|
TinyString result;
|
||||||
|
rc_runtime_format_achievement_measured(&s_rcheevos_runtime, achievement.id, result.GetWriteableCharArray(),
|
||||||
|
result.GetWritableBufferSize());
|
||||||
|
result.UpdateSize();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void CheevosEventHandler(const rc_runtime_event_t* runtime_event)
|
void CheevosEventHandler(const rc_runtime_event_t* runtime_event)
|
||||||
{
|
{
|
||||||
static const char* events[] = {"RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED", "RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED",
|
static const char* events[] = {"RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED", "RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common/string.h"
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -114,6 +115,7 @@ u32 GetLeaderboardCount();
|
||||||
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
|
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
|
||||||
|
|
||||||
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
|
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
|
||||||
|
TinyString GetAchievementProgressText(const Achievement& achievement);
|
||||||
|
|
||||||
void UnlockAchievement(u32 achievement_id, bool add_notification = true);
|
void UnlockAchievement(u32 achievement_id, bool add_notification = true);
|
||||||
void SubmitLeaderboard(u32 leaderboard_id, int value);
|
void SubmitLeaderboard(u32 leaderboard_id, int value);
|
||||||
|
|
|
@ -4101,7 +4101,6 @@ static void DrawAchievement(const Cheevos::Achievement& cheevo)
|
||||||
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
|
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
|
||||||
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
|
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint));
|
||||||
const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max);
|
const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max);
|
||||||
SmallString text;
|
|
||||||
|
|
||||||
ImGui::PushFont(g_large_font);
|
ImGui::PushFont(g_large_font);
|
||||||
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, cheevo.title.c_str(), cheevo.title.c_str() + cheevo.title.size(),
|
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, cheevo.title.c_str(), cheevo.title.c_str() + cheevo.title.size(),
|
||||||
|
@ -4129,7 +4128,7 @@ static void DrawAchievement(const Cheevos::Achievement& cheevo)
|
||||||
dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y),
|
dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y),
|
||||||
ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor()));
|
ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor()));
|
||||||
|
|
||||||
text.Format("%u / %u", progress.first, progress.second);
|
const TinyString text(GetAchievementProgressText(cheevo));
|
||||||
const ImVec2 text_size = ImGui::CalcTextSize(text);
|
const ImVec2 text_size = ImGui::CalcTextSize(text);
|
||||||
const ImVec2 text_pos(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
|
const ImVec2 text_pos(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f),
|
||||||
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f));
|
progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f));
|
||||||
|
|
Loading…
Reference in a new issue