Upgrade rcheevos to v10.1.0

This commit is contained in:
Silent
2021-06-21 19:00:14 +02:00
committed by Connor McLaughlin
parent 9d26a85967
commit d567f1e870
29 changed files with 1636 additions and 1399 deletions

View File

@ -108,7 +108,7 @@ char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length) {
next = &(*next)->right;
}
*next = rc_alloc_scratch(NULL, &used, sizeof(rc_scratch_string_t), RC_ALIGNOF(rc_scratch_string_t), &parse->scratch, RC_OFFSETOF(parse->scratch.objs, __rc_scratch_string_t));
*next = (rc_scratch_string_t*)rc_alloc_scratch(NULL, &used, sizeof(rc_scratch_string_t), RC_ALIGNOF(rc_scratch_string_t), &parse->scratch, RC_OFFSETOF(parse->scratch.objs, __rc_scratch_string_t));
ptr = (char*)rc_alloc_scratch(parse->buffer, &parse->offset, length + 1, RC_ALIGNOF(char), &parse->scratch, -1);
if (!ptr || !*next) {
@ -142,6 +142,7 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in
parse->first_memref = 0;
parse->variables = 0;
parse->measured_target = 0;
parse->lines_read = 0;
parse->has_required_hits = 0;
}
@ -186,6 +187,7 @@ const char* rc_error_str(int ret)
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
case RC_INVALID_COMPARISON: return "Invalid comparison";
case RC_INVALID_STATE: return "Invalid state";
case RC_INVALID_JSON: return "Invalid JSON";
default: return "Unknown error";
}

View File

@ -69,6 +69,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
aux = *memaddr;
self = RC_ALLOC(rc_condition_t, parse);
self->current_hits = 0;
self->is_true = 0;
if (*aux != 0 && aux[1] == ':') {
switch (*aux) {
@ -184,7 +185,12 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return 0;
}
parse->has_required_hits = 1;
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else if (*aux == '.') {
@ -196,7 +202,12 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return 0;
}
parse->has_required_hits = 1;
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else {
@ -228,10 +239,18 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
switch (self->oper) {
case RC_OPERATOR_MULT:
if (self->operand2.type == RC_OPERAND_FP)
if (self->operand2.type == RC_OPERAND_FP) {
value = (int)((double)value * self->operand2.value.dbl);
else
}
else {
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
* to the type's size. this allows negative multiplication through twos-complements. i.e.
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
*/
value *= rc_evaluate_operand(&self->operand2, eval_state);
}
break;
case RC_OPERATOR_DIV:

View File

@ -17,6 +17,7 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_OR_NEXT:
case RC_CONDITION_ADD_ADDRESS:
case RC_CONDITION_RESET_NEXT_IF:
condition->pause = *in_pause;
break;
@ -34,7 +35,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
unsigned measured_target = 0;
self = RC_ALLOC(rc_condset_t, parse);
self->has_pause = self->is_paused = 0;
self->has_pause = self->is_paused = self->has_indirect_memrefs = 0;
next = &self->conditions;
if (**memaddr == 'S' || **memaddr == 's' || !**memaddr) {
@ -54,15 +55,13 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
if ((*next)->oper == RC_OPERATOR_NONE) {
switch ((*next)->type) {
case RC_CONDITION_ADD_ADDRESS:
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_SUB_HITS:
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_OR_NEXT:
/* these conditions don't require a right hand size (implied *1) */
break;
case RC_CONDITION_MEASURED:
/* right hand side is not required when Measured is used in a value */
if (is_value)
break;
/* fallthrough to default */
@ -75,6 +74,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
self->has_pause |= (*next)->type == RC_CONDITION_PAUSE_IF;
in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS;
self->has_indirect_memrefs |= in_add_address;
switch ((*next)->type) {
case RC_CONDITION_MEASURED:
@ -140,6 +140,30 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
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) {
for (; condition != 0; condition = condition->next) {
if (condition->pause != processing_pause)
continue;
if (condition->type == RC_CONDITION_ADD_ADDRESS) {
eval_state->add_address = rc_evaluate_condition_value(condition, eval_state);
continue;
}
/* call rc_get_memref_value to update the indirect memrefs. it won't do anything with non-indirect
* memrefs and avoids a second check of is_indirect. also, we ignore the response, so it doesn't
* matter what operand type we pass. assume RC_OPERAND_ADDRESS is the quickest. */
if (rc_operand_is_memref(&condition->operand1))
rc_get_memref_value(condition->operand1.value.memref, RC_OPERAND_ADDRESS, eval_state);
if (rc_operand_is_memref(&condition->operand2))
rc_get_memref_value(condition->operand2.value.memref, RC_OPERAND_ADDRESS, eval_state);
eval_state->add_address = 0;
}
}
static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc_eval_state_t* eval_state) {
rc_condition_t* condition;
int set_valid, cond_valid, and_next, or_next, reset_next;
@ -155,9 +179,8 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
for (condition = self->conditions; condition != 0; condition = condition->next) {
if (condition->pause != processing_pause) {
if (condition->pause != processing_pause)
continue;
}
/* STEP 1: process modifier conditions */
switch (condition->type) {
@ -283,6 +306,17 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
case RC_CONDITION_PAUSE_IF:
/* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */
if (cond_valid) {
/* indirect memrefs are not updated as part of the rc_update_memref_values call.
* an active pause aborts processing of the remaining part of the pause subset and the entire non-pause subset.
* if the set has any indirect memrefs, manually update them now so the deltas are correct */
if (self->has_indirect_memrefs) {
/* first, update any indirect memrefs in the remaining part of the pause subset */
rc_condset_update_indirect_memrefs(self, condition->next, 1, eval_state);
/* then, update all indirect memrefs in the non-pause subset */
rc_condset_update_indirect_memrefs(self, self->conditions, 0, eval_state);
}
return 1;
}

View File

@ -1,4 +1,3 @@
#include "rcheevos.h"
#include "rc_consoles.h"
#include <ctype.h>
@ -163,14 +162,23 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_SG1000:
return "SG-1000";
case RC_CONSOLE_SHARPX1:
return "Sharp X1";
case RC_CONSOLE_SUPER_NINTENDO:
return "Super Nintendo Entertainment System";
case RC_CONSOLE_SUPER_CASSETTEVISION:
return "Super CassetteVision";
case RC_CONSOLE_WONDERSWAN:
return "WonderSwan";
case RC_CONSOLE_SUPERVISION:
return "Watara Supervision";
case RC_CONSOLE_THOMSONTO8:
return "Thomson TO8";
case RC_CONSOLE_TIC80:
return "TIC-80";
case RC_CONSOLE_VECTREX:
return "Vectrex";
@ -187,6 +195,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_WII_U:
return "Wii-U";
case RC_CONSOLE_WONDERSWAN:
return "WonderSwan";
case RC_CONSOLE_X68K:
return "X68K";
@ -327,10 +338,14 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
/* ===== Magnavox Odyssey 2 ===== */
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
/* Internal and external RAMs are reachable using unique instructions.
* The real addresses provided are virtual and for mapping purposes only. */
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Internal RAM" },
{ 0x000040U, 0x00013FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "External RAM" }
};
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 };
/* ===== Master System ===== */
/* http://www.smspower.org/Development/MemoryMap */
@ -363,8 +378,9 @@ static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_ms
/* ===== Neo Geo Pocket ===== */
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
/* The docs suggest there's Work RAM exposed from $0000-$6FFF, Sound RAM from $7000-$7FFF, and Video
* RAM from $8000-$BFFF, but both MednafenNGP and FBNeo only expose system RAM from $4000-$7FFF */
{ 0x000000U, 0x003FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
@ -456,6 +472,14 @@ static const rc_memory_region_t _rc_memory_regions_playstation[] = {
};
static const rc_memory_regions_t rc_memory_regions_playstation = { _rc_memory_regions_playstation, 2 };
/* ===== PlayStation 2 ===== */
/* https://psi-rockin.github.io/ps2tek/ */
static const rc_memory_region_t _rc_memory_regions_playstation2[] = {
{ 0x00000000U, 0x000FFFFFU, 0x00000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Kernel RAM" },
{ 0x00100000U, 0x01FFFFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_playstation2 = { _rc_memory_regions_playstation2, 2 };
/* ===== Pokemon Mini ===== */
/* https://www.pokemon-mini.net/documentation/memory-map/ */
static const rc_memory_region_t _rc_memory_regions_pokemini[] = {
@ -483,12 +507,17 @@ static const rc_memory_regions_t rc_memory_regions_saturn = { _rc_memory_regions
/* ===== SG-1000 ===== */
/* http://www.smspower.org/Development/MemoryMap */
static const rc_memory_region_t _rc_memory_regions_sg1000[] = {
{ 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
/* TODO: should cartridge memory be exposed ($0000-$BFFF)? it's usually just ROM data, but may contain on-cartridge RAM
* This not is also concerning: http://www.smspower.org/Development/MemoryMap
* Cartridges may disable the system RAM and thus take over the full 64KB address space. */
{ 0x000000U, 0x0003FFU, 0xC000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
/* https://github.com/libretro/FBNeo/blob/697801c6262be6ca91615cf905444d3e039bc06f/src/burn/drv/sg1000/d_sg1000.cpp#L210-L237 */
/* Expansion mode B exposes 8KB at $C000. The first 2KB hides the System RAM, but since the address matches,
we'll leverage that definition and expand it another 6KB */
{ 0x000400U, 0x001FFFU, 0xC400U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" },
/* Expansion mode A exposes 8KB at $2000 */
{ 0x002000U, 0x003FFFU, 0x2000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" },
/* Othello exposes 2KB at $8000, and The Castle exposes 8KB at $8000 */
{ 0x004000U, 0x005FFFU, 0x8000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Extended RAM" }
};
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 1 };
static const rc_memory_regions_t rc_memory_regions_sg1000 = { _rc_memory_regions_sg1000, 4 };
/* ===== Super Cassette Vision ===== */
static const rc_memory_region_t _rc_memory_regions_scv[] = {
@ -510,6 +539,29 @@ static const rc_memory_region_t _rc_memory_regions_snes[] = {
};
static const rc_memory_regions_t rc_memory_regions_snes = { _rc_memory_regions_snes, 2 };
/* ===== Thomson TO8 ===== */
/* https://github.com/mamedev/mame/blob/master/src/mame/drivers/thomson.cpp#L1617 */
static const rc_memory_region_t _rc_memory_regions_thomson_to8[] = {
{ 0x000000U, 0x07FFFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_thomson_to8 = { _rc_memory_regions_thomson_to8, 1 };
/* ===== TIC-80 ===== */
/* https://github.com/nesbox/TIC-80/wiki/RAM */
static const rc_memory_region_t _rc_memory_regions_tic80[] = {
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Video RAM" }, /* have to classify this as system RAM because the core exposes it as part of the RETRO_MEMORY_SYSTEM_RAM */
{ 0x004000U, 0x005FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Tile RAM" },
{ 0x006000U, 0x007FFFU, 0x006000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Sprite RAM" },
{ 0x008000U, 0x00FF7FU, 0x008000U, RC_MEMORY_TYPE_SYSTEM_RAM, "MAP RAM" },
{ 0x00FF80U, 0x00FF8BU, 0x00FF80U, RC_MEMORY_TYPE_SYSTEM_RAM, "Input State" },
{ 0x00FF8CU, 0x014003U, 0x00FF8CU, RC_MEMORY_TYPE_SYSTEM_RAM, "Sound RAM" },
{ 0x014004U, 0x014403U, 0x014004U, RC_MEMORY_TYPE_SAVE_RAM, "Persistent Memory" }, /* this is also returned as part of RETRO_MEMORY_SYSTEM_RAM, but can be extrapolated correctly because the pointer starts at the first SYSTEM_RAM region */
{ 0x014404U, 0x014603U, 0x014404U, RC_MEMORY_TYPE_SYSTEM_RAM, "Sprite Flags" },
{ 0x014604U, 0x014E03U, 0x014604U, RC_MEMORY_TYPE_SYSTEM_RAM, "System Font" },
{ 0x014E04U, 0x017FFFU, 0x014E04U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM"}
};
static const rc_memory_regions_t rc_memory_regions_tic80 = { _rc_memory_regions_tic80, 10 };
/* ===== Vectrex ===== */
/* https://roadsidethoughts.com/vectrex/vectrex-memory-map.htm */
static const rc_memory_region_t _rc_memory_regions_vectrex[] = {
@ -524,6 +576,15 @@ static const rc_memory_region_t _rc_memory_regions_virtualboy[] = {
};
static const rc_memory_regions_t rc_memory_regions_virtualboy = { _rc_memory_regions_virtualboy, 2 };
/* ===== Watara Supervision ===== */
/* https://github.com/libretro/potator/blob/b5e5ba02914fcdf4a8128072dbc709da28e08832/common/memorymap.c#L231-L259 */
static const rc_memory_region_t _rc_memory_regions_watara_supervision[] = {
{ 0x0000U, 0x001FFFU, 0x0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x2000U, 0x003FFFU, 0x2000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Registers" },
{ 0x4000U, 0x005FFFU, 0x4000U, RC_MEMORY_TYPE_VIDEO_RAM, "Video RAM" }
};
static const rc_memory_regions_t rc_memory_regions_watara_supervision = { _rc_memory_regions_watara_supervision, 3 };
/* ===== WonderSwan ===== */
/* http://daifukkat.su/docs/wsman/#ovr_memmap */
static const rc_memory_region_t _rc_memory_regions_wonderswan[] = {
@ -621,11 +682,14 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
return &rc_memory_regions_pcengine;
case RC_CONSOLE_PCFX:
return &rc_memory_regions_pcfx;
return &rc_memory_regions_pcfx;
case RC_CONSOLE_PLAYSTATION:
return &rc_memory_regions_playstation;
case RC_CONSOLE_PLAYSTATION_2:
return &rc_memory_regions_playstation2;
case RC_CONSOLE_POKEMON_MINI:
return &rc_memory_regions_pokemini;
@ -644,6 +708,15 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_SUPER_NINTENDO:
return &rc_memory_regions_snes;
case RC_CONSOLE_SUPERVISION:
return &rc_memory_regions_watara_supervision;
case RC_CONSOLE_THOMSONTO8:
return &rc_memory_regions_thomson_to8;
case RC_CONSOLE_TIC80:
return &rc_memory_regions_tic80;
case RC_CONSOLE_VECTREX:
return &rc_memory_regions_vectrex;
@ -651,7 +724,7 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
return &rc_memory_regions_virtualboy;
case RC_CONSOLE_WONDERSWAN:
return &rc_memory_regions_wonderswan;
return &rc_memory_regions_wonderswan;
default:
return &rc_memory_regions_none;

View File

@ -153,6 +153,10 @@ int rc_lboard_size(const char* memaddr) {
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) {
rc_lboard_t* self;
rc_parse_state_t parse;
if (!buffer || !memaddr)
return 0;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_lboard_t, &parse);
@ -161,7 +165,7 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
rc_parse_lboard_internal(self, memaddr, &parse);
rc_destroy_parse_state(&parse);
return parse.offset >= 0 ? self : 0;
return (parse.offset >= 0) ? self : 0;
}
int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek_ud, lua_State* L) {

View File

@ -95,6 +95,86 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
return RC_OK;
}
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)
{
switch (size)
{
case RC_MEMSIZE_BIT_0:
value = (value >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (value >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (value >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (value >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (value >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (value >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (value >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (value >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = value & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (value >> 4) & 0x0f;
break;
case RC_MEMSIZE_BITCOUNT:
value = rc_bits_set[(value & 0x0F)]
+ rc_bits_set[((value >> 4) & 0x0F)];
break;
default:
break;
}
return value;
}
char rc_memref_shared_size(char size)
{
switch (size) {
case RC_MEMSIZE_BIT_0:
case RC_MEMSIZE_BIT_1:
case RC_MEMSIZE_BIT_2:
case RC_MEMSIZE_BIT_3:
case RC_MEMSIZE_BIT_4:
case RC_MEMSIZE_BIT_5:
case RC_MEMSIZE_BIT_6:
case RC_MEMSIZE_BIT_7:
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
case RC_MEMSIZE_BITCOUNT:
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_transform_memref_value */
return RC_MEMSIZE_8_BITS;
default:
return size;
}
}
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
unsigned value;
@ -103,46 +183,6 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
switch (size)
{
case RC_MEMSIZE_BIT_0:
value = (peek(address, 1, ud) >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (peek(address, 1, ud) >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (peek(address, 1, ud) >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (peek(address, 1, ud) >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (peek(address, 1, ud) >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (peek(address, 1, ud) >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (peek(address, 1, ud) >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (peek(address, 1, ud) >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = peek(address, 1, ud) & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (peek(address, 1, ud) >> 4) & 0x0f;
break;
case RC_MEMSIZE_8_BITS:
value = peek(address, 1, ud);
break;
@ -161,7 +201,15 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
break;
default:
value = 0;
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
{
value = peek(address, 1, ud);
value = rc_transform_memref_value(value, size);
}
else
{
value = 0;
}
break;
}

View File

@ -102,27 +102,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
if (ret != RC_OK)
return ret;
switch (self->size) {
case RC_MEMSIZE_BIT_0:
case RC_MEMSIZE_BIT_1:
case RC_MEMSIZE_BIT_2:
case RC_MEMSIZE_BIT_3:
case RC_MEMSIZE_BIT_4:
case RC_MEMSIZE_BIT_5:
case RC_MEMSIZE_BIT_6:
case RC_MEMSIZE_BIT_7:
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
case RC_MEMSIZE_BITCOUNT:
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
size = RC_MEMSIZE_8_BITS;
break;
default:
size = self->size;
break;
}
size = rc_memref_shared_size(self->size);
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
if (parse->offset < 0)
return parse->offset;
@ -283,7 +263,17 @@ static int rc_luapeek(lua_State* L) {
#endif /* RC_DISABLE_LUA */
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
int rc_operand_is_memref(rc_operand_t* self) {
switch (self->type) {
case RC_OPERAND_CONST:
case RC_OPERAND_FP:
case RC_OPERAND_LUA:
return 0;
default:
return 1;
}
}
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
#ifndef RC_DISABLE_LUA
@ -336,56 +326,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
}
/* step 2: mask off appropriate bits */
switch (self->size)
{
case RC_MEMSIZE_BIT_0:
value = (value >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (value >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (value >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (value >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (value >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (value >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (value >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (value >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = value & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (value >> 4) & 0x0f;
break;
case RC_MEMSIZE_BITCOUNT:
value = rc_bits_set[(value & 0x0F)]
+ rc_bits_set[((value >> 4) & 0x0F)];
break;
default:
break;
}
value = rc_transform_memref_value(value, self->size);
/* step 3: apply logic */
switch (self->type)

View File

@ -1,7 +1,7 @@
#ifndef INTERNAL_H
#define INTERNAL_H
#include "rcheevos.h"
#include "rc_runtime_types.h"
#ifdef __cplusplus
extern "C" {
@ -96,6 +96,7 @@ typedef struct {
rc_value_t** variables;
unsigned measured_target;
int lines_read;
char has_required_hits;
}
@ -116,8 +117,11 @@ void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
char rc_memref_shared_size(char size);
unsigned rc_transform_memref_value(unsigned value, char size);
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
int rc_trigger_state_active(int state);
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);

View File

@ -22,8 +22,9 @@ static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* mema
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
/* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */
if (end == &memaddr[memaddr_len]) {
/* just a memory reference, allocate it */
return &rc_alloc_memref(parse, address, size, 0)->value;
/* if it's not a derived size, we can reference the memref directly */
if (rc_memref_shared_size(size) == size)
return &rc_alloc_memref(parse, address, size, 0)->value;
}
}
@ -35,7 +36,7 @@ static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* mema
return &variable->value;
}
static const char* rc_parse_line(const char* line, const char** end) {
static const char* rc_parse_line(const char* line, const char** end, rc_parse_state_t* parse) {
const char* nextline;
const char* endline;
@ -44,25 +45,31 @@ static const char* rc_parse_line(const char* line, const char** end) {
while (*nextline && *nextline != '\n')
++nextline;
/* find a trailing comment marker (//) */
/* if a trailing comment marker (//) exists, the line stops there */
endline = line;
while (endline < nextline && (endline[0] != '/' || endline[1] != '/' || (endline > line && endline[-1] == '\\')))
++endline;
/* remove trailing whitespace */
if (endline == nextline) {
/* trailing whitespace on a line without a comment marker may be significant, just remove the line ending */
if (endline > line && endline[-1] == '\r')
--endline;
} else {
while (endline > line && isspace(endline[-1]))
/* remove trailing whitespace before the comment marker */
while (endline > line && isspace((int)((unsigned char*)endline)[-1]))
--endline;
}
/* end is pointing at the first character to ignore - makes subtraction for length easier */
/* point end at the first character to ignore, it makes subtraction for length easier */
*end = endline;
/* tally the line */
++parse->lines_read;
/* skip the newline character so we're pointing at the next line */
if (*nextline == '\n')
++nextline;
return nextline;
}
@ -337,7 +344,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
do
{
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
if (endline - line < 2) {
/* ignore full line comments inside a lookup */
@ -436,13 +443,20 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
const char* endline;
const char* ptr;
int hasdisplay = 0;
int display_line = 0;
int chars;
/* special case for empty script to return 1 line read */
if (!*script) {
parse->lines_read = 1;
parse->offset = RC_MISSING_DISPLAY_STRING;
return;
}
/* first pass: process macro initializers */
line = script;
while (*line)
{
nextline = rc_parse_line(line, &endline);
while (*line) {
nextline = rc_parse_line(line, &endline, parse);
if (strncmp(line, "Lookup:", 7) == 0) {
line += 7;
@ -469,7 +483,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
nextlookup = &lookup->next;
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
if (parse->buffer && strncmp(line, "FormatType=", 11) == 0) {
line += 11;
@ -485,10 +499,11 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
} else if (strncmp(line, "Display:", 8) == 0) {
display = nextline;
display_line = parse->lines_read;
do {
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
} while (*line == '?');
}
@ -502,8 +517,12 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
/* second pass, process display string*/
if (display) {
/* point the parser back at the display strings */
int lines_read = parse->lines_read;
parse->lines_read = display_line;
line = display;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
while (*line == '?') {
/* conditional display: ?trigger?string */
@ -525,7 +544,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
line = nextline;
nextline = rc_parse_line(line, &endline);
nextline = rc_parse_line(line, &endline, parse);
}
/* non-conditional display: string */
@ -534,6 +553,9 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
hasdisplay = 1;
nextdisplay = &((*nextdisplay)->next);
}
/* restore the parser state */
parse->lines_read = lines_read;
}
/* finalize */
@ -544,7 +566,7 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
}
}
int rc_richpresence_size(const char* script) {
int rc_richpresence_size_lines(const char* script, int* lines_read) {
rc_richpresence_t* self;
rc_parse_state_t parse;
rc_memref_t* first_memref;
@ -556,13 +578,24 @@ int rc_richpresence_size(const char* script) {
self = RC_ALLOC(rc_richpresence_t, &parse);
rc_parse_richpresence_internal(self, script, &parse);
if (lines_read)
*lines_read = parse.lines_read;
rc_destroy_parse_state(&parse);
return parse.offset;
}
int rc_richpresence_size(const char* script) {
return rc_richpresence_size_lines(script, NULL);
}
rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_State* L, int funcs_ndx) {
rc_richpresence_t* self;
rc_parse_state_t parse;
if (!buffer || !script)
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_richpresence_t, &parse);
@ -572,7 +605,7 @@ rc_richpresence_t* rc_parse_richpresence(void* buffer, const char* script, lua_S
rc_parse_richpresence_internal(self, script, &parse);
rc_destroy_parse_state(&parse);
return parse.offset >= 0 ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, lua_State* L) {

View File

@ -1,3 +1,4 @@
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rhash/md5.h"
@ -217,6 +218,29 @@ rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id, unsigned* measured_value, unsigned* measured_target)
{
const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
if (!measured_value || !measured_target)
return 0;
if (!trigger) {
*measured_value = *measured_target = 0;
return 0;
}
if (rc_trigger_state_active(trigger->state)) {
*measured_value = trigger->measured_value;
*measured_target = trigger->measured_target;
}
else {
/* don't report measured information for inactive triggers */
*measured_value = *measured_target = 0;
}
return 1;
}
static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, unsigned index) {
if (self->lboards[index].owns_memrefs) {
/* if the lboard has one or more memrefs in its buffer, we can't free the buffer.
@ -353,6 +377,11 @@ rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, unsigned id)
return NULL;
}
int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format)
{
return rc_format_value(buffer, size, value, format);
}
int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua_State* L, int funcs_idx) {
rc_richpresence_t* richpresence;
rc_runtime_richpresence_t* previous;
@ -422,7 +451,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
return RC_OK;
}
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L) {
if (self->richpresence && self->richpresence->richpresence)
return rc_get_richpresence_display_string(self->richpresence->richpresence, buffer, buffersize, peek, peek_ud, L);
@ -430,7 +459,7 @@ int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned
return 0;
}
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L) {
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L) {
rc_runtime_event_t runtime_event;
int i;
@ -441,7 +470,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
for (i = self->trigger_count - 1; i >= 0; --i) {
rc_trigger_t* trigger = self->triggers[i].trigger;
int trigger_state;
int old_state, new_state;
if (!trigger)
continue;
@ -460,15 +489,34 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
continue;
}
trigger_state = trigger->state;
switch (rc_evaluate_trigger(trigger, peek, ud, L))
{
case RC_TRIGGER_STATE_RESET:
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
break;
old_state = trigger->state;
new_state = rc_evaluate_trigger(trigger, peek, ud, L);
/* the trigger state doesn't actually change to RESET, RESET just serves as a notification.
* handle the notification, then look at the actual state */
if (new_state == RC_TRIGGER_STATE_RESET)
{
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
new_state = trigger->state;
}
/* if the state hasn't changed, there won't be any events raised */
if (new_state == old_state)
continue;
/* raise an UNPRIMED event when changing from UNPRIMED to anything else */
if (old_state == RC_TRIGGER_STATE_PRIMED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
}
/* raise events for each of the possible new states */
switch (new_state)
{
case RC_TRIGGER_STATE_TRIGGERED:
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
runtime_event.id = self->triggers[i].id;
@ -476,23 +524,21 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
break;
case RC_TRIGGER_STATE_PAUSED:
if (trigger_state != RC_TRIGGER_STATE_PAUSED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
}
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
break;
case RC_TRIGGER_STATE_PRIMED:
if (trigger_state != RC_TRIGGER_STATE_PRIMED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
}
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
break;
case RC_TRIGGER_STATE_ACTIVE:
if (trigger_state != RC_TRIGGER_STATE_ACTIVE) {
/* only raise ACTIVATED event when transitioning from an inactive state.
* note that inactive in this case means active but cannot trigger. */
if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
@ -594,7 +640,9 @@ static int rc_condset_contains_memref(const rc_condset_t* condset, const rc_memr
return 0;
for (cond = condset->conditions; cond; cond = cond->next) {
if (cond->operand1.value.memref == memref || cond->operand2.value.memref == memref)
if (rc_operand_is_memref(&cond->operand1) && cond->operand1.value.memref == memref)
return 1;
if (rc_operand_is_memref(&cond->operand2) && cond->operand2.value.memref == memref)
return 1;
}
@ -630,32 +678,8 @@ static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memr
return 0;
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
unsigned i;
rc_memref_t* memref;
rc_memref_t** last_memref;
if (!self->memrefs)
return;
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
last_memref = &self->memrefs;
memref = *last_memref;
do {
if (memref->address == address && !memref->value.is_indirect) {
*last_memref = memref->next;
break;
}
last_memref = &memref->next;
memref = *last_memref;
} while (memref);
/* if the address is only used indirectly, don't disable anything dependent on it */
if (!memref)
return;
/* disable any achievements dependent on the address */
for (i = 0; i < self->trigger_count; ++i) {
@ -689,3 +713,80 @@ void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
}
}
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
rc_memref_t** last_memref = &self->memrefs;
rc_memref_t* memref = self->memrefs;
while (memref) {
if (memref->address == address && !memref->value.is_indirect) {
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
*last_memref = memref->next;
rc_runtime_invalidate_memref(self, memref);
break;
}
last_memref = &memref->next;
memref = *last_memref;
}
}
void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
rc_runtime_validate_address_t validate_handler) {
rc_memref_t** last_memref = &self->memrefs;
rc_memref_t* memref = self->memrefs;
int num_invalid = 0;
while (memref) {
if (!memref->value.is_indirect && !validate_handler(memref->address)) {
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
*last_memref = memref->next;
rc_runtime_invalidate_memref(self, memref);
++num_invalid;
}
else {
last_memref = &memref->next;
}
memref = *last_memref;
}
if (num_invalid) {
rc_runtime_event_t runtime_event;
int i;
for (i = self->trigger_count - 1; i >= 0; --i) {
rc_trigger_t* trigger = self->triggers[i].trigger;
if (trigger && self->triggers[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
runtime_event.id = self->triggers[i].id;
runtime_event.value = self->triggers[i].invalid_memref->address;
trigger->state = RC_TRIGGER_STATE_DISABLED;
self->triggers[i].invalid_memref = NULL;
event_handler(&runtime_event);
}
}
for (i = self->lboard_count - 1; i >= 0; --i) {
rc_lboard_t* lboard = self->lboards[i].lboard;
if (lboard && self->lboards[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
runtime_event.id = self->lboards[i].id;
runtime_event.value = self->lboards[i].invalid_memref->address;
lboard->state = RC_LBOARD_STATE_DISABLED;
self->lboards[i].invalid_memref = NULL;
event_handler(&runtime_event);
}
}
}
}

View File

@ -1,3 +1,4 @@
#include "rc_runtime.h"
#include "rc_internal.h"
#include "../rhash/md5.h"
@ -336,17 +337,9 @@ static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progres
if (!runtime_trigger->trigger)
continue;
switch (runtime_trigger->trigger->state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
/* don't store state for inactive or triggered achievements */
continue;
default:
break;
}
/* don't store state for inactive or triggered achievements */
if (!rc_trigger_state_active(runtime_trigger->trigger->state))
continue;
if (!progress->buffer) {
if (runtime_trigger->serialized_size) {
@ -469,19 +462,12 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
for (i = 0; i < runtime->trigger_count; ++i) {
rc_runtime_trigger_t* runtime_trigger = &runtime->triggers[i];
if (runtime_trigger->trigger) {
switch (runtime_trigger->trigger->state)
/* don't update state for inactive or triggered achievements */
if (rc_trigger_state_active(runtime_trigger->trigger->state))
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
/* don't update state for inactive or triggered achievements */
break;
default:
/* mark active achievements as unupdated. anything that's still unupdated
* after deserializing the progress will be reset to waiting */
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
break;
/* mark active achievements as unupdated. anything that's still unupdated
* after deserializing the progress will be reset to waiting */
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
}
}
}

View File

@ -65,6 +65,10 @@ int rc_trigger_size(const char* memaddr) {
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) {
rc_trigger_t* self;
rc_parse_state_t parse;
if (!buffer || !memaddr)
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_trigger_t, &parse);
@ -73,7 +77,21 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
rc_parse_trigger_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return parse.offset >= 0 ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
int rc_trigger_state_active(int state)
{
switch (state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
return 0;
default:
return 1;
}
}
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
@ -98,21 +116,28 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
char is_paused;
char is_primed;
/* previously triggered, do nothing - return INACTIVE so caller doesn't think it triggered again */
if (self->state == RC_TRIGGER_STATE_TRIGGERED)
return RC_TRIGGER_STATE_INACTIVE;
switch (self->state)
{
case RC_TRIGGER_STATE_TRIGGERED:
/* previously triggered. do nothing - return INACTIVE so caller doesn't think it triggered again */
return RC_TRIGGER_STATE_INACTIVE;
/* unsupported, do nothing - return INACTIVE */
if (self->state == RC_TRIGGER_STATE_DISABLED)
return RC_TRIGGER_STATE_INACTIVE;
case RC_TRIGGER_STATE_DISABLED:
/* unsupported. do nothing - return INACTIVE */
return RC_TRIGGER_STATE_INACTIVE;
case RC_TRIGGER_STATE_INACTIVE:
/* not yet active. update the memrefs so deltas are correct when it becomes active, then return INACTIVE */
rc_update_memref_values(self->memrefs, peek, ud);
return RC_TRIGGER_STATE_INACTIVE;
default:
break;
}
/* update the memory references */
rc_update_memref_values(self->memrefs, peek, ud);
/* not yet active, only update the memrefs so deltas are correct when it becomes active */
if (self->state == RC_TRIGGER_STATE_INACTIVE)
return RC_TRIGGER_STATE_INACTIVE;
/* process the trigger */
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
@ -174,6 +199,11 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
/* if there were hit counts to clear, return RESET, but don't change the state */
if (self->has_hits) {
self->has_hits = 0;
/* cannot be PRIMED while ResetIf is true */
if (self->state == RC_TRIGGER_STATE_PRIMED)
self->state = RC_TRIGGER_STATE_ACTIVE;
return RC_TRIGGER_STATE_RESET;
}

View File

@ -78,7 +78,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
}
else {
/* if it looks like a floating point number, add the 'f' prefix */
while (isdigit(*buffer_ptr))
while (isdigit(*(unsigned char*)buffer_ptr))
++buffer_ptr;
if (*buffer_ptr == '.')
*ptr++ = 'f';
@ -171,6 +171,10 @@ int rc_value_size(const char* memaddr) {
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) {
rc_value_t* self;
rc_parse_state_t parse;
if (!buffer || !memaddr)
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_value_t, &parse);
@ -179,7 +183,7 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
rc_parse_value_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return parse.offset >= 0 ? self : 0;
return (parse.offset >= 0) ? self : NULL;
}
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {

View File

@ -8,8 +8,8 @@
/* internal helper functions in hash.c */
extern void* rc_file_open(const char* path);
extern void rc_file_seek(void* file_handle, size_t offset, int origin);
extern size_t rc_file_tell(void* file_handle);
extern void rc_file_seek(void* file_handle, int64_t offset, int origin);
extern int64_t rc_file_tell(void* file_handle);
extern size_t rc_file_read(void* file_handle, void* buffer, int requested_bytes);
extern void rc_file_close(void* file_handle);
extern int rc_hash_error(const char* message);
@ -22,8 +22,8 @@ struct cdrom_t
void* file_handle;
int sector_size;
int sector_header_size;
int first_sector_offset;
int first_sector;
int64_t first_sector_offset;
};
static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
@ -38,13 +38,14 @@ static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
};
unsigned char header[32];
const int toc_sector = 16;
const int64_t toc_sector = 16;
cdrom->sector_size = 0;
cdrom->sector_header_size = 0;
rc_file_seek(cdrom->file_handle, toc_sector * 2352 + cdrom->first_sector_offset, SEEK_SET);
rc_file_read(cdrom->file_handle, header, sizeof(header));
if (rc_file_read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
return;
if (memcmp(header, sync_pattern, 12) == 0)
{
@ -219,9 +220,9 @@ static char* cdreader_get_bin_path(const char* cue_path, const char* bin_name)
return bin_filename;
}
static size_t cdreader_get_bin_size(const char* cue_path, const char* bin_name)
static int64_t cdreader_get_bin_size(const char* cue_path, const char* bin_name)
{
size_t size = 0;
int64_t size = 0;
char* bin_filename = cdreader_get_bin_path(cue_path, bin_name);
if (bin_filename)
{
@ -242,7 +243,7 @@ static size_t cdreader_get_bin_size(const char* cue_path, const char* bin_name)
static void* cdreader_open_cue_track(const char* path, uint32_t track)
{
void* file_handle;
size_t file_offset = 0;
int64_t file_offset = 0;
char buffer[1024], mode[16];
char* bin_filename;
char file[256];
@ -253,14 +254,14 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
int previous_sector_size = 0;
int previous_index_sector_offset = 0;
int previous_track_is_data = 0;
int previous_track_sector_offset = 0;
int64_t previous_track_sector_offset = 0;
char previous_track_mode[16];
int largest_track = 0;
int largest_track_sector_count = 0;
int largest_track_offset = 0;
int64_t largest_track_offset = 0;
char largest_track_mode[16];
char largest_track_file[256];
int offset = 0;
int64_t offset = 0;
int done = 0;
size_t num_read = 0;
struct cdrom_t* cdrom = NULL;
@ -290,7 +291,8 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
if (strncasecmp(ptr, "INDEX ", 6) == 0)
{
int m = 0, s = 0, f = 0;
int index, sector_offset;
int index;
int sector_offset;
ptr += 6;
index = atoi(ptr);
@ -336,7 +338,9 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
++scan;
*scan = '\0';
snprintf(message, sizeof(message), "Found %s track %d (sector size %d, track starts at %d)", mode, current_track, sector_size, offset);
/* it's undesirable to truncate offset to 32-bits, but %lld isn't defined in c89. */
snprintf(message, sizeof(message), "Found %s track %d (sector size %d, track starts at %d)",
mode, current_track, sector_size, (int)offset);
verbose_message_callback(message);
}
@ -501,7 +505,8 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
if (verbose_message_callback)
{
if (cdrom->first_sector_offset)
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d, track starts at %d)", track, cdrom->sector_size, cdrom->first_sector_offset);
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d, track starts at %d)",
track, cdrom->sector_size, (int)cdrom->first_sector_offset);
else
snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d)", track, cdrom->sector_size);
@ -531,7 +536,7 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
char mode[16] = "MODE1/";
char sector_size[16];
char file[256];
size_t track_size;
int64_t track_size;
int track_type;
char* bin_path = "";
uint32_t current_track = 0;
@ -539,14 +544,14 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
int lba = 0;
uint32_t largest_track = 0;
size_t largest_track_size = 0;
int64_t largest_track_size = 0;
char largest_track_file[256];
char largest_track_sector_size[16];
int largest_track_lba = 0;
int found = 0;
size_t num_read = 0;
size_t file_offset = 0;
int64_t file_offset = 0;
struct cdrom_t* cdrom = NULL;
file_handle = rc_file_open(path);
@ -584,6 +589,9 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
++ptr;
/* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
while (isspace(*ptr))
++ptr;
current_track = (uint32_t)atoi(ptr);
if (track && current_track != track)
continue;
@ -592,22 +600,34 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
++ptr;
++ptr;
while (isspace(*ptr))
++ptr;
lba = atoi(ptr);
while (isdigit(*ptr))
++ptr;
++ptr;
while (isspace(*ptr))
++ptr;
track_type = atoi(ptr);
while (isdigit(*ptr))
++ptr;
++ptr;
while (isspace(*ptr))
++ptr;
ptr2 = sector_size;
while (isdigit(*ptr))
*ptr2++ = *ptr++;
*ptr2 = '\0';
++ptr;
while (isspace(*ptr))
++ptr;
ptr2 = file;
if (*ptr == '\"')
{
@ -698,6 +718,8 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
cdrom = NULL;
}
free(bin_path);
return cdrom;
}
@ -717,7 +739,7 @@ static void* cdreader_open_track(const char* path, uint32_t track)
static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes)
{
size_t sector_start;
int64_t sector_start;
size_t num_read, total_read = 0;
uint8_t* buffer_ptr = (uint8_t*)buffer;
@ -725,7 +747,7 @@ static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* bu
if (!cdrom)
return 0;
sector_start = sector * cdrom->sector_size + cdrom->sector_header_size + cdrom->first_sector_offset;
sector_start = (int64_t)sector * cdrom->sector_size + cdrom->sector_header_size + cdrom->first_sector_offset;
while (requested_bytes > 2048)
{

View File

@ -4,6 +4,7 @@
#include "md5.h"
#include <stdio.h>
#include <ctype.h>
/* arbitrary limit to prevent allocating and hashing large files */
@ -50,14 +51,27 @@ static void* filereader_open(const char* path)
return fopen(path, "rb");
}
static void filereader_seek(void* file_handle, size_t offset, int origin)
static void filereader_seek(void* file_handle, int64_t offset, int origin)
{
fseek((FILE*)file_handle, (long)offset, origin);
#if defined(_WIN32)
_fseeki64((FILE*)file_handle, offset, origin);
#elif defined(_LARGEFILE64_SOURCE)
fseeko64((FILE*)file_handle, offset, origin);
#else
#pragma message("Using generic fseek may fail for large files")
fseek((FILE*)file_handle, offset, origin);
#endif
}
static size_t filereader_tell(void* file_handle)
static int64_t filereader_tell(void* file_handle)
{
#if defined(_WIN32)
return _ftelli64((FILE*)file_handle);
#elif defined(_LARGEFILE64_SOURCE)
return ftello64((FILE*)file_handle);
#else
return ftell((FILE*)file_handle);
#endif
}
static size_t filereader_read(void* file_handle, void* buffer, size_t requested_bytes)
@ -118,13 +132,13 @@ void* rc_file_open(const char* path)
return handle;
}
void rc_file_seek(void* file_handle, size_t offset, int origin)
void rc_file_seek(void* file_handle, int64_t offset, int origin)
{
if (filereader)
filereader->seek(file_handle, offset, origin);
}
size_t rc_file_tell(void* file_handle)
int64_t rc_file_tell(void* file_handle)
{
return (filereader) ? filereader->tell(file_handle) : 0;
}
@ -367,6 +381,50 @@ static int rc_hash_buffer(char hash[33], uint8_t* buffer, size_t buffer_size)
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector, const char* name, unsigned size, const char* description)
{
uint8_t buffer[2048];
size_t num_read;
if ((num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer))) < sizeof(buffer))
{
char message[128];
snprintf(message, sizeof(message), "Could not read %s", description);
return rc_hash_error(message);
}
if (size > MAX_BUFFER_SIZE)
size = MAX_BUFFER_SIZE;
if (verbose_message_callback)
{
char message[128];
if (name)
snprintf(message, sizeof(message), "Hashing %s title (%u bytes) and contents (%u bytes) ", name, (unsigned)strlen(name), size);
else
snprintf(message, sizeof(message), "Hashing %s contents (%u bytes)", description, size);
verbose_message_callback(message);
}
do
{
md5_append(md5, buffer, (int)num_read);
size -= (unsigned)num_read;
if (size == 0)
break;
++sector;
if (size >= sizeof(buffer))
num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
else
num_read = rc_cd_read_sector(track_handle, sector, buffer, size);
} while (num_read > 0);
return 1;
}
static int rc_hash_3do(char hash[33], const char* path)
{
uint8_t buffer[2048];
@ -500,6 +558,20 @@ static int rc_hash_3do(char hash[33], const char* path)
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_7800(char hash[33], uint8_t* buffer, size_t buffer_size)
{
/* if the file contains a header, ignore it */
if (memcmp(&buffer[1], "ATARI7800", 9) == 0)
{
rc_hash_verbose("Ignoring 7800 header");
buffer += 128;
buffer_size -= 128;
}
return rc_hash_buffer(hash, buffer, buffer_size);
}
static int rc_hash_arcade(char hash[33], const char* path)
{
/* arcade hash is just the hash of the filename (no extension) - the cores are pretty stringent about having the right ROM data */
@ -614,7 +686,7 @@ static int rc_hash_nintendo_ds(char hash[33], const char* path)
uint8_t* hash_buffer;
unsigned int hash_size, arm9_size, arm9_addr, arm7_size, arm7_addr, icon_addr;
size_t num_read;
int offset = 0;
int64_t offset = 0;
md5_state_t md5;
void* file_handle;
@ -745,7 +817,10 @@ static int rc_hash_pce_track(char hash[33], void* track_handle)
* the string "PC Engine CD-ROM SYSTEM" should exist at 32 bytes into the sector
* http://shu.sheldows.com/shu/download/pcedocs/pce_cdrom.html
*/
rc_cd_read_sector(track_handle, 1, buffer, 128);
if (rc_cd_read_sector(track_handle, 1, buffer, 128) < 128)
{
return rc_hash_error("Not a PC Engine CD");
}
/* normal PC Engine CD will have a header block in sector 1 */
if (memcmp("PC Engine CD-ROM SYSTEM", &buffer[32], 23) == 0)
@ -914,13 +989,13 @@ static int rc_hash_pcfx_cd(char hash[33], const char* path)
static int rc_hash_dreamcast(char hash[33], const char* path)
{
uint8_t buffer[2048];
uint8_t buffer[256];
void* track_handle;
void* last_track_handle;
char exe_file[32] = "";
unsigned size;
size_t num_read = 0;
uint32_t sector;
uint32_t track_sector;
int result = 0;
md5_state_t md5;
int i = 0;
@ -940,6 +1015,7 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
return rc_hash_error("Not a Dreamcast CD");
}
/* start the hash with the game meta information */
md5_init(&md5);
md5_append(&md5, (md5_byte_t*)buffer, 256);
@ -955,6 +1031,7 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
verbose_message_callback(message);
}
/* the boot filename is 96 bytes into the meta information (https://mc.pp.se/dc/ip0000.bin.html) */
/* remove whitespace from bootfile */
i = 0;
while (!isspace(buffer[96 + i]) && i < 16)
@ -980,50 +1057,98 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
/* last track contains the boot executable */
last_track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_LAST);
track_sector = rc_cd_absolute_sector_to_track_sector(last_track_handle, sector);
sector = rc_cd_absolute_sector_to_track_sector(last_track_handle, sector);
if ((num_read = rc_cd_read_sector(last_track_handle, sector, buffer, sizeof(buffer))) < sizeof(buffer))
rc_hash_error("Could not read boot executable");
if (size > MAX_BUFFER_SIZE)
size = MAX_BUFFER_SIZE;
if (verbose_message_callback)
if ((int32_t)track_sector < 0)
{
char message[128];
snprintf(message, sizeof(message), "Hashing %s contents (%u bytes)", exe_file, size);
verbose_message_callback(message);
/* boot executable is not in the last track; try the primary data track.
* There's only a handful of games that do this: Q*bert was the first identified. */
rc_cd_close_track(last_track_handle);
rc_hash_verbose("Boot executable not found in last track, trying primary track");
last_track_handle = rc_cd_open_track(path, 3);
track_sector = rc_cd_absolute_sector_to_track_sector(last_track_handle, sector);
}
do
{
md5_append(&md5, buffer, (int)num_read);
size -= (unsigned)num_read;
if (size == 0)
break;
++sector;
if (size >= sizeof(buffer))
num_read = rc_cd_read_sector(last_track_handle, sector, buffer, sizeof(buffer));
else
num_read = rc_cd_read_sector(last_track_handle, sector, buffer, size);
} while (num_read > 0);
result = rc_hash_cd_file(&md5, last_track_handle, track_sector, NULL, size, "boot executable");
rc_cd_close_track(last_track_handle);
result = rc_hash_finalize(&md5, hash);
rc_hash_finalize(&md5, hash);
return result;
}
static int rc_hash_find_playstation_executable(void* track_handle, const char* boot_key, const char* cdrom_prefix,
char exe_name[], unsigned exe_name_size, unsigned* exe_size)
{
uint8_t buffer[2048];
unsigned size;
char* ptr;
char* start;
const size_t boot_key_len = strlen(boot_key);
const size_t cdrom_prefix_len = strlen(cdrom_prefix);
int sector;
sector = rc_cd_find_file_sector(track_handle, "SYSTEM.CNF", NULL);
if (!sector)
return 0;
size = (unsigned)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
buffer[size] = '\0';
for (ptr = (char*)buffer; *ptr; ++ptr)
{
if (strncmp(ptr, boot_key, boot_key_len) == 0)
{
ptr += boot_key_len;
while (isspace(*ptr))
++ptr;
if (*ptr == '=')
{
++ptr;
while (isspace(*ptr))
++ptr;
if (strncmp(ptr, cdrom_prefix, cdrom_prefix_len) == 0)
ptr += cdrom_prefix_len;
if (*ptr == '\\')
++ptr;
start = ptr;
while (!isspace(*ptr) && *ptr != ';')
++ptr;
size = (unsigned)(ptr - start);
if (size >= exe_name_size)
size = exe_name_size - 1;
memcpy(exe_name, start, size);
exe_name[size] = '\0';
if (verbose_message_callback)
{
snprintf((char*)buffer, sizeof(buffer), "Looking for boot executable: %s", exe_name);
verbose_message_callback((const char*)buffer);
}
sector = rc_cd_find_file_sector(track_handle, exe_name, exe_size);
break;
}
}
/* advance to end of line */
while (*ptr && *ptr != '\n')
++ptr;
}
return sector;
}
static int rc_hash_psx(char hash[33], const char* path)
{
uint8_t buffer[2048];
uint8_t buffer[32];
char exe_name[64] = "";
char* ptr;
char* start;
void* track_handle;
uint32_t sector;
unsigned size;
@ -1035,63 +1160,12 @@ static int rc_hash_psx(char hash[33], const char* path)
if (!track_handle)
return rc_hash_error("Could not open track");
sector = rc_cd_find_file_sector(track_handle, "SYSTEM.CNF", NULL);
sector = rc_hash_find_playstation_executable(track_handle, "BOOT", "cdrom:", exe_name, sizeof(exe_name), &size);
if (!sector)
{
sector = rc_cd_find_file_sector(track_handle, "PSX.EXE", &size);
if (sector)
strcpy(exe_name, "PSX.EXE");
}
else
{
size = (unsigned)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
buffer[size] = '\0';
for (ptr = (char*)buffer; *ptr; ++ptr)
{
if (strncmp(ptr, "BOOT", 4) == 0)
{
ptr += 4;
while (isspace(*ptr))
++ptr;
if (*ptr == '=')
{
++ptr;
while (isspace(*ptr))
++ptr;
if (strncmp(ptr, "cdrom:", 6) == 0)
ptr += 6;
if (*ptr == '\\')
++ptr;
start = ptr;
while (!isspace(*ptr) && *ptr != ';')
++ptr;
size = (unsigned)(ptr - start);
if (size >= sizeof(exe_name))
size = sizeof(exe_name) - 1;
memcpy(exe_name, start, size);
exe_name[size] = '\0';
if (verbose_message_callback)
{
snprintf((char*)buffer, sizeof(buffer), "Looking for boot executable: %s", exe_name);
verbose_message_callback((const char*)buffer);
}
sector = rc_cd_find_file_sector(track_handle, exe_name, &size);
break;
}
}
/* advance to end of line */
while (*ptr && *ptr != '\n')
++ptr;
}
memcpy(exe_name, "PSX.EXE", 8);
}
if (!sector)
@ -1121,38 +1195,65 @@ static int rc_hash_psx(char hash[33], const char* path)
size = (((uint8_t)buffer[31] << 24) | ((uint8_t)buffer[30] << 16) | ((uint8_t)buffer[29] << 8) | (uint8_t)buffer[28]) + 2048;
}
if (size > MAX_BUFFER_SIZE)
size = MAX_BUFFER_SIZE;
if (verbose_message_callback)
{
char message[128];
snprintf(message, sizeof(message), "Hashing %s title (%u bytes) and contents (%u bytes) ", exe_name, (unsigned)strlen(exe_name), size);
verbose_message_callback(message);
}
/* there's also a few games that are use a singular engine and only differ via their data files. luckily, they have
* unique serial numbers, and use the serial number as the boot file in the standard way. include the boot file in the hash
/* there's a few games that use a singular engine and only differ via their data files. luckily, they have unique
* serial numbers, and use the serial number as the boot file in the standard way. include the boot file name in the hash.
*/
md5_init(&md5);
md5_append(&md5, (md5_byte_t*)exe_name, (int)strlen(exe_name));
do
result = rc_hash_cd_file(&md5, track_handle, sector, exe_name, size, "primary executable");
rc_hash_finalize(&md5, hash);
}
rc_cd_close_track(track_handle);
return result;
}
static int rc_hash_ps2(char hash[33], const char* path)
{
uint8_t buffer[4];
char exe_name[64] = "";
void* track_handle;
uint32_t sector;
unsigned size;
size_t num_read;
int result = 0;
md5_state_t md5;
track_handle = rc_cd_open_track(path, 1);
if (!track_handle)
return rc_hash_error("Could not open track");
sector = rc_hash_find_playstation_executable(track_handle, "BOOT2", "cdrom0:", exe_name, sizeof(exe_name), &size);
if (!sector)
{
rc_hash_error("Could not locate primary executable");
}
else if ((num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer))) < sizeof(buffer))
{
rc_hash_error("Could not read primary executable");
}
else
{
if (memcmp(buffer, "\x7f\x45\x4c\x46", 4) != 0)
{
md5_append(&md5, buffer, (int)num_read);
if (verbose_message_callback)
{
char message[128];
snprintf(message, sizeof(message), "%s did not contain ELF marker", exe_name);
verbose_message_callback(message);
}
}
size -= (unsigned)num_read;
if (size == 0)
break;
/* there's a few games that use a singular engine and only differ via their data files. luckily, they have unique
* serial numbers, and use the serial number as the boot file in the standard way. include the boot file name in the hash.
*/
md5_init(&md5);
md5_append(&md5, (md5_byte_t*)exe_name, (int)strlen(exe_name));
++sector;
if (size >= sizeof(buffer))
num_read = rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
else
num_read = rc_cd_read_sector(track_handle, sector, buffer, size);
} while (num_read > 0);
result = rc_hash_finalize(&md5, hash);
result = rc_hash_cd_file(&md5, track_handle, sector, exe_name, size, "primary executable");
rc_hash_finalize(&md5, hash);
}
rc_cd_close_track(track_handle);
@ -1217,7 +1318,6 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer,
case RC_CONSOLE_APPLE_II:
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_7800:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_GAMEBOY:
@ -1236,11 +1336,16 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, uint8_t* buffer,
case RC_CONSOLE_POKEMON_MINI:
case RC_CONSOLE_SEGA_32X:
case RC_CONSOLE_SG1000:
case RC_CONSOLE_SUPERVISION:
case RC_CONSOLE_TIC80:
case RC_CONSOLE_VECTREX:
case RC_CONSOLE_VIRTUAL_BOY:
case RC_CONSOLE_WONDERSWAN:
return rc_hash_buffer(hash, buffer, buffer_size);
case RC_CONSOLE_ATARI_7800:
return rc_hash_7800(hash, buffer, buffer_size);
case RC_CONSOLE_ATARI_LYNX:
return rc_hash_lynx(hash, buffer, buffer_size);
@ -1259,9 +1364,10 @@ static int rc_hash_whole_file(char hash[33], int console_id, const char* path)
{
md5_state_t md5;
uint8_t* buffer;
size_t size;
int64_t size;
const size_t buffer_size = 65536;
void* file_handle;
size_t remaining;
int result = 0;
file_handle = rc_file_open(path);
@ -1282,7 +1388,9 @@ static int rc_hash_whole_file(char hash[33], int console_id, const char* path)
}
if (size > MAX_BUFFER_SIZE)
size = MAX_BUFFER_SIZE;
remaining = MAX_BUFFER_SIZE;
else
remaining = (size_t)size;
md5_init(&md5);
@ -1290,17 +1398,17 @@ static int rc_hash_whole_file(char hash[33], int console_id, const char* path)
if (buffer)
{
rc_file_seek(file_handle, 0, SEEK_SET);
while (size >= buffer_size)
while (remaining >= buffer_size)
{
rc_file_read(file_handle, buffer, (int)buffer_size);
md5_append(&md5, buffer, (int)buffer_size);
size -= buffer_size;
remaining -= buffer_size;
}
if (size > 0)
if (remaining > 0)
{
rc_file_read(file_handle, buffer, (int)size);
md5_append(&md5, buffer, (int)size);
rc_file_read(file_handle, buffer, (int)remaining);
md5_append(&md5, buffer, (int)remaining);
}
free(buffer);
@ -1314,7 +1422,7 @@ static int rc_hash_whole_file(char hash[33], int console_id, const char* path)
static int rc_hash_buffered_file(char hash[33], int console_id, const char* path)
{
uint8_t* buffer;
size_t size;
int64_t size;
int result = 0;
void* file_handle;
@ -1338,13 +1446,13 @@ static int rc_hash_buffered_file(char hash[33], int console_id, const char* path
if (size > MAX_BUFFER_SIZE)
size = MAX_BUFFER_SIZE;
buffer = (uint8_t*)malloc(size);
buffer = (uint8_t*)malloc((size_t)size);
if (buffer)
{
rc_file_seek(file_handle, 0, SEEK_SET);
rc_file_read(file_handle, buffer, (int)size);
result = rc_hash_generate_from_buffer(hash, console_id, buffer, size);
result = rc_hash_generate_from_buffer(hash, console_id, buffer, (size_t)size);
free(buffer);
}
@ -1492,7 +1600,6 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_APPLE_II:
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_7800:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_GAMEBOY:
@ -1509,6 +1616,8 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_POKEMON_MINI:
case RC_CONSOLE_SEGA_32X:
case RC_CONSOLE_SG1000:
case RC_CONSOLE_SUPERVISION:
case RC_CONSOLE_TIC80:
case RC_CONSOLE_VECTREX:
case RC_CONSOLE_VIRTUAL_BOY:
case RC_CONSOLE_WONDERSWAN:
@ -1523,6 +1632,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
return rc_hash_whole_file(hash, console_id, path);
case RC_CONSOLE_ATARI_7800:
case RC_CONSOLE_ATARI_LYNX:
case RC_CONSOLE_NINTENDO:
case RC_CONSOLE_SUPER_NINTENDO:
@ -1562,6 +1672,12 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
return rc_hash_psx(hash, path);
case RC_CONSOLE_PLAYSTATION_2:
if (rc_path_compare_extension(path, "m3u"))
return rc_hash_generate_from_playlist(hash, console_id, path);
return rc_hash_ps2(hash, path);
case RC_CONSOLE_DREAMCAST:
if (rc_path_compare_extension(path, "m3u"))
return rc_hash_generate_from_playlist(hash, console_id, path);
@ -1601,7 +1717,7 @@ static void rc_hash_initialize_dsk_iterator(struct rc_hash_iterator* iterator, c
if (file)
{
rc_file_seek(file, 0, SEEK_END);
size = rc_file_tell(file);
size = (size_t)rc_file_tell(file);
rc_file_close(file);
}
}
@ -1655,6 +1771,13 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
const char* ext = rc_path_get_extension(path);
switch (tolower(*ext))
{
case '2':
if (rc_path_compare_extension(ext, "2d"))
{
iterator->consoles[0] = RC_CONSOLE_SHARPX1;
}
break;
case '7':
if (rc_path_compare_extension(ext, "7z"))
{
@ -1680,7 +1803,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
void* file = rc_file_open(path);
if (file)
{
size_t size;
int64_t size;
rc_file_seek(file, 0, SEEK_END);
size = rc_file_tell(file);
@ -1690,17 +1813,18 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_3DO; /* 4DO supports directly opening the bin file */
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION; /* PCSX ReARMed supports directly opening the bin file*/
iterator->consoles[2] = RC_CONSOLE_SEGA_CD; /* Genesis Plus GX supports directly opening the bin file*/
iterator->consoles[2] = RC_CONSOLE_PLAYSTATION_2; /* PCSX2 supports directly opening the bin file*/
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* Genesis Plus GX supports directly opening the bin file*/
/* fallback to megadrive which just does a full hash */
iterator->consoles[3] = RC_CONSOLE_MEGA_DRIVE;
iterator->consoles[4] = RC_CONSOLE_MEGA_DRIVE;
break;
}
}
}
/* bin is associated with MegaDrive, Sega32X and Atari 2600. Since they all use the same
* hashing algorithm, only specify one of them */
/* bin is associated with MegaDrive, Sega32X, Atari 2600, and Watara Supervision.
* Since they all use the same hashing algorithm, only specify one of them */
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
}
else if (rc_path_compare_extension(ext, "bs"))
@ -1713,20 +1837,22 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
if (rc_path_compare_extension(ext, "cue"))
{
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
iterator->consoles[1] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[2] = RC_CONSOLE_3DO;
iterator->consoles[3] = RC_CONSOLE_PCFX;
iterator->consoles[4] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[2] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[3] = RC_CONSOLE_3DO;
iterator->consoles[4] = RC_CONSOLE_PCFX;
iterator->consoles[5] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
need_path = 1;
}
else if (rc_path_compare_extension(ext, "chd"))
{
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
iterator->consoles[1] = RC_CONSOLE_DREAMCAST;
iterator->consoles[2] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[3] = RC_CONSOLE_3DO;
iterator->consoles[4] = RC_CONSOLE_PCFX;
iterator->consoles[5] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
iterator->consoles[3] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[4] = RC_CONSOLE_3DO;
iterator->consoles[5] = RC_CONSOLE_PCFX;
iterator->consoles[6] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
need_path = 1;
}
else if (rc_path_compare_extension(ext, "col"))
@ -1747,6 +1873,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
else if (rc_path_compare_extension(ext, "d88"))
{
iterator->consoles[0] = RC_CONSOLE_PC8800;
iterator->consoles[1] = RC_CONSOLE_SHARPX1;
}
break;
@ -1759,6 +1886,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_NINTENDO;
}
else if (rc_path_compare_extension(ext, "fd"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
}
break;
case 'g':
@ -1787,8 +1918,9 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
case 'i':
if (rc_path_compare_extension(ext, "iso"))
{
iterator->consoles[0] = RC_CONSOLE_3DO;
iterator->consoles[1] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[1] = RC_CONSOLE_3DO;
iterator->consoles[2] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
need_path = 1;
}
break;
@ -1800,6 +1932,13 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
}
break;
case 'k':
if (rc_path_compare_extension(ext, "k7"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* tape */
}
break;
case 'l':
if (rc_path_compare_extension(ext, "lnx"))
{
@ -1835,6 +1974,14 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_MSX;
}
else if (rc_path_compare_extension(ext, "m5"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* cartridge */
}
else if (rc_path_compare_extension(ext, "m7"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* cartridge */
}
break;
case 'n':
@ -1868,6 +2015,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
if (rc_path_compare_extension(ext, "rom"))
{
iterator->consoles[0] = RC_CONSOLE_MSX;
iterator->consoles[1] = RC_CONSOLE_THOMSONTO8; /* cartridge */
}
if (rc_path_compare_extension(ext, "ri"))
{
@ -1890,6 +2038,14 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_PC_ENGINE;
}
else if (rc_path_compare_extension(ext, "sv"))
{
iterator->consoles[0] = RC_CONSOLE_SUPERVISION;
}
else if (rc_path_compare_extension(ext, "sap"))
{
iterator->consoles[0] = RC_CONSOLE_THOMSONTO8; /* disk */
}
break;
case 't':
@ -1897,6 +2053,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_ORIC;
}
else if (rc_path_compare_extension(ext, "tic"))
{
iterator->consoles[0] = RC_CONSOLE_TIC80;
}
break;
case 'v':

View File

@ -98,7 +98,7 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
}
/* Evaluate the signature. */
snprintf(signature, sizeof(signature), "%u%s%u", lboard_id, user_name, lboard_id);
snprintf(signature, sizeof(signature), "%u%s%d", lboard_id, user_name, value);
md5_init(&state);
md5_append(&state, (unsigned char*)signature, (int)strlen(signature));
md5_finish(&state, hash);
@ -298,7 +298,7 @@ int rc_url_append_unum(char* buffer, size_t buffer_size, size_t* buffer_offset,
int written = rc_url_append_param_equals(buffer, buffer_size, *buffer_offset, param);
if (written > 0) {
char num[16];
int chars = sprintf(num, "%u", value);
int chars = snprintf(num, sizeof(num), "%u", value);
if (chars + written < (int)buffer_size)
{