summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/storage/SDL_storage.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/storage/SDL_storage.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/storage/SDL_storage.c')
-rw-r--r--contrib/SDL-3.2.8/src/storage/SDL_storage.c410
1 files changed, 410 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