1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #ifdef SDL_FILESYSTEM_WINDOWS
24
25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26 /* System dependent filesystem routines */
27
28 #include "../../core/windows/SDL_windows.h"
29 #include <shlobj.h>
30
31 #include "SDL_error.h"
32 #include "SDL_stdinc.h"
33 #include "SDL_filesystem.h"
34
35 char *
SDL_GetBasePath(void)36 SDL_GetBasePath(void)
37 {
38 typedef DWORD (WINAPI *GetModuleFileNameExW_t)(HANDLE, HMODULE, LPWSTR, DWORD);
39 GetModuleFileNameExW_t pGetModuleFileNameExW;
40 DWORD buflen = 128;
41 WCHAR *path = NULL;
42 HANDLE psapi = LoadLibrary(TEXT("psapi.dll"));
43 char *retval = NULL;
44 DWORD len = 0;
45 int i;
46
47 if (!psapi) {
48 WIN_SetError("Couldn't load psapi.dll");
49 return NULL;
50 }
51
52 pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(psapi, "GetModuleFileNameExW");
53 if (!pGetModuleFileNameExW) {
54 WIN_SetError("Couldn't find GetModuleFileNameExW");
55 FreeLibrary(psapi);
56 return NULL;
57 }
58
59 while (SDL_TRUE) {
60 void *ptr = SDL_realloc(path, buflen * sizeof (WCHAR));
61 if (!ptr) {
62 SDL_free(path);
63 FreeLibrary(psapi);
64 SDL_OutOfMemory();
65 return NULL;
66 }
67
68 path = (WCHAR *) ptr;
69
70 len = pGetModuleFileNameExW(GetCurrentProcess(), NULL, path, buflen);
71 /* if it truncated, then len >= buflen - 1 */
72 /* if there was enough room (or failure), len < buflen - 1 */
73 if (len < buflen - 1) {
74 break;
75 }
76
77 /* buffer too small? Try again. */
78 buflen *= 2;
79 }
80
81 FreeLibrary(psapi);
82
83 if (len == 0) {
84 SDL_free(path);
85 WIN_SetError("Couldn't locate our .exe");
86 return NULL;
87 }
88
89 for (i = len-1; i > 0; i--) {
90 if (path[i] == '\\') {
91 break;
92 }
93 }
94
95 SDL_assert(i > 0); /* Should have been an absolute path. */
96 path[i+1] = '\0'; /* chop off filename. */
97
98 retval = WIN_StringToUTF8W(path);
99 SDL_free(path);
100
101 return retval;
102 }
103
104 char *
SDL_GetPrefPath(const char * org,const char * app)105 SDL_GetPrefPath(const char *org, const char *app)
106 {
107 /*
108 * Vista and later has a new API for this, but SHGetFolderPath works there,
109 * and apparently just wraps the new API. This is the new way to do it:
110 *
111 * SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE,
112 * NULL, &wszPath);
113 */
114
115 WCHAR path[MAX_PATH];
116 char *retval = NULL;
117 WCHAR* worg = NULL;
118 WCHAR* wapp = NULL;
119 size_t new_wpath_len = 0;
120 BOOL api_result = FALSE;
121
122 if (!app) {
123 SDL_InvalidParamError("app");
124 return NULL;
125 }
126 if (!org) {
127 org = "";
128 }
129
130 if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path))) {
131 WIN_SetError("Couldn't locate our prefpath");
132 return NULL;
133 }
134
135 worg = WIN_UTF8ToStringW(org);
136 if (worg == NULL) {
137 SDL_OutOfMemory();
138 return NULL;
139 }
140
141 wapp = WIN_UTF8ToStringW(app);
142 if (wapp == NULL) {
143 SDL_free(worg);
144 SDL_OutOfMemory();
145 return NULL;
146 }
147
148 new_wpath_len = SDL_wcslen(worg) + SDL_wcslen(wapp) + SDL_wcslen(path) + 3;
149
150 if ((new_wpath_len + 1) > MAX_PATH) {
151 SDL_free(worg);
152 SDL_free(wapp);
153 WIN_SetError("Path too long.");
154 return NULL;
155 }
156
157 if (*worg) {
158 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
159 SDL_wcslcat(path, worg, SDL_arraysize(path));
160 }
161 SDL_free(worg);
162
163 api_result = CreateDirectoryW(path, NULL);
164 if (api_result == FALSE) {
165 if (GetLastError() != ERROR_ALREADY_EXISTS) {
166 SDL_free(wapp);
167 WIN_SetError("Couldn't create a prefpath.");
168 return NULL;
169 }
170 }
171
172 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
173 SDL_wcslcat(path, wapp, SDL_arraysize(path));
174 SDL_free(wapp);
175
176 api_result = CreateDirectoryW(path, NULL);
177 if (api_result == FALSE) {
178 if (GetLastError() != ERROR_ALREADY_EXISTS) {
179 WIN_SetError("Couldn't create a prefpath.");
180 return NULL;
181 }
182 }
183
184 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
185
186 retval = WIN_StringToUTF8W(path);
187
188 return retval;
189 }
190
191 #endif /* SDL_FILESYSTEM_WINDOWS */
192
193 /* vi: set ts=4 sw=4 expandtab: */
194