CHGame 0.3.0
Libraries for the CHGame handheld
Loading...
Searching...
No Matches
Fat.h
Go to the documentation of this file.
1// Fat: read-only FAT16/FAT32 on top of sd::read (CHSd; clean-room, MIT,
2// from HypeRunner: see NOTICE).
3
4/// @file
5/// @brief FAT16/FAT32 files on the SD card: find a file by its 8.3 name and
6/// read its blocks.
7
8/// @defgroup chsd_fat FAT16/FAT32 files
9/// @ingroup lib_chsd
10/// @brief A read-only FAT16/FAT32 reader on top of @ref chsd_sd "sd::read()":
11/// a file name in, the file's runs of card blocks out.
12///
13/// Written from the Microsoft FAT specification (BPB fields, FAT type from
14/// the cluster count, 8.3 directory entries, end-of-chain marks) and the
15/// MBR partition table layout. It only turns a file name into LBA runs:
16/// after that a file is read as raw card blocks through its run list, so
17/// there is no FAT write code, no cache and no file object. Every call
18/// borrows the caller's 512 B buffer (4-byte aligned) and clobbers it; the
19/// volume state is 24 B. What a game does not call is left out of its image
20/// by the linker.
21///
22/// **Names** are 8.3 short names as the directory stores them: 11
23/// characters, capitals, the name and the extension padded with spaces
24/// (`"WORDS DIC"` for `WORDS.DIC`), and `?` stands for any character.
25/// Long-name entries, volume labels and hidden entries are skipped; a file
26/// never matches a folder name, nor a folder a file name. Files are looked
27/// for in the root directory or in a folder of it.
28///
29/// @code
30/// // Once, after gfx_wait() (the card shares SPI1 with the panel):
31/// fat::Run run[4];
32/// uint8_t nRuns = fat::open("WORDS DIC", run, 4, gfx_chunkScratch());
33/// // ... then any block of the file, any time after gfx_wait():
34/// if (!fat::read(run, nRuns, k, gfx_chunkScratch())) { /* the card has gone */ }
35/// @endcode
36///
37/// @warning **Bus rule.** Every call reads the card over SPI1, which the
38/// panel shares: call them only between `gfx_wait()` and the next flush.
39/// CHGfx's `gfx_chunkScratch()` is idle at exactly that time and makes a
40/// good buffer.
41/// @{
42#pragma once
43#include <stdint.h>
44
45/// @brief The FAT16/FAT32 reader (CHSd).
46namespace fat {
47
48/// @brief What the functions that return an `int8_t` report: `OK` (0) or a
49/// negative error.
50enum Err : int8_t {
51 OK = 0, ///< success
52 E_READ = -1, ///< the card did not deliver a block
53 E_NOFS = -10, ///< no FAT16/FAT32 volume: no partition, bad BPB, FAT12
54 E_EXFAT = -11, ///< exFAT (or NTFS) volume: reformat the card as FAT32
55 E_NOTFOUND = -12, ///< no such file or directory (or not mounted)
56 E_FRAG = -13, ///< more runs than the caller has room for
57 E_CHAIN = -14, ///< cluster chain broken, looped, or not the file's length
58};
59
60/// @brief A run of consecutive card blocks holding part of a file (see
61/// runs()).
62struct Run { uint32_t lba, blocks; };
63/// @var fat::Run::lba
64/// The run's first block on the card (an LBA, as sd::read() takes it).
65/// @var fat::Run::blocks
66/// How many 512-byte blocks the run holds (at least 1).
67
68/// @brief A file or folder found in a directory: where it starts and how
69/// long it is.
70struct File { uint32_t cluster, size; }; // first cluster, size in bytes
71/// @var fat::File::cluster
72/// Its first cluster (0 for an empty file; for root(), the FAT16 root's 0
73/// or the FAT32 root's cluster).
74/// @var fat::File::size
75/// Its size in bytes (0 for a folder).
76
77/// @brief Finds the FAT volume on the card and remembers where its parts
78/// are.
79///
80/// Looks for a superfloppy boot sector at LBA 0, else the first MBR
81/// partition of type 01/04/06/0B/0C/0E. An exFAT boot sector at LBA 0, or
82/// an MBR with a type 07 partition and no FAT one, is `E_EXFAT`. Call
83/// `sd::init()` first; everything else here needs a mounted volume.
84/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
85/// @return `OK`, `E_READ`, `E_NOFS` (also FAT12, and a blank card) or
86/// `E_EXFAT`.
87int8_t mount(uint8_t *buf);
88
89/// @brief Finds a file in the root directory.
90/// @param name its 11-character 8.3 name (`"WORDS DIC"`); `?` matches
91/// any character.
92/// @param f receives the file's first cluster and size.
93/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
94/// @return `OK`, `E_NOTFOUND` (also when not mounted) or `E_READ`.
95int8_t find(const char *name, File &f, uint8_t *buf);
96
97/// @brief Finds a folder in the root directory, for match() and list().
98/// @param name its 11-character 8.3 name, padded with spaces
99/// (`"CHCW "`); `?` matches any character.
100/// @param dir receives the folder.
101/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
102/// @return `OK`, `E_NOTFOUND` (also when not mounted) or `E_READ`.
103int8_t folder(const char *name, File &dir, uint8_t *buf);
104
105/// @brief Finds the (`skip` + 1)th file in a folder whose name fits a
106/// pattern.
107///
108/// Call it with `skip` = 0, 1, 2 ... to go through every match, in
109/// directory order.
110/// @param dir the folder, from folder() or root().
111/// @param pattern an 11-character 8.3 name in which `?` matches any
112/// character (`"????????CWD"`: every `.CWD` file).
113/// @param skip how many matching files to pass over first.
114/// @param f receives the file's first cluster and size.
115/// @param nameOut receives its 11-character name (no NUL); `nullptr` if
116/// not wanted.
117/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
118/// @return `OK`; `E_NOTFOUND`: there are no more (or not mounted);
119/// `E_READ`.
120int8_t match(const File &dir, const char *pattern, uint8_t skip, File &f, char *nameOut, uint8_t *buf);
121
122/// @brief The root directory as a folder, for match() and list().
123/// @param dir receives the root directory.
124/// @return `OK`; `E_NOTFOUND`: not mounted.
125int8_t root(File &dir);
126
127/// @brief What list() calls for each entry of a directory.
128/// @param name the entry's 11-character 8.3 name (no NUL), valid only
129/// during the call.
130/// @param f its first cluster and size.
131/// @param isDir `true` for a folder, `false` for a file.
132/// @param ctx the `ctx` given to list().
133/// @return `true` to go on to the next entry, `false` to stop the walk.
134typedef bool (*ListFn)(const char *name, const File &f, bool isDir, void *ctx);
135
136/// @brief Walks a directory, handing every file and folder in it to `fn`,
137/// in directory order.
138///
139/// `fn` gets each one's 11-character short name (no NUL), its first
140/// cluster and size, and whether it is a folder; returning `false` stops
141/// the walk. `.`, `..`, deleted entries, volume labels, long-name parts and
142/// hidden entries are left out. (For a browser: CHStlView.)
143/// @param dir the directory, from root() or folder().
144/// @param fn called once per entry (see @ref ListFn); it must not touch
145/// `buf` (the directory sector is in it) nor read the card.
146/// @param ctx passed to `fn` unchanged; may be `nullptr`.
147/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
148/// @return `OK` (also when `fn` stopped the walk), `E_NOTFOUND` (not
149/// mounted) or `E_READ`.
150int8_t list(const File &dir, ListFn fn, void *ctx, uint8_t *buf);
151
152/// @brief Walks a file's cluster chain once and returns its extents: the
153/// runs of consecutive card blocks that hold it.
154///
155/// The last run is trimmed to the file's size, and a file of 0 bytes has 0
156/// runs. The walk is bounded by the file's size and the chain must end
157/// right there, so a looped or truncated FAT gives `E_CHAIN` instead of a
158/// hang.
159/// @param f the file, from find() or match().
160/// @param out receives the runs.
161/// @param maxRuns how many runs `out` has room for (at most 127 are used).
162/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
163/// @return the number of runs (0 or more), or `E_FRAG` (more runs than
164/// `maxRuns`: copying the file to a freshly formatted card always
165/// helps), `E_CHAIN` or `E_READ`.
166int8_t runs(const File &f, Run *out, uint8_t maxRuns, uint8_t *buf);
167
168/// @brief The usual way in: `sd::init()`, mount(), find() and runs() in one
169/// go.
170///
171/// A game that tells the player why there is no file (no card, exFAT ...)
172/// calls the steps itself: passing the reason on would cost every game some
173/// 40 B of flash.
174/// @param name the file's 11-character 8.3 name in the root directory
175/// (`"WORDS DIC"`).
176/// @param out receives the file's runs.
177/// @param maxRuns how many runs `out` has room for.
178/// @param buf a 512-byte, 4-byte-aligned buffer; clobbered.
179/// @return the number of runs; 0 if any step failed or the file is empty.
180uint8_t open(const char *name, Run *out, uint8_t maxRuns, uint8_t *buf);
181
182/// @brief Reads block `k` of a file through its runs.
183/// @param run the file's runs, from open() or runs().
184/// @param nRuns how many there are.
185/// @param k the block of the file (0 = its first 512 bytes).
186/// @param dst where the 512 bytes go (any buffer of 512 bytes).
187/// @return `true` if the block was read; `false`: past the end of the
188/// file, or the card did not deliver it (then `sd::init()`, or
189/// open(), before the next read).
190bool read(const Run *run, uint32_t nRuns, uint32_t k, uint8_t *dst);
191
192} // namespace fat
193
194/// @}
The FAT16/FAT32 reader (CHSd).
Definition Fat.h:46
int8_t root(File &dir)
The root directory as a folder, for match() and list().
int8_t find(const char *name, File &f, uint8_t *buf)
Finds a file in the root directory.
int8_t list(const File &dir, ListFn fn, void *ctx, uint8_t *buf)
Walks a directory, handing every file and folder in it to fn, in directory order.
uint8_t open(const char *name, Run *out, uint8_t maxRuns, uint8_t *buf)
The usual way in: sd::init(), mount(), find() and runs() in one go.
Err
What the functions that return an int8_t report: OK (0) or a negative error.
Definition Fat.h:50
@ E_NOFS
no FAT16/FAT32 volume: no partition, bad BPB, FAT12
Definition Fat.h:53
@ E_FRAG
more runs than the caller has room for
Definition Fat.h:56
@ OK
success
Definition Fat.h:51
@ E_EXFAT
exFAT (or NTFS) volume: reformat the card as FAT32
Definition Fat.h:54
@ E_CHAIN
cluster chain broken, looped, or not the file's length
Definition Fat.h:57
@ E_NOTFOUND
no such file or directory (or not mounted)
Definition Fat.h:55
@ E_READ
the card did not deliver a block
Definition Fat.h:52
int8_t runs(const File &f, Run *out, uint8_t maxRuns, uint8_t *buf)
Walks a file's cluster chain once and returns its extents: the runs of consecutive card blocks that h...
int8_t match(const File &dir, const char *pattern, uint8_t skip, File &f, char *nameOut, uint8_t *buf)
Finds the (skip + 1)th file in a folder whose name fits a pattern.
bool(* ListFn)(const char *name, const File &f, bool isDir, void *ctx)
What list() calls for each entry of a directory.
Definition Fat.h:134
bool read(const Run *run, uint32_t nRuns, uint32_t k, uint8_t *dst)
Reads block k of a file through its runs.
int8_t mount(uint8_t *buf)
Finds the FAT volume on the card and remembers where its parts are.
int8_t folder(const char *name, File &dir, uint8_t *buf)
Finds a folder in the root directory, for match() and list().
A file or folder found in a directory: where it starts and how long it is.
Definition Fat.h:70
uint32_t size
Its size in bytes (0 for a folder).
Definition Fat.h:70
uint32_t cluster
Its first cluster (0 for an empty file; for root(), the FAT16 root's 0 or the FAT32 root's cluster).
Definition Fat.h:70
A run of consecutive card blocks holding part of a file (see runs()).
Definition Fat.h:62
uint32_t lba
The run's first block on the card (an LBA, as sd::read() takes it).
Definition Fat.h:62
uint32_t blocks
How many 512-byte blocks the run holds (at least 1).
Definition Fat.h:62