File indexing and font discovery
File indexing, font discovery and asset loading have separate policies. The
asset loader constructs a fresh CE::Files::FileIndex for each
preparation. Unicode font selection and text preparation run on the CPU; text
upload uses the resource provider separately. Legacy fonts retain their
provider-owned loading path.
Automatic graphics preparation registers JSON, WAV and shader paths in a fresh
private CE::Assets::Loading::FileRegistry; only exact graphics-manifests.json indexes select documents.
Unlisted JSON remains unopened. Loader::register_files() can publish unmanaged
paths without parsing/decoding; it does not initialize fonts or load BitmapFont. The
graphics path contract uses the
enclosing graphics/ directory for index, texture and source references.
File index
To inspect application files without loading assets, include
<cheryl/core/resources/fileio/file-mgr.h> and <iostream>. With asset_root
naming the content directory:
CE::Files::FileIndex files(asset_root);
const auto& pngs = files.get_files_of_type(".png");
for (const auto& path : pngs)
std::cout << path << '\n';
This prints discovered paths; it neither decodes images nor uploads resources.
pngs borrows this index. Finish traversal before appending another directory,
then obtain the bucket again if needed. Construct a new FileIndex to observe
files added beneath an already scanned directory.
CE::Files::FileIndex
is an independent, incremental index. Construction scans the supplied root;
search_directory() appends an unvisited directory tree. Missing and non-directory
roots add nothing. Directories encountered during a scan are marked visited, so
searching the same root or an already encountered descendant does not refresh it.
Adding new files, deleting files or changing extensions does not update existing
buckets. Create a new index when a fresh scan is required; no refresh API is selected.
Keys use std::filesystem::path equality without canonicalization. Different aliases
or path forms may index the same physical files more than once. Recursion does not
follow directory symlinks, while file status follows symlinks to regular files.
Returned paths retain the walk’s path form. Enumeration order is unspecified and
buckets are neither sorted nor deduplicated.
Extension keys include their leading dot and are lowercased using the current C
character locale. Files with no extension use file_no_ext.
get_files_of_type(".PNG") looks up .png; get_files_of_type("png") selects a
different bucket. A missing lookup creates an empty bucket.
The returned vector is borrowed from the manager and survives other bucket insertions, but subsequent indexing can append to it and invalidate element references or iterators. Reborrow after moving or assigning the manager; destruction or replacement of the owning index invalidates the reference. All access requires external serialization, including lookups, because they can insert buckets.
Filesystem and allocation failures propagate. A failed search can retain a partial index and visited-directory markers, including the root recorded before traversal; reconstruct the index to retry that root. There is no atomic discovery snapshot or automatic recovery from changed files.
Font names and application assets
Games configure fonts once through Context::fonts() rather than repeating
discovery for text and each GUI adapter. The default is embedded when nothing is
configured. FontConfiguration registers stable application references and
retains available style variants; groups contain alternative references for
explicit selection. FontService::configure(FontSelection) also preserves an
existing ordered default/fallback policy. See
catalogue configuration and saved choices.
CE::Text::FontSelection::preferred accepts an
ordered list of names such as {"Arial", "Noto Sans", "DejaVuSans.ttf"}.
Family names come from font metadata. Filename aliases, including their extension,
also select application fonts, regardless of where they are nested under an asset
root. Both forms compare ASCII letters without case. Application discovery scans
asset_directories recursively, defaulting to {"assets"} relative to the working
directory. Games using a different asset root set that list once; preferred names
contain no directories.
Names resolve against application fonts before system fonts, so a matching asset
font overrides an installed copy. The engine discovers system roots automatically;
system files match family names rather than filename aliases. Missing names and
unusable discovery candidates are skipped. FontFile remains available for direct
path/face selection and SystemFontFamily for an explicitly system-only choice.
The text guide defines ordering, style ranking,
failure behavior and the embedded fallback. The lower-level functions below return
candidate paths; their filename heuristic is separate from this name resolution.
Configuration retains immutable font bytes. Adapters use those snapshots and collection indices instead of reopening discovered paths. Missing or unloadable registered families are unavailable for that catalogue; saved-choice restoration checks its current group and available members. Ordinary lookup does not rescan or detect changed files. Reconfigure explicitly when another loading boundary needs a fresh snapshot; already retained fonts remain valid.
System-font candidates
fonts-system.h
returns owned paths and performs no provider work. CE::Text::system_font_directories() lists
/usr/share/fonts, /usr/local/share/fonts, /Library/Fonts and
/System/Library/Fonts, then appends paths derived from nonempty environment values:
Variable |
Appended paths |
|---|---|
|
|
|
|
|
|
|
|
These are candidates on every platform; the list does not check existence or remove duplicates. Keep environment mutation serialized with calls that read it.
CE::Text::find_system_fonts() scans those roots. The explicit-root overload recursively
collects regular files with case-insensitive .ttf, .otf, .ttc or .otc
extensions. It skips missing, non-directory and inaccessible roots and permission-
denied subtrees. Other filesystem errors may truncate a root’s results without
diagnostics; the returned list can be incomplete. Allocation and path-conversion
failures still propagate. Directory symlinks are not followed during recursion;
symlinks to regular font files may appear.
Paths are lexically normalized, sorted by path ordering and deduplicated by path equality. This does not canonicalize aliases or establish that two paths refer to different files. Discovery validates neither file contents nor supported glyphs. Files may change between discovery and loading.
CE::Text::select_default_system_font() checks case-insensitive filenames in this order:
arial.ttf, helvetica.ttc, dejavusans.ttf, liberationsans-regular.ttf,
nimbussans-regular.otf, notosans-regular.ttf, freesans.ttf, segoeui.ttf,
roboto-regular.ttf, calibri.ttf, consola.ttf, proggyvector regular.ttf.
For a preferred filename occurring more than once, the first supplied path wins.
Without a preferred name, the minimum path wins regardless of supplied order; an
empty list produces nullopt. This heuristic performs no I/O or family/coverage
inspection and does not try another candidate if later loading fails.
Legacy font cache
CE::Text::Legacy::Manager loads on the active provider’s loading owner under the cache ownership contract. It uses lexically normalized paths as cache keys, processes the supplied order and retains already loaded entries. The first successful publication when no default exists selects the default; later loads do not replace it. A later failure retains earlier cache publications and their default selection.
load(paths, provider) loads CE::Text::Legacy::AsciiFont at size 32 with
printable ASCII and collection face zero.
Discovery includes collection paths, but this legacy API does not select other
faces. The separate Unicode service selects family
metadata, collection faces and whole-grapheme fallback; the filename heuristic
does not establish those capabilities.
default_font() returns a shared retained handle or nullptr before the first load
and after clear_assets(). Clearing removes cache/default ownership while callers’
shared handles retain their resources. It does not switch the shared provider domain;
provider teardown owns that release. Lookup callers use the same lexically normalized
key when calling the inherited get_asset().