Skip to content

Commit

Permalink
Add retro achievements support
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Dec 23, 2023
1 parent b259fe9 commit b2a4d78
Show file tree
Hide file tree
Showing 177 changed files with 96,519 additions and 256 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/deploy_web.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: Build Web & Deploy to GH
on: [push,pull_request]
permissions:
contents: write
pull-requests: write
issues: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -53,3 +57,12 @@ jobs:
target-folder: branch/dev
force: false
clean: false
- name: Deploy to rcheevos site 🚀
if: github.ref == 'refs/heads/rcheevos'
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages # The branch the action should deploy to.
folder: build/bin # The folder the action should deploy.
target-folder: branch/rcheevos
force: false
clean: false
37 changes: 36 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ set(LINK_LIBS "")
# Set compile_commands.json generation for clangd on by default
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

option(ENABLE_RETRO_ACHIEVEMENTS "Enable Retro Achievements" ON)

if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ELIMINATE\_DUPLICATE\_FUNCTIONS=1 -s ENVIRONMENT=web -s ASSERTIONS=0 -s WASM=1 -DSE_PLATFORM_WEB --shell-file ${PROJECT_SOURCE_DIR}/src/shell.html -s USE_CLOSURE_COMPILER=0 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ELIMINATE\_DUPLICATE\_FUNCTIONS=1 -s ENVIRONMENT=web -s ASSERTIONS=0 -s WASM=1 -DSE_PLATFORM_WEB --shell-file ${PROJECT_SOURCE_DIR}/src/shell.html -s USE_CLOSURE_COMPILER=0 ")
Expand Down Expand Up @@ -288,7 +290,7 @@ if(ENABLE_HTTP_CONTROL_SERVER)
set(SKYEMU_SRC ${SKYEMU_SRC} src/http_control_server.cpp)
endif()

set(SKYEMU_SRC ${SKYEMU_SRC} src/cloud.cpp)
set(SKYEMU_SRC ${SKYEMU_SRC} src/cloud.cpp src/https.cpp)

if(UNICODE_GUI)
set(SKYEMU_SRC ${SKYEMU_SRC} src/utf8proc/utf8proc.c)
Expand All @@ -302,6 +304,39 @@ if(USE_SDL)
include_directories(${SDL2_INCLUDE_DIRS})
endif()

if(ENABLE_RETRO_ACHIEVEMENTS)
add_definitions(-DENABLE_RETRO_ACHIEVEMENTS=1)
add_definitions(-DRC_DISABLE_LUA)
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)
endif()

if(IOS)
set(SKYEMU_SRC ${SKYEMU_SRC} src/ios_support.m)
add_definitions(-DSE_PLATFORM_IOS)
Expand Down
237 changes: 2 additions & 235 deletions src/cloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,10 @@ const char* se_get_pref_path();
#define XXH_IMPLEMENTATION
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h"
#include "https.hpp"

#ifndef EMSCRIPTEN
#include "httplib.h" // for server only
#include <curl/curl.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
extern "C" {
#include "res.h"
}
#else
#include <emscripten.h>
#endif
Expand Down Expand Up @@ -146,53 +141,7 @@ static void em_flush_fs()
#endif
}

#ifndef EMSCRIPTEN
size_t curl_write_data(void* buffer, size_t size, size_t nmemb, void* d)
{
std::vector<uint8_t>* data = (std::vector<uint8_t>*)d;
data->insert(data->end(), (uint8_t*)buffer, (uint8_t*)buffer + size * nmemb);
return size * nmemb;
}
#else
EM_JS(void, em_https_request, (const char* type, const char* url, const char* body, size_t body_size, const char* headers, void* callback), {
var xhr = new XMLHttpRequest();
var method = UTF8ToString(type);
var url_str = UTF8ToString(url);
var body_arr = new Uint8Array(Module.HEAPU8.buffer, body, body_size);
xhr.open(method, url_str);
xhr.responseType = "arraybuffer";

var headers_str = UTF8ToString(headers);
if (headers_str.length > 0) {
var headers_arr = headers_str.split('\n');
for (var i = 0; i < headers_arr.length; i++) {
var header = headers_arr[i].split(':');
if (header.length == 2) {
xhr.setRequestHeader(header[0], header[1]);
} else {
console.log('Invalid header: ' + headers_arr[i]);
}
}
}

xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
var response_size = xhr.response.byteLength;
var response_buffer = Module._malloc(response_size);
var response_view = new Uint8Array(xhr.response);
Module.HEAPU8.set(response_view, response_buffer);
Module.ccall('em_https_request_callback_wrapper', 'void', ['number', 'number', 'number'], [callback, response_buffer, response_size]);
Module._free(response_buffer);
} else {
console.log('The request failed: ' + xhr.status + ' ' + xhr.statusText);
}
};
xhr.onerror = function() {
console.log('The request failed!');
};
xhr.send(body_arr);
});

#ifdef EMSCRIPTEN
EM_JS(void, em_oath_sign_in, (void* drive, const char* client_id), {
var client_id_str = UTF8ToString(client_id);
var redirect_uri = window.location.origin + '/authorized.html';
Expand Down Expand Up @@ -253,15 +202,6 @@ EM_JS(void, em_oath_sign_in, (void* drive, const char* client_id), {
}, 500);
});

extern "C" void em_https_request_callback_wrapper(void* callback, void* data, int size)
{
std::vector<uint8_t> result((uint8_t*)data, (uint8_t*)data + (size_t)size);
std::function<void(const std::vector<uint8_t>&)>* fcallback =
(std::function<void(const std::vector<uint8_t>&)>*)callback;
(*fcallback)(result);
delete fcallback;
}

extern "C" void em_oath_sign_in_callback(cloud_drive_t* drive, const char* refresh_token,
const char* access_token)
{
Expand Down Expand Up @@ -290,165 +230,6 @@ extern "C" void em_oath_sign_in_callback(cloud_drive_t* drive, const char* refre
}
#endif

enum class http_request_e
{
GET,
POST,
PATCH,
};

#ifndef EMSCRIPTEN
// See cacertinmem.c example from libcurl
CURLcode sslctx_function(CURL *curl, void *sslctx, void *parm)
{
CURLcode rv = CURLE_ABORTED_BY_CALLBACK;

uint64_t cacert_pem_len;
const uint8_t* cacert_pem = se_get_resource(SE_CACERT_PEM, &cacert_pem_len);

BIO *cbio = BIO_new_mem_buf(cacert_pem, cacert_pem_len);
X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
int i;
STACK_OF(X509_INFO) *inf;
(void)curl;
(void)parm;

if(!cts || !cbio) {
return rv;
}

inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);

if(!inf) {
BIO_free(cbio);
return rv;
}

for(i = 0; i < sk_X509_INFO_num(inf); i++) {
X509_INFO *itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(cts, itmp->x509);
}
if(itmp->crl) {
X509_STORE_add_crl(cts, itmp->crl);
}
}

sk_X509_INFO_pop_free(inf, X509_INFO_free);
BIO_free(cbio);

rv = CURLE_OK;
return rv;
}
#endif

// Abstraction layer for http requests
void https_request(http_request_e type, const std::string& url, const std::string& body,
const std::vector<std::pair<std::string, std::string>>& headers,
std::function<void(const std::vector<uint8_t>&)> callback)
{
#ifndef EMSCRIPTEN
CURL* curl = curl_easy_init();
if (!curl)
{
printf("[cloud] failed to initialize curl\n");
return;
}

std::vector<uint8_t> result;
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&result);

/* Turn off the default CA locations, otherwise libcurl will load CA
* certificates from the locations that were detected/specified at
* build-time
*/
curl_easy_setopt(curl, CURLOPT_CAINFO, NULL);
curl_easy_setopt(curl, CURLOPT_CAPATH, NULL);

curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctx_function);

switch (type)
{
case http_request_e::GET:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
break;
case http_request_e::POST:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
break;
case http_request_e::PATCH:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
break;
default:
printf("[cloud] invalid request type\n");
return;
}

struct curl_slist* chunk = NULL;
for (auto& header : headers)
{
std::string header_string = header.first + ": " + header.second;
chunk = curl_slist_append(chunk, header_string.c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
printf("[cloud] curl failed: %s\n", curl_easy_strerror(res));
callback({});
}
else
{
callback(result);
}

curl_slist_free_all(chunk);
curl_easy_cleanup(curl);
#else
std::string method;
switch (type)
{
case http_request_e::GET:
method = "GET";
break;
case http_request_e::POST:
method = "POST";
break;
case http_request_e::PATCH:
method = "PATCH";
break;
default:
return;
}

std::string hstring;
for (auto& header : headers)
{
hstring += header.first + ":" + header.second;
if (header != headers.back())
{
hstring += "\n";
}
}

// Deleted when the callback is called
std::function<void(const std::vector<uint8_t>&)>* fcallback =
new std::function<void(const std::vector<uint8_t>&)>(callback);
return em_https_request(method.c_str(), url.c_str(), body.c_str(), body.size(), hstring.c_str(),
(void*)fcallback);
#endif
}

void google_use_refresh_token(cloud_drive_t* drive, std::function<void(cloud_drive_t*)> callback)
{
#ifdef EMSCRIPTEN
Expand Down Expand Up @@ -1121,20 +902,6 @@ cloud_user_info_t cloud_drive_get_user_info(cloud_drive_t* drive)
return info;
}

void cloud_drive_init()
{
#ifndef EMSCRIPTEN
curl_global_init(CURL_GLOBAL_ALL);
#endif
}

void cloud_drive_cleanup()
{
#ifndef EMSCRIPTEN
curl_global_cleanup();
#endif
}

uint64_t cloud_drive_hash(const char* input, size_t input_size)
{
return XXH64(input, input_size, 0);
Expand Down
3 changes: 0 additions & 3 deletions src/cloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ void cloud_drive_sync(cloud_drive_t* cloud_drive, void(*callback)());
// Has lifetime equal to the cloud drive instance.
cloud_user_info_t cloud_drive_get_user_info(cloud_drive_t* cloud_drive);

void cloud_drive_init();
void cloud_drive_cleanup();

bool cloud_drive_pending_login();
bool cloud_drive_pending_logout();

Expand Down
Loading

0 comments on commit b2a4d78

Please sign in to comment.