Atlas - SDL_storage.c
Home / ext / SDL / src / storage Lines: 1 | Size: 12791 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#ifdef SDL_STORAGE_PRIVATE 39 &PRIVATE_userbootstrap, 40#endif 41 &GENERIC_userbootstrap, 42 NULL 43}; 44 45struct SDL_Storage 46{ 47 SDL_StorageInterface iface; 48 void *userdata; 49}; 50 51#define CHECK_STORAGE_MAGIC() \ 52 CHECK_PARAM(!storage) { \ 53 return SDL_SetError("Invalid storage container"); \ 54 } 55 56#define CHECK_STORAGE_MAGIC_RET(result) \ 57 CHECK_PARAM(!storage) { \ 58 SDL_SetError("Invalid storage container"); \ 59 return result; \ 60 } 61 62// we don't make any effort to convert path separators here, because a) 63// everything including Windows will accept a '/' separator and b) that 64// conversion should probably happen in the storage backend anyhow. 65 66static bool ValidateStoragePath(const char *path) 67{ 68 if (SDL_strchr(path, '\\')) { 69 return SDL_SetError("Windows-style path separators ('\\') not permitted, use '/' instead."); 70 } 71 72 const char *ptr; 73 const char *prev = path; 74 while ((ptr = SDL_strchr(prev, '/')) != NULL) { 75 if ((SDL_strncmp(prev, "./", 2) == 0) || (SDL_strncmp(prev, "../", 3) == 0)) { 76 return SDL_SetError("Relative paths not permitted"); 77 } 78 prev = ptr + 1; 79 } 80 81 // check the last path element (or the only path element). 82 if ((SDL_strcmp(prev, ".") == 0) || (SDL_strcmp(prev, "..") == 0)) { 83 return SDL_SetError("Relative paths not permitted"); 84 } 85 86 return true; 87} 88 89SDL_Storage *SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props) 90{ 91 SDL_Storage *storage = NULL; 92 int i = 0; 93 94 // Select the proper storage driver 95 const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_TITLE_DRIVER); 96 if (driver_name && *driver_name != 0) { 97 const char *driver_attempt = driver_name; 98 while (driver_attempt && *driver_attempt != 0 && !storage) { 99 const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); 100 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) 101 : SDL_strlen(driver_attempt); 102 103 for (i = 0; titlebootstrap[i]; ++i) { 104 if ((driver_attempt_len == SDL_strlen(titlebootstrap[i]->name)) && 105 (SDL_strncasecmp(titlebootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { 106 storage = titlebootstrap[i]->create(override, props); 107 break; 108 } 109 } 110 111 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; 112 } 113 } else { 114 for (i = 0; titlebootstrap[i]; ++i) { 115 storage = titlebootstrap[i]->create(override, props); 116 if (storage) { 117 break; 118 } 119 } 120 } 121 if (storage) { 122 SDL_DebugLogBackend("title_storage", titlebootstrap[i]->name); 123 } else { 124 if (driver_name) { 125 SDL_SetError("%s not available", driver_name); 126 } else { 127 SDL_SetError("No available title storage driver"); 128 } 129 } 130 return storage; 131} 132 133SDL_Storage *SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props) 134{ 135 SDL_Storage *storage = NULL; 136 int i = 0; 137 138 // Select the proper storage driver 139 const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_USER_DRIVER); 140 if (driver_name && *driver_name != 0) { 141 const char *driver_attempt = driver_name; 142 while (driver_attempt && *driver_attempt != 0 && !storage) { 143 const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); 144 size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt) 145 : SDL_strlen(driver_attempt); 146 147 for (i = 0; userbootstrap[i]; ++i) { 148 if ((driver_attempt_len == SDL_strlen(userbootstrap[i]->name)) && 149 (SDL_strncasecmp(userbootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { 150 storage = userbootstrap[i]->create(org, app, props); 151 break; 152 } 153 } 154 155 driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; 156 } 157 } else { 158 for (i = 0; userbootstrap[i]; ++i) { 159 storage = userbootstrap[i]->create(org, app, props); 160 if (storage) { 161 break; 162 } 163 } 164 } 165 if (storage) { 166 SDL_DebugLogBackend("user_storage", userbootstrap[i]->name); 167 } else { 168 if (driver_name) { 169 SDL_SetError("%s not available", driver_name); 170 } else { 171 SDL_SetError("No available user storage driver"); 172 } 173 } 174 return storage; 175} 176 177SDL_Storage *SDL_OpenFileStorage(const char *path) 178{ 179 return GENERIC_OpenFileStorage(path); 180} 181 182SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata) 183{ 184 SDL_Storage *storage; 185 186 CHECK_PARAM(!iface) { 187 SDL_InvalidParamError("iface"); 188 return NULL; 189 } 190 CHECK_PARAM(iface->version < sizeof(*iface)) { 191 // Update this to handle older versions of this interface 192 SDL_SetError("Invalid interface, should be initialized with SDL_INIT_INTERFACE()"); 193 return NULL; 194 } 195 196 storage = (SDL_Storage *)SDL_calloc(1, sizeof(*storage)); 197 if (storage) { 198 SDL_copyp(&storage->iface, iface); 199 storage->userdata = userdata; 200 } 201 return storage; 202} 203 204bool SDL_CloseStorage(SDL_Storage *storage) 205{ 206 bool result = true; 207 208 CHECK_STORAGE_MAGIC() 209 210 if (storage->iface.close) { 211 result = storage->iface.close(storage->userdata); 212 } 213 SDL_free(storage); 214 return result; 215} 216 217bool SDL_StorageReady(SDL_Storage *storage) 218{ 219 CHECK_STORAGE_MAGIC_RET(false) 220 221 if (storage->iface.ready) { 222 return storage->iface.ready(storage->userdata); 223 } 224 return true; 225} 226 227bool SDL_GetStorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length) 228{ 229 SDL_PathInfo info; 230 231 if (SDL_GetStoragePathInfo(storage, path, &info)) { 232 if (length) { 233 *length = info.size; 234 } 235 return true; 236 } else { 237 if (length) { 238 *length = 0; 239 } 240 return false; 241 } 242} 243 244bool SDL_ReadStorageFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length) 245{ 246 CHECK_STORAGE_MAGIC() 247 248 CHECK_PARAM(!path) { 249 return SDL_InvalidParamError("path"); 250 } 251 CHECK_PARAM(!ValidateStoragePath(path)) { 252 return false; 253 } 254 CHECK_PARAM(length > 0 && !destination) { 255 return SDL_InvalidParamError("destination"); 256 } 257 258 if (!storage->iface.read_file) { 259 return SDL_Unsupported(); 260 } 261 262 return storage->iface.read_file(storage->userdata, path, destination, length); 263} 264 265bool SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length) 266{ 267 CHECK_STORAGE_MAGIC() 268 269 CHECK_PARAM(!path) { 270 return SDL_InvalidParamError("path"); 271 } 272 CHECK_PARAM(!ValidateStoragePath(path)) { 273 return false; 274 } 275 CHECK_PARAM(length > 0 && !source) { 276 return SDL_InvalidParamError("source"); 277 } 278 279 if (!storage->iface.write_file) { 280 return SDL_Unsupported(); 281 } 282 283 return storage->iface.write_file(storage->userdata, path, source, length); 284} 285 286bool SDL_CreateStorageDirectory(SDL_Storage *storage, const char *path) 287{ 288 CHECK_STORAGE_MAGIC() 289 290 CHECK_PARAM(!path) { 291 return SDL_InvalidParamError("path"); 292 } 293 CHECK_PARAM(!ValidateStoragePath(path)) { 294 return false; 295 } 296 297 if (!storage->iface.mkdir) { 298 return SDL_Unsupported(); 299 } 300 301 return storage->iface.mkdir(storage->userdata, path); 302} 303 304bool SDL_EnumerateStorageDirectory(SDL_Storage *storage, const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata) 305{ 306 CHECK_STORAGE_MAGIC() 307 308 if (!path) { 309 path = ""; // we allow NULL to mean "root of the storage tree". 310 } 311 312 if (!ValidateStoragePath(path)) { 313 return false; 314 } else if (!storage->iface.enumerate) { 315 return SDL_Unsupported(); 316 } 317 318 return storage->iface.enumerate(storage->userdata, path, callback, userdata); 319} 320 321bool SDL_RemoveStoragePath(SDL_Storage *storage, const char *path) 322{ 323 CHECK_STORAGE_MAGIC() 324 325 CHECK_PARAM(!path) { 326 return SDL_InvalidParamError("path"); 327 } 328 CHECK_PARAM(!ValidateStoragePath(path)) { 329 return false; 330 } 331 332 if (!storage->iface.remove) { 333 return SDL_Unsupported(); 334 } 335 336 return storage->iface.remove(storage->userdata, path); 337} 338 339bool SDL_RenameStoragePath(SDL_Storage *storage, const char *oldpath, const char *newpath) 340{ 341 CHECK_STORAGE_MAGIC() 342 343 CHECK_PARAM(!oldpath) { 344 return SDL_InvalidParamError("oldpath"); 345 } 346 CHECK_PARAM(!newpath) { 347 return SDL_InvalidParamError("newpath"); 348 } 349 350 if (!ValidateStoragePath(oldpath)) { 351 return false; 352 } 353 if (!ValidateStoragePath(newpath)) { 354 return false; 355 } 356 if (!storage->iface.rename) { 357 return SDL_Unsupported(); 358 } 359 360 return storage->iface.rename(storage->userdata, oldpath, newpath); 361} 362 363bool SDL_CopyStorageFile(SDL_Storage *storage, const char *oldpath, const char *newpath) 364{ 365 CHECK_STORAGE_MAGIC() 366 367 CHECK_PARAM(!oldpath) { 368 return SDL_InvalidParamError("oldpath"); 369 } 370 CHECK_PARAM(!newpath) { 371 return SDL_InvalidParamError("newpath"); 372 } 373 374 if (!ValidateStoragePath(oldpath)) { 375 return false; 376 } 377 if (!ValidateStoragePath(newpath)) { 378 return false; 379 } 380 if (!storage->iface.copy) { 381 return SDL_Unsupported(); 382 } 383 384 return storage->iface.copy(storage->userdata, oldpath, newpath); 385} 386 387bool SDL_GetStoragePathInfo(SDL_Storage *storage, const char *path, SDL_PathInfo *info) 388{ 389 SDL_PathInfo dummy; 390 391 if (!info) { 392 info = &dummy; 393 } 394 SDL_zerop(info); 395 396 CHECK_STORAGE_MAGIC() 397 398 CHECK_PARAM(!path) { 399 return SDL_InvalidParamError("path"); 400 } 401 CHECK_PARAM(!ValidateStoragePath(path)) { 402 return false; 403 } 404 405 if (!storage->iface.info) { 406 return SDL_Unsupported(); 407 } 408 409 return storage->iface.info(storage->userdata, path, info); 410} 411 412Uint64 SDL_GetStorageSpaceRemaining(SDL_Storage *storage) 413{ 414 CHECK_STORAGE_MAGIC_RET(0) 415 416 if (!storage->iface.space_remaining) { 417 SDL_Unsupported(); 418 return 0; 419 } 420 421 return storage->iface.space_remaining(storage->userdata); 422} 423 424static bool GlobStorageDirectoryGetPathInfo(const char *path, SDL_PathInfo *info, void *userdata) 425{ 426 return SDL_GetStoragePathInfo((SDL_Storage *) userdata, path, info); 427} 428 429static bool GlobStorageDirectoryEnumerator(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata) 430{ 431 return SDL_EnumerateStorageDirectory((SDL_Storage *) userdata, path, cb, cbuserdata); 432} 433 434char **SDL_GlobStorageDirectory(SDL_Storage *storage, const char *path, const char *pattern, SDL_GlobFlags flags, int *count) 435{ 436 CHECK_STORAGE_MAGIC_RET(NULL) 437 438 if (!path) { 439 path = ""; // we allow NULL to mean "root of the storage tree". 440 } 441 442 if (!ValidateStoragePath(path)) { 443 return NULL; 444 } 445 446 return SDL_InternalGlobDirectory(path, pattern, flags, count, GlobStorageDirectoryEnumerator, GlobStorageDirectoryGetPathInfo, storage); 447} 448 449[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.