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(&params);
+	params.client_id = CLIENT_ID;
+	params.flags = DiscordCreateFlags_Default;
+	params.event_data = &app;
+	params.activity_events = &activities_events;
+
+	DISCORD_REQUIRE(DiscordCreate(DISCORD_VERSION, &params, &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