upgrade to rcheevos 10.5 (#14622)

This commit is contained in:
Jamiras 2022-11-13 15:24:42 -07:00 committed by GitHub
parent 106a6c268e
commit e46e03094f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 528 additions and 496 deletions

View File

@ -82,6 +82,11 @@ enum {
RC_CONSOLE_ZEEBO = 70,
RC_CONSOLE_ARDUBOY = 71,
RC_CONSOLE_WASM4 = 72,
RC_CONSOLE_ARCADIA_2001 = 73,
RC_CONSOLE_INTERTON_VC_4000 = 74,
RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER = 75,
RC_CONSOLE_PC_ENGINE_CD = 76,
RC_CONSOLE_ATARI_JAGUAR_CD = 77,
RC_CONSOLE_HUBS = 100,
RC_CONSOLE_EVENTS = 101

View File

@ -95,6 +95,7 @@ extern "C" {
#define RC_HASH_CDTRACK_FIRST_DATA ((uint32_t)-1)
#define RC_HASH_CDTRACK_LAST ((uint32_t)-2)
#define RC_HASH_CDTRACK_LARGEST ((uint32_t)-3)
#define RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION ((uint32_t)-4)
/* opens a track from the specified file. track 0 indicates the largest data track should be opened.
* returns a handle to be passed to the other functions, or NULL if the track could not be opened.

View File

@ -56,6 +56,7 @@ enum {
RC_MEMSIZE_32_BITS_BE,
RC_MEMSIZE_FLOAT,
RC_MEMSIZE_MBF32,
RC_MEMSIZE_MBF32_LE,
RC_MEMSIZE_VARIABLE
};
@ -169,7 +170,8 @@ enum {
RC_OPERATOR_NONE,
RC_OPERATOR_MULT,
RC_OPERATOR_DIV,
RC_OPERATOR_AND
RC_OPERATOR_AND,
RC_OPERATOR_XOR
};
typedef struct rc_condition_t rc_condition_t;

View File

@ -10,7 +10,9 @@
#include <string.h>
#define RETROACHIEVEMENTS_HOST "https://retroachievements.org"
#define RETROACHIEVEMENTS_IMAGE_HOST "http://i.retroachievements.org"
#define RETROACHIEVEMENTS_IMAGE_HOST "https://media.retroachievements.org"
#define RETROACHIEVEMENTS_HOST_NONSSL "http://retroachievements.org"
#define RETROACHIEVEMENTS_IMAGE_HOST_NONSSL "http://media.retroachievements.org"
static char* g_host = NULL;
static char* g_imagehost = NULL;
@ -1054,6 +1056,16 @@ static void rc_api_update_host(char** host, const char* hostname) {
void rc_api_set_host(const char* hostname) {
rc_api_update_host(&g_host, hostname);
if (!hostname) {
/* also clear out the image hostname */
rc_api_set_image_host(NULL);
}
else if (strcmp(hostname, RETROACHIEVEMENTS_HOST_NONSSL) == 0) {
/* if just pointing at the non-HTTPS host, explicitly use the default image host
* so it doesn't try to use the web host directly */
rc_api_set_image_host(RETROACHIEVEMENTS_IMAGE_HOST_NONSSL);
}
}
void rc_api_set_image_host(const char* hostname) {

View File

@ -50,6 +50,10 @@ static int rc_parse_operator(const char** memaddr) {
++(*memaddr);
return RC_OPERATOR_AND;
case '^':
++(*memaddr);
return RC_OPERATOR_XOR;
case '\0':/* end of string */
case '_': /* next condition */
case 'S': /* next condset */
@ -135,6 +139,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
case RC_OPERATOR_MULT:
case RC_OPERATOR_DIV:
case RC_OPERATOR_AND:
case RC_OPERATOR_XOR:
/* modifying operators are only valid on modifying statements */
if (can_modify)
break;
@ -260,5 +265,11 @@ void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self,
rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED);
value->value.u32 &= amount.value.u32;
break;
case RC_OPERATOR_XOR:
rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED);
value->value.u32 ^= amount.value.u32;
break;
}
}

View File

@ -84,6 +84,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, in
switch ((*next)->oper)
{
case RC_OPERATOR_AND:
case RC_OPERATOR_XOR:
case RC_OPERATOR_DIV:
case RC_OPERATOR_MULT:
case RC_OPERATOR_NONE:
@ -185,6 +186,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
measured_value.type = RC_VALUE_TYPE_NONE;
measured_from_hits = 0;
can_measure = 1;
total_hits = 0;
eval_state->primed = 1;
set_valid = 1;

View File

@ -21,6 +21,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_ARCADE:
return "Arcade";
case RC_CONSOLE_ARCADIA_2001:
return "Arcadia 2001";
case RC_CONSOLE_ARDUBOY:
return "Arduboy";
@ -36,6 +39,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_ATARI_JAGUAR:
return "Atari Jaguar";
case RC_CONSOLE_ATARI_JAGUAR_CD:
return "Atari Jaguar CD";
case RC_CONSOLE_ATARI_LYNX:
return "Atari Lynx";
@ -57,6 +63,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_DREAMCAST:
return "Dreamcast";
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
return "Elektor TV Games Computer";
case RC_CONSOLE_EVENTS:
return "Events";
@ -90,6 +99,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_INTELLIVISION:
return "Intellivision";
case RC_CONSOLE_INTERTON_VC_4000:
return "Interton VC 4000";
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
return "Magnavox Odyssey 2";
@ -147,6 +159,9 @@ const char* rc_console_name(int console_id)
case RC_CONSOLE_PC_ENGINE:
return "PC Engine";
case RC_CONSOLE_PC_ENGINE_CD:
return "PC Engine CD";
case RC_CONSOLE_PLAYSTATION:
return "PlayStation";
@ -274,6 +289,16 @@ static const rc_memory_region_t _rc_memory_regions_appleii[] = {
};
static const rc_memory_regions_t rc_memory_regions_appleii = { _rc_memory_regions_appleii, 2 };
/* ===== Arcadia 2001 ===== */
/* https://amigan.yatho.com/a-coding.txt */
/* RAM banks 1 and 2 only exist on some variant models - no game actually uses them */
static const rc_memory_region_t _rc_memory_regions_arcadia_2001[] = {
{ 0x000000U, 0x0000FFU, 0x001800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RAM bank 3 */
{ 0x000100U, 0x0001FFU, 0x001900U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "I/O Area" },
{ 0x000200U, 0x0002FFU, 0x001A00U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }, /* RAM bank 4 */
};
static const rc_memory_regions_t rc_memory_regions_arcadia_2001 = { _rc_memory_regions_arcadia_2001, 3 };
/* ===== Arduboy ===== */
/* https://scienceprog.com/avr-microcontroller-memory-map/ (Atmega32) */
static const rc_memory_region_t _rc_memory_regions_arduboy[] = {
@ -356,6 +381,16 @@ static const rc_memory_region_t _rc_memory_regions_dreamcast[] = {
};
static const rc_memory_regions_t rc_memory_regions_dreamcast = { _rc_memory_regions_dreamcast, 1 };
/* ===== Elektor TV Games Computer ===== */
/* https://amigan.yatho.com/e-coding.txt */
static const rc_memory_region_t _rc_memory_regions_elektor_tv_games[] = {
{ 0x000000U, 0x0013FFU, 0x000800U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x001400U, 0x0014FFU, 0x001C00U, RC_MEMORY_TYPE_UNUSED, "Unused" }, /* mirror of $1D00-$1DFF */
{ 0x001500U, 0x0016FFU, 0x001D00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "I/O Area" }, /* two 256-byte I/O areas */
{ 0x001700U, 0x0017FFU, 0x001F00U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
};
static const rc_memory_regions_t rc_memory_regions_elektor_tv_games = { _rc_memory_regions_elektor_tv_games, 4 };
/* ===== Fairchild Channel F ===== */
static const rc_memory_region_t _rc_memory_regions_fairchild_channel_f[] = {
/* "System RAM" is actually just a bunch of registers internal to CPU so all carts have it.
@ -454,6 +489,16 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
};
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 10 };
/* ===== Interton VC 4000 ===== */
/* https://amigan.yatho.com/i-coding.txt */
/* Cartridge RAM is not persisted, it's just expanded storage */
static const rc_memory_region_t _rc_memory_regions_interton_vc_4000[] = {
{ 0x000000U, 0x0003FFU, 0x001800U, RC_MEMORY_TYPE_SYSTEM_RAM, "Cartridge RAM" },
{ 0x000400U, 0x0004FFU, 0x001E00U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "I/O Area" },
{ 0x000500U, 0x0005FFU, 0x001F00U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
};
static const rc_memory_regions_t rc_memory_regions_interton_vc_4000 = { _rc_memory_regions_interton_vc_4000, 3 };
/* ===== 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[] = {
@ -479,6 +524,15 @@ static const rc_memory_region_t _rc_memory_regions_megadrive[] = {
};
static const rc_memory_regions_t rc_memory_regions_megadrive = { _rc_memory_regions_megadrive, 2 };
/* ===== MegaDrive 32X (Genesis 32X) ===== */
/* https://en.wikibooks.org/wiki/Genesis_Programming/68K_Memory_map/ */
static const rc_memory_region_t _rc_memory_regions_megadrive_32x[] = {
{ 0x000000U, 0x00FFFFU, 0xFF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x010000U, 0x04FFFFU, 0x200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "32X RAM"},
{ 0x050000U, 0x05FFFFU, 0x000000U, RC_MEMORY_TYPE_SAVE_RAM, "Cartridge RAM" }
};
static const rc_memory_regions_t rc_memory_regions_megadrive_32x = { _rc_memory_regions_megadrive_32x, 3 };
/* ===== MSX ===== */
/* https://www.msx.org/wiki/The_Memory */
/* MSX only has 64KB of addressable RAM, of which 32KB is reserved for the system/BIOS.
@ -501,6 +555,35 @@ static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
};
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
/* ===== Neo Geo CD ===== */
/* https://wiki.neogeodev.org/index.php?title=68k_memory_map */
/* NeoCD exposes $000000-$1FFFFF as System RAM, but it seems like only the WORKRAM section is used.
* This is consistent with http://www.hardmvs.fr/manuals/NeoGeoProgrammersGuide.pdf (page25), which says:
*
* Furthermore, the NEO-GEO provides addresses 100000H-10FFFFH as a work area, out of which the
* addresses 10F300H-10FFFFH are reserved exclusively for use by the system program. Therefore,
* every game is to use addresses 100000H-10F2FFH.
*
* Also note that PRG files (game ROM) can be loaded anywhere else in the $000000-$1FFFFF range.
* AoF3 illustrates this pretty clearly: https://wiki.neogeodev.org/index.php?title=IPL_file
*
* PROG_CD.PRG,0,0
* PROG_CDX.PRG,0,058000
* CNV_NM.PRG,0,0C0000
* FIX_DATA.PRG,0,0FD000
* OBJACTLK.PRG,0,130000
* SSEL_CNV.PRG,0,15A000
* SSEL_BAK.PRG,0,16F000
* HITMSG.PRG,0,170000
* SSEL_SPR.PRG,0,19D000
*/
static const rc_memory_region_t _rc_memory_regions_neo_geo_cd[] = {
{ 0x000000U, 0x00F2FFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
/* NOTE: some BIOS settings are exposed through the reserved RAM: https://wiki.neogeodev.org/index.php?title=68k_ASM_defines */
{ 0x00F300U, 0x00FFFFU, 0x0010F300U, RC_MEMORY_TYPE_SYSTEM_RAM, "Reserved RAM" },
};
static const rc_memory_regions_t rc_memory_regions_neo_geo_cd = { _rc_memory_regions_neo_geo_cd, 2 };
/* ===== Nintendo Entertainment System ===== */
/* https://wiki.nesdev.com/w/index.php/CPU_memory_map */
static const rc_memory_region_t _rc_memory_regions_nes[] = {
@ -566,11 +649,18 @@ static const rc_memory_regions_t rc_memory_regions_pc8800 = { _rc_memory_regions
/* http://www.archaicpixels.com/Memory_Map */
static const rc_memory_region_t _rc_memory_regions_pc_engine[] = {
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
};
static const rc_memory_regions_t rc_memory_regions_pc_engine = { _rc_memory_regions_pc_engine, 1 };
/* ===== PC Engine CD===== */
/* http://www.archaicpixels.com/Memory_Map */
static const rc_memory_region_t _rc_memory_regions_pc_engine_cd[] = {
{ 0x000000U, 0x001FFFU, 0x1F0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
{ 0x002000U, 0x011FFFU, 0x100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD 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" }
};
static const rc_memory_regions_t rc_memory_regions_pc_engine = { _rc_memory_regions_pc_engine, 4 };
static const rc_memory_regions_t rc_memory_regions_pc_engine_cd = { _rc_memory_regions_pc_engine_cd, 4 };
/* ===== PC-FX ===== */
/* http://daifukkat.su/pcfx/data/memmap.html */
@ -758,6 +848,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_APPLE_II:
return &rc_memory_regions_appleii;
case RC_CONSOLE_ARCADIA_2001:
return &rc_memory_regions_arcadia_2001;
case RC_CONSOLE_ARDUBOY:
return &rc_memory_regions_arduboy;
@ -768,6 +861,7 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
return &rc_memory_regions_atari7800;
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_ATARI_JAGUAR_CD:
return &rc_memory_regions_atari_jaguar;
case RC_CONSOLE_ATARI_LYNX:
@ -782,6 +876,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_DREAMCAST:
return &rc_memory_regions_dreamcast;
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
return &rc_memory_regions_elektor_tv_games;
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
return &rc_memory_regions_fairchild_channel_f;
@ -801,6 +898,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_INTELLIVISION:
return &rc_memory_regions_intellivision;
case RC_CONSOLE_INTERTON_VC_4000:
return &rc_memory_regions_interton_vc_4000;
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
return &rc_memory_regions_magnavox_odyssey_2;
@ -808,17 +908,20 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
return &rc_memory_regions_master_system;
case RC_CONSOLE_MEGA_DRIVE:
case RC_CONSOLE_SEGA_32X:
/* NOTE: 32x adds an extra 512KB of memory (256KB RAM + 256KB VRAM) to the
* Genesis, but we currently don't support it. */
return &rc_memory_regions_megadrive;
case RC_CONSOLE_SEGA_32X:
return &rc_memory_regions_megadrive_32x;
case RC_CONSOLE_MSX:
return &rc_memory_regions_msx;
case RC_CONSOLE_NEOGEO_POCKET:
return &rc_memory_regions_neo_geo_pocket;
case RC_CONSOLE_NEO_GEO_CD:
return &rc_memory_regions_neo_geo_cd;
case RC_CONSOLE_NINTENDO:
return &rc_memory_regions_nes;
@ -837,6 +940,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
case RC_CONSOLE_PC_ENGINE:
return &rc_memory_regions_pc_engine;
case RC_CONSOLE_PC_ENGINE_CD:
return &rc_memory_regions_pc_engine_cd;
case RC_CONSOLE_PCFX:
return &rc_memory_regions_pcfx;

View File

@ -95,6 +95,7 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
switch (*aux++) {
case 'f': case 'F': *size = RC_MEMSIZE_FLOAT; break;
case 'm': case 'M': *size = RC_MEMSIZE_MBF32; break;
case 'l': case 'L': *size = RC_MEMSIZE_MBF32_LE; break;
default:
return RC_INVALID_FP_OPERAND;
@ -119,8 +120,9 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
static float rc_build_float(unsigned mantissa_bits, int exponent, int sign) {
/* 32-bit float has a 23-bit mantissa and 8-bit exponent */
const unsigned mantissa = mantissa_bits | 0x800000;
double dbl = ((double)mantissa) / ((double)0x800000);
const unsigned implied_bit = 1 << 23;
const unsigned mantissa = mantissa_bits | implied_bit;
double dbl = ((double)mantissa) / ((double)implied_bit);
if (exponent > 127) {
/* exponent above 127 is a special number */
@ -151,7 +153,16 @@ static float rc_build_float(unsigned mantissa_bits, int exponent, int sign) {
}
else if (exponent < 0) {
/* exponent from -1 to -127 is a number less than 1 */
exponent = -exponent;
if (exponent == -127) {
/* exponent -127 (all exponent bits were zero) is a denormalized value
* (no implied leading bit) with exponent -126 */
dbl = ((double)mantissa_bits) / ((double)implied_bit);
exponent = 126;
} else {
exponent = -exponent;
}
while (exponent > 30) {
dbl /= (double)(1 << 30);
exponent -= 30;
@ -170,12 +181,7 @@ static void rc_transform_memref_float(rc_typed_value_t* value) {
const unsigned mantissa = (value->value.u32 & 0x7FFFFF);
const int exponent = (int)((value->value.u32 >> 23) & 0xFF) - 127;
const int sign = (value->value.u32 & 0x80000000);
if (mantissa == 0 && exponent == -127)
value->value.f32 = (sign) ? -0.0f : 0.0f;
else
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}
@ -196,6 +202,21 @@ static void rc_transform_memref_mbf32(rc_typed_value_t* value) {
value->type = RC_VALUE_TYPE_FLOAT;
}
static void rc_transform_memref_mbf32_le(rc_typed_value_t* value) {
/* decodes a Microsoft Binary Format float */
/* Locomotive BASIC (CPC) uses MBF40, but in little endian format */
const unsigned mantissa = value->value.u32 & 0x007FFFFF;
const int exponent = (int)(value->value.u32 >> 24) - 129;
const int sign = (value->value.u32 & 0x00800000);
if (mantissa == 0 && exponent == -129)
value->value.f32 = (sign) ? -0.0f : 0.0f;
else
value->value.f32 = rc_build_float(mantissa, exponent, sign);
value->type = RC_VALUE_TYPE_FLOAT;
}
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
void rc_transform_memref_value(rc_typed_value_t* value, char size) {
@ -288,6 +309,10 @@ void rc_transform_memref_value(rc_typed_value_t* value, char size) {
rc_transform_memref_mbf32(value);
break;
case RC_MEMSIZE_MBF32_LE:
rc_transform_memref_mbf32_le(value);
break;
default:
break;
}
@ -314,6 +339,7 @@ static const unsigned rc_memref_masks[] = {
0xffffffff, /* RC_MEMSIZE_32_BITS_BE */
0xffffffff, /* RC_MEMSIZE_FLOAT */
0xffffffff, /* RC_MEMSIZE_MBF32 */
0xffffffff, /* RC_MEMSIZE_MBF32_LE */
0xffffffff /* RC_MEMSIZE_VARIABLE */
};
@ -349,6 +375,7 @@ static const char rc_memref_shared_sizes[] = {
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_32_BITS_BE */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_FLOAT */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32 */
RC_MEMSIZE_32_BITS, /* RC_MEMSIZE_MBF32_LE */
RC_MEMSIZE_32_BITS /* RC_MEMSIZE_VARIABLE */
};

View File

@ -300,6 +300,7 @@ int rc_operand_is_float_memref(const rc_operand_t* self) {
switch (self->size) {
case RC_MEMSIZE_FLOAT:
case RC_MEMSIZE_MBF32:
case RC_MEMSIZE_MBF32_LE:
return 1;
default:
@ -319,6 +320,13 @@ int rc_operand_is_memref(const rc_operand_t* self) {
}
}
int rc_operand_is_float(const rc_operand_t* self) {
if (self->type == RC_OPERAND_FP)
return 1;
return rc_operand_is_float_memref(self);
}
unsigned rc_transform_operand_value(unsigned value, const rc_operand_t* self) {
switch (self->type)
{

View File

@ -154,6 +154,7 @@ int rc_condition_is_combining(const rc_condition_t* self);
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse);
void rc_evaluate_operand(rc_typed_value_t* value, rc_operand_t* self, rc_eval_state_t* eval_state);
int rc_operand_is_float_memref(const rc_operand_t* self);
int rc_operand_is_float(const rc_operand_t* self);
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);
int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud, lua_State* L);

View File

@ -111,6 +111,11 @@ static const rc_disallowed_setting_t _rc_disallowed_ppsspp_settings[] = {
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_quasi88_settings[] = {
{ "q88_cpu_clock", ",1,2" },
{ NULL, NULL }
};
static const rc_disallowed_setting_t _rc_disallowed_smsplus_settings[] = {
{ "smsplus_region", "pal" },
{ NULL, NULL }
@ -144,6 +149,7 @@ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
{ "PPSSPP", _rc_disallowed_ppsspp_settings },
{ "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
{ "PicoDrive", _rc_disallowed_picodrive_settings },
{ "QUASI88", _rc_disallowed_quasi88_settings },
{ "SMS Plus GX", _rc_disallowed_smsplus_settings },
{ "Snes9x", _rc_disallowed_snes9x_settings },
{ "Virtual Jaguar", _rc_disallowed_virtual_jaguar_settings },

View File

@ -534,10 +534,11 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
display = nextline;
display_line = parse->lines_read;
/* scan as long as we find conditional lines or full line comments */
do {
line = nextline;
nextline = rc_parse_line(line, &endline, parse);
} while (*line == '?');
} while (*line == '?' || (line[0] == '/' && line[1] == '/'));
}
line = nextline;
@ -557,38 +558,48 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
nextline = rc_parse_line(line, &endline, parse);
while (*line == '?') {
/* conditional display: ?trigger?string */
ptr = ++line;
while (ptr < endline && *ptr != '?')
++ptr;
do {
if (line[0] == '?') {
/* conditional display: ?trigger?string */
ptr = ++line;
while (ptr < endline && *ptr != '?')
++ptr;
if (ptr < endline) {
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, firstlookup);
if (parse->offset < 0)
return;
trigger = &((*nextdisplay)->trigger);
rc_parse_trigger_internal(trigger, &line, parse);
trigger->memrefs = 0;
if (parse->offset < 0)
return;
if (parse->buffer)
nextdisplay = &((*nextdisplay)->next);
if (ptr < endline) {
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, firstlookup);
if (parse->offset < 0)
return;
trigger = &((*nextdisplay)->trigger);
rc_parse_trigger_internal(trigger, &line, parse);
trigger->memrefs = 0;
if (parse->offset < 0)
return;
if (parse->buffer)
nextdisplay = &((*nextdisplay)->next);
}
}
else if (line[0] != '/' || line[1] != '/') {
break;
}
line = nextline;
nextline = rc_parse_line(line, &endline, parse);
}
} while (1);
/* non-conditional display: string */
*nextdisplay = rc_parse_richpresence_display_internal(line, endline, parse, firstlookup);
if (*nextdisplay) {
hasdisplay = 1;
nextdisplay = &((*nextdisplay)->next);
}
/* restore the parser state */
parse->lines_read = lines_read;
/* restore the parser state */
parse->lines_read = lines_read;
}
else {
/* this should only happen if the line is blank.
* expect parse->offset to be RC_MISSING_DISPLAY_STRING and leave parse->lines_read
* on the current line for error tracking. */
}
}
/* finalize */

View File

@ -429,7 +429,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
previous_ptr = NULL;
previous = self->richpresence;
while (previous) {
if (previous && memcmp(self->richpresence->md5, md5, 16) == 0) {
if (previous && self->richpresence->richpresence && memcmp(self->richpresence->md5, md5, 16) == 0) {
/* unchanged. reset all of the conditions */
rc_reset_richpresence(self->richpresence->richpresence);
@ -685,7 +685,7 @@ void rc_runtime_reset(rc_runtime_t* self) {
rc_reset_lboard(self->lboards[i].lboard);
}
if (self->richpresence) {
if (self->richpresence && self->richpresence->richpresence) {
rc_richpresence_display_t* display = self->richpresence->richpresence->first_display;
while (display != 0) {
rc_reset_trigger(&display->trigger);

View File

@ -111,6 +111,7 @@ void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_stat
case RC_OPERATOR_MULT:
case RC_OPERATOR_DIV:
case RC_OPERATOR_AND:
case RC_OPERATOR_XOR:
case RC_OPERATOR_NONE:
break;

View File

@ -22,6 +22,7 @@ struct cdrom_t
void* file_handle; /* the file handle for reading the track data */
int sector_size; /* the size of each sector in the track data */
int sector_header_size; /* the offset to the raw data within a sector block */
int raw_data_size; /* the amount of raw data within a sector block */
int64_t file_track_offset;/* the offset of the track data within the file */
int track_first_sector; /* the first absolute sector associated to the track (includes pregap) */
int track_pregap_sectors; /* the number of pregap sectors */
@ -58,6 +59,7 @@ static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
cdrom->sector_size = 0;
cdrom->sector_header_size = 0;
cdrom->raw_data_size = 2048;
rc_file_seek(cdrom->file_handle, toc_sector * 2352 + cdrom->file_track_offset, SEEK_SET);
if (rc_file_read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
@ -219,6 +221,7 @@ static int cdreader_open_bin(struct cdrom_t* cdrom, const char* path, const char
{
cdrom->sector_size = 2352;
cdrom->sector_header_size = 0;
cdrom->raw_data_size = 2352; /* no header or footer data on audio tracks */
}
}
@ -282,6 +285,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
char* bin_filename = NULL;
char *ptr, *ptr2, *end;
int done = 0;
int session = 1;
size_t num_read = 0;
struct cdrom_t* cdrom = NULL;
@ -389,6 +393,13 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
done = 1;
break;
}
if (track == RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION && session == 2)
{
track = current_track.id;
done = 1;
break;
}
}
}
else if (strncasecmp(ptr, "TRACK ", 6) == 0)
@ -465,6 +476,20 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
if (ptr2 - ptr < (int)sizeof(current_track.filename))
memcpy(current_track.filename, ptr, ptr2 - ptr);
}
else if (strncasecmp(ptr, "REM ", 4) == 0)
{
ptr += 4;
while (*ptr == ' ')
++ptr;
if (strncasecmp(ptr, "SESSION ", 8) == 0)
{
ptr += 8;
while (*ptr == ' ')
++ptr;
session = atoi(ptr);
}
}
while (*ptr && *ptr != '\n')
++ptr;
@ -794,18 +819,18 @@ static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* bu
sector_start = (int64_t)(sector - cdrom->track_first_sector) * cdrom->sector_size +
cdrom->sector_header_size + cdrom->file_track_offset;
while (requested_bytes > 2048)
while (requested_bytes > (size_t)cdrom->raw_data_size)
{
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, 2048);
num_read = rc_file_read(cdrom->file_handle, buffer_ptr, cdrom->raw_data_size);
total_read += num_read;
if (num_read < 2048)
if (num_read < (size_t)cdrom->raw_data_size)
return total_read;
buffer_ptr += 2048;
buffer_ptr += cdrom->raw_data_size;
sector_start += cdrom->sector_size;
requested_bytes -= 2048;
requested_bytes -= cdrom->raw_data_size;
}
rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);

View File

@ -223,6 +223,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
{
uint8_t buffer[2048], *tmp;
int sector;
unsigned num_sectors = 0;
size_t filename_length;
const char* slash;
@ -247,6 +248,8 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
}
else
{
unsigned logical_block_size;
/* find the cd information */
if (!rc_cd_read_sector(track_handle, rc_cd_first_track_sector(track_handle) + 16, buffer, 256))
return 0;
@ -255,6 +258,15 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
* https://www.cdroller.com/htm/readdata.html
*/
sector = buffer[156 + 2] | (buffer[156 + 3] << 8) | (buffer[156 + 4] << 16);
/* if the table of contents spans more than one sector, it's length of section will exceed it's logical block size */
logical_block_size = (buffer[128] | (buffer[128 + 1] << 8)); /* logical block size */
if (logical_block_size == 0) {
num_sectors = 1;
} else {
num_sectors = (buffer[156 + 10] | (buffer[156 + 11] << 8)); /* length of section */
num_sectors /= logical_block_size;
}
}
/* fetch and process the directory record */
@ -265,18 +277,31 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
while (tmp < buffer + sizeof(buffer))
{
if (!*tmp)
return 0;
{
/* end of this path table block. if the path table spans multiple sectors, keep scanning */
if (num_sectors > 1)
{
--num_sectors;
if (rc_cd_read_sector(track_handle, ++sector, buffer, sizeof(buffer)))
{
tmp = buffer;
continue;
}
}
break;
}
/* filename is 33 bytes into the record and the format is "FILENAME;version" or "DIRECTORY" */
if ((tmp[33 + filename_length] == ';' || tmp[33 + filename_length] == '\0') &&
if ((tmp[32] == filename_length || tmp[33 + filename_length] == ';') &&
strncasecmp((const char*)(tmp + 33), path, filename_length) == 0)
{
sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
if (verbose_message_callback)
{
snprintf((char*)buffer, sizeof(buffer), "Found %s at sector %d", path, sector);
verbose_message_callback((const char*)buffer);
char message[128];
snprintf(message, sizeof(message), "Found %s at sector %d", path, sector);
verbose_message_callback(message);
}
if (size)
@ -347,6 +372,34 @@ int rc_path_compare_extension(const char* path, const char* ext)
/* ===================================================== */
static void rc_hash_byteswap16(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_byteswap32(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_finalize(md5_state_t* md5, char hash[33])
{
md5_byte_t digest[16];
@ -415,6 +468,9 @@ static int rc_hash_cd_file(md5_state_t* md5, void* track_handle, uint32_t sector
verbose_message_callback(message);
}
if (size < (unsigned)num_read)
size = (unsigned)num_read;
do
{
md5_append(md5, buffer, (int)num_read);
@ -684,6 +740,93 @@ static int rc_hash_text(char hash[33], const uint8_t* buffer, size_t buffer_size
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_jaguar_cd(char hash[33], const char* path)
{
uint8_t buffer[2352];
char message[128];
void* track_handle;
md5_state_t md5;
int byteswapped;
unsigned size = 0;
unsigned offset = 0;
unsigned sector = 0;
unsigned remaining;
unsigned i;
/* Jaguar CD header is in the first sector of the first data track OF THE SECOND SESSION.
* The first track must be an audio track, but may be a warning message or actual game audio */
track_handle = rc_cd_open_track(path, RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION);
if (!track_handle)
return rc_hash_error("Could not open track");
/* The header is an unspecified distance into the first sector, but usually two bytes in.
* It consists of 64 bytes of "TAIR" or "ATRI" repeating, depending on whether or not the data
* is byteswapped. Then another 32 byte that reads "ATARI APPROVED DATA HEADER ATRI "
* (possibly byteswapped). Then a big-endian 32-bit value for the address where the boot code
* should be loaded, and a second big-endian 32-bit value for the size of the boot code. */
sector = rc_cd_first_track_sector(track_handle);
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
for (i = 64; i < sizeof(buffer) - 32 - 4 * 3; i++)
{
if (memcmp(&buffer[i], "TARA IPARPVODED TA AEHDAREA RT I", 32) == 0)
{
byteswapped = 1;
offset = i + 32 + 4;
size = (buffer[offset] << 16) | (buffer[offset + 1] << 24) | (buffer[offset + 2]) | (buffer[offset + 3] << 8);
break;
}
else if (memcmp(&buffer[i], "ATARI APPROVED DATA HEADER ATRI ", 32) == 0)
{
byteswapped = 0;
offset = i + 32 + 4;
size = (buffer[offset] << 24) | (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | (buffer[offset + 3]);
break;
}
}
if (size == 0) /* did not see ATARI APPROVED DATA HEADER */
{
rc_cd_close_track(track_handle);
return rc_hash_error("Not a Jaguar CD");
}
md5_init(&md5);
offset += 4;
if (verbose_message_callback)
{
snprintf(message, sizeof(message), "Hashing boot executable (%u bytes starting at %u bytes into sector %u)", size, offset, sector);
rc_hash_verbose(message);
}
do
{
if (byteswapped)
rc_hash_byteswap16(buffer, &buffer[sizeof(buffer)]);
remaining = sizeof(buffer) - offset;
if (remaining >= size)
{
md5_append(&md5, &buffer[offset], size);
size = 0;
break;
}
md5_append(&md5, &buffer[offset], remaining);
size -= remaining;
offset = 0;
} while (rc_cd_read_sector(track_handle, ++sector, buffer, sizeof(buffer)) == sizeof(buffer));
rc_cd_close_track(track_handle);
if (size > 0)
return rc_hash_error("Not enough data");
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size)
{
/* if the file contains a header, ignore it */
@ -698,6 +841,70 @@ static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size
return rc_hash_buffer(hash, buffer, buffer_size);
}
static int rc_hash_neogeo_cd(char hash[33], const char* path)
{
char buffer[1024], *ptr;
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");
/* https://wiki.neogeodev.org/index.php?title=IPL_file, https://wiki.neogeodev.org/index.php?title=PRG_file
* IPL file specifies data to be loaded before the game starts. PRG files are the executable code
*/
sector = rc_cd_find_file_sector(track_handle, "IPL.TXT", &size);
if (!sector)
{
rc_cd_close_track(track_handle);
return rc_hash_error("Not a NeoGeo CD game disc");
}
if (rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer)) == 0)
{
rc_cd_close_track(track_handle);
return 0;
}
md5_init(&md5);
buffer[sizeof(buffer) - 1] = '\0';
ptr = &buffer[0];
do
{
char* start = ptr;
while (*ptr && *ptr != '.')
++ptr;
if (memcmp(ptr, ".PRG", 4) == 0)
{
ptr += 4;
*ptr++ = '\0';
sector = rc_cd_find_file_sector(track_handle, start, &size);
if (!sector || !rc_hash_cd_file(&md5, track_handle, sector, NULL, size, start))
{
char error[128];
rc_cd_close_track(track_handle);
snprintf(error, sizeof(error), "Could not read %.16s", start);
return rc_hash_error(error);
}
}
while (*ptr && *ptr != '\n')
++ptr;
if (*ptr != '\n')
break;
++ptr;
} while (*ptr != '\0' && *ptr != '\x1a');
rc_cd_close_track(track_handle);
return rc_hash_finalize(&md5, hash);
}
static int rc_hash_nes(char hash[33], const uint8_t* buffer, size_t buffer_size)
{
/* if the file contains a header, ignore it */
@ -719,34 +926,6 @@ static int rc_hash_nes(char hash[33], const uint8_t* buffer, size_t 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 char* path)
{
uint8_t* buffer;
@ -818,9 +997,9 @@ static int rc_hash_n64(char hash[33], const char* path)
rc_file_read(file_handle, buffer, (int)buffer_size);
if (is_v64)
rc_hash_v64_to_z64(buffer, stop);
rc_hash_byteswap16(buffer, stop);
else if (is_n64)
rc_hash_n64_to_z64(buffer, stop);
rc_hash_byteswap32(buffer, stop);
md5_append(&md5, buffer, (int)buffer_size);
remaining -= buffer_size;
@ -832,9 +1011,9 @@ static int rc_hash_n64(char hash[33], const char* path)
stop = buffer + remaining;
if (is_v64)
rc_hash_v64_to_z64(buffer, stop);
rc_hash_byteswap16(buffer, stop);
else if (is_n64)
rc_hash_n64_to_z64(buffer, stop);
rc_hash_byteswap32(buffer, stop);
md5_append(&md5, buffer, (int)remaining);
}
@ -1454,18 +1633,30 @@ static int rc_hash_psp(char hash[33], const char* path)
*/
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\PARAM.SFO", &size);
if (!sector)
{
rc_cd_close_track(track_handle);
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"))
{
rc_cd_close_track(track_handle);
return 0;
}
sector = rc_cd_find_file_sector(track_handle, "PSP_GAME\\SYSDIR\\EBOOT.BIN", &size);
if (!sector)
{
rc_cd_close_track(track_handle);
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"))
{
rc_cd_close_track(track_handle);
return 0;
}
rc_cd_close_track(track_handle);
return rc_hash_finalize(&md5, hash);
@ -1606,16 +1797,19 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
case RC_CONSOLE_AMSTRAD_PC:
case RC_CONSOLE_APPLE_II:
case RC_CONSOLE_ARCADIA_2001:
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_COMMODORE_64:
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
case RC_CONSOLE_GAMEBOY:
case RC_CONSOLE_GAMEBOY_ADVANCE:
case RC_CONSOLE_GAMEBOY_COLOR:
case RC_CONSOLE_GAME_GEAR:
case RC_CONSOLE_INTELLIVISION:
case RC_CONSOLE_INTERTON_VC_4000:
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
case RC_CONSOLE_MASTER_SYSTEM:
case RC_CONSOLE_MEGA_DRIVE:
@ -1898,18 +2092,20 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
return rc_hash_error(buffer);
}
case RC_CONSOLE_ARCADIA_2001:
case RC_CONSOLE_ATARI_2600:
case RC_CONSOLE_ATARI_JAGUAR:
case RC_CONSOLE_COLECOVISION:
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
case RC_CONSOLE_GAMEBOY:
case RC_CONSOLE_GAMEBOY_ADVANCE:
case RC_CONSOLE_GAMEBOY_COLOR:
case RC_CONSOLE_GAME_GEAR:
case RC_CONSOLE_INTELLIVISION:
case RC_CONSOLE_INTERTON_VC_4000:
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
case RC_CONSOLE_MASTER_SYSTEM:
case RC_CONSOLE_MEGA_DRIVE:
case RC_CONSOLE_MEGADUCK:
case RC_CONSOLE_NEOGEO_POCKET:
case RC_CONSOLE_ORIC:
@ -1928,6 +2124,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_AMSTRAD_PC:
case RC_CONSOLE_APPLE_II:
case RC_CONSOLE_COMMODORE_64:
case RC_CONSOLE_MEGA_DRIVE:
case RC_CONSOLE_MSX:
case RC_CONSOLE_PC8800:
/* generic whole-file hash with m3u support - don't buffer */
@ -1940,6 +2137,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_ATARI_7800:
case RC_CONSOLE_ATARI_LYNX:
case RC_CONSOLE_NINTENDO:
case RC_CONSOLE_PC_ENGINE:
case RC_CONSOLE_SUPER_NINTENDO:
/* additional logic whole-file hash - buffer then call rc_hash_generate_from_buffer */
return rc_hash_buffered_file(hash, console_id, path);
@ -1953,13 +2151,25 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_ARCADE:
return rc_hash_arcade(hash, path);
case RC_CONSOLE_ATARI_JAGUAR_CD:
return rc_hash_jaguar_cd(hash, path);
case RC_CONSOLE_DREAMCAST:
if (rc_path_compare_extension(path, "m3u"))
return rc_hash_generate_from_playlist(hash, console_id, path);
return rc_hash_dreamcast(hash, path);
case RC_CONSOLE_NEO_GEO_CD:
return rc_hash_neogeo_cd(hash, path);
case RC_CONSOLE_NINTENDO_64:
return rc_hash_n64(hash, path);
case RC_CONSOLE_NINTENDO_DS:
return rc_hash_nintendo_ds(hash, path);
case RC_CONSOLE_PC_ENGINE:
case RC_CONSOLE_PC_ENGINE_CD:
if (rc_path_compare_extension(path, "cue") || rc_path_compare_extension(path, "chd"))
return rc_hash_pce_cd(hash, path);
@ -1989,12 +2199,6 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
case RC_CONSOLE_PSP:
return rc_hash_psp(hash, path);
case RC_CONSOLE_DREAMCAST:
if (rc_path_compare_extension(path, "m3u"))
return rc_hash_generate_from_playlist(hash, console_id, path);
return rc_hash_dreamcast(hash, path);
case RC_CONSOLE_SEGA_CD:
case RC_CONSOLE_SATURN:
if (rc_path_compare_extension(path, "m3u"))
@ -2131,14 +2335,15 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
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 */
/* fallback to megadrive which just does a full hash. */
iterator->consoles[4] = RC_CONSOLE_MEGA_DRIVE;
break;
}
}
}
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, MegaDuck, and Fairchild Channel F.
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, MegaDuck,
* Fairchild Channel F, Arcadia 2001, and Interton VC 4000.
* Since they all use the same hashing algorithm, only specify one of them */
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
}
@ -2155,9 +2360,11 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[5] = RC_CONSOLE_3DO;
iterator->consoles[6] = RC_CONSOLE_PCFX;
iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD;
iterator->consoles[8] = RC_CONSOLE_ATARI_JAGUAR_CD;
need_path = 1;
}
else if (rc_path_compare_extension(ext, "chd"))
@ -2166,7 +2373,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE;
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
iterator->consoles[5] = RC_CONSOLE_3DO;
iterator->consoles[6] = RC_CONSOLE_PCFX;
need_path = 1;
@ -2346,6 +2553,10 @@ 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, "pgm"))
{
iterator->consoles[0] = RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER;
}
break;
case 'r':
@ -2395,6 +2606,10 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
{
iterator->consoles[0] = RC_CONSOLE_TIC80;
}
else if (rc_path_compare_extension(ext, "tvc"))
{
iterator->consoles[0] = RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER;
}
break;
case 'v':

View File

@ -1,402 +0,0 @@
#include "rc_url.h"
#include "../rcheevos/rc_compat.h"
#include "../rhash/md5.h"
#include <stdio.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) {
for (;;) {
switch (*str) {
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
case '-': case '_': case '.': case '~':
if (len < 2)
return -1;
*encoded++ = *str++;
--len;
break;
case ' ':
if (len < 2)
return -1;
*encoded++ = '+';
++str;
--len;
break;
default:
if (len < 4)
return -1;
snprintf(encoded, len, "%%%02x", (unsigned char)*str);
encoded += 3;
++str;
len -= 3;
break;
case '\0':
*encoded = 0;
return 0;
}
}
}
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token,
unsigned cheevo_id, int hardcore, const char* game_hash) {
char urle_user_name[64];
char urle_login_token[64];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=awardachievement&u=%s&t=%s&a=%u&h=%d",
urle_user_name,
urle_login_token,
cheevo_id,
hardcore ? 1 : 0
);
if (game_hash && strlen(game_hash) == 32 && (size - (size_t)written) >= 35) {
written += snprintf(buffer + written, size - (size_t)written, "&m=%s", game_hash);
}
return (size_t)written >= size ? -1 : 0;
}
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value) {
char urle_user_name[64];
char urle_login_token[64];
char signature[64];
unsigned char hash[16];
md5_state_t state;
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
/* Evaluate the signature. */
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);
written = snprintf(
buffer,
size,
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_login_token,
lboard_id,
value,
hash[ 0], hash[ 1], hash[ 2], hash[ 3], hash[ 4], hash[ 5], hash[ 6], hash[ 7],
hash[ 8], hash[ 9], hash[10], hash[11],hash[12], hash[13], hash[14], hash[15]
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_get_gameid(char* buffer, size_t size, const char* hash) {
int written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=gameid&m=%s",
hash
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_get_patch(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid) {
char urle_user_name[64];
char urle_login_token[64];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=patch&u=%s&t=%s&g=%u",
urle_user_name,
urle_login_token,
gameid
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_get_badge_image(char* buffer, size_t size, const char* badge_name) {
int written = snprintf(
buffer,
size,
"http://i.retroachievements.org/Badge/%s",
badge_name
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_login_with_password(char* buffer, size_t size, const char* user_name, const char* password) {
char urle_user_name[64];
char urle_password[256];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_password, sizeof(urle_password), password) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&p=%s",
urle_user_name,
urle_password
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_login_with_token(char* buffer, size_t size, const char* user_name, const char* login_token) {
char urle_user_name[64];
char urle_login_token[64];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=login&u=%s&t=%s",
urle_user_name,
urle_login_token
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_get_unlock_list(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid, int hardcore) {
char urle_user_name[64];
char urle_login_token[64];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=%d",
urle_user_name,
urle_login_token,
gameid,
hardcore ? 1 : 0
);
return (size_t)written >= size ? -1 : 0;
}
int rc_url_post_playing(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned gameid) {
char urle_user_name[64];
char urle_login_token[64];
int written;
if (rc_url_encode(urle_user_name, sizeof(urle_user_name), user_name) != 0) {
return -1;
}
if (rc_url_encode(urle_login_token, sizeof(urle_login_token), login_token) != 0) {
return -1;
}
written = snprintf(
buffer,
size,
RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php?r=postactivity&u=%s&t=%s&a=3&m=%u",
urle_user_name,
urle_login_token,
gameid
);
return (size_t)written >= size ? -1 : 0;
}
static int rc_url_append_param_equals(char* buffer, size_t buffer_size, size_t buffer_offset, const char* param)
{
int written = 0;
size_t param_len;
if (buffer_offset >= buffer_size)
return -1;
if (buffer_offset) {
buffer += buffer_offset;
buffer_size -= buffer_offset;
if (buffer[-1] != '?') {
*buffer++ = '&';
buffer_size--;
written = 1;
}
}
param_len = strlen(param);
if (param_len + 1 >= buffer_size)
return -1;
memcpy(buffer, param, param_len);
buffer[param_len] = '=';
written += (int)param_len + 1;
return written + (int)buffer_offset;
}
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);
if (written > 0) {
char num[16];
int chars = snprintf(num, sizeof(num), "%u", value);
if (chars + written < (int)buffer_size) {
memcpy(&buffer[written], num, chars + 1);
*buffer_offset = written + chars;
return 0;
}
}
return -1;
}
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);
if (written > 0) {
buffer += written;
buffer_size -= written;
if (rc_url_encode(buffer, buffer_size, value) == 0) {
written += (int)strlen(buffer);
*buffer_offset = written;
return 0;
}
}
return -1;
}
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* base_url = RCHEEVOS_URL_PROTOCOL"://retroachievements.org/dorequest.php";
size_t written = strlen(base_url);
int failure = 0;
if (url_buffer_size < written + 1)
return -1;
memcpy(url_buffer, base_url, written);
url_buffer[written++] = '?';
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);
*buffer_offset += written;
return failure;
}
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)
{
size_t written = 0;
int failure = rc_url_build_dorequest(url_buffer, url_buffer_size, &written, "ping", user_name);
failure |= rc_url_append_unum(url_buffer, url_buffer_size, &written, "g", gameid);
written = 0;
failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "t", login_token);
if (rich_presence && *rich_presence)
failure |= rc_url_append_str(post_buffer, post_buffer_size, &written, "m", rich_presence);
if (failure) {
if (url_buffer_size)
url_buffer[0] = '\0';
if (post_buffer_size)
post_buffer[0] = '\0';
}
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

View File

@ -5617,7 +5617,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE,
"Disables cheats, rewind, pause, slow-motion, and loading save states. Achievements earned in hardcore mode are uniquely marked so that you can show others what you've achieved without emulator assistance features. Toggling this setting at runtime will restart the game."
"Disables cheats, rewind, slow-motion, and loading save states. Achievements earned in hardcore mode are uniquely marked so that you can show others what you've achieved without emulator assistance features. Toggling this setting at runtime will restart the game."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEEVOS_LEADERBOARDS_ENABLE,
@ -8205,7 +8205,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE,
"Pause achievements hardcore mode for the current session. This action will enable save states, cheats, rewind, pause, and slow-motion."
"Pause achievements hardcore mode for the current session. This action will enable cheats, rewind, slow-motion, and loading save states."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME,
@ -8213,7 +8213,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME,
"Resume achievements hardcore mode for the current session. This action will disable save states, cheats, rewind, pause, and slow-motion and reset the current game."
"Resume achievements hardcore mode for the current session. This action will disable cheats, rewind, slow-motion, and loading save states and reset the current game."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_NOT_LOGGED_IN,

View File

@ -4970,6 +4970,7 @@ static unsigned menu_displaylist_parse_content_information(
tmp[_len ] = ':';
tmp[_len+1] = ' ';
tmp[_len+2] = '\n';
tmp[_len+3] = '\0';
strlcat(tmp, rcheevos_get_hash(), sizeof(tmp));
if (menu_entries_append(info->list, tmp, cheevos_hash_str,
MENU_ENUM_LABEL_VALUE_CONTENT_INFO_CHEEVOS_HASH,