All files loaded from TPHD, no gamecube files remain.

This commit is contained in:
MelonSpeedruns
2026-07-12 11:15:52 -04:00
parent 46d93c908c
commit 6bab72092b
9 changed files with 171 additions and 23 deletions
+5
View File
@@ -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();
@@ -11,6 +11,11 @@
#include <cstring>
#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);
+40
View File
@@ -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<J2DPicture*>(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);
}
+6
View File
@@ -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
+8
View File
@@ -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);
+6 -2
View File
@@ -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
+11 -3
View File
@@ -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);
}
}
+7
View File
@@ -114,6 +114,13 @@ bool LoadRelAsset(void* dst, const char* dvdPath, std::initializer_list<OffsetVe
}
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::initializer_list<OffsetVersion> 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
+76 -18
View File
@@ -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<const u8> 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<u8> bmdBytes(static_cast<u8*>(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<ArcFileInfo> parseRarcFiles(std::span<const u8> arc) {
@@ -652,6 +661,7 @@ std::vector<ArcFileInfo> parseRarcFiles(std::span<const u8> arc) {
: std::move(fname),
static_cast<u32>(dataBase + entry.data_offset),
entry.data_size,
(typeFlags & JKRARCHIVE_ATTR_COMPRESSION) != 0,
});
}
}
@@ -665,28 +675,34 @@ std::vector<ArcFileInfo> parseRarcFiles(std::span<const u8> 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<u8> arcBytes, const std::vector<ArcFileInfo>& 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<HdOverlayEntry*>(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<std::vector<u8>*> try_load_hd_archive(std::string_view gcPath) {
auto hdFiles = parseRarcFiles(std::span<const u8>(
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<const TphdPack> 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<u8>* mountBytes;
@@ -1006,22 +1050,25 @@ std::optional<std::vector<u8>*> 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<const TphdPack> gcPack;
if (!gcPackPath.empty()) {
gcPack = load_pack_cached(gcPackPath);
}
auto hdFiles = parseRarcFiles(std::span<const u8>(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<const TphdPack> 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);
}
}