| 166 | bool savValid(int handle) override { return handle >= 0 && handle < MAX_SAV_HANDLES && mSlots[handle].arch; } |
| 167 | |
| 168 | int savRead(int handle, const char* path, char** outBuf, int* outSize) override |
| 169 | { |
| 170 | FSStream stream(mSlots[handle].arch.fs(), archivePath(path), FS_OPEN_READ); |
| 171 | if (!stream.good()) { |
| 172 | const Result res = stream.result(); |
| 173 | stream.close(); |
| 174 | return (int)res; |
| 175 | } |
| 176 | |
| 177 | const u32 size = stream.size(); |
| 178 | char* buf = (char*)ScriptHeap::get().alloc(size + 1); |
| 179 | if (!buf) { |
| 180 | stream.close(); |
| 181 | return -3; |
| 182 | } |
| 183 | |
| 184 | const u32 read = stream.read(buf, size); |
| 185 | const Result res = stream.result(); |
| 186 | stream.close(); |
| 187 | if (read != size) { |
| 188 | // A short read is a failure even when FS reported success: the |
| 189 | // script has no other way to know the buffer is not the file, |
| 190 | // and handing it back would make a truncated save look whole. |
| 191 | Logging::warning("[script] sav_read '{}': {} of {} bytes (res 0x{:08X})", path, read, size, (u32)res); |
| 192 | ScriptHeap::get().release(buf); |
| 193 | return R_FAILED(res) ? (int)res : -4; |
| 194 | } |
| 195 | |
| 196 | buf[read] = '\0'; |
| 197 | *outBuf = buf; |
| 198 | *outSize = (int)read; |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | int savWrite(int handle, const char* path, const void* data, size_t size) override |
| 203 | { |