mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 03:32:46 +00:00
upgrade to rcheevos 10.7 (#15152)
This commit is contained in:
parent
54055558f1
commit
08a5288144
@ -218,10 +218,11 @@ uint8_t* rcheevos_patch_address(unsigned address)
|
||||
static unsigned rcheevos_peek(unsigned address,
|
||||
unsigned num_bytes, void* ud)
|
||||
{
|
||||
uint8_t* data = rc_libretro_memory_find(
|
||||
&rcheevos_locals.memory, address);
|
||||
unsigned avail;
|
||||
uint8_t* data = rc_libretro_memory_find_avail(
|
||||
&rcheevos_locals.memory, address, &avail);
|
||||
|
||||
if (data)
|
||||
if (data && avail >= num_bytes)
|
||||
{
|
||||
switch (num_bytes)
|
||||
{
|
||||
|
11
deps/rcheevos/CHANGELOG.md
vendored
11
deps/rcheevos/CHANGELOG.md
vendored
@ -1,3 +1,14 @@
|
||||
# v10.7.0
|
||||
* add hash method and memory map for Gamecube
|
||||
* add console enum, hash method, and memory map for DSi
|
||||
* add console enum, hash method, and memory map for TI-83
|
||||
* add console enum, hash method, and memory map for Uzebox
|
||||
* add constant for rcheevos version; include in start session server API call
|
||||
* fix SubSource calculations using float values
|
||||
* fix game identification for homebrew Jaguar CD games
|
||||
* fix game identification for CD with many files at root directory
|
||||
* address _CRT_SECURE_NO_WARNINGS warnings
|
||||
|
||||
# v10.6.0
|
||||
* add RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED
|
||||
* use optimized comparators for most common condition logic
|
||||
|
20
deps/rcheevos/include/rc_api_request.h
vendored
20
deps/rcheevos/include/rc_api_request.h
vendored
@ -10,15 +10,25 @@ extern "C" {
|
||||
/**
|
||||
* A block of memory for variable length data (like strings and arrays).
|
||||
*/
|
||||
typedef struct rc_api_buffer_t {
|
||||
typedef struct rc_api_buffer_chunk_t {
|
||||
/* The current location where data is being written */
|
||||
char* write;
|
||||
/* The first byte past the end of data where writing cannot occur */
|
||||
char* end;
|
||||
/* The first byte of the data */
|
||||
char* start;
|
||||
/* The next block in the allocated memory chain */
|
||||
struct rc_api_buffer_t* next;
|
||||
/* The buffer containing the data. The actual size may be larger than 256 bytes for buffers allocated in
|
||||
* the next chain. The 256 byte size specified is for the initial allocation within the container object. */
|
||||
struct rc_api_buffer_chunk_t* next;
|
||||
}
|
||||
rc_api_buffer_chunk_t;
|
||||
|
||||
/**
|
||||
* A preallocated block of memory for variable length data (like strings and arrays).
|
||||
*/
|
||||
typedef struct rc_api_buffer_t {
|
||||
/* The chunk data (will point at the local data member) */
|
||||
struct rc_api_buffer_chunk_t chunk;
|
||||
/* Small chunk of memory pre-allocated for the chunk */
|
||||
char data[256];
|
||||
}
|
||||
rc_api_buffer_t;
|
||||
@ -41,7 +51,7 @@ rc_api_request_t;
|
||||
* Common attributes for all server responses.
|
||||
*/
|
||||
typedef struct rc_api_response_t {
|
||||
/* Server-provided success indicator (non-zero on failure) */
|
||||
/* Server-provided success indicator (non-zero on success, zero on failure) */
|
||||
int succeeded;
|
||||
/* Server-provided message associated to the failure */
|
||||
const char* error_message;
|
||||
|
3
deps/rcheevos/include/rc_consoles.h
vendored
3
deps/rcheevos/include/rc_consoles.h
vendored
@ -87,6 +87,9 @@ enum {
|
||||
RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER = 75,
|
||||
RC_CONSOLE_PC_ENGINE_CD = 76,
|
||||
RC_CONSOLE_ATARI_JAGUAR_CD = 77,
|
||||
RC_CONSOLE_NINTENDO_DSI = 78,
|
||||
RC_CONSOLE_TI83 = 79,
|
||||
RC_CONSOLE_UZEBOX = 80,
|
||||
|
||||
RC_CONSOLE_HUBS = 100,
|
||||
RC_CONSOLE_EVENTS = 101
|
||||
|
3
deps/rcheevos/include/rc_runtime.h
vendored
3
deps/rcheevos/include/rc_runtime.h
vendored
@ -121,7 +121,8 @@ enum {
|
||||
RC_RUNTIME_EVENT_LBOARD_TRIGGERED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED,
|
||||
RC_RUNTIME_EVENT_LBOARD_DISABLED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED
|
||||
};
|
||||
|
||||
typedef struct rc_runtime_event_t {
|
||||
|
98
deps/rcheevos/src/rapi/rc_api_common.c
vendored
98
deps/rcheevos/src/rapi/rc_api_common.c
vendored
@ -299,6 +299,8 @@ int rc_json_get_required_object(rc_json_field_t* fields, size_t field_count, rc_
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!json)
|
||||
@ -359,6 +361,8 @@ int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator,
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!field->value_start || *field->value_start != '[') {
|
||||
@ -453,6 +457,8 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -564,6 +570,8 @@ int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_na
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -615,6 +623,8 @@ int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* fi
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -656,11 +666,13 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (*field->value_start == '\"') {
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if (sscanf(field->value_start + 1, "%d-%d-%d %d:%d:%d",
|
||||
if (sscanf_s(field->value_start + 1, "%d-%d-%d %d:%d:%d",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
|
||||
tm.tm_mon--; /* 0-based */
|
||||
tm.tm_year -= 1900; /* 1900 based */
|
||||
@ -671,9 +683,11 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
|
||||
* timezone conversion twice and manually removing the difference */
|
||||
{
|
||||
time_t local_timet = mktime(&tm);
|
||||
struct tm* gmt_tm = gmtime(&local_timet);
|
||||
time_t skewed_timet = mktime(gmt_tm); /* applies local time adjustment second time */
|
||||
time_t tz_offset = skewed_timet - local_timet;
|
||||
time_t skewed_timet, tz_offset;
|
||||
struct tm gmt_tm;
|
||||
gmtime_s(&gmt_tm, &local_timet);
|
||||
skewed_timet = mktime(&gmt_tm); /* applies local time adjustment second time */
|
||||
tz_offset = skewed_timet - local_timet;
|
||||
*out = local_timet - tz_offset;
|
||||
}
|
||||
|
||||
@ -698,6 +712,8 @@ int rc_json_get_bool(int* out, const rc_json_field_t* field, const char* field_n
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (src) {
|
||||
@ -733,31 +749,32 @@ int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_js
|
||||
/* --- rc_buf --- */
|
||||
|
||||
void rc_buf_init(rc_api_buffer_t* buffer) {
|
||||
buffer->write = &buffer->data[0];
|
||||
buffer->end = &buffer->data[sizeof(buffer->data)];
|
||||
buffer->next = NULL;
|
||||
buffer->chunk.write = buffer->chunk.start = &buffer->data[0];
|
||||
buffer->chunk.end = &buffer->data[sizeof(buffer->data)];
|
||||
buffer->chunk.next = NULL;
|
||||
}
|
||||
|
||||
void rc_buf_destroy(rc_api_buffer_t* buffer) {
|
||||
rc_api_buffer_chunk_t *chunk;
|
||||
#ifdef DEBUG_BUFFERS
|
||||
int count = 0;
|
||||
int wasted = 0;
|
||||
int total = 0;
|
||||
#endif
|
||||
|
||||
/* first buffer is not allocated */
|
||||
buffer = buffer->next;
|
||||
/* first chunk is not allocated. skip it. */
|
||||
chunk = buffer->chunk.next;
|
||||
|
||||
/* deallocate any additional buffers */
|
||||
while (buffer) {
|
||||
rc_api_buffer_t* next = buffer->next;
|
||||
while (chunk) {
|
||||
rc_api_buffer_chunk_t* next = chunk->next;
|
||||
#ifdef DEBUG_BUFFERS
|
||||
total += (int)(buffer->end - buffer->data);
|
||||
wasted += (int)(buffer->end - buffer->write);
|
||||
total += (int)(chunk->end - chunk->data);
|
||||
wasted += (int)(chunk->end - chunk->write);
|
||||
++count;
|
||||
#endif
|
||||
free(buffer);
|
||||
buffer = next;
|
||||
free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_BUFFERS
|
||||
@ -767,47 +784,50 @@ void rc_buf_destroy(rc_api_buffer_t* buffer) {
|
||||
}
|
||||
|
||||
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount) {
|
||||
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
|
||||
size_t remaining;
|
||||
while (buffer) {
|
||||
remaining = buffer->end - buffer->write;
|
||||
while (chunk) {
|
||||
remaining = chunk->end - chunk->write;
|
||||
if (remaining >= amount)
|
||||
return buffer->write;
|
||||
return chunk->write;
|
||||
|
||||
if (!buffer->next) {
|
||||
/* allocate a chunk of memory that is a multiple of 256-bytes. casting it to an rc_api_buffer_t will
|
||||
* effectively unbound the data field, so use write and end pointers to track how data is being used.
|
||||
if (!chunk->next) {
|
||||
/* allocate a chunk of memory that is a multiple of 256-bytes. the first 32 bytes will be associated
|
||||
* to the chunk header, and the remaining will be used for data.
|
||||
*/
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_t) - sizeof(buffer->data);
|
||||
const size_t alloc_size = (amount + buffer_prefix_size + 0xFF) & ~0xFF;
|
||||
buffer->next = (rc_api_buffer_t*)malloc(alloc_size);
|
||||
if (!buffer->next)
|
||||
const size_t chunk_header_size = sizeof(rc_api_buffer_chunk_t);
|
||||
const size_t alloc_size = (chunk_header_size + amount + 0xFF) & ~0xFF;
|
||||
chunk->next = (rc_api_buffer_chunk_t*)malloc(alloc_size);
|
||||
if (!chunk->next)
|
||||
break;
|
||||
|
||||
buffer->next->write = buffer->next->data;
|
||||
buffer->next->end = buffer->next->write + (alloc_size - buffer_prefix_size);
|
||||
buffer->next->next = NULL;
|
||||
chunk->next->start = (char*)chunk->next + chunk_header_size;
|
||||
chunk->next->write = chunk->next->start;
|
||||
chunk->next->end = (char*)chunk->next + alloc_size;
|
||||
chunk->next->next = NULL;
|
||||
}
|
||||
|
||||
buffer = buffer->next;
|
||||
chunk = chunk->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end) {
|
||||
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
|
||||
do {
|
||||
if (buffer->write == start) {
|
||||
size_t offset = (end - buffer->data);
|
||||
if (chunk->write == start) {
|
||||
size_t offset = (end - chunk->start);
|
||||
offset = (offset + 7) & ~7;
|
||||
buffer->write = &buffer->data[offset];
|
||||
chunk->write = &chunk->start[offset];
|
||||
|
||||
if (buffer->write > buffer->end)
|
||||
buffer->write = buffer->end;
|
||||
if (chunk->write > chunk->end)
|
||||
chunk->write = chunk->end;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = buffer->next;
|
||||
} while (buffer);
|
||||
chunk = chunk->next;
|
||||
} while (chunk);
|
||||
}
|
||||
|
||||
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount) {
|
||||
@ -830,13 +850,13 @@ void rc_api_format_md5(char checksum[33], const unsigned char digest[16]) {
|
||||
/* --- rc_url_builder --- */
|
||||
|
||||
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer, size_t estimated_size) {
|
||||
rc_api_buffer_t* used_buffer;
|
||||
rc_api_buffer_chunk_t* used_buffer;
|
||||
|
||||
memset(builder, 0, sizeof(*builder));
|
||||
builder->buffer = buffer;
|
||||
builder->write = builder->start = rc_buf_reserve(buffer, estimated_size);
|
||||
|
||||
used_buffer = buffer;
|
||||
used_buffer = &buffer->chunk;
|
||||
while (used_buffer && used_buffer->write != builder->write)
|
||||
used_buffer = used_buffer->next;
|
||||
|
||||
@ -859,7 +879,7 @@ static int rc_url_builder_reserve(rc_api_url_builder_t* builder, size_t amount)
|
||||
if (remaining < amount) {
|
||||
const size_t used = builder->write - builder->start;
|
||||
const size_t current_size = builder->end - builder->start;
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_t) - sizeof(builder->buffer->data);
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_chunk_t);
|
||||
char* new_start;
|
||||
size_t new_size = (current_size < 256) ? 256 : current_size * 2;
|
||||
do {
|
||||
|
3
deps/rcheevos/src/rapi/rc_api_common.h
vendored
3
deps/rcheevos/src/rapi/rc_api_common.h
vendored
@ -14,6 +14,7 @@ typedef struct rc_api_url_builder_t {
|
||||
char* write;
|
||||
char* start;
|
||||
char* end;
|
||||
/* pointer to a preallocated rc_api_buffer_t */
|
||||
rc_api_buffer_t* buffer;
|
||||
int result;
|
||||
}
|
||||
@ -23,6 +24,8 @@ void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer,
|
||||
void rc_url_builder_append(rc_api_url_builder_t* builder, const char* data, size_t len);
|
||||
const char* rc_url_builder_finalize(rc_api_url_builder_t* builder);
|
||||
|
||||
#define RC_JSON_NEW_FIELD(n) {n,0,0,0}
|
||||
|
||||
typedef struct rc_json_field_t {
|
||||
const char* name;
|
||||
const char* value_start;
|
||||
|
80
deps/rcheevos/src/rapi/rc_api_info.c
vendored
80
deps/rcheevos/src/rapi/rc_api_info.c
vendored
@ -39,27 +39,27 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"AchievementID"},
|
||||
{"Response"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("AchievementID"),
|
||||
RC_JSON_NEW_FIELD("Response")
|
||||
/* unused fields
|
||||
{"Offset"},
|
||||
{"Count"},
|
||||
{"FriendsOnly"},
|
||||
RC_JSON_NEW_FIELD("Offset"),
|
||||
RC_JSON_NEW_FIELD("Count"),
|
||||
RC_JSON_NEW_FIELD("FriendsOnly")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t response_fields[] = {
|
||||
{"NumEarned"},
|
||||
{"TotalPlayers"},
|
||||
{"GameID"},
|
||||
{"RecentWinner"} /* array */
|
||||
RC_JSON_NEW_FIELD("NumEarned"),
|
||||
RC_JSON_NEW_FIELD("TotalPlayers"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("RecentWinner") /* array */
|
||||
};
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"DateAwarded"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("DateAwarded")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -143,38 +143,38 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
|
||||
char format[16];
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"LeaderboardData"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("LeaderboardData")
|
||||
};
|
||||
|
||||
rc_json_field_t leaderboarddata_fields[] = {
|
||||
{"LBID"},
|
||||
{"LBFormat"},
|
||||
{"LowerIsBetter"},
|
||||
{"LBTitle"},
|
||||
{"LBDesc"},
|
||||
{"LBMem"},
|
||||
{"GameID"},
|
||||
{"LBAuthor"},
|
||||
{"LBCreated"},
|
||||
{"LBUpdated"},
|
||||
{"Entries"} /* array */
|
||||
RC_JSON_NEW_FIELD("LBID"),
|
||||
RC_JSON_NEW_FIELD("LBFormat"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("LBTitle"),
|
||||
RC_JSON_NEW_FIELD("LBDesc"),
|
||||
RC_JSON_NEW_FIELD("LBMem"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("LBAuthor"),
|
||||
RC_JSON_NEW_FIELD("LBCreated"),
|
||||
RC_JSON_NEW_FIELD("LBUpdated"),
|
||||
RC_JSON_NEW_FIELD("Entries") /* array */
|
||||
/* unused fields
|
||||
{"GameTitle"},
|
||||
{"ConsoleID"},
|
||||
{"ConsoleName"},
|
||||
{"ForumTopicID"},
|
||||
{"GameIcon"},
|
||||
RC_JSON_NEW_FIELD("GameTitle"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
RC_JSON_NEW_FIELD("ConsoleName"),
|
||||
RC_JSON_NEW_FIELD("ForumTopicID"),
|
||||
RC_JSON_NEW_FIELD("GameIcon")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"Rank"},
|
||||
{"Index"},
|
||||
{"Score"},
|
||||
{"DateSubmitted"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("Index"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("DateSubmitted")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -281,9 +281,9 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
|
||||
char* end;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Response"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Response")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
|
130
deps/rcheevos/src/rapi/rc_api_runtime.c
vendored
130
deps/rcheevos/src/rapi/rc_api_runtime.c
vendored
@ -31,9 +31,9 @@ int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_res
|
||||
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"GameID"},
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("GameID")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -76,6 +76,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
rc_json_field_t iterator;
|
||||
const char* str;
|
||||
const char* last_author = "";
|
||||
const char* last_author_field = "";
|
||||
size_t last_author_len = 0;
|
||||
size_t len;
|
||||
unsigned timet;
|
||||
@ -83,46 +84,46 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
char format[16];
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"PatchData"} /* nested object */
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("PatchData") /* nested object */
|
||||
};
|
||||
|
||||
rc_json_field_t patchdata_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"ConsoleID"},
|
||||
{"ImageIcon"},
|
||||
{"RichPresencePatch"},
|
||||
{"Achievements"}, /* array */
|
||||
{"Leaderboards"} /* array */
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
RC_JSON_NEW_FIELD("ImageIcon"),
|
||||
RC_JSON_NEW_FIELD("RichPresencePatch"),
|
||||
RC_JSON_NEW_FIELD("Achievements"), /* array */
|
||||
RC_JSON_NEW_FIELD("Leaderboards") /* array */
|
||||
/* unused fields
|
||||
{"ForumTopicID"},
|
||||
{"Flags"},
|
||||
RC_JSON_NEW_FIELD("ForumTopicID"),
|
||||
RC_JSON_NEW_FIELD("Flags")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t achievement_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"Description"},
|
||||
{"Flags"},
|
||||
{"Points"},
|
||||
{"MemAddr"},
|
||||
{"Author"},
|
||||
{"BadgeName"},
|
||||
{"Created"},
|
||||
{"Modified"}
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("Description"),
|
||||
RC_JSON_NEW_FIELD("Flags"),
|
||||
RC_JSON_NEW_FIELD("Points"),
|
||||
RC_JSON_NEW_FIELD("MemAddr"),
|
||||
RC_JSON_NEW_FIELD("Author"),
|
||||
RC_JSON_NEW_FIELD("BadgeName"),
|
||||
RC_JSON_NEW_FIELD("Created"),
|
||||
RC_JSON_NEW_FIELD("Modified")
|
||||
};
|
||||
|
||||
rc_json_field_t leaderboard_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"Description"},
|
||||
{"Mem"},
|
||||
{"Format"},
|
||||
{"LowerIsBetter"},
|
||||
{"Hidden"}
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("Description"),
|
||||
RC_JSON_NEW_FIELD("Mem"),
|
||||
RC_JSON_NEW_FIELD("Format"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("Hidden")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -199,8 +200,8 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
if (!rc_json_get_required_string(&achievement->badge_name, &response->response, &achievement_fields[7], "BadgeName"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
len = achievement_fields[7].value_end - achievement_fields[7].value_start;
|
||||
if (len == last_author_len && memcmp(achievement_fields[7].value_start, last_author, len) == 0) {
|
||||
len = achievement_fields[6].value_end - achievement_fields[6].value_start;
|
||||
if (len == last_author_len && memcmp(achievement_fields[6].value_start, last_author_field, len) == 0) {
|
||||
achievement->author = last_author;
|
||||
}
|
||||
else {
|
||||
@ -208,6 +209,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
last_author = achievement->author;
|
||||
last_author_field = achievement_fields[6].value_start;
|
||||
last_author_len = len;
|
||||
}
|
||||
|
||||
@ -291,8 +293,8 @@ int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_reques
|
||||
|
||||
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response) {
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -345,11 +347,11 @@ int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_ap
|
||||
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Score"},
|
||||
{"AchievementID"},
|
||||
{"AchievementsRemaining"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("AchievementID"),
|
||||
RC_JSON_NEW_FIELD("AchievementsRemaining")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -428,48 +430,48 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Response"} /* nested object */
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Response") /* nested object */
|
||||
};
|
||||
|
||||
rc_json_field_t response_fields[] = {
|
||||
{"Score"},
|
||||
{"BestScore"},
|
||||
{"RankInfo"}, /* nested object */
|
||||
{"TopEntries"} /* array */
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("BestScore"),
|
||||
RC_JSON_NEW_FIELD("RankInfo"), /* nested object */
|
||||
RC_JSON_NEW_FIELD("TopEntries") /* array */
|
||||
/* unused fields
|
||||
{"LBData"}, / * array * /
|
||||
{"ScoreFormatted"},
|
||||
{"TopEntriesFriends"}, / * array * /
|
||||
RC_JSON_NEW_FIELD("LBData"), / * array * /
|
||||
RC_JSON_NEW_FIELD("ScoreFormatted"),
|
||||
RC_JSON_NEW_FIELD("TopEntriesFriends") / * array * /
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
/* unused fields
|
||||
rc_json_field_t lbdata_fields[] = {
|
||||
{"Format"},
|
||||
{"LeaderboardID"},
|
||||
{"GameID"},
|
||||
{"Title"},
|
||||
{"LowerIsBetter"}
|
||||
RC_JSON_NEW_FIELD("Format"),
|
||||
RC_JSON_NEW_FIELD("LeaderboardID"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter")
|
||||
};
|
||||
* unused fields */
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"Rank"},
|
||||
{"Score"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("Score")
|
||||
/* unused fields
|
||||
{"DateSubmitted"},
|
||||
RC_JSON_NEW_FIELD("DateSubmitted")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t rank_info_fields[] = {
|
||||
{"Rank"},
|
||||
{"NumEntries"}
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("NumEntries")
|
||||
/* unused fields
|
||||
{"LowerIsBetter"},
|
||||
{"UserRank"},
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("UserRank")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
|
31
deps/rcheevos/src/rapi/rc_api_user.c
vendored
31
deps/rcheevos/src/rapi/rc_api_user.c
vendored
@ -1,6 +1,8 @@
|
||||
#include "rc_api_user.h"
|
||||
#include "rc_api_common.h"
|
||||
|
||||
#include "../rcheevos/rc_version.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* --- Login --- */
|
||||
@ -32,13 +34,13 @@ int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_requ
|
||||
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"User"},
|
||||
{"Token"},
|
||||
{"Score"},
|
||||
{"Messages"},
|
||||
{"DisplayName"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Token"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("Messages"),
|
||||
RC_JSON_NEW_FIELD("DisplayName")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -86,6 +88,7 @@ int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_st
|
||||
*/
|
||||
rc_url_builder_append_unum_param(&builder, "a", 3);
|
||||
rc_url_builder_append_unum_param(&builder, "m", api_params->game_id);
|
||||
rc_url_builder_append_str_param(&builder, "l", RCHEEVOS_VERSION_STRING);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
}
|
||||
|
||||
@ -94,8 +97,8 @@ int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_st
|
||||
|
||||
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response) {
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@ -128,12 +131,12 @@ int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_a
|
||||
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"UserUnlocks"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("UserUnlocks")
|
||||
/* unused fields
|
||||
{ "GameID" },
|
||||
{ "HardcoreMode" }
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("HardcoreMode")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
|
40
deps/rcheevos/src/rcheevos/compat.c
vendored
40
deps/rcheevos/src/rcheevos/compat.c
vendored
@ -3,6 +3,8 @@
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef RC_C89_HELPERS
|
||||
|
||||
int rc_strncasecmp(const char* left, const char* right, size_t length)
|
||||
{
|
||||
while (length)
|
||||
@ -44,20 +46,40 @@ char* rc_strdup(const char* str)
|
||||
{
|
||||
const size_t length = strlen(str);
|
||||
char* buffer = (char*)malloc(length + 1);
|
||||
memcpy(buffer, str, length + 1);
|
||||
if (buffer)
|
||||
memcpy(buffer, str, length + 1);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int rc_snprintf(char* buffer, size_t size, const char* format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list args;
|
||||
int result;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
/* assume buffer is large enough and ignore size */
|
||||
(void)size;
|
||||
result = vsprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
va_start(args, format);
|
||||
|
||||
return result;
|
||||
#ifdef __STDC_WANT_SECURE_LIB__
|
||||
result = vsprintf_s(buffer, size, format, args);
|
||||
#else
|
||||
/* assume buffer is large enough and ignore size */
|
||||
(void)size;
|
||||
result = vsprintf(buffer, format, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __STDC_WANT_SECURE_LIB__
|
||||
|
||||
struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer)
|
||||
{
|
||||
struct tm* tm = gmtime(timer);
|
||||
memcpy(buf, tm, sizeof(*tm));
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
3
deps/rcheevos/src/rcheevos/condset.c
vendored
3
deps/rcheevos/src/rcheevos/condset.c
vendored
@ -210,8 +210,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc
|
||||
|
||||
case RC_CONDITION_SUB_SOURCE:
|
||||
rc_evaluate_condition_value(&value, condition, eval_state);
|
||||
rc_typed_value_convert(&value, RC_VALUE_TYPE_SIGNED);
|
||||
value.value.i32 = -value.value.i32;
|
||||
rc_typed_value_negate(&value);
|
||||
rc_typed_value_add(&eval_state->add_value, &value);
|
||||
eval_state->add_address = 0;
|
||||
continue;
|
||||
|
60
deps/rcheevos/src/rcheevos/consoleinfo.c
vendored
60
deps/rcheevos/src/rcheevos/consoleinfo.c
vendored
@ -135,6 +135,9 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
return "Nintendo DS";
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return "Nintendo DSi";
|
||||
|
||||
case RC_CONSOLE_NINTENDO_3DS:
|
||||
return "Nintendo 3DS";
|
||||
|
||||
@ -204,9 +207,15 @@ const char* rc_console_name(int console_id)
|
||||
case RC_CONSOLE_THOMSONTO8:
|
||||
return "Thomson TO8";
|
||||
|
||||
case RC_CONSOLE_TI83:
|
||||
return "TI-83";
|
||||
|
||||
case RC_CONSOLE_TIC80:
|
||||
return "TIC-80";
|
||||
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
return "Uzebox";
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return "Vectrex";
|
||||
|
||||
@ -441,6 +450,13 @@ static const rc_memory_region_t _rc_memory_regions_gameboy_advance[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_gameboy_advance = { _rc_memory_regions_gameboy_advance, 2 };
|
||||
|
||||
/* ===== GameCube ===== */
|
||||
/* https://wiibrew.org/wiki/Memory_map */
|
||||
static const rc_memory_region_t _rc_memory_regions_gamecube[] = {
|
||||
{ 0x00000000U, 0x017FFFFF, 0x80000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_gamecube = { _rc_memory_regions_gamecube, 1 };
|
||||
|
||||
/* ===== Game Gear ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
static const rc_memory_region_t _rc_memory_regions_game_gear[] = {
|
||||
@ -631,6 +647,13 @@ static const rc_memory_region_t _rc_memory_regions_nintendo_ds[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_nintendo_ds = { _rc_memory_regions_nintendo_ds, 1 };
|
||||
|
||||
/* ===== Nintendo DSi ===== */
|
||||
/* https://problemkaputt.de/gbatek.htm#dsiiomap */
|
||||
static const rc_memory_region_t _rc_memory_regions_nintendo_dsi[] = {
|
||||
{ 0x000000U, 0xFFFFFFU, 0x02000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_nintendo_dsi = { _rc_memory_regions_nintendo_dsi, 1 };
|
||||
|
||||
/* ===== Oric ===== */
|
||||
static const rc_memory_region_t _rc_memory_regions_oric[] = {
|
||||
/* actual size depends on machine type - up to 64KB */
|
||||
@ -762,6 +785,13 @@ static const rc_memory_region_t _rc_memory_regions_thomson_to8[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_thomson_to8 = { _rc_memory_regions_thomson_to8, 1 };
|
||||
|
||||
/* ===== TI-83 ===== */
|
||||
/* https://tutorials.eeems.ca/ASMin28Days/lesson/day03.html#mem */
|
||||
static const rc_memory_region_t _rc_memory_regions_ti83[] = {
|
||||
{ 0x000000U, 0x007FFFU, 0x008000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_ti83 = { _rc_memory_regions_ti83, 1 };
|
||||
|
||||
/* ===== TIC-80 ===== */
|
||||
/* https://github.com/nesbox/TIC-80/wiki/RAM */
|
||||
static const rc_memory_region_t _rc_memory_regions_tic80[] = {
|
||||
@ -778,6 +808,13 @@ static const rc_memory_region_t _rc_memory_regions_tic80[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_tic80 = { _rc_memory_regions_tic80, 10 };
|
||||
|
||||
/* ===== Uzebox ===== */
|
||||
/* https://uzebox.org/index.php */
|
||||
static const rc_memory_region_t _rc_memory_regions_uzebox[] = {
|
||||
{ 0x000000U, 0x000FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_uzebox = { _rc_memory_regions_uzebox, 1 };
|
||||
|
||||
/* ===== Vectrex ===== */
|
||||
/* https://roadsidethoughts.com/vectrex/vectrex-memory-map.htm */
|
||||
static const rc_memory_region_t _rc_memory_regions_vectrex[] = {
|
||||
@ -812,6 +849,14 @@ static const rc_memory_region_t _rc_memory_regions_wasm4[] = {
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_wasm4 = { _rc_memory_regions_wasm4, 1 };
|
||||
|
||||
/* ===== Wii ===== */
|
||||
/* https://wiibrew.org/wiki/Memory_map */
|
||||
static const rc_memory_region_t _rc_memory_regions_wii[] = {
|
||||
{ 0x00000000U, 0x017FFFFF, 0x80000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" },
|
||||
{ 0x01800000U, 0x057FFFFF, 0x90000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_wii = { _rc_memory_regions_wii, 2 };
|
||||
|
||||
/* ===== WonderSwan ===== */
|
||||
/* http://daifukkat.su/docs/wsman/#ovr_memmap */
|
||||
static const rc_memory_region_t _rc_memory_regions_wonderswan[] = {
|
||||
@ -892,6 +937,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_GAMEBOY_ADVANCE:
|
||||
return &rc_memory_regions_gameboy_advance;
|
||||
|
||||
case RC_CONSOLE_GAMECUBE:
|
||||
return &rc_memory_regions_gamecube;
|
||||
|
||||
case RC_CONSOLE_GAME_GEAR:
|
||||
return &rc_memory_regions_game_gear;
|
||||
|
||||
@ -931,6 +979,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
return &rc_memory_regions_nintendo_ds;
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return &rc_memory_regions_nintendo_dsi;
|
||||
|
||||
case RC_CONSOLE_ORIC:
|
||||
return &rc_memory_regions_oric;
|
||||
|
||||
@ -979,9 +1030,15 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_THOMSONTO8:
|
||||
return &rc_memory_regions_thomson_to8;
|
||||
|
||||
case RC_CONSOLE_TI83:
|
||||
return &rc_memory_regions_ti83;
|
||||
|
||||
case RC_CONSOLE_TIC80:
|
||||
return &rc_memory_regions_tic80;
|
||||
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
return &rc_memory_regions_uzebox;
|
||||
|
||||
case RC_CONSOLE_VECTREX:
|
||||
return &rc_memory_regions_vectrex;
|
||||
|
||||
@ -991,6 +1048,9 @@ const rc_memory_regions_t* rc_console_memory_regions(int console_id)
|
||||
case RC_CONSOLE_WASM4:
|
||||
return &rc_memory_regions_wasm4;
|
||||
|
||||
case RC_CONSOLE_WII:
|
||||
return &rc_memory_regions_wii;
|
||||
|
||||
case RC_CONSOLE_WONDERSWAN:
|
||||
return &rc_memory_regions_wonderswan;
|
||||
|
||||
|
2
deps/rcheevos/src/rcheevos/operand.c
vendored
2
deps/rcheevos/src/rcheevos/operand.c
vendored
@ -59,6 +59,8 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
|
||||
self->value.luafunc = luaL_ref(parse->L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
#else
|
||||
(void)parse;
|
||||
#endif /* RC_DISABLE_LUA */
|
||||
|
||||
self->type = RC_OPERAND_LUA;
|
||||
|
12
deps/rcheevos/src/rcheevos/rc_compat.h
vendored
12
deps/rcheevos/src/rcheevos/rc_compat.h
vendored
@ -30,6 +30,7 @@ extern "C" {
|
||||
#elif __STDC_VERSION__ < 199901L
|
||||
|
||||
/* C89 redefinitions */
|
||||
#define RC_C89_HELPERS 1
|
||||
|
||||
#ifndef snprintf
|
||||
extern int rc_snprintf(char* buffer, size_t size, const char* format, ...);
|
||||
@ -53,6 +54,17 @@ extern "C" {
|
||||
|
||||
#endif /* __STDC_VERSION__ < 199901L */
|
||||
|
||||
#ifndef __STDC_WANT_SECURE_LIB__
|
||||
/* _CRT_SECURE_NO_WARNINGS redefinitions */
|
||||
#define strcpy_s(dest, sz, src) strcpy(dest, src)
|
||||
#define sscanf_s sscanf
|
||||
|
||||
/* NOTE: Microsoft secure gmtime_s parameter order differs from C11 standard */
|
||||
#include <time.h>
|
||||
extern struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer);
|
||||
#define gmtime_s rc_gmtime_s
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
3
deps/rcheevos/src/rcheevos/rc_internal.h
vendored
3
deps/rcheevos/src/rcheevos/rc_internal.h
vendored
@ -83,6 +83,8 @@ typedef struct {
|
||||
}
|
||||
rc_typed_value_t;
|
||||
|
||||
#define RC_MEASURED_UNKNOWN 0xFFFFFFFF
|
||||
|
||||
typedef struct {
|
||||
rc_typed_value_t add_value;/* AddSource/SubSource */
|
||||
int add_hits; /* AddHits */
|
||||
@ -182,6 +184,7 @@ void rc_typed_value_convert(rc_typed_value_t* value, char new_type);
|
||||
void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
void rc_typed_value_multiply(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amount);
|
||||
void rc_typed_value_negate(rc_typed_value_t* value);
|
||||
int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper);
|
||||
void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref);
|
||||
|
||||
|
12
deps/rcheevos/src/rcheevos/rc_libretro.c
vendored
12
deps/rcheevos/src/rcheevos/rc_libretro.c
vendored
@ -288,7 +288,7 @@ int rc_libretro_is_system_allowed(const char* library_name, int console_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
|
||||
unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, unsigned address, unsigned* avail) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < regions->count; ++i) {
|
||||
@ -297,15 +297,25 @@ unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regio
|
||||
if (regions->data[i] == NULL)
|
||||
break;
|
||||
|
||||
if (avail)
|
||||
*avail = (unsigned)(size - address);
|
||||
|
||||
return ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= (unsigned)size;
|
||||
}
|
||||
|
||||
if (avail)
|
||||
*avail = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address) {
|
||||
return rc_libretro_memory_find_avail(regions, address, NULL);
|
||||
}
|
||||
|
||||
void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
|
||||
rc_libretro_verbose_message_callback = callback;
|
||||
}
|
||||
|
1
deps/rcheevos/src/rcheevos/rc_libretro.h
vendored
1
deps/rcheevos/src/rcheevos/rc_libretro.h
vendored
@ -53,6 +53,7 @@ int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct
|
||||
void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions);
|
||||
|
||||
unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, unsigned address);
|
||||
unsigned char* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, unsigned address, unsigned* avail);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Disk Identification |
|
||||
|
29
deps/rcheevos/src/rcheevos/rc_version.h
vendored
Normal file
29
deps/rcheevos/src/rcheevos/rc_version.h
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef RC_VERSION_H
|
||||
#define RC_VERSION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RCHEEVOS_VERSION_MAJOR 10
|
||||
#define RCHEEVOS_VERSION_MINOR 7
|
||||
#define RCHEEVOS_VERSION_PATCH 0
|
||||
|
||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||
#define RCHEEVOS_VERSION RCHEEVOS_MAKE_VERSION(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR, RCHEEVOS_VERSION_PATCH)
|
||||
|
||||
#define RCHEEVOS_MAKE_STRING(num) #num
|
||||
#define RCHEEVOS_MAKE_VERSION_STRING(major, minor, patch) RCHEEVOS_MAKE_STRING(major) "." RCHEEVOS_MAKE_STRING(minor) "." RCHEEVOS_MAKE_STRING(patch)
|
||||
#define RCHEEVOS_MAKE_VERSION_STRING_SHORT(major, minor) RCHEEVOS_MAKE_STRING(major) "." RCHEEVOS_MAKE_STRING(minor)
|
||||
|
||||
#if RCHEEVOS_VERSION_PATCH > 0
|
||||
#define RCHEEVOS_VERSION_STRING RCHEEVOS_MAKE_VERSION_STRING(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR, RCHEEVOS_VERSION_PATCH)
|
||||
#else
|
||||
#define RCHEEVOS_VERSION_STRING RCHEEVOS_MAKE_VERSION_STRING_SHORT(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RC_VERSION_H */
|
37
deps/rcheevos/src/rcheevos/runtime.c
vendored
37
deps/rcheevos/src/rcheevos/runtime.c
vendored
@ -231,7 +231,7 @@ int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id
|
||||
}
|
||||
|
||||
if (rc_trigger_state_active(trigger->state)) {
|
||||
*measured_value = trigger->measured_value;
|
||||
*measured_value = (trigger->measured_value == RC_MEASURED_UNKNOWN) ? 0 : trigger->measured_value;
|
||||
*measured_target = trigger->measured_target;
|
||||
}
|
||||
else {
|
||||
@ -257,7 +257,7 @@ int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, unsigned
|
||||
}
|
||||
|
||||
/* cap the value at the target so we can count past the target: "107 >= 100" */
|
||||
value = trigger->measured_value;
|
||||
value = (trigger->measured_value == RC_MEASURED_UNKNOWN) ? 0 : trigger->measured_value;
|
||||
if (value > trigger->measured_target)
|
||||
value = trigger->measured_target;
|
||||
|
||||
@ -534,6 +534,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
for (i = self->trigger_count - 1; i >= 0; --i) {
|
||||
rc_trigger_t* trigger = self->triggers[i].trigger;
|
||||
int old_state, new_state;
|
||||
unsigned old_measured_value;
|
||||
|
||||
if (!trigger)
|
||||
continue;
|
||||
@ -552,13 +553,13 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
continue;
|
||||
}
|
||||
|
||||
old_measured_value = trigger->measured_value;
|
||||
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.
|
||||
/* 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)
|
||||
{
|
||||
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);
|
||||
@ -566,6 +567,32 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
new_state = trigger->state;
|
||||
}
|
||||
|
||||
/* if the measured value changed and the achievement hasn't triggered, send a notification */
|
||||
if (trigger->measured_value != old_measured_value && old_measured_value != RC_MEASURED_UNKNOWN &&
|
||||
trigger->measured_target != 0 && trigger->measured_value <= trigger->measured_target &&
|
||||
new_state != RC_TRIGGER_STATE_TRIGGERED &&
|
||||
new_state != RC_TRIGGER_STATE_INACTIVE && new_state != RC_TRIGGER_STATE_WAITING) {
|
||||
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
|
||||
if (trigger->measured_as_percent) {
|
||||
/* if reporting measured value as a percentage, only send the notification if the percentage changes */
|
||||
unsigned old_percent = (unsigned)(((unsigned long long)old_measured_value * 100) / trigger->measured_target);
|
||||
unsigned new_percent = (unsigned)(((unsigned long long)trigger->measured_value * 100) / trigger->measured_target);
|
||||
if (old_percent != new_percent) {
|
||||
runtime_event.value = new_percent;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
}
|
||||
else {
|
||||
runtime_event.value = trigger->measured_value;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
|
||||
runtime_event.value = 0; /* achievement loop expects this to stay at 0 */
|
||||
}
|
||||
|
||||
/* if the state hasn't changed, there won't be any events raised */
|
||||
if (new_state == old_state)
|
||||
continue;
|
||||
|
7
deps/rcheevos/src/rcheevos/trigger.c
vendored
7
deps/rcheevos/src/rcheevos/trigger.c
vendored
@ -42,8 +42,8 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
|
||||
*next = 0;
|
||||
*memaddr = aux;
|
||||
|
||||
self->measured_value = 0;
|
||||
self->measured_target = parse->measured_target;
|
||||
self->measured_value = parse->measured_target ? RC_MEASURED_UNKNOWN : 0;
|
||||
self->measured_as_percent = parse->measured_as_percent;
|
||||
self->state = RC_TRIGGER_STATE_WAITING;
|
||||
self->has_hits = 0;
|
||||
@ -283,6 +283,9 @@ void rc_reset_trigger(rc_trigger_t* self) {
|
||||
rc_reset_trigger_hitcounts(self);
|
||||
|
||||
self->state = RC_TRIGGER_STATE_WAITING;
|
||||
self->measured_value = 0;
|
||||
|
||||
if (self->measured_target)
|
||||
self->measured_value = RC_MEASURED_UNKNOWN;
|
||||
|
||||
self->has_hits = 0;
|
||||
}
|
||||
|
20
deps/rcheevos/src/rcheevos/value.c
vendored
20
deps/rcheevos/src/rcheevos/value.c
vendored
@ -428,6 +428,26 @@ static rc_typed_value_t* rc_typed_value_convert_into(rc_typed_value_t* dest, con
|
||||
return dest;
|
||||
}
|
||||
|
||||
void rc_typed_value_negate(rc_typed_value_t* value) {
|
||||
switch (value->type)
|
||||
{
|
||||
case RC_VALUE_TYPE_UNSIGNED:
|
||||
rc_typed_value_convert(value, RC_VALUE_TYPE_SIGNED);
|
||||
/* fallthrough to RC_VALUE_TYPE_SIGNED */
|
||||
|
||||
case RC_VALUE_TYPE_SIGNED:
|
||||
value->value.i32 = -(value->value.i32);
|
||||
break;
|
||||
|
||||
case RC_VALUE_TYPE_FLOAT:
|
||||
value->value.f32 = -(value->value.f32);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount) {
|
||||
rc_typed_value_t converted;
|
||||
|
||||
|
14
deps/rcheevos/src/rhash/cdreader.c
vendored
14
deps/rcheevos/src/rhash/cdreader.c
vendored
@ -124,6 +124,8 @@ static void* cdreader_open_bin_track(const char* path, uint32_t track)
|
||||
return NULL;
|
||||
|
||||
cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
|
||||
if (!cdrom)
|
||||
return NULL;
|
||||
cdrom->file_handle = file_handle;
|
||||
#ifndef NDEBUG
|
||||
cdrom->track_id = track;
|
||||
@ -343,7 +345,7 @@ static void* cdreader_open_cue_track(const char* path, uint32_t track)
|
||||
++ptr;
|
||||
|
||||
/* convert mm:ss:ff to sector count */
|
||||
sscanf(ptr, "%d:%d:%d", &m, &s, &f);
|
||||
sscanf_s(ptr, "%d:%d:%d", &m, &s, &f);
|
||||
sector_offset = ((m * 60) + s) * 75 + f;
|
||||
|
||||
if (current_track.first_sector == -1)
|
||||
@ -719,8 +721,8 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||
largest_track_size = track_size;
|
||||
largest_track = current_track;
|
||||
largest_track_lba = lba;
|
||||
strcpy(largest_track_file, file);
|
||||
strcpy(largest_track_sector_size, sector_size);
|
||||
strcpy_s(largest_track_file, sizeof(largest_track_file), file);
|
||||
strcpy_s(largest_track_sector_size, sizeof(largest_track_sector_size), sector_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -748,8 +750,8 @@ static void* cdreader_open_gdi_track(const char* path, uint32_t track)
|
||||
if (largest_track != 0 && largest_track != current_track)
|
||||
{
|
||||
current_track = largest_track;
|
||||
strcpy(file, largest_track_file);
|
||||
strcpy(sector_size, largest_track_sector_size);
|
||||
strcpy_s(file, sizeof(file), largest_track_file);
|
||||
strcpy_s(sector_size, sizeof(sector_size), largest_track_sector_size);
|
||||
lba = largest_track_lba;
|
||||
}
|
||||
|
||||
@ -869,7 +871,7 @@ void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader)
|
||||
cdreader->first_track_sector = cdreader_first_track_sector;
|
||||
}
|
||||
|
||||
void rc_hash_init_default_cdreader()
|
||||
void rc_hash_init_default_cdreader(void)
|
||||
{
|
||||
struct rc_hash_cdreader cdreader;
|
||||
rc_hash_get_default_cdreader(&cdreader);
|
||||
|
263
deps/rcheevos/src/rhash/hash.c
vendored
263
deps/rcheevos/src/rhash/hash.c
vendored
@ -48,7 +48,13 @@ static struct rc_hash_filereader* filereader = NULL;
|
||||
|
||||
static void* filereader_open(const char* path)
|
||||
{
|
||||
#if defined(__STDC_WANT_SECURE_LIB__)
|
||||
FILE* fp;
|
||||
fopen_s(&fp, path, "rb");
|
||||
return fp;
|
||||
#else
|
||||
return fopen(path, "rb");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void filereader_seek(void* file_handle, int64_t offset, int origin)
|
||||
@ -268,7 +274,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
if (logical_block_size == 0) {
|
||||
num_sectors = 1;
|
||||
} else {
|
||||
num_sectors = (buffer[156 + 10] | (buffer[156 + 11] << 8)); /* length of section */
|
||||
num_sectors = (buffer[156 + 10] | (buffer[156 + 11] << 8) | (buffer[156 + 12] << 16) | (buffer[156 + 13] << 24)); /* length of section */
|
||||
num_sectors /= logical_block_size;
|
||||
}
|
||||
}
|
||||
@ -278,9 +284,9 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
return 0;
|
||||
|
||||
tmp = buffer;
|
||||
while (tmp < buffer + sizeof(buffer))
|
||||
do
|
||||
{
|
||||
if (!*tmp)
|
||||
if (tmp >= buffer + sizeof(buffer) || !*tmp)
|
||||
{
|
||||
/* end of this path table block. if the path table spans multiple sectors, keep scanning */
|
||||
if (num_sectors > 1)
|
||||
@ -316,7 +322,7 @@ static uint32_t rc_cd_find_file_sector(void* track_handle, const char* path, uns
|
||||
|
||||
/* the first byte of the record is the length of the record */
|
||||
tmp += *tmp;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -744,13 +750,16 @@ static int rc_hash_text(char hash[33], const uint8_t* buffer, size_t buffer_size
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
/* helper variable only used for testing */
|
||||
const char* _rc_hash_jaguar_cd_homebrew_hash = NULL;
|
||||
|
||||
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;
|
||||
int byteswapped = 0;
|
||||
unsigned size = 0;
|
||||
unsigned offset = 0;
|
||||
unsigned sector = 0;
|
||||
@ -795,40 +804,86 @@ static int rc_hash_jaguar_cd(char hash[33], const char* path)
|
||||
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);
|
||||
}
|
||||
|
||||
i = 0; /* only loop once */
|
||||
do
|
||||
{
|
||||
if (byteswapped)
|
||||
rc_hash_byteswap16(buffer, &buffer[sizeof(buffer)]);
|
||||
md5_init(&md5);
|
||||
|
||||
remaining = sizeof(buffer) - offset;
|
||||
if (remaining >= size)
|
||||
offset += 4;
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
md5_append(&md5, &buffer[offset], size);
|
||||
size = 0;
|
||||
break;
|
||||
snprintf(message, sizeof(message), "Hashing boot executable (%u bytes starting at %u bytes into sector %u)", size, offset, sector);
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
md5_append(&md5, &buffer[offset], remaining);
|
||||
size -= remaining;
|
||||
offset = 0;
|
||||
} while (rc_cd_read_sector(track_handle, ++sector, buffer, sizeof(buffer)) == sizeof(buffer));
|
||||
if (size > MAX_BUFFER_SIZE)
|
||||
size = MAX_BUFFER_SIZE;
|
||||
|
||||
rc_cd_close_track(track_handle);
|
||||
do
|
||||
{
|
||||
if (byteswapped)
|
||||
rc_hash_byteswap16(buffer, &buffer[sizeof(buffer)]);
|
||||
|
||||
if (size > 0)
|
||||
return rc_hash_error("Not enough data");
|
||||
remaining = sizeof(buffer) - offset;
|
||||
if (remaining >= size)
|
||||
{
|
||||
md5_append(&md5, &buffer[offset], size);
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
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");
|
||||
|
||||
rc_hash_finalize(&md5, hash);
|
||||
|
||||
/* homebrew games all seem to have the same boot executable and store the actual game code in track 2.
|
||||
* if we generated something other than the homebrew hash, return it. assume all homebrews are byteswapped. */
|
||||
if (strcmp(hash, "254487b59ab21bc005338e85cbf9fd2f") != 0 || !byteswapped) {
|
||||
if (_rc_hash_jaguar_cd_homebrew_hash == NULL || strcmp(hash, _rc_hash_jaguar_cd_homebrew_hash) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if we've already been through the loop a second time, just return the hash */
|
||||
if (i == 1)
|
||||
return 1;
|
||||
++i;
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf(message, sizeof(message), "Potential homebrew at sector %u, checking for KART data in track 2", sector);
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
track_handle = rc_cd_open_track(path, 2);
|
||||
if (!track_handle)
|
||||
return rc_hash_error("Could not open track");
|
||||
|
||||
/* track 2 of the homebrew code has the 64 bytes or ATRI followed by 32 bytes of "ATARI APPROVED DATA HEADER ATRI!",
|
||||
* then 64 bytes of KART repeating. */
|
||||
sector = rc_cd_first_track_sector(track_handle);
|
||||
rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer));
|
||||
if (memcmp(&buffer[0x5E], "RT!IRTKA", 8) != 0)
|
||||
return rc_hash_error("Homebrew executable not found in track 2");
|
||||
|
||||
/* found KART data*/
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf(message, sizeof(message), "Found KART data in track 2");
|
||||
rc_hash_verbose(message);
|
||||
}
|
||||
|
||||
offset = 0xA6;
|
||||
size = (buffer[offset] << 16) | (buffer[offset + 1] << 24) | (buffer[offset + 2]) | (buffer[offset + 3] << 8);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int rc_hash_lynx(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
@ -883,7 +938,7 @@ static int rc_hash_neogeo_cd(char hash[33], const char* path)
|
||||
while (*ptr && *ptr != '.')
|
||||
++ptr;
|
||||
|
||||
if (memcmp(ptr, ".PRG", 4) == 0)
|
||||
if (strncasecmp(ptr, ".PRG", 4) == 0)
|
||||
{
|
||||
ptr += 4;
|
||||
*ptr++ = '\0';
|
||||
@ -1143,6 +1198,119 @@ static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_gamecube(char hash[33], const char* path)
|
||||
{
|
||||
md5_state_t md5;
|
||||
void* file_handle;
|
||||
const uint32_t BASE_HEADER_SIZE = 0x2440;
|
||||
const uint32_t MAX_HEADER_SIZE = 1024 * 1024;
|
||||
uint32_t apploader_header_size, apploader_body_size, apploader_trailer_size, header_size;
|
||||
uint8_t quad_buffer[4];
|
||||
uint8_t addr_buffer[0xD8];
|
||||
uint8_t* buffer;
|
||||
uint32_t dol_offset;
|
||||
uint32_t dol_offsets[18];
|
||||
uint32_t dol_sizes[18];
|
||||
uint32_t dol_buf_size = 0;
|
||||
uint32_t ix;
|
||||
|
||||
file_handle = rc_file_open(path);
|
||||
|
||||
/* Verify Gamecube */
|
||||
rc_file_seek(file_handle, 0x1c, SEEK_SET);
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
if (quad_buffer[0] != 0xC2|| quad_buffer[1] != 0x33 || quad_buffer[2] != 0x9F || quad_buffer[3] != 0x3D)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Not a Gamecube disc");
|
||||
}
|
||||
|
||||
/* GetApploaderSize */
|
||||
rc_file_seek(file_handle, BASE_HEADER_SIZE + 0x14, SEEK_SET);
|
||||
apploader_header_size = 0x20;
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
apploader_body_size =
|
||||
(quad_buffer[0] << 24) | (quad_buffer[1] << 16) | (quad_buffer[2] << 8) | quad_buffer[3];
|
||||
rc_file_read(file_handle, quad_buffer, 4);
|
||||
apploader_trailer_size =
|
||||
(quad_buffer[0] << 24) | (quad_buffer[1] << 16) | (quad_buffer[2] << 8) | quad_buffer[3];
|
||||
header_size = BASE_HEADER_SIZE + apploader_header_size + apploader_body_size + apploader_trailer_size;
|
||||
if (header_size > MAX_HEADER_SIZE) header_size = MAX_HEADER_SIZE;
|
||||
|
||||
/* Hash headers */
|
||||
buffer = (uint8_t*)malloc(header_size);
|
||||
if (!buffer)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
}
|
||||
rc_file_seek(file_handle, 0, SEEK_SET);
|
||||
rc_file_read(file_handle, buffer, header_size);
|
||||
md5_init(&md5);
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
char message[128];
|
||||
snprintf(message, sizeof(message), "Hashing %u byte header", header_size);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
md5_append(&md5, buffer, header_size);
|
||||
|
||||
/* GetBootDOLOffset
|
||||
* Base header size is guaranteed larger than 0x423 therefore buffer contains dol_offset right now
|
||||
*/
|
||||
dol_offset = (buffer[0x420] << 24) | (buffer[0x421] << 16) | (buffer[0x422] << 8) | buffer[0x423];
|
||||
free(buffer);
|
||||
|
||||
/* Find offsetsand sizes for the 7 main.dol code segments and 11 main.dol data segments */
|
||||
rc_file_seek(file_handle, dol_offset, SEEK_SET);
|
||||
rc_file_read(file_handle, addr_buffer, 0xD8);
|
||||
for (ix = 0; ix < 18; ix++)
|
||||
{
|
||||
dol_offsets[ix] =
|
||||
(addr_buffer[0x0 + ix * 4] << 24) |
|
||||
(addr_buffer[0x1 + ix * 4] << 16) |
|
||||
(addr_buffer[0x2 + ix * 4] << 8) |
|
||||
addr_buffer[0x3 + ix * 4];
|
||||
dol_sizes[ix] =
|
||||
(addr_buffer[0x90 + ix * 4] << 24) |
|
||||
(addr_buffer[0x91 + ix * 4] << 16) |
|
||||
(addr_buffer[0x92 + ix * 4] << 8) |
|
||||
addr_buffer[0x93 + ix * 4];
|
||||
dol_buf_size = (dol_sizes[ix] > dol_buf_size) ? dol_sizes[ix] : dol_buf_size;
|
||||
}
|
||||
|
||||
/* Iterate through the 18 main.dol segments and hash each */
|
||||
buffer = (uint8_t*)malloc(dol_buf_size);
|
||||
if (!buffer)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
}
|
||||
for (ix = 0; ix < 18; ix++)
|
||||
{
|
||||
if (dol_sizes[ix] == 0)
|
||||
continue;
|
||||
rc_file_seek(file_handle, dol_offsets[ix], SEEK_SET);
|
||||
rc_file_read(file_handle, buffer, dol_sizes[ix]);
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
char message[128];
|
||||
if (ix < 7)
|
||||
snprintf(message, sizeof(message), "Hashing %u byte main.dol code segment %u", dol_sizes[ix], ix);
|
||||
else
|
||||
snprintf(message, sizeof(message), "Hashing %u byte main.dol data segment %u", dol_sizes[ix], ix - 7);
|
||||
verbose_message_callback(message);
|
||||
}
|
||||
md5_append(&md5, buffer, dol_sizes[ix]);
|
||||
}
|
||||
|
||||
/* Finalize */
|
||||
rc_file_close(file_handle);
|
||||
free(buffer);
|
||||
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_pce(char hash[33], const uint8_t* buffer, size_t buffer_size)
|
||||
{
|
||||
/* if the file contains a header, ignore it (expect ROM data to be multiple of 128KB) */
|
||||
@ -1725,7 +1893,11 @@ static struct rc_buffered_file rc_buffered_file;
|
||||
static void* rc_file_open_buffered_file(const char* path)
|
||||
{
|
||||
struct rc_buffered_file* handle = (struct rc_buffered_file*)malloc(sizeof(struct rc_buffered_file));
|
||||
memcpy(handle, &rc_buffered_file, sizeof(rc_buffered_file));
|
||||
(void)path;
|
||||
|
||||
if (handle)
|
||||
memcpy(handle, &rc_buffered_file, sizeof(rc_buffered_file));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -1829,7 +2001,9 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
case RC_CONSOLE_SG1000:
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
case RC_CONSOLE_TI83:
|
||||
case RC_CONSOLE_TIC80:
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
case RC_CONSOLE_VECTREX:
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
case RC_CONSOLE_WASM4:
|
||||
@ -1857,6 +2031,7 @@ int rc_hash_generate_from_buffer(char hash[33], int console_id, const uint8_t* b
|
||||
|
||||
case RC_CONSOLE_NINTENDO_64:
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return rc_hash_file_from_buffer(hash, console_id, buffer, buffer_size);
|
||||
}
|
||||
}
|
||||
@ -2120,7 +2295,9 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
case RC_CONSOLE_SEGA_32X:
|
||||
case RC_CONSOLE_SG1000:
|
||||
case RC_CONSOLE_SUPERVISION:
|
||||
case RC_CONSOLE_TI83:
|
||||
case RC_CONSOLE_TIC80:
|
||||
case RC_CONSOLE_UZEBOX:
|
||||
case RC_CONSOLE_VECTREX:
|
||||
case RC_CONSOLE_VIRTUAL_BOY:
|
||||
case RC_CONSOLE_WASM4:
|
||||
@ -2167,6 +2344,9 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
|
||||
return rc_hash_dreamcast(hash, path);
|
||||
|
||||
case RC_CONSOLE_GAMECUBE:
|
||||
return rc_hash_gamecube(hash, path);
|
||||
|
||||
case RC_CONSOLE_NEO_GEO_CD:
|
||||
return rc_hash_neogeo_cd(hash, path);
|
||||
|
||||
@ -2174,6 +2354,7 @@ int rc_hash_generate_from_file(char hash[33], int console_id, const char* path)
|
||||
return rc_hash_n64(hash, path);
|
||||
|
||||
case RC_CONSOLE_NINTENDO_DS:
|
||||
case RC_CONSOLE_NINTENDO_DSI:
|
||||
return rc_hash_nintendo_ds(hash, path);
|
||||
|
||||
case RC_CONSOLE_PC_ENGINE_CD:
|
||||
@ -2313,6 +2494,15 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
break;
|
||||
|
||||
case '8':
|
||||
/* http://tibasicdev.wikidot.com/file-extensions */
|
||||
if (rc_path_compare_extension(ext, "83g") ||
|
||||
rc_path_compare_extension(ext, "83p"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_TI83;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
if (rc_path_compare_extension(ext, "a78"))
|
||||
{
|
||||
@ -2537,7 +2727,7 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "nds"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_NINTENDO_DS;
|
||||
iterator->consoles[0] = RC_CONSOLE_NINTENDO_DS; /* ASSERT: handles both DS and DSi */
|
||||
}
|
||||
else if (rc_path_compare_extension(ext, "n64") ||
|
||||
rc_path_compare_extension(ext, "ndd"))
|
||||
@ -2619,6 +2809,13 @@ void rc_hash_initialize_iterator(struct rc_hash_iterator* iterator, const char*
|
||||
}
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
if (rc_path_compare_extension(ext, "uze"))
|
||||
{
|
||||
iterator->consoles[0] = RC_CONSOLE_UZEBOX;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
if (rc_path_compare_extension(ext, "vb"))
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user