From 6bab72092bc0f341067e761a0fe4bc582c00ee59 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Sun, 12 Jul 2026 11:15:52 -0400 Subject: [PATCH] All files loaded from TPHD, no gamecube files remain. --- include/d/d_file_sel_info.h | 5 ++ libs/JSystem/src/JKernel/JKRAramArchive.cpp | 12 +++ src/d/d_file_sel_info.cpp | 40 +++++++++ src/d/d_file_select.cpp | 6 ++ src/d/d_menu_fishing.cpp | 8 ++ src/d/d_menu_save.cpp | 8 +- src/d/d_msg_scrn_place.cpp | 14 ++- src/dusk/dvd_asset.cpp | 7 ++ src/dusk/tphd/HdAssetLayer.cpp | 94 +++++++++++++++++---- 9 files changed, 171 insertions(+), 23 deletions(-) diff --git a/include/d/d_file_sel_info.h b/include/d/d_file_sel_info.h index 973349c469..7fd033cd6d 100644 --- a/include/d/d_file_sel_info.h +++ b/include/d/d_file_sel_info.h @@ -27,6 +27,11 @@ public: void setSaveDate(dSv_save_c*); void setPlayTime(dSv_save_c*); void modeWait(); + + #if TARGET_PC + void fileSelectWide(); + #endif + void modeMove(); void _draw(); diff --git a/libs/JSystem/src/JKernel/JKRAramArchive.cpp b/libs/JSystem/src/JKernel/JKRAramArchive.cpp index d6923ee06b..73ec185073 100644 --- a/libs/JSystem/src/JKernel/JKRAramArchive.cpp +++ b/libs/JSystem/src/JKernel/JKRAramArchive.cpp @@ -11,6 +11,11 @@ #include #include "os_report.h" +#if DUSK_TPHD +#include "dusk/settings.h" +#include "dusk/tphd/HdAssetLayer.hpp" +#endif + JKRAramArchive::JKRAramArchive() {} JKRAramArchive::JKRAramArchive(s32 entryNumber, JKRArchive::EMountDirection mountDirection) @@ -217,6 +222,13 @@ void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) { if (compression == COMPRESSION_YAZ0) { this->setExpandSize(pEntry, *pOutSize); } +#if DUSK_TPHD + // ARAM mounts bypass register_mounted_hd_archive; hook the fresh per-file buffer here. + if (dusk::tphd_active() && mStringTable != NULL) { + dusk::tphd::register_copied_hd_resource( + mEntryNum, mStringTable + pEntry->getNameOffset(), outBuf, *pOutSize); + } +#endif } else { if (compression == COMPRESSION_YAZ0) { *pOutSize = this->getExpandSize(pEntry); diff --git a/src/d/d_file_sel_info.cpp b/src/d/d_file_sel_info.cpp index d54bc4424d..ece78329dd 100644 --- a/src/d/d_file_sel_info.cpp +++ b/src/d/d_file_sel_info.cpp @@ -219,7 +219,47 @@ void dFile_info_c::modeWait() {} void dFile_info_c::modeMove() {} +#if TARGET_PC + +static u64 const heart_tags[] = { + MULTI_CHAR('hear_20'), + MULTI_CHAR('hear_21'), + MULTI_CHAR('hear_22'), + MULTI_CHAR('hear_23'), + MULTI_CHAR('hear_24'), + MULTI_CHAR('hear_25'), + MULTI_CHAR('hear_26'), + MULTI_CHAR('hear_27'), + MULTI_CHAR('hear_28'), + MULTI_CHAR('hear_29'), + MULTI_CHAR('hear_30'), + MULTI_CHAR('hear_31'), + MULTI_CHAR('hear_32'), + MULTI_CHAR('hear_33'), + MULTI_CHAR('hear_34'), + MULTI_CHAR('hear_35'), + MULTI_CHAR('hear_36'), + MULTI_CHAR('hear_37'), + MULTI_CHAR('hear_38'), + MULTI_CHAR('hear_39'), +}; + +void dFile_info_c::fileSelectWide() { + // TPHD Exclusions + if (dusk::tphd_active()) { + mFileInfo.Scr->search(MULTI_CHAR('t_w_01'))->hide(); + + for (int i = 0; i < 20; i++) { + static_cast(mFileInfo.Scr->search(heart_tags[i])) + ->setBlackWhite( + JUtility::TColor(231, 25, 0, 0), JUtility::TColor(255, 255, 255, 255)); + } + } +} +#endif + void dFile_info_c::_draw() { + fileSelectWide(); dComIfGd_set2DOpa(&mFileInfo); } diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 6e5e2e4954..3a46a0fc24 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -4209,6 +4209,12 @@ void dFile_select_c::fileSelectWide() { } break; } + + // TPHD Exclusions + if (dusk::tphd_active()) { + mSelDt.ScrDt->search(MULTI_CHAR('stamps_n'))->hide(); + fileSel.Scr->search(MULTI_CHAR('w_sel_03'))->hide(); + } } #endif diff --git a/src/d/d_menu_fishing.cpp b/src/d/d_menu_fishing.cpp index 6a3d0aefe8..bf56dffe70 100644 --- a/src/d/d_menu_fishing.cpp +++ b/src/d/d_menu_fishing.cpp @@ -334,7 +334,15 @@ void dMenu_Fishing_c::screenSetBase() { field_0x19c[1][i]->setString(0x20, ""); mpFishNameString[i] = (J2DTextBox*)mpScreen->search(name_0[i]); + + #if TARGET_PC + if (!dusk::tphd_active()) { + mpScreen->search(fname_0[i])->hide(); + } + #else mpScreen->search(fname_0[i])->hide(); + #endif + mpFishNameString[i]->setFont(mDoExt_getSubFont()); mpFishNameString[i]->setString(0x20, ""); dMeter2Info_getStringKanji(name_id[i], mpFishNameString[i]->getStringPtr(), NULL); diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index d272c57500..fb1713a2ac 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -2966,11 +2966,15 @@ void dMenu_save_c::menuSaveWide() { mSaveSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); mSaveSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - #if TARGET_PC if (mSelIcon) { mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } - #endif + + if (dusk::tphd_active()) { + mSaveSel.Scr->search(MULTI_CHAR('w_sel_03'))->hide(); + mSaveSel.Scr->search(MULTI_CHAR('w_sel_04'))->hide(); + mSaveSel.Scr->search(MULTI_CHAR('w_btn_n'))->hide(); + } } #endif diff --git a/src/d/d_msg_scrn_place.cpp b/src/d/d_msg_scrn_place.cpp index 93c2ec7c20..83781d39dc 100644 --- a/src/d/d_msg_scrn_place.cpp +++ b/src/d/d_msg_scrn_place.cpp @@ -20,6 +20,14 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() { static u64 t_tag[7] = { MULTI_CHAR('sfontb0'), MULTI_CHAR('sfontb1'), MULTI_CHAR('sfontb2'), MULTI_CHAR('sfontl0'), MULTI_CHAR('sfontl1'), MULTI_CHAR('sfontl2'), MULTI_CHAR('sfont00'), }; + + #if TARGET_PC + if (dusk::tphd_active()) { + t_tag[0] = MULTI_CHAR('sfontb1'); + t_tag[1] = MULTI_CHAR('sfontl1'); + } + #endif + init(); dCamera_c* camera = dCam_getBody(); @@ -54,7 +62,7 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() { mpBaseParent->paneTrans(g_MsgObject_HIO_c.mStageTitleBasePosX, g_MsgObject_HIO_c.mStageTitleBasePosY - mScaleY); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < DUSK_IF_ELSE((dusk::tphd_active() ? 2 : 7), 7); i++) { mpTm_c[i] = JKR_NEW CPaneMgr(mpScreen, t_tag[i], 0, NULL); ((J2DTextBox*)mpTm_c[i]->getPanePtr())->setFont(mDoExt_getRubyFont()); ((J2DTextBox*)mpTm_c[i]->getPanePtr())->setString(0x80, ""); @@ -69,7 +77,7 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() { mLineSpace = ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getLineSpace(); mCharSpace = ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getCharSpace(); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < DUSK_IF_ELSE((dusk::tphd_active() ? 2 : 7), 7); i++) { ((J2DTextBox*)mpTm_c[i]->getPanePtr())->setLineSpace(mLineSpace); ((J2DTextBox*)mpTm_c[i]->getPanePtr()) ->resize(mpTm_c[i]->getSizeX() * 1.2f, mpTm_c[i]->getSizeY()); @@ -145,7 +153,7 @@ void dMsgScrnPlace_c::fukiAlpha(f32 i_rate) { mpPmP_c->setAlphaRate(i_rate); mpBaseParent->setAlphaRate(i_rate * g_MsgObject_HIO_c.mStageTitleBaseAlpha); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < DUSK_IF_ELSE((dusk::tphd_active() ? 2 : 7), 7); i++) { mpTm_c[i]->setAlphaRate(i_rate); } } diff --git a/src/dusk/dvd_asset.cpp b/src/dusk/dvd_asset.cpp index 75c82107cc..842c7d0066 100644 --- a/src/dusk/dvd_asset.cpp +++ b/src/dusk/dvd_asset.cpp @@ -114,6 +114,13 @@ bool LoadRelAsset(void* dst, const char* dvdPath, std::initializer_list offset, s32 size) { + #if TARGET_PC + // TODO: Load from TPHD RPX instead. + if (dusk::tphd_active()) { + return false; + } + #endif + auto resOffset = GetOffsetForVersion(offset); // On TARGET_PC, cDyl_InitCallback skips DynamicModuleControl::initialize() due to static linking diff --git a/src/dusk/tphd/HdAssetLayer.cpp b/src/dusk/tphd/HdAssetLayer.cpp index c05f4bdcf0..b90340072b 100644 --- a/src/dusk/tphd/HdAssetLayer.cpp +++ b/src/dusk/tphd/HdAssetLayer.cpp @@ -78,6 +78,7 @@ struct HdOverlayEntry { std::string dvdPath; std::filesystem::path arcPath; std::filesystem::path packPath; + std::filesystem::path gcPackPath; // per-texture fallback when packPath lacks an entry size_t size = 0; }; @@ -262,6 +263,13 @@ const TmpkEntry* findGtxBySuffix(const TphdPack& pack, std::string_view arcRelPa return nullptr; } +// HD (Revo) pack first; per-texture fallback to the GC sibling pack. +const TmpkEntry* findGtxBySuffixChain(const TphdPack& pack, const TphdPack* gcPack, + std::string_view arcRelPath) { + if (const TmpkEntry* e = findGtxBySuffix(pack, arcRelPath)) return e; + return gcPack != nullptr ? findGtxBySuffix(*gcPack, arcRelPath) : nullptr; +} + // Post-deswizzle CPU expansions to RGBA8. Used for formats whose HD layout // can't be directly sampled with a GPU view swizzle (IA4 nibble unpack, // RGB565 16-bit), and as a fallback if R8_PC/RG8_PC view swizzle isn't @@ -486,13 +494,13 @@ void applyTimgAttributes(ResTIMG* timg, const GtxSurface& s, s32 imageOffset) { timg->maxAnisotropy = GX_ANISO_4; } -bool register_hd_bti_replacement_for_buffer(const TphdPack& pack, std::string_view resourceName, - void* buffer, size_t resourceSize, bool replaceExistingPointer) { +bool register_hd_bti_replacement_for_buffer(const TphdPack& pack, const TphdPack* gcPack, + std::string_view resourceName, void* buffer, size_t resourceSize, bool replaceExistingPointer) { if (buffer == nullptr || resourceSize < 0x20 || !endsWithSuffixCI(resourceName, ".bti")) { return false; } - const TmpkEntry* gtx = findGtxBySuffix(pack, resourceName); + const TmpkEntry* gtx = findGtxBySuffixChain(pack, gcPack, resourceName); if (!gtx) { return false; } @@ -547,13 +555,13 @@ u32 bmdSlotBtiOffset(std::span bmd, u32 slotIdx) { return 0; } -size_t register_hd_bmd_textures_for_buffer(const TphdPack& pack, std::string_view resourceName, - void* buffer, size_t resourceSize, bool replaceExistingPointer) { +size_t register_hd_bmd_textures_for_buffer(const TphdPack& pack, const TphdPack* gcPack, + std::string_view resourceName, void* buffer, size_t resourceSize, bool replaceExistingPointer) { if (buffer == nullptr || resourceSize < 0x20) return 0; if (!endsWithSuffixCI(resourceName, ".bmd") && !endsWithSuffixCI(resourceName, ".bdl")) return 0; - const TmpkEntry* gtx = findGtxBySuffix(pack, resourceName); + const TmpkEntry* gtx = findGtxBySuffixChain(pack, gcPack, resourceName); if (gtx == nullptr) return 0; std::span bmdBytes(static_cast(buffer), resourceSize); @@ -594,6 +602,7 @@ struct ArcFileInfo { std::string path; // e.g. "bmdr/model.bmd" u32 dataOffset; // absolute offset from arc base u32 dataSize; + bool compressed; }; std::vector parseRarcFiles(std::span arc) { @@ -652,6 +661,7 @@ std::vector parseRarcFiles(std::span arc) { : std::move(fname), static_cast(dataBase + entry.data_offset), entry.data_size, + (typeFlags & JKRARCHIVE_ATTR_COMPRESSION) != 0, }); } } @@ -665,28 +675,34 @@ std::vector parseRarcFiles(std::span arc) { // arcBytes must point at the mounted archive bytes the game will later use; // aurora's pointer lookups depend on those addresses staying valid. void register_hd_textures_for_arc(std::span arcBytes, const std::vector& files, - const TphdPack& pack, std::string_view arcLabel) { + const TphdPack& pack, const TphdPack* gcPack, std::string_view arcLabel) { ZoneScoped; ZoneText(arcLabel.data(), arcLabel.size()); size_t bmdReg = 0; size_t btiReg = 0; + size_t deferred = 0; // Phase A: per-slot textures inside BMD/BDL models. for (const auto& f : files) { - bmdReg += register_hd_bmd_textures_for_buffer(pack, f.path, arcBytes.data() + f.dataOffset, f.dataSize, false); + if (f.compressed) { + ++deferred; + continue; + } + bmdReg += register_hd_bmd_textures_for_buffer(pack, gcPack, f.path, arcBytes.data() + f.dataOffset, f.dataSize, false); } // Phase B: standalone .bti files. Each BTI is its own arc entry; the // game loads it via JUTTexture (or similar) which calls GXInitTexObj // with `(u8*)resTIMG + imageOffset`. Register that exact pointer. for (const auto& f : files) { - if (register_hd_bti_replacement_for_buffer(pack, f.path, arcBytes.data() + f.dataOffset, f.dataSize, false)) { + if (f.compressed) continue; + if (register_hd_bti_replacement_for_buffer(pack, gcPack, f.path, arcBytes.data() + f.dataOffset, f.dataSize, false)) { ++btiReg; } } - HdLog.info("registerHdTextures[{}]: {} BMD-slot, {} standalone-BTI replacements", - arcLabel, bmdReg, btiReg); + HdLog.info("registerHdTextures[{}]: {} BMD-slot, {} standalone-BTI replacements, {} compressed deferred", + arcLabel, bmdReg, btiReg, deferred); } // HD arcs whose Wii-U layouts don't match the GC UI pipeline. @@ -722,10 +738,24 @@ std::filesystem::path hd_pack_path_for_arc(std::string_view resPath) { return packPath; } +// Layout arcs to mount from HD content instead of the ISO. +constexpr std::string_view kHdLayoutMountList[] = { + "res/Layout/clctres.arc", + "res/Layout/fishres.arc", + "res/Layout/insectRes.arc", + "res/Layout/letres.arc", + "res/Layout/ringres.arc", + "res/Layout/skillres.arc", +}; + bool should_skip_hd_arc_mount(std::string_view resPath) { - // Layout HD archives do not match the GC UI pipeline, but their pack.gz - // textures can still be registered against the vanilla archive. + return false; + + // Layout arcs mount only via kHdLayoutMountList. if (is_layout_arc_path(resPath)) { + for (auto mount : kHdLayoutMountList) { + if (resPath == mount) return false; + } return true; } for (auto skip : kHdSkipList) { @@ -738,6 +768,15 @@ bool should_register_hd_pack_for_vanilla_arc(std::string_view resPath) { return resPath.starts_with("res/Layout/"); } +std::filesystem::path fallback_path_for_arc(std::string_view resPath, + const std::filesystem::path& preferred) { + if (!resPath.starts_with("res/Layout/")) return {}; + std::filesystem::path sibling = g_contentPath / std::string(resPath); + sibling.replace_extension(".pack.gz"); + if (sibling == preferred || !path_exists(sibling)) return {}; + return sibling; +} + void* overlay_open(void* userData) { auto* entry = static_cast(userData); if (entry == nullptr) return nullptr; @@ -869,6 +908,7 @@ void rebuild_hd_overlay_locked() { entry.dvdPath = "/" + resPath; entry.arcPath = arcPath; entry.packPath = hd_pack_path_for_arc(resPath); + entry.gcPackPath = fallback_path_for_arc(resPath, entry.packPath); entry.size = *fileSize; overlayFiles.push_back({ @@ -987,9 +1027,13 @@ std::optional*> try_load_hd_archive(std::string_view gcPath) { auto hdFiles = parseRarcFiles(std::span( hdBytesOpt->data(), hdBytesOpt->size())); - // Sidecar pack.gz holds the HD textures. + // Sidecar pack.gz holds the HD textures; GC sibling pack as per-texture fallback. auto hdPackPath = hd_pack_path_for_arc(resPath); auto hdPack = load_pack_cached(hdPackPath); + std::shared_ptr gcPack; + if (auto gcPackPath = fallback_path_for_arc(resPath, hdPackPath); !gcPackPath.empty()) { + gcPack = load_pack_cached(gcPackPath); + } // std::list keeps element addresses stable for aurora's pointer map. std::vector* mountBytes; @@ -1006,22 +1050,25 @@ std::optional*> try_load_hd_archive(std::string_view gcPath) { mountBytes->size(), hdPack ? "yes" : "no"); if (hdPack != nullptr) { - register_hd_textures_for_arc(*mountBytes, hdFiles, *hdPack, filename); + register_hd_textures_for_arc(*mountBytes, hdFiles, *hdPack, gcPack.get(), filename); } return mountBytes; } void register_mounted_hd_archive(s32 entryNum, void* arcBytes, size_t arcSize) { + if (g_contentPath.empty()) return; if (entryNum < 0 || arcBytes == nullptr || arcSize == 0) return; std::filesystem::path packPath; + std::filesystem::path gcPackPath; std::string label; { std::lock_guard lk{g_cacheMutex}; auto it = g_entryNumToOverlay().find(entryNum); if (it == g_entryNumToOverlay().end()) return; packPath = it->second->packPath; + gcPackPath = it->second->gcPackPath; label = it->second->arcPath.filename().string(); } @@ -1035,13 +1082,18 @@ void register_mounted_hd_archive(s32 entryNum, void* arcBytes, size_t arcSize) { if (hdPack == nullptr) { return; } + std::shared_ptr gcPack; + if (!gcPackPath.empty()) { + gcPack = load_pack_cached(gcPackPath); + } auto hdFiles = parseRarcFiles(std::span(arcSpan.data(), arcSpan.size())); - register_hd_textures_for_arc(arcSpan, hdFiles, *hdPack, label); + register_hd_textures_for_arc(arcSpan, hdFiles, *hdPack, gcPack.get(), label); } void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, void* buffer, size_t resourceSize) { + if (g_contentPath.empty()) return; if (entryNum < 0 || buffer == nullptr || resourceSize < 0x20) return; const bool isBti = endsWithSuffixCI(resourceName, ".bti"); @@ -1050,6 +1102,7 @@ void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, vo if (!isBti && !isBmd) return; std::filesystem::path packPath; + std::filesystem::path gcPackPath; { std::lock_guard lk{g_cacheMutex}; auto it = g_entryNumToOverlay().find(entryNum); @@ -1057,17 +1110,22 @@ void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, vo return; } packPath = it->second->packPath; + gcPackPath = it->second->gcPackPath; } auto hdPack = load_pack_cached(packPath); if (hdPack == nullptr) { return; } + std::shared_ptr gcPack; + if (!gcPackPath.empty()) { + gcPack = load_pack_cached(gcPackPath); + } if (isBti) { - register_hd_bti_replacement_for_buffer(*hdPack, resourceName, buffer, resourceSize, true); + register_hd_bti_replacement_for_buffer(*hdPack, gcPack.get(), resourceName, buffer, resourceSize, true); } else { - register_hd_bmd_textures_for_buffer(*hdPack, resourceName, buffer, resourceSize, true); + register_hd_bmd_textures_for_buffer(*hdPack, gcPack.get(), resourceName, buffer, resourceSize, true); } }