Atlas - SDL_genericstorage.c
Home / ext / SDL / src / storage / generic Lines: 1 | Size: 12003 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 26 27static char *GENERIC_INTERNAL_CreateFullPath(const char *base, const char *relative) 28{ 29 const char *rel = relative; 30 31#ifdef SDL_PLATFORM_ANDROID 32 if (rel) { 33 // Removes any leading slash 34 if (rel[0] == '/' || rel[0] == '\\') { 35 rel += 1; 36 } 37 } 38#endif 39 40 char *result = NULL; 41 SDL_asprintf(&result, "%s%s", base ? base : "", rel ? rel : ""); 42 return result; 43} 44 45static bool GENERIC_CloseStorage(void *userdata) 46{ 47 SDL_free(userdata); 48 return true; 49} 50 51typedef struct GenericEnumerateData 52{ 53 size_t base_len; 54 SDL_EnumerateDirectoryCallback real_callback; 55 void *real_userdata; 56} GenericEnumerateData; 57 58static SDL_EnumerationResult SDLCALL GENERIC_EnumerateDirectory(void *userdata, const char *dirname, const char *fname) 59{ 60 // SDL_EnumerateDirectory will return the full path, so for Storage we 61 // can take the base directory and add its length to the dirname string, 62 // effectively trimming the root without having to strdup anything. 63 const GenericEnumerateData *wrap_data = (GenericEnumerateData *)userdata; 64 65 dirname += wrap_data->base_len; // skip the base, just return the part inside of the Storage. 66 67 #ifdef SDL_PLATFORM_WINDOWS 68 char *dirnamecpy = NULL; 69 const size_t slen = SDL_strlen(dirname); 70 if (slen && (dirname[slen - 1] == '\\')) { 71 dirnamecpy = SDL_strdup(dirname); 72 if (!dirnamecpy) { 73 return SDL_ENUM_FAILURE; 74 } 75 dirnamecpy[slen - 1] = '/'; // storage layer always uses '/' path separators. 76 dirname = dirnamecpy; 77 } 78 const SDL_EnumerationResult retval = wrap_data->real_callback(wrap_data->real_userdata, dirname, fname); 79 SDL_free(dirnamecpy); 80 return retval; 81 #else 82 return wrap_data->real_callback(wrap_data->real_userdata, dirname, fname); 83 #endif 84} 85 86static bool GENERIC_EnumerateStorageDirectory(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata) 87{ 88 bool result = false; 89 GenericEnumerateData wrap_data; 90 91 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 92 if (fullpath) { 93 wrap_data.base_len = userdata ? SDL_strlen((char *)userdata) : 0; 94 wrap_data.real_callback = callback; 95 wrap_data.real_userdata = callback_userdata; 96 97 result = SDL_EnumerateDirectory(fullpath, GENERIC_EnumerateDirectory, &wrap_data); 98 99 SDL_free(fullpath); 100 } 101 return result; 102} 103 104static bool GENERIC_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info) 105{ 106 bool result = false; 107 108 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 109 if (fullpath) { 110 result = SDL_GetPathInfo(fullpath, info); 111 112 SDL_free(fullpath); 113 } 114 return result; 115} 116 117static bool GENERIC_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length) 118{ 119 bool result = false; 120 121 if (length > SDL_SIZE_MAX) { 122 return SDL_SetError("Read size exceeds SDL_SIZE_MAX"); 123 } 124 125 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 126 if (fullpath) { 127 SDL_IOStream *stream = SDL_IOFromFile(fullpath, "rb"); 128 if (stream) { 129 // FIXME: Should SDL_ReadIO use u64 now...? 130 if (SDL_ReadIO(stream, destination, (size_t)length) == length) { 131 result = true; 132 } else { 133 SDL_SetError("File length did not exactly match the destination length"); 134 } 135 SDL_CloseIO(stream); 136 } 137 SDL_free(fullpath); 138 } 139 return result; 140} 141 142static bool GENERIC_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length) 143{ 144 // TODO: Recursively create subdirectories with SDL_CreateDirectory 145 bool result = false; 146 147 if (length > SDL_SIZE_MAX) { 148 return SDL_SetError("Write size exceeds SDL_SIZE_MAX"); 149 } 150 151 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 152 if (fullpath) { 153 SDL_IOStream *stream = SDL_IOFromFile(fullpath, "wb"); 154 155 if (stream) { 156 // FIXME: Should SDL_WriteIO use u64 now...? 157 if (SDL_WriteIO(stream, source, (size_t)length) == length) { 158 result = true; 159 } else { 160 SDL_SetError("Resulting file length did not exactly match the source length"); 161 } 162 SDL_CloseIO(stream); 163 } 164 SDL_free(fullpath); 165 } 166 return result; 167} 168 169static bool GENERIC_CreateStorageDirectory(void *userdata, const char *path) 170{ 171 // TODO: Recursively create subdirectories with SDL_CreateDirectory 172 bool result = false; 173 174 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 175 if (fullpath) { 176 result = SDL_CreateDirectory(fullpath); 177 178 SDL_free(fullpath); 179 } 180 return result; 181} 182 183static bool GENERIC_RemoveStoragePath(void *userdata, const char *path) 184{ 185 bool result = false; 186 187 char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); 188 if (fullpath) { 189 result = SDL_RemovePath(fullpath); 190 191 SDL_free(fullpath); 192 } 193 return result; 194} 195 196static bool GENERIC_RenameStoragePath(void *userdata, const char *oldpath, const char *newpath) 197{ 198 bool result = false; 199 200 char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath); 201 char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath); 202 if (fulloldpath && fullnewpath) { 203 result = SDL_RenamePath(fulloldpath, fullnewpath); 204 } 205 SDL_free(fulloldpath); 206 SDL_free(fullnewpath); 207 208 return result; 209} 210 211static bool GENERIC_CopyStorageFile(void *userdata, const char *oldpath, const char *newpath) 212{ 213 bool result = false; 214 215 char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath); 216 char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath); 217 if (fulloldpath && fullnewpath) { 218 result = SDL_CopyFile(fulloldpath, fullnewpath); 219 } 220 SDL_free(fulloldpath); 221 SDL_free(fullnewpath); 222 223 return result; 224} 225 226static Uint64 GENERIC_GetStorageSpaceRemaining(void *userdata) 227{ 228 // TODO: There's totally a way to query a folder root's quota... 229 return SDL_MAX_UINT64; 230} 231 232static const SDL_StorageInterface GENERIC_title_iface = { 233 sizeof(SDL_StorageInterface), 234 GENERIC_CloseStorage, 235 NULL, // ready 236 GENERIC_EnumerateStorageDirectory, 237 GENERIC_GetStoragePathInfo, 238 GENERIC_ReadStorageFile, 239 NULL, // write_file 240 NULL, // mkdir 241 NULL, // remove 242 NULL, // rename 243 NULL, // copy 244 NULL // space_remaining 245}; 246 247static SDL_Storage *GENERIC_Title_Create(const char *override, SDL_PropertiesID props) 248{ 249 SDL_Storage *result = NULL; 250 char *basepath = NULL; 251 252 if (override != NULL) { 253 const size_t slen = SDL_strlen(override); 254 if (slen > 0) { 255 // make sure override has a path separator at the end. If you're not on Windows and used '\\', that's on you. 256 const bool need_sep = ((override[slen - 1] != '/') && (override[slen - 1] != '\\')); 257 if (SDL_asprintf(&basepath, "%s%s", override, need_sep ? "/" : "") == -1) { 258 return NULL; 259 } 260 } else { 261 // override == "" -> empty base (not "/") 262 basepath = SDL_strdup(""); 263 } 264 } else { 265 const char *base = SDL_GetBasePath(); 266 basepath = base ? SDL_strdup(base) : NULL; 267 } 268 269 if (basepath != NULL) { 270 result = SDL_OpenStorage(&GENERIC_title_iface, basepath); 271 if (result == NULL) { 272 SDL_free(basepath); // otherwise CloseStorage will free it. 273 } 274 } 275 276 return result; 277} 278 279TitleStorageBootStrap GENERIC_titlebootstrap = { 280 "generic", 281 "SDL generic title storage driver", 282 GENERIC_Title_Create 283}; 284 285static const SDL_StorageInterface GENERIC_user_iface = { 286 sizeof(SDL_StorageInterface), 287 GENERIC_CloseStorage, 288 NULL, // ready 289 GENERIC_EnumerateStorageDirectory, 290 GENERIC_GetStoragePathInfo, 291 GENERIC_ReadStorageFile, 292 GENERIC_WriteStorageFile, 293 GENERIC_CreateStorageDirectory, 294 GENERIC_RemoveStoragePath, 295 GENERIC_RenameStoragePath, 296 GENERIC_CopyStorageFile, 297 GENERIC_GetStorageSpaceRemaining 298}; 299 300static SDL_Storage *GENERIC_User_Create(const char *org, const char *app, SDL_PropertiesID props) 301{ 302 SDL_Storage *result; 303 char *prefpath = SDL_GetPrefPath(org, app); 304 if (prefpath == NULL) { 305 return NULL; 306 } 307 308 result = SDL_OpenStorage(&GENERIC_user_iface, prefpath); 309 if (result == NULL) { 310 SDL_free(prefpath); // otherwise CloseStorage will free it. 311 } 312 return result; 313} 314 315UserStorageBootStrap GENERIC_userbootstrap = { 316 "generic", 317 "SDL generic user storage driver", 318 GENERIC_User_Create 319}; 320 321static const SDL_StorageInterface GENERIC_file_iface = { 322 sizeof(SDL_StorageInterface), 323 GENERIC_CloseStorage, 324 NULL, // ready 325 GENERIC_EnumerateStorageDirectory, 326 GENERIC_GetStoragePathInfo, 327 GENERIC_ReadStorageFile, 328 GENERIC_WriteStorageFile, 329 GENERIC_CreateStorageDirectory, 330 GENERIC_RemoveStoragePath, 331 GENERIC_RenameStoragePath, 332 GENERIC_CopyStorageFile, 333 GENERIC_GetStorageSpaceRemaining 334}; 335 336SDL_Storage *GENERIC_OpenFileStorage(const char *path) 337{ 338 SDL_Storage *result; 339 char *basepath = NULL; 340 char *prepend = NULL; 341 342#ifdef SDL_PLATFORM_ANDROID 343 // Use a base path of "." so the filesystem operations fall back to internal storage and the asset system 344 if (!path || !*path) { 345 path = "./"; 346 } 347#else 348 if (!path || !*path) { 349#ifdef SDL_PLATFORM_WINDOWS 350 path = "C:/"; 351#else 352 path = "/"; 353#endif 354 } 355 356 bool is_absolute = false; 357#ifdef SDL_PLATFORM_WINDOWS 358 const char ch = (char) SDL_toupper(path[0]); 359 is_absolute = (ch == '/') || // some sort of absolute Unix-style path. 360 (ch == '\\') || // some sort of absolute Windows-style path. 361 (((ch >= 'A') && (ch <= 'Z')) && (path[1] == ':') && ((path[2] == '\\') || (path[2] == '/'))); // an absolute path with a drive letter. 362#else 363 is_absolute = (path[0] == '/'); // some sort of absolute Unix-style path. 364#endif 365 if (!is_absolute) { 366 prepend = SDL_GetCurrentDirectory(); 367 if (!prepend) { 368 return NULL; 369 } 370 } 371#endif // SDL_PLATFORM_ANDROID 372 373 const size_t len = SDL_strlen(path); 374 const char *appended_separator = ""; 375#ifdef SDL_PLATFORM_WINDOWS 376 if ((path[len-1] != '/') && (path[len-1] != '\\')) { 377 appended_separator = "/"; 378 } 379#else 380 if (path[len-1] != '/') { 381 appended_separator = "/"; 382 } 383#endif 384 const int rc = SDL_asprintf(&basepath, "%s%s%s", prepend ? prepend : "", path, appended_separator); 385 SDL_free(prepend); 386 if (rc < 0) { 387 return NULL; 388 } 389 390 result = SDL_OpenStorage(&GENERIC_file_iface, basepath); 391 if (result == NULL) { 392 SDL_free(basepath); 393 } 394 return result; 395} 396[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.