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