Mercurial > wgsdk
changeset 0:d91dfd53b8b4
Initial commit
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 07 Aug 2022 07:26:27 -0400 |
parents | |
children | 7abb5d8b20ea |
files | .gitignore LICENSE Makefile README.md src/config.c src/dirtools.c src/include/config.h src/include/dirtools.h src/include/main.h src/include/timer.h src/main.c src/timer.c |
diffstat | 12 files changed, 429 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,4 @@ +*.o +*.dll +*.exe +discord_game_sdk \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2019 ClAndrew +Copyright (c) 2022 Paper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,22 @@ +CFLAGS=-Wall -mdll -Isrc/include -I"discord_game_sdk/c" +ifeq ($(PROCESSOR_ARCHITECTURE),x86) + ifeq ($(PROCESSOR_ARCHITEW6432),x86) + CFLAGS += -I"/c/Program Files/Winamp SDK" + else + CFLAGS += -I"/c/Program Files (x86)/Winamp SDK" + endif +else + CFLAGS += -I"/c/Program Files (x86)/Winamp SDK" +endif +LDFLAGS=-L"discord_game_sdk/lib/x86" -ldiscord_game_sdk +DEPS=src/include/config.h src/include/dirtools.h src/include/main.h src/include/timer.h +OBJ=src/config.o src/dirtools.o src/main.o src/timer.o + +src/%.o: src/%.c $(DEPS) + gcc -c $(CFLAGS) $< -o $@ + +gen_DiscordGameSDK.dll: $(OBJ) + gcc -o $@ $(CFLAGS) $^ $(LDFLAGS) + +clean: + rm -f src/*.o *.dll \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,3 @@ +# Discord GameSDK Winamp Plugin + +This plugin (largely based off of clandrew's [wdrp](https://github.com/clandrew/wdrp)) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config.c Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,72 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "dirtools.h" +#include "config.h" +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#define MAX_LINE_LENGTH 128 + +static unsigned int crc32b(unsigned char *message) { + int i, j; + unsigned int byte, crc, mask; + + i = 0; + crc = 0xFFFFFFFF; + while (message[i] != 0) { + byte = message[i]; // Get next byte. + crc = crc ^ byte; + for (j = 7; j >= 0; j--) { // Do eight times. + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + i = i + 1; + } + return ~crc; +} + +int cfg_load(struct config_t* config) { + char* path = NULL; + char line[MAX_LINE_LENGTH] = {0}; + FILE* config_fp; + path = dirtools_concat_paths(getenv("APPDATA"), "Winamp\\Plugins\\wgsdk\\config.txt"); + config_fp = fopen(path, "r"); + if (config_fp == NULL) { + free(path); + return 1; + } + while (fgets(line, MAX_LINE_LENGTH, config_fp)) { + switch (crc32b((unsigned char*)strtok(line, "="))) { + case 0x2a666380: // display_title + config->display_title = atoi(strtok(NULL, "=")); + break; + case 0xbb631f7f: // show_elapsed_time + config->show_elapsed_time = atoi(strtok(NULL, "=")); + break; + default: + break; + } + } + free(path); + return 0; +} + +int cfg_save(struct config_t config) { + char* path = NULL; + FILE* config_fp; + path = dirtools_concat_paths(getenv("APPDATA"), "Winamp\\Plugins\\wgsdk"); + assert(dirtools_create_directory(path)); + free(path); + path = dirtools_concat_paths(getenv("APPDATA"), "Winamp\\Plugins\\wgsdk\\config.txt"); + config_fp = fopen(path, "w"); + if (config_fp == NULL) { + return 1; + } + fprintf(config_fp, "display_title=%d\n", config.display_title); + fprintf(config_fp, "show_elapsed_time=%d\n", config.show_elapsed_time); + fclose(config_fp); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dirtools.c Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,36 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include "dirtools.h" + +int dirtools_create_directory(char* path) { + struct stat st = {0}; + char* alltoks = calloc(strlen(path), sizeof(char)), *tok; + + for (tok = strtok(path, "\\"); tok != NULL; tok = strtok(NULL, "\\")) { + strcat(alltoks, tok); + if (stat(alltoks, &st) == -1) { + CreateDirectoryA(alltoks, NULL); + } + } + + return 0; +} + +char* dirtools_concat_paths(char* a, char* b) { + if (a[0] == '\0' || b[0] == '\0') + return NULL; + char* out = calloc((strlen(a) + strlen(b) + 2), sizeof(char)); + if (out == NULL) + return out; + strcpy(out, a); + if (a[strlen(a)] != '\\' && b[0] != '\\') + strcat(out, "\\"); + strcat(out, b); + return out; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/include/config.h Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,10 @@ +#ifndef __config_h +#define __config_h +struct config_t { + int display_title; + int show_elapsed_time; +}; + +int cfg_load(struct config_t* config); +int cfg_save(struct config_t config); +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/include/dirtools.h Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,5 @@ +#ifndef __dirtools_h +#define __dirtools_h +int dirtools_create_directory(char* path); +char* dirtools_concat_paths(char* a, char* b); +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/include/main.h Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,27 @@ +#ifndef __main_h +#define __main_h +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +typedef struct { + int version; // version of the plugin structure + char *description; // name/title of the plugin + int(*init)(); // function which will be executed on init event + void(*config)(); // function which will be executed on config event + void(*quit)(); // function which will be executed on quit event + HWND hwndParent; // hwnd of the Winamp client main window (stored by Winamp when dll is loaded) + HINSTANCE hDllInstance; // hinstance of this plugin DLL. (stored by Winamp when dll is loaded) +} winamp_general_purpose_plugin; + +struct app_t { + struct IDiscordCore* core; + struct IDiscordUsers* users; + struct IDiscordActivityManager* activities; +}; + +void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD); +int init(); +void quit(); +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/include/timer.h Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,20 @@ +#ifndef __timer_h +#define __timer_h +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +struct timer_t { + int initialized; + int is_timer_alive; + UINT interval; + HWND winampClientWindow; + TIMERPROC timer_proc; +}; + +void timer_init(struct timer_t* timer, HWND winampClientWindow, TIMERPROC timer_proc); +void timer_set(struct timer_t* timer, HWND winampClientWindow); +void timer_stop(struct timer_t* timer, HWND winampClientWindow); + +#endif // __timer_h \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,176 @@ +#include <assert.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> +#include <Winamp/wa_ipc.h> +#include "main.h" +#include "discord_game_sdk.h" +#include "timer.h" +#include "config.h" +#include "dirtools.h" +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#define CLIENT_ID 969367220599803955 +#define DISCORD_REQUIRE(x) \ + assert(x == DiscordResult_Ok) + +#define GPPHDR_VER 0x10 + +winamp_general_purpose_plugin g_plugin = { + GPPHDR_VER, // version of the plugin, defined in "gen_myplugin.h" + "Discord GameSDK", // name/title of the plugin, defined in "gen_myplugin.h" + init, // function name which will be executed on init event + NULL, // function name which will be executed on config event + quit, // function name which will be executed on quit event + 0, // handle to Winamp main window, loaded by winamp when this dll is loaded + 0 // hinstance to this dll, loaded by winamp when this dll is loaded +}; + +WNDPROC g_lpWndProcOld = 0; + +struct timer_t timer_callbacks = { .interval = 16 }; +struct config_t config = { + .display_title = 1, + .show_elapsed_time = 1 +}; + +struct DiscordActivity activity = { + .application_id = CLIENT_ID, + .name = "Winamp", + .instance = 0 +}; + +struct IDiscordActivityEvents activities_events; + +struct app_t app; + +void update_activity_callback(void* data, enum EDiscordResult result) +{ + DISCORD_REQUIRE(result); +} + +void report_current_song_status(int playbackState) +{ + assert(playbackState != 0); + activity.timestamps.start = 0; + strcpy(activity.state, playbackState == 1 ? "(Playing)" : "(Paused)"); + + if (playbackState == 1) { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ULARGE_INTEGER ul; + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + long long dtn = ((ul.QuadPart - 116444736000000000ULL)/10000000); + + activity.timestamps.start = dtn - SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000; + } else { + activity.timestamps.start = 0; + } + + char* detailsMessage = calloc(256, sizeof(char)); + //if (g_pluginSettings.DisplayTitleInStatus) + //{ + wchar_t* title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE); + assert(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, title, -1, detailsMessage, 256, NULL, NULL)); + //} + //else + //{ + // strcpy(activity.details, ""); + //} + strcpy(activity.details, detailsMessage); + free(detailsMessage); + + app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); +} + +void report_idle_status(void) +{ + activity.timestamps.start = 0; + strcpy(activity.state, "(Idle)"); + + app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); +} + +void update_rich_presence_details(void) +{ + LONG isPlayingResult = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + + switch (isPlayingResult) { + case 1: + report_current_song_status(1); + break; + case 3: + report_current_song_status(3); + break; + case 0: + report_idle_status(); + break; + default: break; + } +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS) + { + // Notification sent from Winamp on any change in playback. + + update_rich_presence_details(); + } + + return CallWindowProc(g_lpWndProcOld, hwnd, message, wParam, lParam); +} + +int init() { + char* path = dirtools_concat_paths(getenv("APPDATA"), "Winamp\\Plugins\\wgsdk"); + printf("%s", path); + memset(&app, 0, sizeof(app)); + if (IsWindowUnicode(g_plugin.hwndParent)) { + g_lpWndProcOld = (WNDPROC)SetWindowLongW(g_plugin.hwndParent, -4, (LONG)WndProc); + } else { + g_lpWndProcOld = (WNDPROC)SetWindowLongA(g_plugin.hwndParent, -4, (LONG)WndProc); + } + + memset(&activity, 0, sizeof(activity)); + memset(&activities_events, 0, sizeof(activities_events)); + + struct DiscordCreateParams params; + DiscordCreateParamsSetDefault(¶ms); + params.client_id = CLIENT_ID; + params.flags = DiscordCreateFlags_Default; + params.event_data = &app; + params.activity_events = &activities_events; + + DISCORD_REQUIRE(DiscordCreate(DISCORD_VERSION, ¶ms, &app.core)); + + app.activities = app.core->get_activity_manager(app.core); + + timer_init(&timer_callbacks, g_plugin.hwndParent, TimerProc); + timer_set(&timer_callbacks, g_plugin.hwndParent); + + cfg_load(&config); + + strcpy(activity.assets.large_image, "winamp-logo"); + + update_rich_presence_details(); + + return 0; +} + +void quit() { + assert(!cfg_save(config)); + app.activities->clear_activity(app.activities, &app, update_activity_callback); +} + +void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) { + DISCORD_REQUIRE(app.core->run_callbacks(app.core)); +} + +__declspec(dllexport) winamp_general_purpose_plugin* winampGetGeneralPurposePlugin() { + return &g_plugin; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timer.c Sun Aug 07 07:26:27 2022 -0400 @@ -0,0 +1,32 @@ +#include <assert.h> +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include "timer.h" +void timer_init(struct timer_t* timer, HWND winampClientWindow, TIMERPROC timer_proc) { + timer->winampClientWindow = winampClientWindow; + timer->timer_proc = timer_proc; + timer->initialized = 1; +} + +void timer_set(struct timer_t* timer, HWND winampClientWindow) { + assert(timer->initialized); + + if (timer->is_timer_alive) + return; + + timer->is_timer_alive = 1; + SetTimer(winampClientWindow, 1, timer->interval, timer->timer_proc); +} + +void timer_stop(struct timer_t* timer, HWND winampClientWindow) { + assert(timer->initialized); + + if (!timer->is_timer_alive) + return; + + + timer->is_timer_alive = 0; + KillTimer(winampClientWindow, 1); +} \ No newline at end of file