summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/storage
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/storage')
-rw-r--r--contrib/SDL-3.2.8/src/storage/SDL_storage.c410
-rw-r--r--contrib/SDL-3.2.8/src/storage/SDL_sysstorage.h51
-rw-r--r--contrib/SDL-3.2.8/src/storage/generic/SDL_genericstorage.c351
-rw-r--r--contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage.c215
-rw-r--r--contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage_proc.h14
5 files changed, 1041 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/storage/SDL_storage.c b/contrib/SDL-3.2.8/src/storage/SDL_storage.c
new file mode 100644
index 0000000..75952ff
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/storage/SDL_storage.c
@@ -0,0 +1,410 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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
22#include "SDL_internal.h"
23
24#include "SDL_sysstorage.h"
25#include "../filesystem/SDL_sysfilesystem.h"
26
27// Available title storage drivers
28static TitleStorageBootStrap *titlebootstrap[] = {
29 &GENERIC_titlebootstrap,
30 NULL
31};
32
33// Available user storage drivers
34static UserStorageBootStrap *userbootstrap[] = {
35#ifdef SDL_STORAGE_STEAM
36 &STEAM_userbootstrap,
37#endif
38 &GENERIC_userbootstrap,
39 NULL
40};
41
42struct SDL_Storage
43{
44 SDL_StorageInterface iface;
45 void *userdata;
46};
47
48#define CHECK_STORAGE_MAGIC() \
49 if (!storage) { \
50 return SDL_SetError("Invalid storage container"); \
51 }
52
53#define CHECK_STORAGE_MAGIC_RET(result) \
54 if (!storage) { \
55 SDL_SetError("Invalid storage container"); \
56 return result; \
57 }
58
59// we don't make any effort to convert path separators here, because a)
60// everything including Windows will accept a '/' separator and b) that
61// conversion should probably happen in the storage backend anyhow.
62
63static bool ValidateStoragePath(const char *path)
64{
65 if (SDL_strchr(path, '\\')) {
66 return SDL_SetError("Windows-style path separators ('\\') not permitted, use '/' instead.");
67 }
68
69 const char *ptr;
70 const char *prev = path;
71 while ((ptr = SDL_strchr(prev, '/')) != NULL) {
72 if ((SDL_strncmp(prev, "./", 2) == 0) || (SDL_strncmp(prev, "../", 3) == 0)) {
73 return SDL_SetError("Relative paths not permitted");
74 }
75 prev = ptr + 1;
76 }
77
78 // check the last path element (or the only path element).
79 if ((SDL_strcmp(prev, ".") == 0) || (SDL_strcmp(prev, "..") == 0)) {
80 return SDL_SetError("Relative paths not permitted");
81 }
82
83 return true;
84}
85
86SDL_Storage *SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props)
87{
88 SDL_Storage *storage = NULL;
89 int i = 0;
90
91 // Select the proper storage driver
92 const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_TITLE_DRIVER);
93 if (driver_name && *driver_name != 0) {
94 const char *driver_attempt = driver_name;
95 while (driver_attempt && *driver_attempt != 0 && !storage) {
96 const char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
97 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt)
98 : SDL_strlen(driver_attempt);
99
100 for (i = 0; titlebootstrap[i]; ++i) {
101 if ((driver_attempt_len == SDL_strlen(titlebootstrap[i]->name)) &&
102 (SDL_strncasecmp(titlebootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
103 storage = titlebootstrap[i]->create(override, props);
104 break;
105 }
106 }
107
108 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
109 }
110 } else {
111 for (i = 0; titlebootstrap[i]; ++i) {
112 storage = titlebootstrap[i]->create(override, props);
113 if (storage) {
114 break;
115 }
116 }
117 }
118 if (!storage) {
119 if (driver_name) {
120 SDL_SetError("%s not available", driver_name);
121 } else {
122 SDL_SetError("No available title storage driver");
123 }
124 }
125 return storage;
126}
127
128SDL_Storage *SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props)
129{
130 SDL_Storage *storage = NULL;
131 int i = 0;
132
133 // Select the proper storage driver
134 const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_USER_DRIVER);
135 if (driver_name && *driver_name != 0) {
136 const char *driver_attempt = driver_name;
137 while (driver_attempt && *driver_attempt != 0 && !storage) {
138 const char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
139 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt)
140 : SDL_strlen(driver_attempt);
141
142 for (i = 0; userbootstrap[i]; ++i) {
143 if ((driver_attempt_len == SDL_strlen(userbootstrap[i]->name)) &&
144 (SDL_strncasecmp(userbootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
145 storage = userbootstrap[i]->create(org, app, props);
146 break;
147 }
148 }
149
150 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
151 }
152 } else {
153 for (i = 0; userbootstrap[i]; ++i) {
154 storage = userbootstrap[i]->create(org, app, props);
155 if (storage) {
156 break;
157 }
158 }
159 }
160 if (!storage) {
161 if (driver_name) {
162 SDL_SetError("%s not available", driver_name);
163 } else {
164 SDL_SetError("No available user storage driver");
165 }
166 }
167 return storage;
168}
169
170SDL_Storage *SDL_OpenFileStorage(const char *path)
171{
172 return GENERIC_OpenFileStorage(path);
173}
174
175SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata)
176{
177 SDL_Storage *storage;
178
179 if (!iface) {
180 SDL_InvalidParamError("iface");
181 return NULL;
182 }
183 if (iface->version < sizeof(*iface)) {
184 // Update this to handle older versions of this interface
185 SDL_SetError("Invalid interface, should be initialized with SDL_INIT_INTERFACE()");
186 return NULL;
187 }
188
189 storage = (SDL_Storage *)SDL_calloc(1, sizeof(*storage));
190 if (storage) {
191 SDL_copyp(&storage->iface, iface);
192 storage->userdata = userdata;
193 }
194 return storage;
195}
196
197bool SDL_CloseStorage(SDL_Storage *storage)
198{
199 bool result = true;
200
201 CHECK_STORAGE_MAGIC()
202
203 if (storage->iface.close) {
204 result = storage->iface.close(storage->userdata);
205 }
206 SDL_free(storage);
207 return result;
208}
209
210bool SDL_StorageReady(SDL_Storage *storage)
211{
212 CHECK_STORAGE_MAGIC_RET(false)
213
214 if (storage->iface.ready) {
215 return storage->iface.ready(storage->userdata);
216 }
217 return true;
218}
219
220bool SDL_GetStorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length)
221{
222 SDL_PathInfo info;
223
224 if (SDL_GetStoragePathInfo(storage, path, &info)) {
225 if (length) {
226 *length = info.size;
227 }
228 return true;
229 } else {
230 if (length) {
231 *length = 0;
232 }
233 return false;
234 }
235}
236
237bool SDL_ReadStorageFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length)
238{
239 CHECK_STORAGE_MAGIC()
240
241 if (!path) {
242 return SDL_InvalidParamError("path");
243 } else if (!ValidateStoragePath(path)) {
244 return false;
245 } else if (!storage->iface.read_file) {
246 return SDL_Unsupported();
247 }
248
249 return storage->iface.read_file(storage->userdata, path, destination, length);
250}
251
252bool SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length)
253{
254 CHECK_STORAGE_MAGIC()
255
256 if (!path) {
257 return SDL_InvalidParamError("path");
258 } else if (!ValidateStoragePath(path)) {
259 return false;
260 } else if (!storage->iface.write_file) {
261 return SDL_Unsupported();
262 }
263
264 return storage->iface.write_file(storage->userdata, path, source, length);
265}
266
267bool SDL_CreateStorageDirectory(SDL_Storage *storage, const char *path)
268{
269 CHECK_STORAGE_MAGIC()
270
271 if (!path) {
272 return SDL_InvalidParamError("path");
273 } else if (!ValidateStoragePath(path)) {
274 return false;
275 } else if (!storage->iface.mkdir) {
276 return SDL_Unsupported();
277 }
278
279 return storage->iface.mkdir(storage->userdata, path);
280}
281
282bool SDL_EnumerateStorageDirectory(SDL_Storage *storage, const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata)
283{
284 CHECK_STORAGE_MAGIC()
285
286 if (!path) {
287 path = ""; // we allow NULL to mean "root of the storage tree".
288 }
289
290 if (!ValidateStoragePath(path)) {
291 return false;
292 } else if (!storage->iface.enumerate) {
293 return SDL_Unsupported();
294 }
295
296 return storage->iface.enumerate(storage->userdata, path, callback, userdata);
297}
298
299bool SDL_RemoveStoragePath(SDL_Storage *storage, const char *path)
300{
301 CHECK_STORAGE_MAGIC()
302
303 if (!path) {
304 return SDL_InvalidParamError("path");
305 } else if (!ValidateStoragePath(path)) {
306 return false;
307 } else if (!storage->iface.remove) {
308 return SDL_Unsupported();
309 }
310
311 return storage->iface.remove(storage->userdata, path);
312}
313
314bool SDL_RenameStoragePath(SDL_Storage *storage, const char *oldpath, const char *newpath)
315{
316 CHECK_STORAGE_MAGIC()
317
318 if (!oldpath) {
319 return SDL_InvalidParamError("oldpath");
320 } else if (!newpath) {
321 return SDL_InvalidParamError("newpath");
322 } else if (!ValidateStoragePath(oldpath)) {
323 return false;
324 } else if (!ValidateStoragePath(newpath)) {
325 return false;
326 } else if (!storage->iface.rename) {
327 return SDL_Unsupported();
328 }
329
330 return storage->iface.rename(storage->userdata, oldpath, newpath);
331}
332
333bool SDL_CopyStorageFile(SDL_Storage *storage, const char *oldpath, const char *newpath)
334{
335 CHECK_STORAGE_MAGIC()
336
337 if (!oldpath) {
338 return SDL_InvalidParamError("oldpath");
339 } else if (!newpath) {
340 return SDL_InvalidParamError("newpath");
341 } else if (!ValidateStoragePath(oldpath)) {
342 return false;
343 } else if (!ValidateStoragePath(newpath)) {
344 return false;
345 } else if (!storage->iface.copy) {
346 return SDL_Unsupported();
347 }
348
349 return storage->iface.copy(storage->userdata, oldpath, newpath);
350}
351
352bool SDL_GetStoragePathInfo(SDL_Storage *storage, const char *path, SDL_PathInfo *info)
353{
354 SDL_PathInfo dummy;
355
356 if (!info) {
357 info = &dummy;
358 }
359 SDL_zerop(info);
360
361 CHECK_STORAGE_MAGIC()
362
363 if (!path) {
364 return SDL_InvalidParamError("path");
365 } else if (!ValidateStoragePath(path)) {
366 return false;
367 } else if (!storage->iface.info) {
368 return SDL_Unsupported();
369 }
370
371 return storage->iface.info(storage->userdata, path, info);
372}
373
374Uint64 SDL_GetStorageSpaceRemaining(SDL_Storage *storage)
375{
376 CHECK_STORAGE_MAGIC_RET(0)
377
378 if (!storage->iface.space_remaining) {
379 SDL_Unsupported();
380 return 0;
381 }
382
383 return storage->iface.space_remaining(storage->userdata);
384}
385
386static bool GlobStorageDirectoryGetPathInfo(const char *path, SDL_PathInfo *info, void *userdata)
387{
388 return SDL_GetStoragePathInfo((SDL_Storage *) userdata, path, info);
389}
390
391static bool GlobStorageDirectoryEnumerator(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata)
392{
393 return SDL_EnumerateStorageDirectory((SDL_Storage *) userdata, path, cb, cbuserdata);
394}
395
396char **SDL_GlobStorageDirectory(SDL_Storage *storage, const char *path, const char *pattern, SDL_GlobFlags flags, int *count)
397{
398 CHECK_STORAGE_MAGIC_RET(NULL)
399
400 if (!path) {
401 path = ""; // we allow NULL to mean "root of the storage tree".
402 }
403
404 if (!ValidateStoragePath(path)) {
405 return NULL;
406 }
407
408 return SDL_InternalGlobDirectory(path, pattern, flags, count, GlobStorageDirectoryEnumerator, GlobStorageDirectoryGetPathInfo, storage);
409}
410
diff --git a/contrib/SDL-3.2.8/src/storage/SDL_sysstorage.h b/contrib/SDL-3.2.8/src/storage/SDL_sysstorage.h
new file mode 100644
index 0000000..57d60d6
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/storage/SDL_sysstorage.h
@@ -0,0 +1,51 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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
22#ifndef SDL_sysstorage_h_
23#define SDL_sysstorage_h_
24
25#include "SDL_internal.h"
26
27typedef struct TitleStorageBootStrap
28{
29 const char *name;
30 const char *desc;
31 SDL_Storage *(*create)(const char*, SDL_PropertiesID);
32} TitleStorageBootStrap;
33
34typedef struct UserStorageBootStrap
35{
36 const char *name;
37 const char *desc;
38 SDL_Storage *(*create)(const char*, const char*, SDL_PropertiesID);
39} UserStorageBootStrap;
40
41// Not all of these are available in a given build. Use #ifdefs, etc.
42
43extern TitleStorageBootStrap GENERIC_titlebootstrap;
44// Steam does not have title storage APIs
45
46extern UserStorageBootStrap GENERIC_userbootstrap;
47extern UserStorageBootStrap STEAM_userbootstrap;
48
49extern SDL_Storage *GENERIC_OpenFileStorage(const char *path);
50
51#endif // SDL_sysstorage_h_
diff --git a/contrib/SDL-3.2.8/src/storage/generic/SDL_genericstorage.c b/contrib/SDL-3.2.8/src/storage/generic/SDL_genericstorage.c
new file mode 100644
index 0000000..cc0804f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/storage/generic/SDL_genericstorage.c
@@ -0,0 +1,351 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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
22#include "SDL_internal.h"
23
24#include "../SDL_sysstorage.h"
25
26
27static char *GENERIC_INTERNAL_CreateFullPath(const char *base, const char *relative)
28{
29 char *result = NULL;
30 SDL_asprintf(&result, "%s%s", base ? base : "", relative);
31 return result;
32}
33
34static bool GENERIC_CloseStorage(void *userdata)
35{
36 SDL_free(userdata);
37 return true;
38}
39
40typedef struct GenericEnumerateData
41{
42 size_t base_len;
43 SDL_EnumerateDirectoryCallback real_callback;
44 void *real_userdata;
45} GenericEnumerateData;
46
47static SDL_EnumerationResult SDLCALL GENERIC_EnumerateDirectory(void *userdata, const char *dirname, const char *fname)
48{
49 // SDL_EnumerateDirectory will return the full path, so for Storage we
50 // can take the base directory and add its length to the dirname string,
51 // effectively trimming the root without having to strdup anything.
52 const GenericEnumerateData *wrap_data = (GenericEnumerateData *)userdata;
53
54 dirname += wrap_data->base_len; // skip the base, just return the part inside of the Storage.
55
56 #ifdef SDL_PLATFORM_WINDOWS
57 char *dirnamecpy = NULL;
58 const size_t slen = SDL_strlen(dirname);
59 if (slen && (dirname[slen - 1] == '\\')) {
60 dirnamecpy = SDL_strdup(dirname);
61 if (!dirnamecpy) {
62 return SDL_ENUM_FAILURE;
63 }
64 dirnamecpy[slen - 1] = '/'; // storage layer always uses '/' path separators.
65 dirname = dirnamecpy;
66 }
67 const SDL_EnumerationResult retval = wrap_data->real_callback(wrap_data->real_userdata, dirname, fname);
68 SDL_free(dirnamecpy);
69 return retval;
70 #else
71 return wrap_data->real_callback(wrap_data->real_userdata, dirname, fname);
72 #endif
73}
74
75static bool GENERIC_EnumerateStorageDirectory(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)
76{
77 bool result = false;
78 GenericEnumerateData wrap_data;
79
80 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
81 if (fullpath) {
82 wrap_data.base_len = SDL_strlen((char *)userdata);
83 wrap_data.real_callback = callback;
84 wrap_data.real_userdata = callback_userdata;
85
86 result = SDL_EnumerateDirectory(fullpath, GENERIC_EnumerateDirectory, &wrap_data);
87
88 SDL_free(fullpath);
89 }
90 return result;
91}
92
93static bool GENERIC_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
94{
95 bool result = false;
96
97 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
98 if (fullpath) {
99 result = SDL_GetPathInfo(fullpath, info);
100
101 SDL_free(fullpath);
102 }
103 return result;
104}
105
106static bool GENERIC_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
107{
108 bool result = false;
109
110 if (length > SDL_SIZE_MAX) {
111 return SDL_SetError("Read size exceeds SDL_SIZE_MAX");
112 }
113
114 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
115 if (fullpath) {
116 SDL_IOStream *stream = SDL_IOFromFile(fullpath, "rb");
117 if (stream) {
118 // FIXME: Should SDL_ReadIO use u64 now...?
119 if (SDL_ReadIO(stream, destination, (size_t)length) == length) {
120 result = true;
121 } else {
122 SDL_SetError("File length did not exactly match the destination length");
123 }
124 SDL_CloseIO(stream);
125 }
126 SDL_free(fullpath);
127 }
128 return result;
129}
130
131static bool GENERIC_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
132{
133 // TODO: Recursively create subdirectories with SDL_CreateDirectory
134 bool result = false;
135
136 if (length > SDL_SIZE_MAX) {
137 return SDL_SetError("Write size exceeds SDL_SIZE_MAX");
138 }
139
140 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
141 if (fullpath) {
142 SDL_IOStream *stream = SDL_IOFromFile(fullpath, "wb");
143
144 if (stream) {
145 // FIXME: Should SDL_WriteIO use u64 now...?
146 if (SDL_WriteIO(stream, source, (size_t)length) == length) {
147 result = true;
148 } else {
149 SDL_SetError("Resulting file length did not exactly match the source length");
150 }
151 SDL_CloseIO(stream);
152 }
153 SDL_free(fullpath);
154 }
155 return result;
156}
157
158static bool GENERIC_CreateStorageDirectory(void *userdata, const char *path)
159{
160 // TODO: Recursively create subdirectories with SDL_CreateDirectory
161 bool result = false;
162
163 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
164 if (fullpath) {
165 result = SDL_CreateDirectory(fullpath);
166
167 SDL_free(fullpath);
168 }
169 return result;
170}
171
172static bool GENERIC_RemoveStoragePath(void *userdata, const char *path)
173{
174 bool result = false;
175
176 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
177 if (fullpath) {
178 result = SDL_RemovePath(fullpath);
179
180 SDL_free(fullpath);
181 }
182 return result;
183}
184
185static bool GENERIC_RenameStoragePath(void *userdata, const char *oldpath, const char *newpath)
186{
187 bool result = false;
188
189 char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath);
190 char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath);
191 if (fulloldpath && fullnewpath) {
192 result = SDL_RenamePath(fulloldpath, fullnewpath);
193 }
194 SDL_free(fulloldpath);
195 SDL_free(fullnewpath);
196
197 return result;
198}
199
200static bool GENERIC_CopyStorageFile(void *userdata, const char *oldpath, const char *newpath)
201{
202 bool result = false;
203
204 char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath);
205 char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath);
206 if (fulloldpath && fullnewpath) {
207 result = SDL_CopyFile(fulloldpath, fullnewpath);
208 }
209 SDL_free(fulloldpath);
210 SDL_free(fullnewpath);
211
212 return result;
213}
214
215static Uint64 GENERIC_GetStorageSpaceRemaining(void *userdata)
216{
217 // TODO: There's totally a way to query a folder root's quota...
218 return SDL_MAX_UINT64;
219}
220
221static const SDL_StorageInterface GENERIC_title_iface = {
222 sizeof(SDL_StorageInterface),
223 GENERIC_CloseStorage,
224 NULL, // ready
225 GENERIC_EnumerateStorageDirectory,
226 GENERIC_GetStoragePathInfo,
227 GENERIC_ReadStorageFile,
228 NULL, // write_file
229 NULL, // mkdir
230 NULL, // remove
231 NULL, // rename
232 NULL, // copy
233 NULL // space_remaining
234};
235
236static SDL_Storage *GENERIC_Title_Create(const char *override, SDL_PropertiesID props)
237{
238 SDL_Storage *result = NULL;
239 char *basepath = NULL;
240
241 if (override != NULL) {
242 // make sure override has a path separator at the end. If you're not on Windows and used '\\', that's on you.
243 const size_t slen = SDL_strlen(override);
244 const bool need_sep = (!slen || ((override[slen-1] != '/') && (override[slen-1] != '\\')));
245 if (SDL_asprintf(&basepath, "%s%s", override, need_sep ? "/" : "") == -1) {
246 return NULL;
247 }
248 } else {
249 const char *base = SDL_GetBasePath();
250 basepath = base ? SDL_strdup(base) : NULL;
251 }
252
253 if (basepath != NULL) {
254 result = SDL_OpenStorage(&GENERIC_title_iface, basepath);
255 if (result == NULL) {
256 SDL_free(basepath); // otherwise CloseStorage will free it.
257 }
258 }
259
260 return result;
261}
262
263TitleStorageBootStrap GENERIC_titlebootstrap = {
264 "generic",
265 "SDL generic title storage driver",
266 GENERIC_Title_Create
267};
268
269static const SDL_StorageInterface GENERIC_user_iface = {
270 sizeof(SDL_StorageInterface),
271 GENERIC_CloseStorage,
272 NULL, // ready
273 GENERIC_EnumerateStorageDirectory,
274 GENERIC_GetStoragePathInfo,
275 GENERIC_ReadStorageFile,
276 GENERIC_WriteStorageFile,
277 GENERIC_CreateStorageDirectory,
278 GENERIC_RemoveStoragePath,
279 GENERIC_RenameStoragePath,
280 GENERIC_CopyStorageFile,
281 GENERIC_GetStorageSpaceRemaining
282};
283
284static SDL_Storage *GENERIC_User_Create(const char *org, const char *app, SDL_PropertiesID props)
285{
286 SDL_Storage *result;
287 char *prefpath = SDL_GetPrefPath(org, app);
288 if (prefpath == NULL) {
289 return NULL;
290 }
291
292 result = SDL_OpenStorage(&GENERIC_user_iface, prefpath);
293 if (result == NULL) {
294 SDL_free(prefpath); // otherwise CloseStorage will free it.
295 }
296 return result;
297}
298
299UserStorageBootStrap GENERIC_userbootstrap = {
300 "generic",
301 "SDL generic user storage driver",
302 GENERIC_User_Create
303};
304
305static const SDL_StorageInterface GENERIC_file_iface = {
306 sizeof(SDL_StorageInterface),
307 GENERIC_CloseStorage,
308 NULL, // ready
309 GENERIC_EnumerateStorageDirectory,
310 GENERIC_GetStoragePathInfo,
311 GENERIC_ReadStorageFile,
312 GENERIC_WriteStorageFile,
313 GENERIC_CreateStorageDirectory,
314 GENERIC_RemoveStoragePath,
315 GENERIC_RenameStoragePath,
316 GENERIC_CopyStorageFile,
317 GENERIC_GetStorageSpaceRemaining
318};
319
320SDL_Storage *GENERIC_OpenFileStorage(const char *path)
321{
322 SDL_Storage *result;
323 size_t len = 0;
324 char *basepath = NULL;
325
326 if (path) {
327 len += SDL_strlen(path);
328 }
329 if (len > 0) {
330 #ifdef SDL_PLATFORM_WINDOWS
331 const bool appended_separator = (path[len-1] == '/') || (path[len-1] == '\\');
332 #else
333 const bool appended_separator = (path[len-1] == '/');
334 #endif
335 if (appended_separator) {
336 basepath = SDL_strdup(path);
337 if (!basepath) {
338 return NULL;
339 }
340 } else {
341 if (SDL_asprintf(&basepath, "%s/", path) < 0) {
342 return NULL;
343 }
344 }
345 }
346 result = SDL_OpenStorage(&GENERIC_file_iface, basepath);
347 if (result == NULL) {
348 SDL_free(basepath);
349 }
350 return result;
351}
diff --git a/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage.c b/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage.c
new file mode 100644
index 0000000..8f735f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage.c
@@ -0,0 +1,215 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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
22#include "SDL_internal.h"
23
24#include "../SDL_sysstorage.h"
25
26// !!! FIXME: Async API can use SteamRemoteStorage_ReadFileAsync
27// !!! FIXME: Async API can use SteamRemoteStorage_WriteFileAsync
28
29#define STEAM_PROC(ret, func, parms) \
30 typedef ret (*steamfntype_##func) parms;
31#include "SDL_steamstorage_proc.h"
32
33typedef struct STEAM_RemoteStorage
34{
35 SDL_SharedObject *libsteam_api;
36 #define STEAM_PROC(ret, func, parms) \
37 steamfntype_##func func;
38 #include "SDL_steamstorage_proc.h"
39} STEAM_RemoteStorage;
40
41static bool STEAM_CloseStorage(void *userdata)
42{
43 bool result = true;
44 STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
45 void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
46 if (steamremotestorage == NULL) {
47 result = SDL_SetError("SteamRemoteStorage unavailable");
48 } else if (!steam->SteamAPI_ISteamRemoteStorage_EndFileWriteBatch(steamremotestorage)) {
49 result = SDL_SetError("SteamRemoteStorage()->EndFileWriteBatch() failed");
50 }
51 SDL_UnloadObject(steam->libsteam_api);
52 SDL_free(steam);
53 return result;
54}
55
56static bool STEAM_StorageReady(void *userdata)
57{
58 return true;
59}
60
61static bool STEAM_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
62{
63 STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
64 void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
65 if (steamremotestorage == NULL) {
66 return SDL_SetError("SteamRemoteStorage unavailable");
67 }
68
69 if (info) {
70 SDL_zerop(info);
71 info->type = SDL_PATHTYPE_FILE;
72 info->size = steam->SteamAPI_ISteamRemoteStorage_GetFileSize(steamremotestorage, path);
73 }
74 return true;
75}
76
77static bool STEAM_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
78{
79 bool result = false;
80 STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
81 void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
82 if (steamremotestorage == NULL) {
83 return SDL_SetError("SteamRemoteStorage unavailable");
84 }
85 if (length > SDL_MAX_SINT32) {
86 return SDL_SetError("SteamRemoteStorage only supports INT32_MAX read size");
87 }
88 if (steam->SteamAPI_ISteamRemoteStorage_FileRead(steamremotestorage, path, destination, (Sint32) length) == length) {
89 result = true;
90 } else {
91 SDL_SetError("SteamAPI_ISteamRemoteStorage_FileRead() failed");
92 }
93 return result;
94}
95
96static bool STEAM_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
97{
98 bool result = false;
99 STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
100 void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
101 if (steamremotestorage == NULL) {
102 return SDL_SetError("SteamRemoteStorage unavailable");
103 }
104 if (length > SDL_MAX_SINT32) {
105 return SDL_SetError("SteamRemoteStorage only supports INT32_MAX write size");
106 }
107 if (steam->SteamAPI_ISteamRemoteStorage_FileWrite(steamremotestorage, path, source, (Sint32) length) == length) {
108 result = true;
109 } else {
110 SDL_SetError("SteamAPI_ISteamRemoteStorage_FileRead() failed");
111 }
112 return result;
113}
114
115static Uint64 STEAM_GetStorageSpaceRemaining(void *userdata)
116{
117 Uint64 total, remaining;
118 STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
119 void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
120 if (steamremotestorage == NULL) {
121 SDL_SetError("SteamRemoteStorage unavailable");
122 return 0;
123 }
124 if (!steam->SteamAPI_ISteamRemoteStorage_GetQuota(steamremotestorage, &total, &remaining)) {
125 SDL_SetError("SteamRemoteStorage()->GetQuota failed");
126 return 0;
127 }
128 return remaining;
129}
130
131static const SDL_StorageInterface STEAM_user_iface = {
132 sizeof(SDL_StorageInterface),
133 STEAM_CloseStorage,
134 STEAM_StorageReady,
135 NULL, // enumerate
136 STEAM_GetStoragePathInfo,
137 STEAM_ReadStorageFile,
138 STEAM_WriteStorageFile,
139 NULL, // mkdir
140 NULL, // remove
141 NULL, // rename
142 NULL, // copy
143 STEAM_GetStorageSpaceRemaining
144};
145
146static SDL_Storage *STEAM_User_Create(const char *org, const char *app, SDL_PropertiesID props)
147{
148 SDL_Storage *result;
149 STEAM_RemoteStorage *steam;
150 void *steamremotestorage;
151
152 steam = (STEAM_RemoteStorage*) SDL_malloc(sizeof(STEAM_RemoteStorage));
153 if (steam == NULL) {
154 return NULL;
155 }
156
157 steam->libsteam_api = SDL_LoadObject(
158#if defined(_WIN64)
159 "steam_api64.dll"
160#elif defined(_WIN32)
161 "steam_api.dll"
162#elif defined(__APPLE__)
163 "libsteam_api.dylib"
164#else
165 "libsteam_api.so"
166#endif
167 );
168 if (steam->libsteam_api == NULL) {
169 SDL_free(steam);
170 return NULL;
171 }
172
173 #define STEAM_PROC(ret, func, parms) \
174 steam->func = (steamfntype_##func) SDL_LoadFunction(steam->libsteam_api, #func); \
175 if (steam->func == NULL) { \
176 SDL_SetError("Could not load function " #func); \
177 goto steamfail; \
178 }
179 #include "SDL_steamstorage_proc.h"
180
181 steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
182 if (steamremotestorage == NULL) {
183 SDL_SetError("SteamRemoteStorage unavailable");
184 goto steamfail;
185 }
186 if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(steamremotestorage)) {
187 SDL_SetError("Steam cloud is disabled for this user");
188 goto steamfail;
189 }
190 if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(steamremotestorage)) {
191 SDL_SetError("Steam cloud is disabled for this application");
192 goto steamfail;
193 }
194 if (!steam->SteamAPI_ISteamRemoteStorage_BeginFileWriteBatch(steamremotestorage)) {
195 SDL_SetError("SteamRemoteStorage()->BeginFileWriteBatch failed");
196 goto steamfail;
197 }
198
199 result = SDL_OpenStorage(&STEAM_user_iface, steam);
200 if (result == NULL) {
201 goto steamfail;
202 }
203 return result;
204
205steamfail:
206 SDL_UnloadObject(steam->libsteam_api);
207 SDL_free(steam);
208 return NULL;
209}
210
211UserStorageBootStrap STEAM_userbootstrap = {
212 "steam",
213 "SDL Steam user storage driver",
214 STEAM_User_Create
215};
diff --git a/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage_proc.h b/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage_proc.h
new file mode 100644
index 0000000..9d259e7
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/storage/steam/SDL_steamstorage_proc.h
@@ -0,0 +1,14 @@
1STEAM_PROC(void*, SteamAPI_SteamRemoteStorage_v016, (void))
2
3STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount, (void*))
4STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp, (void*))
5
6STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_BeginFileWriteBatch, (void*))
7STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_EndFileWriteBatch, (void*))
8
9STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_GetFileSize, (void*, const char*))
10STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_FileRead, (void*, const char*, void*, Sint32))
11STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_FileWrite, (void*, const char*, const void*, Sint32))
12STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_GetQuota, (void*, Uint64*, Uint64*))
13
14#undef STEAM_PROC