Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
memory-card-filesystem.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include <EASTL/functional.h>
30#include <stdint.h>
31
32#include "psyqo/memory-card.hh"
33
34namespace psyqo {
35
36class GPU;
37
78 public:
87 struct Icon {
88 uint8_t frameCount; // 1..3
89 uint16_t clut[16]; // 16-colour palette, BGR555
90 uint8_t pixels[3][128]; // up to 3 frames of 16x16 4bpp
91 };
92
96 struct FileEntry {
97 char name[21]; // null-terminated Sony filename
98 uint16_t sizeInBlocks; // 1..15
99 uint8_t firstBlock; // 1..15
100 };
101
113 struct FileInfo {
114 uint8_t title[65]; // 64-byte Shift-JIS title field + NUL
115 Icon icon; // palette + up to 3 animation frames
116 };
117
118 explicit MemoryCardFileSystem(MemoryCard &card) : m_card(card) {}
119
123 [[nodiscard]] bool isIdle() const { return !m_busy; }
124
127
128 // -- Asynchronous operations (the callback basis) -----------------------
129 // Each starts a transaction and returns immediately; `callback` fires from
130 // the main loop, during GPU pumping, once the transaction completes. Only
131 // one may be in flight at a time (asserts idle).
132
138 void getCardState(Port port, eastl::function<void(Error)> &&callback);
139
149 void format(Port port, eastl::function<void(Error)> &&callback);
150
159 void getFreeBlockCount(Port port, uint32_t *outFreeBlocks, eastl::function<void(Error)> &&callback);
160
170 void listFiles(Port port, FileEntry *out, uint32_t maxEntries, uint32_t *outCount,
171 eastl::function<void(Error)> &&callback);
172
184 void fileExists(Port port, const char *name, bool *outExists, eastl::function<void(Error)> &&callback);
185
201 void readFile(Port port, const char *name, void *buffer, uint32_t maxLen, uint32_t *outLen,
202 eastl::function<void(Error)> &&callback);
203
218 void readFileInfo(Port port, const char *name, FileInfo *out, eastl::function<void(Error)> &&callback);
219
238 void writeFile(Port port, const char *name, const char *title, const Icon &icon, const void *data, uint32_t dataLen,
239 eastl::function<void(Error)> &&callback);
240
247 void deleteFile(Port port, const char *name, eastl::function<void(Error)> &&callback);
248
249 // -- Blocking variants --------------------------------------------------
250 // These run the same transaction but pump the GPU until it finishes and
251 // return the error directly. They still take a few hundred milliseconds for
252 // a non-trivial payload, so they are best used at a deliberate save point.
253 // Calling one from inside a queued callback, such as another operation's
254 // completion callback, pumps the callback queue from within the pump; this
255 // is supported, and the remaining queued callbacks still run once each, in
256 // order.
257
259 Error formatBlocking(GPU &gpu, Port port);
260 Error getFreeBlockCountBlocking(GPU &gpu, Port port, uint32_t *outFreeBlocks);
261 Error listFilesBlocking(GPU &gpu, Port port, FileEntry *out, uint32_t maxEntries, uint32_t *outCount);
262 Error fileExistsBlocking(GPU &gpu, Port port, const char *name, bool *outExists);
263 Error readFileBlocking(GPU &gpu, Port port, const char *name, void *buffer, uint32_t maxLen, uint32_t *outLen);
264 Error readFileInfoBlocking(GPU &gpu, Port port, const char *name, FileInfo *out);
265 Error writeFileBlocking(GPU &gpu, Port port, const char *name, const char *title, const Icon &icon,
266 const void *data, uint32_t dataLen);
267 Error deleteFileBlocking(GPU &gpu, Port port, const char *name);
268
269 private:
270 // A 4-byte aligned 128-byte frame buffer.
271 struct Frame {
272 alignas(4) uint8_t bytes[128];
273 };
274
275 // Directory allocation states (low byte of the 32-bit state word). The high
276 // nibble separates in-use (0x5x) from available (0xax); the low nibble is
277 // the block's position in its chain, preserved across deletion so a deleted
278 // chain stays recoverable.
279 static constexpr uint8_t c_stateFirst = 0x51; // in use: first or only block
280 static constexpr uint8_t c_stateMiddle = 0x52; // in use: middle block
281 static constexpr uint8_t c_stateLast = 0x53; // in use: last block
282 static constexpr uint8_t c_stateFree = 0xa0; // available: never allocated
283 static constexpr uint8_t c_stateDeletedFirst = 0xa1; // available: a deleted first block
284 static constexpr uint8_t c_stateDeletedMiddle = 0xa2; // available: a deleted middle block
285 static constexpr uint8_t c_stateDeletedLast = 0xa3; // available: a deleted last block
286
287 // Directory entry field offsets.
288 static constexpr uint32_t c_offAlloc = 0x00;
289 static constexpr uint32_t c_offSize = 0x04;
290 static constexpr uint32_t c_offNext = 0x08;
291 static constexpr uint32_t c_offName = 0x0a;
292 static constexpr uint32_t c_offChecksum = 0x7f;
293 static constexpr uint32_t c_maxNameLength = 20;
294
295 static uint8_t frameChecksum(const uint8_t *frame);
296 static void finishDirEntry(uint8_t *entry);
297 // Marks an in-use block (0x5x) deleted, keeping its chain position (0x5x ->
298 // 0xax) and so its links, so the deleted chain stays recoverable.
299 static uint8_t deletedState(uint8_t inUse) { return static_cast<uint8_t>((inUse & 0x0f) | 0xa0); }
300 static bool nameMatches(const uint8_t *entry, const char *name);
301 static bool findFirstBlock(const Frame *dir15, const char *name, int *outBlock);
302 // The set of blocks (bit (slot + 1)) reachable from a valid first-block
303 // head. Anything else - free, deleted, or an orphaned middle/last block with
304 // no head - is available for allocation.
305 static uint16_t reachableBlocks(const Frame *dir15);
306
307 // -- Asynchronous transaction engine -----------------------------------
308 // The operation in flight, and where in it we are. Each device sector
309 // completion advances the machine by exactly one sector transfer.
310 enum class Op : uint8_t {
311 None,
312 GetCardState,
313 Format,
314 GetFreeBlockCount,
315 ListFiles,
316 FileExists,
317 ReadFile,
318 ReadInfo,
319 WriteFile,
320 DeleteFile,
321 };
322 enum class Phase : uint8_t {
323 Fail, // m_result is set; finish on the next tick
324 Header, // read sector 0, validate "MC"
325 Probe, // read sector m_probeSector, compare it with sector 0
326 ReadDir, // read the 15 directory frames (m_idx = 0..14)
327 ReadTitle, // read a file's first frame to size its header (readFile)
328 ReadData, // read a file's payload frames (readFile)
329 ReadInfo, // read a file's title + icon frames (readFileInfo)
330 WriteData, // write title/icon/payload frames (writeFile)
331 WriteDir, // commit the dirty directory frames (write/delete)
332 Format, // zero the probe sectors, then write the format frames (m_idx = 0..66)
333 };
334 enum class StepResult : uint8_t { Continue, Done };
335
336 void begin(Op op, Port port, eastl::function<void(Error)> &&callback);
337 Error runBlocking(GPU &gpu);
338 // Issues exactly one asynchronous device sector transfer for the current
339 // phase, or finishes the transaction if the current phase has no transfer
340 // left to do.
341 void issueOrFinish();
342 // The device's per-sector completion callback: consumes the transfer's
343 // result, advances the per-operation state machine, then chains the next
344 // transfer (or finishes).
345 void onSectorDone(Error error);
346 StepResult afterReadDir();
347 void finish(Error error);
348
349 MemoryCard &m_card;
350
351 // engine state
352 bool m_busy = false;
353 bool m_lockHeld = false;
354 eastl::function<void(Error)> m_callback;
355 Error m_result = Error::OK;
356 Op m_op = Op::None;
357 Phase m_phase = Phase::Fail;
358 Port m_port = Port::Port0;
359 uint32_t m_idx = 0; // step within the current phase
360
361 // working buffers / per-operation state
362 Frame m_dir[15];
363 Frame m_scratch;
364 Frame m_probe;
365 // The card's size in blocks, as measured by the Probe phase: 16, or the
366 // block where sector 0 first repeats on a smaller card.
367 uint32_t m_cardBlocks = 16;
368 uint16_t m_probeSector = 0;
369 uint8_t m_chain[16];
370 uint32_t m_chainLen = 0;
371 uint32_t m_headerFrames = 0;
372 uint32_t m_blocksNeeded = 0;
373 uint32_t m_iconFrames = 0;
374 bool m_dirty[15] = {};
375 // The directory slots to commit, in the order they must be written: for a
376 // create the file's head (the 0x51 first block) is written last; for a
377 // delete its head (cut to 0xa1) is written first. So a power loss mid-commit
378 // can only orphan tail blocks, never leave a referenced file pointing at a
379 // half-written chain.
380 uint8_t m_writeOrder[15] = {};
381 uint32_t m_writeOrderLen = 0;
382 uint32_t m_dataOffset = 0;
383 uint32_t m_written = 0;
384 uint32_t m_blockIdx = 0; // current block within the chain
385 uint32_t m_frameInBlock = 0; // current frame within the current block
386
387 // saved operation arguments
388 const char *m_name = nullptr;
389 const char *m_title = nullptr;
390 void *m_readBuffer = nullptr;
391 const void *m_writeData = nullptr;
392 uint32_t m_dataLen = 0;
393 uint32_t m_maxLen = 0;
394 uint32_t *m_outLen = nullptr;
395 uint32_t *m_outFreeBlocks = nullptr;
396 uint32_t *m_outCount = nullptr;
397 bool *m_outExists = nullptr;
398 FileEntry *m_outEntries = nullptr;
399 FileInfo *m_outInfo = nullptr;
400 uint32_t m_maxEntries = 0;
401 Icon m_icon = {};
402};
403
404} // namespace psyqo
uint8_t error
Definition cdlreadn.c:858
The singleton GPU class.
Definition gpu.hh:83
A Sony-compatible memory card filesystem.
Definition memory-card-filesystem.hh:77
Error fileExistsBlocking(GPU &gpu, Port port, const char *name, bool *outExists)
Definition memory-card-filesystem.cpp:786
void format(Port port, eastl::function< void(Error)> &&callback)
Writes a fresh, empty Sony filesystem to the card.
Definition memory-card-filesystem.cpp:688
void readFileInfo(Port port, const char *name, FileInfo *out, eastl::function< void(Error)> &&callback)
Reads a file's title and icon.
Definition memory-card-filesystem.cpp:731
Error deleteFileBlocking(GPU &gpu, Port port, const char *name)
Definition memory-card-filesystem.cpp:823
Error formatBlocking(GPU &gpu, Port port)
Definition memory-card-filesystem.cpp:764
void deleteFile(Port port, const char *name, eastl::function< void(Error)> &&callback)
Deletes a file, freeing all of its blocks.
Definition memory-card-filesystem.cpp:751
void getCardState(Port port, eastl::function< void(Error)> &&callback)
Determines whether a usable, formatted card is present.
Definition memory-card-filesystem.cpp:683
bool isIdle() const
Whether the filesystem is ready to accept a new operation.
Definition memory-card-filesystem.hh:123
void listFiles(Port port, FileEntry *out, uint32_t maxEntries, uint32_t *outCount, eastl::function< void(Error)> &&callback)
Lists the files on the card.
Definition memory-card-filesystem.cpp:701
Error getFreeBlockCountBlocking(GPU &gpu, Port port, uint32_t *outFreeBlocks)
Definition memory-card-filesystem.cpp:769
Error readFileInfoBlocking(GPU &gpu, Port port, const char *name, FileInfo *out)
Definition memory-card-filesystem.cpp:805
void writeFile(Port port, const char *name, const char *title, const Icon &icon, const void *data, uint32_t dataLen, eastl::function< void(Error)> &&callback)
Creates or overwrites a file.
Definition memory-card-filesystem.cpp:739
Error writeFileBlocking(GPU &gpu, Port port, const char *name, const char *title, const Icon &icon, const void *data, uint32_t dataLen)
Definition memory-card-filesystem.cpp:812
void readFile(Port port, const char *name, void *buffer, uint32_t maxLen, uint32_t *outLen, eastl::function< void(Error)> &&callback)
Reads the payload of a file.
Definition memory-card-filesystem.cpp:720
void fileExists(Port port, const char *name, bool *outExists, eastl::function< void(Error)> &&callback)
Reports whether a named file exists, into *outExists.
Definition memory-card-filesystem.cpp:711
Error readFileBlocking(GPU &gpu, Port port, const char *name, void *buffer, uint32_t maxLen, uint32_t *outLen)
Definition memory-card-filesystem.cpp:794
void getFreeBlockCount(Port port, uint32_t *outFreeBlocks, eastl::function< void(Error)> &&callback)
Counts the free 8KiB blocks (0..15) into *outFreeBlocks.
Definition memory-card-filesystem.cpp:693
Error getCardStateBlocking(GPU &gpu, Port port)
Definition memory-card-filesystem.cpp:759
MemoryCardFileSystem(MemoryCard &card)
Definition memory-card-filesystem.hh:118
Error listFilesBlocking(GPU &gpu, Port port, FileEntry *out, uint32_t maxEntries, uint32_t *outCount)
Definition memory-card-filesystem.cpp:776
A low level driver for the PlayStation memory cards.
Definition memory-card.hh:67
Error
The error codes returned by every memory card operation.
Definition memory-card.hh:75
Port
The memory card port to talk to.
Definition memory-card.hh:98
uint32_t out
Definition cpu.c:62
Definition lua.hh:40
Definition pocketstation-memmap.c:256
A directory listing entry.
Definition memory-card-filesystem.hh:96
uint16_t sizeInBlocks
Definition memory-card-filesystem.hh:98
char name[21]
Definition memory-card-filesystem.hh:97
uint8_t firstBlock
Definition memory-card-filesystem.hh:99
A file's on-card metadata: its title and icon.
Definition memory-card-filesystem.hh:113
uint8_t title[65]
Definition memory-card-filesystem.hh:114
Icon icon
Definition memory-card-filesystem.hh:115
A save icon, in the native PlayStation format.
Definition memory-card-filesystem.hh:87
uint16_t clut[16]
Definition memory-card-filesystem.hh:89
uint8_t pixels[3][128]
Definition memory-card-filesystem.hh:90
uint8_t frameCount
Definition memory-card-filesystem.hh:88
static void * buffer
Definition syscalls.h:274