Skip to content

Commit

Permalink
【rcheevos】Add skeleton functions
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Aug 31, 2023
1 parent a2cd396 commit ed1be74
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 0 deletions.
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,39 @@ if(USE_SDL)
include_directories(${SDL2_INCLUDE_DIRS})
endif()

set(USE_RETRO_ACHIEVEMENTS 1)
if(USE_RETRO_ACHIEVEMENTS)
set(RCHEEVOS_SRC src/rcheevos/src/rapi/rc_api_common.c
src/rcheevos/src/rapi/rc_api_editor.c
src/rcheevos/src/rapi/rc_api_info.c
src/rcheevos/src/rapi/rc_api_runtime.c
src/rcheevos/src/rapi/rc_api_user.c
src/rcheevos/src/rcheevos/alloc.c
src/rcheevos/src/rcheevos/compat.c
src/rcheevos/src/rcheevos/condition.c
src/rcheevos/src/rcheevos/condset.c
src/rcheevos/src/rcheevos/consoleinfo.c
src/rcheevos/src/rcheevos/format.c
src/rcheevos/src/rcheevos/lboard.c
src/rcheevos/src/rcheevos/memref.c
src/rcheevos/src/rcheevos/operand.c
src/rcheevos/src/rcheevos/rc_client.c
src/rcheevos/src/rcheevos/rc_validate.c
src/rcheevos/src/rcheevos/richpresence.c
src/rcheevos/src/rcheevos/runtime.c
src/rcheevos/src/rcheevos/runtime_progress.c
src/rcheevos/src/rcheevos/trigger.c
src/rcheevos/src/rcheevos/value.c
src/rcheevos/src/rhash/cdreader.c
src/rcheevos/src/rhash/hash.c
src/rcheevos/src/rhash/md5.c
src/rcheevos/src/rurl/url.c
)
set(SKYEMU_SRC ${SKYEMU_SRC} ${RCHEEVOS_SRC} src/retro_achievements.cpp)
include_directories(src/rcheevos/include)
add_definitions(-DRC_DISABLE_LUA)
endif()

if(IOS)
set(SKYEMU_SRC ${SKYEMU_SRC} src/ios_support.m)
add_definitions(-DPLATFORM_IOS)
Expand Down
17 changes: 17 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "capstone/include/capstone/capstone.h"
#include "miniz.h"
#include "localization.h"
#include "retro_achievements.h"
#include "rcheevos/include/rc_client.h"

#if defined(EMSCRIPTEN)
#include <emscripten.h>
Expand Down Expand Up @@ -4394,6 +4396,7 @@ void se_update_frame() {
#endif
se_update_key_turbo(&emu_state);
se_update_solar_sensor(&emu_state);
ra_poll_requests();

if(emu_state.run_mode == SB_MODE_RESET){
se_reset_core();
Expand Down Expand Up @@ -5239,6 +5242,18 @@ static void se_init_audio(){
});
se_reset_audio_ring();
}
static void se_ra_login_callback(int result, const char* error_message, rc_client_t* client, void* userdata) {
// TODO: show cool "logged in" banner or something
const rc_client_user_t* user = rc_client_get_user_info(client);
printf("logged in as %s (score: %d)\n", user->display_name, user->score);
}
static uint32_t se_ra_read_memory_callback(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client){
// TODO: handle reading from consoles
return 0;
}
static void se_init_retro_achievements(){
ra_initialize_client(se_ra_read_memory_callback);
}

// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
bool se_begin_menu_bar(){
Expand Down Expand Up @@ -6139,6 +6154,7 @@ static void init(void) {
};
gui_state.last_touch_time=-10000;
se_init_audio();
se_init_retro_achievements();
sg_push_debug_group("LCD Shader Init");

gui_state.lcd_prog = sg_make_shader(lcdprog_shader_desc(sg_query_backend()));
Expand Down Expand Up @@ -6185,6 +6201,7 @@ static void init(void) {
#endif
}
static void cleanup(void) {
ra_shutdown_client();
simgui_shutdown();
se_free_all_images();
sg_shutdown();
Expand Down
144 changes: 144 additions & 0 deletions src/retro_achievements.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
extern "C" {
#include "retro_achievements.h"
}
#include "rcheevos/include/rc_client.h"
#include "httplib.h"
#include <stdio.h>
#include <future>
#include <memory>

rc_client_t* ra_client = NULL;

// httplib doesn't have a way to make async requests, so we need to do it ourselves
struct AsyncRequest
{
std::unique_ptr<httplib::Client> client;
std::future<httplib::Result> result_future;
rc_client_server_callback_t callback;
void* callback_data;
};

std::vector<std::unique_ptr<AsyncRequest>> async_requests;

static void server_callback(const rc_api_request_t* request,
rc_client_server_callback_t callback, void* callback_data, rc_client_t* client)
{
// RetroAchievements may not allow hardcore unlocks if we don't properly identify ourselves.
const char* user_agent = "SkyEmu/4.0";

std::unique_ptr<AsyncRequest> async_request(new AsyncRequest);

// Unfortunately request->url gives us the full URL, so we need to extract the GET parameters
// to pass to httplib
// TODO: switch to SSLClient and https, needs OpenSSL
std::string full_url = request->url;
std::string host = "http://retroachievements.org";
std::string get_params;
std::string::size_type last_slash = full_url.find_last_of('/');
if (last_slash != std::string::npos)
{
get_params = full_url.substr(last_slash);
}
else
{
printf("[rcheevos]: could not parse URL: %s\n", request->url);
return;
}

async_request->client.reset(new httplib::Client(host));
async_request->callback = callback;
async_request->callback_data = callback_data;

// Copy it here as request is destroyed as soon as we return
std::string content_type = request->content_type;
std::string post_data = request->post_data;
if(request->post_data)
{
httplib::Result (httplib::Client::*gf)(const std::string&, const std::string&, const std::string&) = &httplib::Client::Post;
async_request->result_future = std::async(std::launch::async, gf, async_request->client.get(), get_params, post_data, content_type);
async_requests.push_back(std::move(async_request));
}
else
{
httplib::Result (httplib::Client::*gf)(const std::string&) = &httplib::Client::Get;
async_request->result_future = std::async(std::launch::async, gf, async_request->client.get(), get_params);
async_requests.push_back(std::move(async_request));
}
}

static void log_message(const char* message, const rc_client_t* client)
{
printf("[rcheevos]: %s\n", message);
}

void ra_initialize_client(rc_client_read_memory_func_t memory_read_func)
{
if(ra_client)
{
printf("[rcheevos]: client already initialized!\n");
}
else
{
ra_client = rc_client_create(memory_read_func, server_callback);
#ifndef NDEBUG
rc_client_enable_logging(ra_client, RC_CLIENT_LOG_LEVEL_VERBOSE, log_message);
#endif
// TODO: should probably be an option after we're finished testing
rc_client_set_hardcore_enabled(ra_client, 0);
}
}

void ra_shutdown_client()
{
if(ra_client)
{
rc_client_destroy(ra_client);
ra_client = nullptr;
}
}

bool ra_is_logged_in()
{
return rc_client_get_user_info(ra_client) != NULL;
}

void ra_login_credentials(const char* username, const char* password, rc_client_callback_t login_callback)
{
rc_client_begin_login_with_password(ra_client, username, password, login_callback, NULL);
}

void ra_poll_requests()
{
// Check if any of our asynchronous requests have finished, and if so, call the callback
auto it = async_requests.begin();
while(it != async_requests.end())
{
const std::unique_ptr<AsyncRequest>& request = *it;
if (request->result_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
httplib::Result result = request->result_future.get();
if(result.error() == httplib::Error::Success)
{
rc_api_server_response_t response;
response.body = result->body.c_str();
response.body_length = result->body.length();
response.http_status_code = result->status;
request->callback(&response, request->callback_data);
}
else
{
printf("[rcheevos]: http request failed: %s\n", to_string(result.error()).c_str());
}
it = async_requests.erase(it);
}
else
{
++it;
}
}
}

void ra_login_token(const char* username, const char* token, rc_client_callback_t login_callback)
{
rc_client_begin_login_with_token(ra_client, username, token, login_callback, NULL);
}
19 changes: 19 additions & 0 deletions src/retro_achievements.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef RETRO_ACHIEVEMENTS
#define RETRO_ACHIEVEMENTS
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

struct rc_client_t;
typedef struct rc_client_t rc_client_t;

typedef uint32_t (*rc_client_read_memory_func_t)(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client);
typedef void (*rc_client_callback_t)(int result, const char* error_message, rc_client_t* client, void* userdata);

bool ra_is_logged_in();
void ra_initialize_client(rc_client_read_memory_func_t memory_read_func);
void ra_login_credentials(const char* username, const char* password, rc_client_callback_t login_callback);
void ra_login_token(const char* username, const char* token, rc_client_callback_t login_callback);
void ra_poll_requests();
void ra_shutdown_client();
#endif

0 comments on commit ed1be74

Please sign in to comment.