Skip to content

Commit

Permalink
export functions for RAIntegration-specific functionality (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Jan 4, 2024
1 parent 3cadf84 commit 98efa19
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 37 deletions.
62 changes: 58 additions & 4 deletions include/rc_client_raintegration.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,58 @@
#undef RC_CLIENT_SUPPORTS_RAINTEGRATION /* Windows required for RAIntegration */
#endif

#include <stdint.h>

#include "rc_export.h"

RC_BEGIN_C_DECLS

typedef struct rc_client_t rc_client_t; /* forward reference; in rc_client.h */

/* types needed to implement raintegration */

typedef struct rc_client_raintegration_menu_item_t {
const char* label;
uint32_t id;
uint8_t checked;
uint8_t enabled;
} rc_client_raintegration_menu_item_t;

typedef struct rc_client_raintegration_menu_t {
rc_client_raintegration_menu_item_t* items;
uint32_t num_items;
} rc_client_raintegration_menu_t;

enum {
RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE = 0,
RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED = 1, /* [menu_item] checked changed */
RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED = 2, /* hardcore was enabled or disabled */
RC_CLIENT_RAINTEGRATION_EVENT_PAUSE = 3 /* emulated system should be paused */
};

typedef struct rc_client_raintegration_event_t {
uint32_t type;

const rc_client_raintegration_menu_item_t* menu_item;
} rc_client_raintegration_event_t;

typedef void (RC_CCONV *rc_client_raintegration_event_handler_t)(const rc_client_raintegration_event_t* event,
rc_client_t* client);

typedef void (RC_CCONV *rc_client_raintegration_write_memory_func_t)(uint32_t address, uint8_t* buffer,
uint32_t num_bytes, rc_client_t* client);

/* types needed to integrate raintegration */

#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION

#ifndef RC_CLIENT_SUPPORTS_EXTERNAL
#define RC_CLIENT_SUPPORTS_EXTERNAL /* external rc_client required for RAIntegration */
#endif

#include "rc_client.h"

#include <wtypes.h> /* HWND */

RC_BEGIN_C_DECLS
#include "rc_client.h"

RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_load_raintegration(rc_client_t* client,
const wchar_t* search_directory, HWND main_window_handle,
Expand All @@ -24,8 +65,21 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_load_raintegration(

RC_EXPORT void RC_CCONV rc_client_unload_raintegration(rc_client_t* client);

RC_END_C_DECLS
RC_EXPORT void RC_CCONV rc_client_raintegration_update_main_window_handle(rc_client_t* client, HWND main_window_handle);

RC_EXPORT const rc_client_raintegration_menu_t* RC_CCONV rc_client_raintegration_get_menu(const rc_client_t* client);

RC_EXPORT void RC_CCONV rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu);
RC_EXPORT void RC_CCONV rc_client_raintegration_update_menu_item(const rc_client_t* client, const rc_client_raintegration_menu_item_t* menu_item);
RC_EXPORT int RC_CCONV rc_client_raintegration_activate_menu_item(const rc_client_t* client, uint32_t nMenuItemId);

RC_EXPORT void RC_CCONV rc_client_raintegration_set_write_memory_function(rc_client_t* client, rc_client_raintegration_write_memory_func_t handler);

RC_EXPORT void RC_CCONV rc_client_raintegration_set_event_handler(rc_client_t* client,
rc_client_raintegration_event_handler_t handler);

#endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */

RC_END_C_DECLS

#endif /* RC_CLIENT_RAINTEGRATION_H */
156 changes: 138 additions & 18 deletions src/rc_client_raintegration.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,19 @@ static void rc_client_raintegration_load_dll(rc_client_t* client,
memset(raintegration, 0, sizeof(*raintegration));
raintegration->hDLL = hDLL;

raintegration->get_version = (rc_client_raintegration_get_string_func)GetProcAddress(hDLL, "_RA_IntegrationVersion");
raintegration->get_host_url = (rc_client_raintegration_get_string_func)GetProcAddress(hDLL, "_RA_HostUrl");
raintegration->init_client = (rc_client_raintegration_init_client_func)GetProcAddress(hDLL, "_RA_InitClient");
raintegration->init_client_offline = (rc_client_raintegration_init_client_func)GetProcAddress(hDLL, "_RA_InitOffline");
raintegration->shutdown = (rc_client_raintegration_action_func)GetProcAddress(hDLL, "_RA_Shutdown");
raintegration->get_version = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_IntegrationVersion");
raintegration->get_host_url = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_HostUrl");
raintegration->init_client = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitClient");
raintegration->init_client_offline = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitOffline");
raintegration->shutdown = (rc_client_raintegration_action_func_t)GetProcAddress(hDLL, "_RA_Shutdown");

raintegration->get_external_client = (rc_client_raintegration_get_external_client)GetProcAddress(hDLL, "_Rcheevos_GetExternalClient");
raintegration->update_main_window_handle = (rc_client_raintegration_hwnd_action_func_t)GetProcAddress(hDLL, "_RA_UpdateHWnd");

raintegration->get_external_client = (rc_client_raintegration_get_external_client_func_t)GetProcAddress(hDLL, "_Rcheevos_GetExternalClient");
raintegration->get_menu = (rc_client_raintegration_get_menu_func_t)GetProcAddress(hDLL, "_Rcheevos_RAIntegrationGetMenu");
raintegration->activate_menu_item = (rc_client_raintegration_activate_menuitem_func_t)GetProcAddress(hDLL, "_Rcheevos_ActivateRAIntegrationMenuItem");
raintegration->set_write_memory_function = (rc_client_raintegration_set_write_memory_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationWriteMemoryFunction");
raintegration->set_event_handler = (rc_client_raintegration_set_event_handler_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationEventHandler");

if (!raintegration->get_version ||
!raintegration->init_client ||
Expand Down Expand Up @@ -135,7 +141,7 @@ int rc_client_version_less(const char* left, const char* right)
static void rc_client_init_raintegration(rc_client_t* client,
rc_client_version_validation_callback_data_t* version_validation_callback_data)
{
rc_client_raintegration_init_client_func init_func = client->state.raintegration->init_client;
rc_client_raintegration_init_client_func_t init_func = client->state.raintegration->init_client;

if (client->state.raintegration->get_host_url) {
const char* host_url = client->state.raintegration->get_host_url();
Expand Down Expand Up @@ -195,6 +201,8 @@ static void rc_client_init_raintegration(rc_client_t* client,
/* attach the external client and call the callback */
client->state.external_client = external_client;

client->state.raintegration->bIsInited = 1;

version_validation_callback_data->callback(RC_OK, NULL,
client, version_validation_callback_data->callback_userdata);
}
Expand Down Expand Up @@ -339,6 +347,127 @@ rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client
return &callback_data->async_handle;
}

void rc_client_raintegration_update_main_window_handle(rc_client_t* client, HWND main_window_handle)
{
if (client && client->state.raintegration &&
client->state.raintegration->bIsInited &&
client->state.raintegration->update_main_window_handle)
{
client->state.raintegration->update_main_window_handle(main_window_handle);
}
}

void rc_client_raintegration_set_write_memory_function(rc_client_t* client, rc_client_raintegration_write_memory_func_t handler)
{
if (client && client->state.raintegration && client->state.raintegration->set_write_memory_function)
client->state.raintegration->set_write_memory_function(client, handler);
}

void rc_client_raintegration_set_event_handler(rc_client_t* client,
rc_client_raintegration_event_handler_t handler)
{
if (client && client->state.raintegration && client->state.raintegration->set_event_handler)
client->state.raintegration->set_event_handler(client, handler);
}

const rc_client_raintegration_menu_t* rc_client_raintegration_get_menu(const rc_client_t* client)
{
if (!client || !client->state.raintegration ||
!client->state.raintegration->bIsInited ||
!client->state.raintegration->get_menu)
{
return NULL;
}

return client->state.raintegration->get_menu();
}

void rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu)
{
HMENU hPopupMenu = NULL;
const rc_client_raintegration_menu_t* menu;

if (!client || !client->state.raintegration)
return;

/* destroy the existing menu */
if (client->state.raintegration->hPopupMenu)
DestroyMenu(client->state.raintegration->hPopupMenu);

/* create the popup menu */
hPopupMenu = CreatePopupMenu();

menu = rc_client_raintegration_get_menu(client);
if (menu && menu->num_items)
{
const rc_client_raintegration_menu_item_t* menuitem = menu->items;
const rc_client_raintegration_menu_item_t* stop = menu->items + menu->num_items;

for (; menuitem < stop; ++menuitem)
{
if (menuitem->id == 0)
AppendMenuA(hPopupMenu, MF_SEPARATOR, 0U, NULL);
else
{
UINT flags = MF_STRING;
if (menuitem->checked)
flags |= MF_CHECKED;
if (!menuitem->enabled)
flags |= MF_DISABLED | MF_GRAYED;

AppendMenuA(hPopupMenu, flags, menuitem->id, menuitem->label);
}
}
}

/* add/update the item containing the popup menu */
{
int nIndex = GetMenuItemCount(hMenu);
const char* menuText = "&RetroAchievements";
char buffer[64];

UINT flags = MF_POPUP | MF_STRING;
if (!menu || !menu->num_items)
flags |= MF_DISABLED | MF_GRAYED;

while (--nIndex >= 0)
{
if (GetMenuStringA(hMenu, nIndex, buffer, sizeof(buffer) - 1, MF_BYPOSITION))
{
if (strcmp(buffer, menuText) == 0)
break;
}
}

if (nIndex == -1)
AppendMenuA(hMenu, flags, (UINT_PTR)hPopupMenu, menuText);
else
ModifyMenuA(hMenu, nIndex, flags | MF_BYPOSITION, (UINT_PTR)hPopupMenu, menuText);
}

client->state.raintegration->hPopupMenu = hPopupMenu;
}

void rc_client_raintegration_update_menu_item(const rc_client_t* client, const rc_client_raintegration_menu_item_t* menuitem)
{
if (client && client->state.raintegration && client->state.raintegration->hPopupMenu)
{
UINT flags = MF_STRING;
if (menuitem->checked)
flags |= MF_CHECKED;

CheckMenuItem(client->state.raintegration->hPopupMenu, menuitem->id, flags | MF_BYCOMMAND);
}
}

int rc_client_raintegration_activate_menu_item(const rc_client_t* client, uint32_t nMenuItemId)
{
if (!client || !client->state.raintegration || !client->state.raintegration->activate_menu_item)
return 0;

return client->state.raintegration->activate_menu_item(nMenuItemId);
}

void rc_client_unload_raintegration(rc_client_t* client)
{
HINSTANCE hDLL;
Expand All @@ -348,17 +477,8 @@ void rc_client_unload_raintegration(rc_client_t* client)

RC_CLIENT_LOG_INFO(client, "Unloading RA_Integration")

if (client->state.raintegration->shutdown) {
#ifdef __cplusplus
try {
#endif
client->state.raintegration->shutdown();
#ifdef __cplusplus
}
catch (std::runtime_error&) {
}
#endif
}
if (client->state.raintegration->shutdown)
client->state.raintegration->shutdown();

rc_mutex_lock(&client->state.mutex);
hDLL = client->state.raintegration->hDLL;
Expand Down
34 changes: 24 additions & 10 deletions src/rc_client_raintegration_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,36 @@ RC_BEGIN_C_DECLS

/* RAIntegration follows the same calling convention as rcheevos */

typedef void (RC_CCONV *rc_client_raintegration_action_func)(void);
typedef const char* (RC_CCONV *rc_client_raintegration_get_string_func)(void);
typedef int (RC_CCONV *rc_client_raintegration_init_client_func)(HWND hMainWnd, const char* sClientName, const char* sClientVersion);
typedef int (RC_CCONV *rc_client_raintegration_get_external_client)(rc_client_external_t* pClient, int nVersion);
typedef void (RC_CCONV* rc_client_raintegration_action_func_t)(void);
typedef const char* (RC_CCONV* rc_client_raintegration_get_string_func_t)(void);
typedef int (RC_CCONV* rc_client_raintegration_init_client_func_t)(HWND hMainWnd, const char* sClientName, const char* sClientVersion);
typedef int (RC_CCONV* rc_client_raintegration_get_external_client_func_t)(rc_client_external_t* pClient, int nVersion);
typedef void (RC_CCONV* rc_client_raintegration_hwnd_action_func_t)(HWND hWnd);
typedef const rc_client_raintegration_menu_t* (RC_CCONV* rc_client_raintegration_get_menu_func_t)(void);
typedef int (RC_CCONV* rc_client_raintegration_activate_menuitem_func_t)(uint32_t nMenuItemId);
typedef void (RC_CCONV* rc_client_raintegration_set_write_memory_func_t)(rc_client_t* pClient, rc_client_raintegration_write_memory_func_t handler);
typedef void (RC_CCONV* rc_client_raintegration_set_event_handler_func_t)(rc_client_t* pClient, rc_client_raintegration_event_handler_t handler);

typedef struct rc_client_raintegration_t
{
HINSTANCE hDLL;
HMENU hPopupMenu;
uint8_t bIsInited;

rc_client_raintegration_get_string_func get_version;
rc_client_raintegration_get_string_func get_host_url;
rc_client_raintegration_init_client_func init_client;
rc_client_raintegration_init_client_func init_client_offline;
rc_client_raintegration_action_func shutdown;
rc_client_raintegration_get_string_func_t get_version;
rc_client_raintegration_get_string_func_t get_host_url;
rc_client_raintegration_init_client_func_t init_client;
rc_client_raintegration_init_client_func_t init_client_offline;
rc_client_raintegration_action_func_t shutdown;

rc_client_raintegration_get_external_client get_external_client;
rc_client_raintegration_hwnd_action_func_t update_main_window_handle;

rc_client_raintegration_set_write_memory_func_t set_write_memory_function;
rc_client_raintegration_set_event_handler_func_t set_event_handler;
rc_client_raintegration_get_menu_func_t get_menu;
rc_client_raintegration_activate_menuitem_func_t activate_menu_item;

rc_client_raintegration_get_external_client_func_t get_external_client;

} rc_client_raintegration_t;

Expand Down
8 changes: 4 additions & 4 deletions test/rcheevos-test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\include;$(ProjectDir)..\src\rcheevos;$(ProjectDir)lua\src</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_MBCS;RC_STATIC;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_MBCS;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
Expand All @@ -88,7 +88,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\include;$(ProjectDir)..\src\rcheevos;$(ProjectDir)lua\src</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_MBCS;RC_STATIC;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_MBCS;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
Expand All @@ -104,7 +104,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\include;$(ProjectDir)..\src\rcheevos;$(ProjectDir)lua\src</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_MBCS;RC_STATIC;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_MBCS;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand All @@ -122,7 +122,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\include;$(ProjectDir)..\src\rcheevos;$(ProjectDir)lua\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_MBCS;;RC_STATIC;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_MBCS;RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand Down
1 change: 1 addition & 0 deletions test/rcheevos-test.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
<ClCompile Include="test_rc_client_raintegration.c">
<Filter>tests</Filter>
</ClCompile>
<ClCompile Include="..\src\rc_version.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\rcheevos.h">
Expand Down
Loading

0 comments on commit 98efa19

Please sign in to comment.