1 #include "discord_rpc.h"
2 
3 #define WIN32_LEAN_AND_MEAN
4 #define NOMCX
5 #define NOSERVICE
6 #define NOIME
7 #include <windows.h>
8 #include <psapi.h>
9 #include <wchar.h>
10 #include <stdio.h>
11 
get_process_id(void)12 int get_process_id(void)
13 {
14     return (int)GetCurrentProcessId();
15 }
16 
17 /**
18  * Updated fixes for MinGW and WinXP
19  * This block is written the way it does not involve changing the rest of the code
20  * Checked to be compiling
21  * 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW
22  * #include guarded, functions redirected to <string.h> substitutes
23  * 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h>
24  * The entire function is rewritten
25  */
26 #ifdef __MINGW32__
27 /* strsafe.h fixes */
StringCbPrintfW(LPWSTR pszDest,size_t cbDest,LPCWSTR pszFormat,...)28 static HRESULT StringCbPrintfW(LPWSTR pszDest,
29       size_t cbDest, LPCWSTR pszFormat, ...)
30 {
31    HRESULT ret;
32    va_list va;
33    va_start(va, pszFormat);
34    cbDest /= 2; /* Size is divided by 2 to convert from bytes to wide characters - causes segfault */
35    /* othervise */
36    ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
37    pszDest[cbDest - 1] = 0; /* Terminate the string in case a buffer overflow; -1 will be returned */
38    va_end(va);
39    return ret;
40 }
41 #else
42 #include <strsafe.h>
43 #endif /* __MINGW32__ */
44 
45 /* winreg.h fixes */
46 #ifndef LSTATUS
47 #define LSTATUS LONG
48 #endif
49 #ifdef RegSetKeyValueW
50 #undefine RegSetKeyValueW
51 #endif
52 #define RegSetKeyValueW regset
regset(HKEY hkey,LPCWSTR subkey,LPCWSTR name,DWORD type,const void * data,DWORD len)53 static LSTATUS regset(HKEY hkey,
54       LPCWSTR subkey,
55       LPCWSTR name,
56       DWORD type,
57       const void* data,
58       DWORD len)
59 {
60    LSTATUS ret;
61    HKEY htkey = hkey, hsubkey = NULL;
62    if (subkey && subkey[0])
63    {
64       if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) !=
65             ERROR_SUCCESS)
66          return ret;
67       htkey = hsubkey;
68    }
69    ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
70    if (hsubkey && hsubkey != hkey)
71       RegCloseKey(hsubkey);
72    return ret;
73 }
74 
Discord_RegisterW(const wchar_t * applicationId,const wchar_t * command)75 static void Discord_RegisterW(
76       const wchar_t* applicationId, const wchar_t* command)
77 {
78    /* https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
79     * we want to register games so we can run them as discord-<appid>://
80     * Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions. */
81 
82    DWORD len;
83    LSTATUS result;
84    wchar_t urlProtocol = 0;
85    wchar_t keyName[256];
86    wchar_t protocolName[64];
87    wchar_t protocolDescription[128];
88    wchar_t exeFilePath[MAX_PATH];
89    DWORD exeLen = GetModuleFileNameW(NULL, exeFilePath, MAX_PATH);
90    wchar_t openCommand[1024];
91 
92    if (command && command[0])
93       StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", command);
94    else
95       StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", exeFilePath);
96 
97    StringCbPrintfW(protocolName, sizeof(protocolName),
98          L"discord-%S", applicationId);
99    StringCbPrintfW(
100          protocolDescription, sizeof(protocolDescription),
101          L"URL:Run game %S protocol", applicationId);
102    StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%S", protocolName);
103    HKEY key;
104    LSTATUS status =
105       RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL);
106    if (status != ERROR_SUCCESS)
107    {
108       fprintf(stderr, "Error creating key\n");
109       return;
110    }
111    len = (DWORD)lstrlenW(protocolDescription) + 1;
112    result =
113       RegSetKeyValueW(key, NULL, NULL, REG_SZ, protocolDescription, len * sizeof(wchar_t));
114    if (FAILED(result))
115       fprintf(stderr, "Error writing description\n");
116 
117    len = (DWORD)lstrlenW(protocolDescription) + 1;
118    result = RegSetKeyValueW(key, NULL, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
119    if (FAILED(result))
120       fprintf(stderr, "Error writing description\n");
121 
122    result = RegSetKeyValueW(
123          key, L"DefaultIcon", NULL, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t));
124    if (FAILED(result))
125       fprintf(stderr, "Error writing icon\n");
126 
127    len = (DWORD)lstrlenW(openCommand) + 1;
128    result = RegSetKeyValueW(
129          key, L"shell\\open\\command", NULL, REG_SZ, openCommand, len * sizeof(wchar_t));
130    if (FAILED(result))
131       fprintf(stderr, "Error writing command\n");
132    RegCloseKey(key);
133 }
134 
Discord_Register(const char * applicationId,const char * command)135 void Discord_Register(const char* applicationId, const char* command)
136 {
137    wchar_t appId[32];
138    wchar_t openCommand[1024];
139    const wchar_t* wcommand = NULL;
140 
141    MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
142 
143    if (command && command[0])
144    {
145       const int commandBufferLen =
146          sizeof(openCommand) / sizeof(*openCommand);
147       MultiByteToWideChar(CP_UTF8, 0, command, -1,
148             openCommand, commandBufferLen);
149       wcommand = openCommand;
150    }
151 
152    Discord_RegisterW(appId, wcommand);
153 }
154 
Discord_RegisterSteamGame(const char * applicationId,const char * steamId)155 void Discord_RegisterSteamGame(
156       const char* applicationId,
157       const char* steamId)
158 {
159    DWORD pathChars, pathBytes, i;
160    HKEY key;
161    wchar_t steamPath[MAX_PATH];
162    wchar_t command[1024];
163    wchar_t appId[32];
164    wchar_t wSteamId[32];
165    MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
166    MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32);
167    LSTATUS status = RegOpenKeyExW(HKEY_CURRENT_USER,
168          L"Software\\Valve\\Steam", 0, KEY_READ, &key);
169    if (status != ERROR_SUCCESS)
170    {
171       fprintf(stderr, "Error opening Steam key\n");
172       return;
173    }
174 
175    pathBytes = sizeof(steamPath);
176    status    = RegQueryValueExW(key,
177          L"SteamExe", NULL, NULL, (BYTE*)steamPath, &pathBytes);
178    RegCloseKey(key);
179    if (status != ERROR_SUCCESS || pathBytes < 1)
180    {
181       fprintf(stderr, "Error reading SteamExe key\n");
182       return;
183    }
184 
185    pathChars = pathBytes / sizeof(wchar_t);
186    for (i = 0; i < pathChars; ++i)
187    {
188       if (steamPath[i] == L'/')
189          steamPath[i] = L'\\';
190    }
191 
192    StringCbPrintfW(command, sizeof(command),
193          L"\"%s\" steam://rungameid/%s", steamPath, wSteamId);
194 
195    Discord_RegisterW(appId, command);
196 }
197