@@ -35,26 +35,35 @@ static constexpr bool kForceUncompressedDDS = false;
3535
3636TextureAsset::TextureAsset () = default ;
3737
38+ // / Derive the loader container hint from a resolved asset path. CAS
39+ // / blobs resolve to bare content-hash file names with no extension
40+ // / (all texture flavors are KTX2 containers), while every legacy
41+ // / on-disk path — including the headless ``.nop`` dummy — carries an
42+ // / extension for the loader's path-suffix sniff. Keying on the
43+ // / *resolved* path shape (rather than whether the *requested* name
44+ // / was a qualified ``<apverid>:<name>`` ref) matters because bare
45+ // / legacy names can still resolve to CAS blobs: a missing texture
46+ // / falls back to the builtin package's ``textures/white``, and that
47+ // / fallback must load rather than fail the asset (which is fatal if
48+ // / the render path later touches it).
49+ static auto DeriveContainerHint (const std::string& path) -> std::string {
50+ auto slash_pos = path.find_last_of (" /\\ " );
51+ auto dot_pos =
52+ path.find (' .' , slash_pos == std::string::npos ? 0 : slash_pos + 1 );
53+ if (dot_pos == std::string::npos) {
54+ return " .ktx2" ;
55+ }
56+ return {};
57+ }
58+
3859TextureAsset::TextureAsset (const std::string& file_in, TextureType type_in,
3960 TextureMinQuality min_quality_in)
4061 : file_name_(file_in), type_(type_in), min_quality_(min_quality_in) {
4162 file_name_full_ = g_base->assets ->FindAssetFile (
4263 type_ == TextureType::kCubeMap ? Assets::FileType::kCubeMapTexture
4364 : Assets::FileType::kTexture ,
4465 file_in);
45- // CAS-form ref (``<apverid>:<asset_name>``) resolves to a CAS blob
46- // whose on-disk name is just a hash — no extension. Set an
47- // explicit container hint so the loader can dispatch without
48- // sniffing the path. Hardcoded to ``.ktx2`` (FALLBACK_V1 produces
49- // KTX2 per initiative decision #12); Phase 3 construct-mode
50- // replaces this with per-profile dispatch. Headless mode resolves
51- // to a ``.nop`` dummy path which has its own loader branch, so
52- // we leave the container empty in that case and let the matcher's
53- // path-suffix fallback pick the right branch.
54- if (file_in.find (' :' ) != std::string::npos
55- && !file_name_full_.ends_with (" .nop" )) {
56- container_ = " .ktx2" ;
57- }
66+ container_ = DeriveContainerHint (file_name_full_);
5867 valid_ = true ;
5968}
6069
@@ -116,10 +125,8 @@ auto TextureAsset::ReResolveSource() -> bool {
116125 return false ;
117126 }
118127 file_name_full_ = new_full;
119- // Re-derive the container hint exactly as the constructor does: CAS blobs
120- // are extensionless KTX2; the headless ``.nop`` dummy keeps it empty so the
121- // matcher's path-suffix fallback picks the right branch.
122- container_ = file_name_full_.ends_with (" .nop" ) ? " " : " .ktx2" ;
128+ // Re-derive the container hint exactly as the constructor does.
129+ container_ = DeriveContainerHint (file_name_full_);
123130 return true ;
124131}
125132
0 commit comments