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};
216