| 238 | bool savValid(int handle) override { return handle >= 0 && handle < MAX_SAV_HANDLES && mSlots[handle].arch; } |
| 239 | |
| 240 | int savRead(int handle, const char* path, char** outBuf, int* outSize) override |
| 241 | { |
| 242 | FSStream stream(mSlots[handle].arch.fs(), archivePath(path), FS_OPEN_READ); |
| 243 | if (!stream.good()) { |
| 244 | const Result res = stream.result(); |
| 245 | stream.close(); |
| 246 | return (int)res; |
| 247 | } |
| 248 | |
| 249 | const u32 size = stream.size(); |
| 250 | char* buf = (char*)ScriptHeap::get().alloc(size + 1); |
| 251 | if (!buf) { |
| 252 | stream.close(); |
| 253 | return -3; |
| 254 | } |
| 255 | |
| 256 | const u32 read = stream.read(buf, size); |
| 257 | const Result res = stream.result(); |
| 258 | stream.close(); |
| 259 | if (read != size) { |
| 260 | // A short read is a failure even when FS reported success: the |
| 261 | // script has no other way to know the buffer is not the file, |
| 262 | // and handing it back would make a truncated save look whole. |
| 263 | Logging::warning("[script] sav_read '{}': {} of {} bytes (res 0x{:08X})", path, read, size, (u32)res); |
| 264 | ScriptHeap::get().release(buf); |
| 265 | return R_FAILED(res) ? (int)res : -4; |
| 266 | } |
| 267 | |
| 268 | buf[read] = '\0'; |
| 269 | *outBuf = buf; |
| 270 | *outSize = (int)read; |
| 271 | return 0; |
| 272 | } |
| 273 | |
| 274 | int savWrite(int handle, const char* path, const void* data, size_t size) override |
| 275 | { |