| 271 | bool savValid(int handle) override { return handle >= 0 && handle < MAX_SAV_HANDLES && mSlots[handle].arch; } |
| 272 | |
| 273 | int savRead(int handle, const char* path, char** outBuf, int* outSize) override |
| 274 | { |
| 275 | FSStream stream(mSlots[handle].arch.fs(), archivePath(path), FS_OPEN_READ); |
| 276 | if (!stream.good()) { |
| 277 | const Result res = stream.result(); |
| 278 | stream.close(); |
| 279 | return (int)res; |
| 280 | } |
| 281 | |
| 282 | const u32 size = stream.size(); |
| 283 | char* buf = (char*)ScriptHeap::get().alloc(size + 1); |
| 284 | if (!buf) { |
| 285 | stream.close(); |
| 286 | return -3; |
| 287 | } |
| 288 | |
| 289 | const u32 read = stream.read(buf, size); |
| 290 | const Result res = stream.result(); |
| 291 | stream.close(); |
| 292 | if (read != size) { |
| 293 | // A short read is a failure even when FS reported success: the |
| 294 | // script has no other way to know the buffer is not the file, |
| 295 | // and handing it back would make a truncated save look whole. |
| 296 | Logging::warning("[script] sav_read '{}': {} of {} bytes (res 0x{:08X})", path, read, size, (u32)res); |
| 297 | ScriptHeap::get().release(buf); |
| 298 | return R_FAILED(res) ? (int)res : -4; |
| 299 | } |
| 300 | |
| 301 | buf[read] = '\0'; |
| 302 | *outBuf = buf; |
| 303 | *outSize = (int)read; |
| 304 | return 0; |
| 305 | } |
| 306 | |
| 307 | int savWrite(int handle, const char* path, const void* data, size_t size) override |
| 308 | { |