mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-12 21:45:19 -04:00
Compare commits
3 Commits
rpx-loading
..
tphd
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bab72092b | |||
| 46d93c908c | |||
| f34e26db30 |
@@ -27,6 +27,11 @@ public:
|
|||||||
void setSaveDate(dSv_save_c*);
|
void setSaveDate(dSv_save_c*);
|
||||||
void setPlayTime(dSv_save_c*);
|
void setPlayTime(dSv_save_c*);
|
||||||
void modeWait();
|
void modeWait();
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
void fileSelectWide();
|
||||||
|
#endif
|
||||||
|
|
||||||
void modeMove();
|
void modeMove();
|
||||||
void _draw();
|
void _draw();
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,4 @@ 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);
|
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::initializer_list<OffsetVersion> offset, s32 size);
|
||||||
|
|
||||||
#ifdef DUSK_TPHD
|
|
||||||
/**
|
|
||||||
* Load bytes from the RPX by Wii U virtual address
|
|
||||||
*/
|
|
||||||
bool LoadRPXAsset(void* dst, uint32_t virtualAddress, s32 size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace dusk
|
} // namespace dusk
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "os_report.h"
|
#include "os_report.h"
|
||||||
|
|
||||||
|
#if DUSK_TPHD
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
#include "dusk/tphd/HdAssetLayer.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
JKRAramArchive::JKRAramArchive() {}
|
JKRAramArchive::JKRAramArchive() {}
|
||||||
|
|
||||||
JKRAramArchive::JKRAramArchive(s32 entryNumber, JKRArchive::EMountDirection mountDirection)
|
JKRAramArchive::JKRAramArchive(s32 entryNumber, JKRArchive::EMountDirection mountDirection)
|
||||||
@@ -217,6 +222,13 @@ void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) {
|
|||||||
if (compression == COMPRESSION_YAZ0) {
|
if (compression == COMPRESSION_YAZ0) {
|
||||||
this->setExpandSize(pEntry, *pOutSize);
|
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 {
|
} else {
|
||||||
if (compression == COMPRESSION_YAZ0) {
|
if (compression == COMPRESSION_YAZ0) {
|
||||||
*pOutSize = this->getExpandSize(pEntry);
|
*pOutSize = this->getExpandSize(pEntry);
|
||||||
|
|||||||
@@ -371,41 +371,11 @@ JKRHeap* daPy_anmHeap_c::setAnimeHeap() {
|
|||||||
#include "dusk/dvd_asset.hpp"
|
#include "dusk/dvd_asset.hpp"
|
||||||
using GameVersion = dusk::version::GameVersion;
|
using GameVersion = dusk::version::GameVersion;
|
||||||
static const u8* l_sightDL_get() {
|
static const u8* l_sightDL_get() {
|
||||||
#if DUSK_TPHD
|
|
||||||
if(dusk::tphd_active()) {
|
|
||||||
static u8 buf[0x89];
|
|
||||||
static bool _ = (
|
|
||||||
dusk::LoadRPXAsset(
|
|
||||||
buf,
|
|
||||||
0x1011a260,
|
|
||||||
0x89
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
static u8 buf[0x89];
|
|
||||||
static bool _ = (
|
|
||||||
dusk::LoadDolAsset(
|
|
||||||
buf,
|
|
||||||
{
|
|
||||||
{GameVersion::GcnUsa, 0x803BA0C0},
|
|
||||||
{GameVersion::GcnPal, 0x803BBDA0},
|
|
||||||
{GameVersion::GcnJpn, 0x803B4220}
|
|
||||||
},
|
|
||||||
0x89
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static u8 buf[0x89];
|
static u8 buf[0x89];
|
||||||
static bool _ = (
|
static bool _ = (
|
||||||
dusk::LoadDolAsset(
|
dusk::LoadDolAsset(
|
||||||
buf,
|
buf,
|
||||||
{
|
{
|
||||||
{GameVersion::GcnUsa, 0x803BA0C0},
|
{GameVersion::GcnUsa, 0x803BA0C0},
|
||||||
{GameVersion::GcnPal, 0x803BBDA0},
|
{GameVersion::GcnPal, 0x803BBDA0},
|
||||||
{GameVersion::GcnJpn, 0x803B4220}
|
{GameVersion::GcnJpn, 0x803B4220}
|
||||||
@@ -415,7 +385,6 @@ static const u8* l_sightDL_get() {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
return buf;
|
return buf;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#define l_sightDL (l_sightDL_get())
|
#define l_sightDL (l_sightDL_get())
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -219,7 +219,47 @@ void dFile_info_c::modeWait() {}
|
|||||||
|
|
||||||
void dFile_info_c::modeMove() {}
|
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() {
|
void dFile_info_c::_draw() {
|
||||||
|
fileSelectWide();
|
||||||
dComIfGd_set2DOpa(&mFileInfo);
|
dComIfGd_set2DOpa(&mFileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4209,6 +4209,12 @@ void dFile_select_c::fileSelectWide() {
|
|||||||
}
|
}
|
||||||
break;
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -334,7 +334,15 @@ void dMenu_Fishing_c::screenSetBase() {
|
|||||||
field_0x19c[1][i]->setString(0x20, "");
|
field_0x19c[1][i]->setString(0x20, "");
|
||||||
|
|
||||||
mpFishNameString[i] = (J2DTextBox*)mpScreen->search(name_0[i]);
|
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();
|
mpScreen->search(fname_0[i])->hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
mpFishNameString[i]->setFont(mDoExt_getSubFont());
|
mpFishNameString[i]->setFont(mDoExt_getSubFont());
|
||||||
mpFishNameString[i]->setString(0x20, "");
|
mpFishNameString[i]->setString(0x20, "");
|
||||||
dMeter2Info_getStringKanji(name_id[i], mpFishNameString[i]->getStringPtr(), NULL);
|
dMeter2Info_getStringKanji(name_id[i], mpFishNameString[i]->getStringPtr(), NULL);
|
||||||
|
|||||||
@@ -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_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f);
|
||||||
mSaveSel.Scr->search(MULTI_CHAR('w_uzu09'))->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) {
|
if (mSelIcon) {
|
||||||
mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp);
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() {
|
|||||||
static u64 t_tag[7] = {
|
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'),
|
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();
|
init();
|
||||||
|
|
||||||
dCamera_c* camera = dCam_getBody();
|
dCamera_c* camera = dCam_getBody();
|
||||||
@@ -54,7 +62,7 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() {
|
|||||||
mpBaseParent->paneTrans(g_MsgObject_HIO_c.mStageTitleBasePosX,
|
mpBaseParent->paneTrans(g_MsgObject_HIO_c.mStageTitleBasePosX,
|
||||||
g_MsgObject_HIO_c.mStageTitleBasePosY - mScaleY);
|
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);
|
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())->setFont(mDoExt_getRubyFont());
|
||||||
((J2DTextBox*)mpTm_c[i]->getPanePtr())->setString(0x80, "");
|
((J2DTextBox*)mpTm_c[i]->getPanePtr())->setString(0x80, "");
|
||||||
@@ -69,7 +77,7 @@ dMsgScrnPlace_c::dMsgScrnPlace_c() {
|
|||||||
mLineSpace = ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getLineSpace();
|
mLineSpace = ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getLineSpace();
|
||||||
mCharSpace = ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getCharSpace();
|
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())->setLineSpace(mLineSpace);
|
||||||
((J2DTextBox*)mpTm_c[i]->getPanePtr())
|
((J2DTextBox*)mpTm_c[i]->getPanePtr())
|
||||||
->resize(mpTm_c[i]->getSizeX() * 1.2f, mpTm_c[i]->getSizeY());
|
->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);
|
mpPmP_c->setAlphaRate(i_rate);
|
||||||
mpBaseParent->setAlphaRate(i_rate * g_MsgObject_HIO_c.mStageTitleBaseAlpha);
|
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);
|
mpTm_c[i]->setAlphaRate(i_rate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
#include "dusk/math.h"
|
#include "dusk/math.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if DUSK_TPHD
|
||||||
|
#include "dusk/tphd/HdAssetLayer.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//#pragma nosyminline on
|
//#pragma nosyminline on
|
||||||
#endif
|
#endif
|
||||||
@@ -1205,6 +1209,10 @@ void dPa_control_c::createCommon(void const* param_0) {
|
|||||||
mHeap = mDoExt_createSolidHeapFromSystem(0, 0);
|
mHeap = mDoExt_createSolidHeapFromSystem(0, 0);
|
||||||
JKRHEAP_NAME(mHeap, "dPa_control_c::mHeap");
|
JKRHEAP_NAME(mHeap, "dPa_control_c::mHeap");
|
||||||
JUT_ASSERT(2518, mHeap != NULL);
|
JUT_ASSERT(2518, mHeap != NULL);
|
||||||
|
#if DUSK_TPHD
|
||||||
|
dusk::tphd::register_hd_particle_textures("common-r", (void*)param_0,
|
||||||
|
m_resHeap->getSize((void*)param_0));
|
||||||
|
#endif
|
||||||
mCommonResMng = JKR_NEW_ARGS (mHeap, 0) JPAResourceManager(param_0, mHeap);
|
mCommonResMng = JKR_NEW_ARGS (mHeap, 0) JPAResourceManager(param_0, mHeap);
|
||||||
JUT_ASSERT(2521, mCommonResMng != NULL);
|
JUT_ASSERT(2521, mCommonResMng != NULL);
|
||||||
mCommonResMng->swapTexture(mDoGph_gInf_c::getFrameBufferTimg(), "dummy");
|
mCommonResMng->swapTexture(mDoGph_gInf_c::getFrameBufferTimg(), "dummy");
|
||||||
@@ -1231,6 +1239,13 @@ void dPa_control_c::createRoomScene() {
|
|||||||
mSceneHeap = mDoExt_createSolidHeapFromGame(0, 0);
|
mSceneHeap = mDoExt_createSolidHeapFromGame(0, 0);
|
||||||
JKRHEAP_NAME(mSceneHeap, "dPa_control_c::mSceneHeap");
|
JKRHEAP_NAME(mSceneHeap, "dPa_control_c::mSceneHeap");
|
||||||
JUT_ASSERT(2573, mSceneHeap != NULL);
|
JUT_ASSERT(2573, mSceneHeap != NULL);
|
||||||
|
#if DUSK_TPHD
|
||||||
|
{
|
||||||
|
char stem[16];
|
||||||
|
std::snprintf(stem, sizeof(stem), "Pscene%03u", static_cast<unsigned>(field_0x18));
|
||||||
|
dusk::tphd::register_hd_particle_textures(stem, m_sceneRes, m_resHeap->getSize(m_sceneRes));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
mSceneResMng = JKR_NEW_ARGS (mSceneHeap, 0) JPAResourceManager(m_sceneRes, mSceneHeap);
|
mSceneResMng = JKR_NEW_ARGS (mSceneHeap, 0) JPAResourceManager(m_sceneRes, mSceneHeap);
|
||||||
JUT_ASSERT(2576, mSceneResMng != NULL);
|
JUT_ASSERT(2576, mSceneResMng != NULL);
|
||||||
mSceneResMng->swapTexture(mDoGph_gInf_c::getFrameBufferTimg(), "dummy");
|
mSceneResMng->swapTexture(mDoGph_gInf_c::getFrameBufferTimg(), "dummy");
|
||||||
|
|||||||
+7
-146
@@ -8,11 +8,6 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef DUSK_TPHD
|
|
||||||
#include <zlib.h>
|
|
||||||
#include <fstream>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
|
|
||||||
static const u8* s_dolData = nullptr; // pointer to dol data
|
static const u8* s_dolData = nullptr; // pointer to dol data
|
||||||
@@ -119,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) {
|
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);
|
auto resOffset = GetOffsetForVersion(offset);
|
||||||
|
|
||||||
// On TARGET_PC, cDyl_InitCallback skips DynamicModuleControl::initialize() due to static linking
|
// On TARGET_PC, cDyl_InitCallback skips DynamicModuleControl::initialize() due to static linking
|
||||||
@@ -141,145 +143,4 @@ bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DUSK_TPHD
|
|
||||||
enum struct SectionType : uint32_t // sh_type
|
|
||||||
{
|
|
||||||
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
|
||||||
SHT_NULL = 0,
|
|
||||||
SHT_PROGBITS = 1,
|
|
||||||
SHT_SYMTAB = 2,
|
|
||||||
SHT_STRTAB = 3,
|
|
||||||
SHT_RELA = 4,
|
|
||||||
SHT_HASH = 5,
|
|
||||||
SHT_DYNAMIC = 6,
|
|
||||||
SHT_NOTE = 7,
|
|
||||||
SHT_NOBITS = 8,
|
|
||||||
SHT_REL = 9,
|
|
||||||
SHT_SHLIB = 10,
|
|
||||||
SHT_DYNSYM = 11,
|
|
||||||
SHT_INIT_ARRAY = 14,
|
|
||||||
SHT_FINI_ARRAY = 15,
|
|
||||||
SHT_PREINIT_ARRAY = 16,
|
|
||||||
SHT_GROUP = 17,
|
|
||||||
SHT_SYMTAB_SHNDX = 18,
|
|
||||||
|
|
||||||
// https://refspecs.linuxfoundation.org/LSB_2.1.0/LSB-Core-generic/LSB-Core-generic/elftypes.html
|
|
||||||
SHT_LOPROC = 0x70000000,
|
|
||||||
SHT_HIPROC = 0x7fffffff,
|
|
||||||
SHT_LOUSER = 0x80000000,
|
|
||||||
SHT_HIUSER = 0xffffffff,
|
|
||||||
|
|
||||||
// from https://gist.github.com/exjam/b4290ad23828cbc04db4 and looking at the rpx itself
|
|
||||||
SHT_RPL_EXPORTS = 0x80000001,
|
|
||||||
SHT_RPL_IMPORTS = 0x80000002,
|
|
||||||
SHT_RPL_CRCS = 0x80000003,
|
|
||||||
SHT_RPL_FILEINFO = 0x80000004
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct SectionFlags : uint32_t {
|
|
||||||
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
|
||||||
SHF_WRITE = 0x1,
|
|
||||||
SHF_ALLOC = 0x2,
|
|
||||||
SHF_EXECINSTR = 0x4,
|
|
||||||
|
|
||||||
// from https://gist.github.com/exjam/b4290ad23828cbc04db4 and looking at the rpx itself
|
|
||||||
SHF_DEFLATED = 0x08000000
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Elf32_Ehdr {
|
|
||||||
uint8_t e_ident[0x10];
|
|
||||||
BE(u16) e_type;
|
|
||||||
BE(u16) e_machine;
|
|
||||||
BE(u32) e_version;
|
|
||||||
BE(u32) e_entry;
|
|
||||||
BE(u32) e_phoff;
|
|
||||||
BE(u32) e_shoff;
|
|
||||||
BE(u32) e_flags;
|
|
||||||
BE(u16) e_ehsize;
|
|
||||||
BE(u16) e_phentsize;
|
|
||||||
BE(u16) e_phnum;
|
|
||||||
BE(u16) e_shentsize;
|
|
||||||
BE(u16) e_shnum;
|
|
||||||
BE(u16) e_shstrndx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Elf32_Shdr {
|
|
||||||
BE(u32) sh_name;
|
|
||||||
BE(u32) sh_type; // SectionType
|
|
||||||
BE(u32) sh_flags;
|
|
||||||
BE(u32) sh_addr;
|
|
||||||
BE(u32) sh_offset;
|
|
||||||
BE(u32) sh_size;
|
|
||||||
BE(u32) sh_link;
|
|
||||||
BE(u32) sh_info;
|
|
||||||
BE(u32) sh_addralign;
|
|
||||||
BE(u32) sh_entsize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RPXSection {
|
|
||||||
u32 vaddr;
|
|
||||||
std::string data;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<RPXSection> g_rpxSections;
|
|
||||||
|
|
||||||
static bool EnsureRPXParsed() {
|
|
||||||
if (!g_rpxSections.empty()) return true;
|
|
||||||
|
|
||||||
std::ifstream rpx(dusk::tphd_content_path().parent_path() / "code" / "Zelda.rpx", std::ios::binary);
|
|
||||||
if (!rpx.is_open()) {
|
|
||||||
DuskLog.fatal("dvd_asset: Failed to open Zelda.rpx");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Elf32_Ehdr ehdr;
|
|
||||||
rpx.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr));
|
|
||||||
|
|
||||||
for (int i = 0; i < ehdr.e_shnum; i++) {
|
|
||||||
rpx.seekg(ehdr.e_shoff + ehdr.e_shentsize * i, std::ios::beg);
|
|
||||||
|
|
||||||
Elf32_Shdr shdr;
|
|
||||||
rpx.read(reinterpret_cast<char*>(&shdr), sizeof(shdr));
|
|
||||||
|
|
||||||
if(shdr.sh_addr != 0 && shdr.sh_offset != 0 && (shdr.sh_type & ((int)SectionType::SHT_NULL | (int)SectionType::SHT_NOBITS)) == 0) {
|
|
||||||
rpx.seekg(shdr.sh_offset, std::ios::beg);
|
|
||||||
|
|
||||||
RPXSection& section = g_rpxSections.emplace_back();
|
|
||||||
section.vaddr = shdr.sh_addr;
|
|
||||||
|
|
||||||
if(shdr.sh_flags & (int)SectionFlags::SHF_DEFLATED) {
|
|
||||||
BE(u32) size;
|
|
||||||
std::string inData(shdr.sh_size - 4, '\0');
|
|
||||||
rpx.read(reinterpret_cast<char*>(&size), sizeof(size));
|
|
||||||
rpx.read(inData.data(), inData.size());
|
|
||||||
|
|
||||||
section.data.resize(size);
|
|
||||||
|
|
||||||
unsigned long outSize = section.data.size();
|
|
||||||
uncompress(reinterpret_cast<unsigned char*>(section.data.data()), &outSize, reinterpret_cast<unsigned char*>(inData.data()), inData.size());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
section.data.resize(shdr.sh_size);
|
|
||||||
rpx.read(section.data.data(), section.data.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LoadRPXAsset(void* dst, uint32_t virtualAddress, s32 size) {
|
|
||||||
if(!EnsureRPXParsed()) return false;
|
|
||||||
|
|
||||||
const auto& it = std::ranges::find_if(g_rpxSections, [virtualAddress, size](const RPXSection& section) { return section.vaddr <= virtualAddress && virtualAddress + size <= section.vaddr + section.data.size(); });
|
|
||||||
if(it == g_rpxSections.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
it->data.copy(static_cast<char*>(dst), size, virtualAddress - it->vaddr);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace dusk
|
} // namespace dusk
|
||||||
|
|||||||
+134
-29
@@ -78,6 +78,7 @@ struct HdOverlayEntry {
|
|||||||
std::string dvdPath;
|
std::string dvdPath;
|
||||||
std::filesystem::path arcPath;
|
std::filesystem::path arcPath;
|
||||||
std::filesystem::path packPath;
|
std::filesystem::path packPath;
|
||||||
|
std::filesystem::path gcPackPath; // per-texture fallback when packPath lacks an entry
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -262,6 +263,13 @@ const TmpkEntry* findGtxBySuffix(const TphdPack& pack, std::string_view arcRelPa
|
|||||||
return nullptr;
|
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
|
// 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,
|
// 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
|
// RGB565 16-bit), and as a fallback if R8_PC/RG8_PC view swizzle isn't
|
||||||
@@ -455,10 +463,12 @@ void registerHdSurface(const Gx2FormatMapping& m, const GtxSurface& s,
|
|||||||
std::lock_guard lk{g_cacheMutex};
|
std::lock_guard lk{g_cacheMutex};
|
||||||
g_textureBuffers().emplace_back(std::move(decoded.bytes));
|
g_textureBuffers().emplace_back(std::move(decoded.bytes));
|
||||||
const auto& bytes = g_textureBuffers().back();
|
const auto& bytes = g_textureBuffers().back();
|
||||||
|
const u32 texWidth = m.isBcn ? ((s.width + 3u) & ~3u) : s.width;
|
||||||
|
const u32 texHeight = m.isBcn ? ((s.height + 3u) & ~3u) : s.height;
|
||||||
const aurora::texture::RawTextureReplacement replacement{
|
const aurora::texture::RawTextureReplacement replacement{
|
||||||
.bytes = {bytes.data(), bytes.size()},
|
.bytes = {bytes.data(), bytes.size()},
|
||||||
.width = s.width,
|
.width = texWidth,
|
||||||
.height = s.height,
|
.height = texHeight,
|
||||||
.mipCount = std::max(decoded.mipCount, 1u),
|
.mipCount = std::max(decoded.mipCount, 1u),
|
||||||
.gxFormat = m.newGxFormat,
|
.gxFormat = m.newGxFormat,
|
||||||
.label = gtxName,
|
.label = gtxName,
|
||||||
@@ -476,13 +486,21 @@ void registerHdSurface(const Gx2FormatMapping& m, const GtxSurface& s,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool register_hd_bti_replacement_for_buffer(const TphdPack& pack, std::string_view resourceName,
|
void applyTimgAttributes(ResTIMG* timg, const GtxSurface& s, s32 imageOffset) {
|
||||||
void* buffer, size_t resourceSize, bool replaceExistingPointer) {
|
timg->imageOffset = imageOffset;
|
||||||
|
const u8 hdMips = static_cast<u8>(std::clamp<u32>(s.mipCount, 1u, 11u));
|
||||||
|
timg->mipmapCount = hdMips;
|
||||||
|
timg->maxLOD = static_cast<s8>((hdMips - 1) * 8);
|
||||||
|
timg->maxAnisotropy = GX_ANISO_4;
|
||||||
|
}
|
||||||
|
|
||||||
|
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")) {
|
if (buffer == nullptr || resourceSize < 0x20 || !endsWithSuffixCI(resourceName, ".bti")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TmpkEntry* gtx = findGtxBySuffix(pack, resourceName);
|
const TmpkEntry* gtx = findGtxBySuffixChain(pack, gcPack, resourceName);
|
||||||
if (!gtx) {
|
if (!gtx) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -503,10 +521,7 @@ bool register_hd_bti_replacement_for_buffer(const TphdPack& pack, std::string_vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* timg = reinterpret_cast<ResTIMG*>(buffer);
|
auto* timg = reinterpret_cast<ResTIMG*>(buffer);
|
||||||
timg->imageOffset = 0x20;
|
applyTimgAttributes(timg, s, 0x20);
|
||||||
const u8 hdMips = static_cast<u8>(std::clamp<u32>(s.mipCount, 1u, 11u));
|
|
||||||
timg->mipmapCount = hdMips;
|
|
||||||
timg->maxLOD = static_cast<s8>((hdMips - 1) * 8);
|
|
||||||
registerHdSurface(*m, s, static_cast<u8*>(buffer) + 0x20, gtx->name, 0, replaceExistingPointer);
|
registerHdSurface(*m, s, static_cast<u8*>(buffer) + 0x20, gtx->name, 0, replaceExistingPointer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -540,13 +555,13 @@ u32 bmdSlotBtiOffset(std::span<const u8> bmd, u32 slotIdx) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t register_hd_bmd_textures_for_buffer(const TphdPack& pack, std::string_view resourceName,
|
size_t register_hd_bmd_textures_for_buffer(const TphdPack& pack, const TphdPack* gcPack,
|
||||||
void* buffer, size_t resourceSize, bool replaceExistingPointer) {
|
std::string_view resourceName, void* buffer, size_t resourceSize, bool replaceExistingPointer) {
|
||||||
if (buffer == nullptr || resourceSize < 0x20) return 0;
|
if (buffer == nullptr || resourceSize < 0x20) return 0;
|
||||||
if (!endsWithSuffixCI(resourceName, ".bmd") &&
|
if (!endsWithSuffixCI(resourceName, ".bmd") &&
|
||||||
!endsWithSuffixCI(resourceName, ".bdl")) return 0;
|
!endsWithSuffixCI(resourceName, ".bdl")) return 0;
|
||||||
|
|
||||||
const TmpkEntry* gtx = findGtxBySuffix(pack, resourceName);
|
const TmpkEntry* gtx = findGtxBySuffixChain(pack, gcPack, resourceName);
|
||||||
if (gtx == nullptr) return 0;
|
if (gtx == nullptr) return 0;
|
||||||
|
|
||||||
std::span<u8> bmdBytes(static_cast<u8*>(buffer), resourceSize);
|
std::span<u8> bmdBytes(static_cast<u8*>(buffer), resourceSize);
|
||||||
@@ -572,11 +587,7 @@ size_t register_hd_bmd_textures_for_buffer(const TphdPack& pack, std::string_vie
|
|||||||
}
|
}
|
||||||
|
|
||||||
const u32 newImgOff = 0x20 + i * 0x20;
|
const u32 newImgOff = 0x20 + i * 0x20;
|
||||||
timg->imageOffset = static_cast<s32>(newImgOff);
|
applyTimgAttributes(timg, s, static_cast<s32>(newImgOff));
|
||||||
const u8 hdMips = static_cast<u8>(std::clamp<u32>(s.mipCount, 1u, 11u));
|
|
||||||
timg->mipmapCount = hdMips;
|
|
||||||
timg->maxLOD = static_cast<s8>((hdMips - 1) * 8);
|
|
||||||
timg->maxAnisotropy = GX_ANISO_4;
|
|
||||||
registerHdSurface(*m, s, bmdBytes.data() + btiAbs + newImgOff, gtx->name, i,
|
registerHdSurface(*m, s, bmdBytes.data() + btiAbs + newImgOff, gtx->name, i,
|
||||||
replaceExistingPointer);
|
replaceExistingPointer);
|
||||||
++reg;
|
++reg;
|
||||||
@@ -591,6 +602,7 @@ struct ArcFileInfo {
|
|||||||
std::string path; // e.g. "bmdr/model.bmd"
|
std::string path; // e.g. "bmdr/model.bmd"
|
||||||
u32 dataOffset; // absolute offset from arc base
|
u32 dataOffset; // absolute offset from arc base
|
||||||
u32 dataSize;
|
u32 dataSize;
|
||||||
|
bool compressed;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ArcFileInfo> parseRarcFiles(std::span<const u8> arc) {
|
std::vector<ArcFileInfo> parseRarcFiles(std::span<const u8> arc) {
|
||||||
@@ -649,6 +661,7 @@ std::vector<ArcFileInfo> parseRarcFiles(std::span<const u8> arc) {
|
|||||||
: std::move(fname),
|
: std::move(fname),
|
||||||
static_cast<u32>(dataBase + entry.data_offset),
|
static_cast<u32>(dataBase + entry.data_offset),
|
||||||
entry.data_size,
|
entry.data_size,
|
||||||
|
(typeFlags & JKRARCHIVE_ATTR_COMPRESSION) != 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -662,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;
|
// arcBytes must point at the mounted archive bytes the game will later use;
|
||||||
// aurora's pointer lookups depend on those addresses staying valid.
|
// 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,
|
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;
|
ZoneScoped;
|
||||||
ZoneText(arcLabel.data(), arcLabel.size());
|
ZoneText(arcLabel.data(), arcLabel.size());
|
||||||
size_t bmdReg = 0;
|
size_t bmdReg = 0;
|
||||||
size_t btiReg = 0;
|
size_t btiReg = 0;
|
||||||
|
size_t deferred = 0;
|
||||||
|
|
||||||
// Phase A: per-slot textures inside BMD/BDL models.
|
// Phase A: per-slot textures inside BMD/BDL models.
|
||||||
for (const auto& f : files) {
|
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
|
// Phase B: standalone .bti files. Each BTI is its own arc entry; the
|
||||||
// game loads it via JUTTexture (or similar) which calls GXInitTexObj
|
// game loads it via JUTTexture (or similar) which calls GXInitTexObj
|
||||||
// with `(u8*)resTIMG + imageOffset`. Register that exact pointer.
|
// with `(u8*)resTIMG + imageOffset`. Register that exact pointer.
|
||||||
for (const auto& f : files) {
|
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;
|
++btiReg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HdLog.info("registerHdTextures[{}]: {} BMD-slot, {} standalone-BTI replacements",
|
HdLog.info("registerHdTextures[{}]: {} BMD-slot, {} standalone-BTI replacements, {} compressed deferred",
|
||||||
arcLabel, bmdReg, btiReg);
|
arcLabel, bmdReg, btiReg, deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HD arcs whose Wii-U layouts don't match the GC UI pipeline.
|
// HD arcs whose Wii-U layouts don't match the GC UI pipeline.
|
||||||
@@ -719,10 +738,24 @@ std::filesystem::path hd_pack_path_for_arc(std::string_view resPath) {
|
|||||||
return packPath;
|
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) {
|
bool should_skip_hd_arc_mount(std::string_view resPath) {
|
||||||
// Layout HD archives do not match the GC UI pipeline, but their pack.gz
|
return false;
|
||||||
// textures can still be registered against the vanilla archive.
|
|
||||||
|
// Layout arcs mount only via kHdLayoutMountList.
|
||||||
if (is_layout_arc_path(resPath)) {
|
if (is_layout_arc_path(resPath)) {
|
||||||
|
for (auto mount : kHdLayoutMountList) {
|
||||||
|
if (resPath == mount) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (auto skip : kHdSkipList) {
|
for (auto skip : kHdSkipList) {
|
||||||
@@ -735,6 +768,15 @@ bool should_register_hd_pack_for_vanilla_arc(std::string_view resPath) {
|
|||||||
return resPath.starts_with("res/Layout/");
|
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) {
|
void* overlay_open(void* userData) {
|
||||||
auto* entry = static_cast<HdOverlayEntry*>(userData);
|
auto* entry = static_cast<HdOverlayEntry*>(userData);
|
||||||
if (entry == nullptr) return nullptr;
|
if (entry == nullptr) return nullptr;
|
||||||
@@ -866,6 +908,7 @@ void rebuild_hd_overlay_locked() {
|
|||||||
entry.dvdPath = "/" + resPath;
|
entry.dvdPath = "/" + resPath;
|
||||||
entry.arcPath = arcPath;
|
entry.arcPath = arcPath;
|
||||||
entry.packPath = hd_pack_path_for_arc(resPath);
|
entry.packPath = hd_pack_path_for_arc(resPath);
|
||||||
|
entry.gcPackPath = fallback_path_for_arc(resPath, entry.packPath);
|
||||||
entry.size = *fileSize;
|
entry.size = *fileSize;
|
||||||
|
|
||||||
overlayFiles.push_back({
|
overlayFiles.push_back({
|
||||||
@@ -894,6 +937,50 @@ void rebuild_hd_overlay_locked() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void register_hd_particle_textures(std::string_view jpcStem, void* jpcBuffer, size_t jpcSize) {
|
||||||
|
if (g_contentPath.empty() || jpcBuffer == nullptr || jpcSize < 0x10) return;
|
||||||
|
auto* jpc = static_cast<u8*>(jpcBuffer);
|
||||||
|
if (std::memcmp(jpc, "JPAC2-10", 8) != 0) return;
|
||||||
|
|
||||||
|
const std::filesystem::path sidecar =
|
||||||
|
g_contentPath / "tex" / "Particle" / (std::string(jpcStem) + ".jpc.gtx.gz");
|
||||||
|
auto gz = read_file(sidecar);
|
||||||
|
if (!gz) return;
|
||||||
|
auto gfx2 = decompressGzip(*gz);
|
||||||
|
if (!gfx2) return;
|
||||||
|
auto surfaces = parseGtx(*gfx2);
|
||||||
|
if (surfaces.empty()) return;
|
||||||
|
|
||||||
|
// JPAC2-10 header: texture count @ +0x0A (BE u16), texture table @ +0x0C (BE u32).
|
||||||
|
const u32 texCnt = *reinterpret_cast<const BE(u16)*>(jpc + 0x0A);
|
||||||
|
const u32 texTableOff = *reinterpret_cast<const BE(u32)*>(jpc + 0x0C);
|
||||||
|
if (texCnt != surfaces.size()) {
|
||||||
|
HdLog.warn("HD particle {}: jpc texCnt {} != sidecar surfaces {} -> skip",
|
||||||
|
jpcStem, texCnt, surfaces.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t reg = 0;
|
||||||
|
u32 off = texTableOff;
|
||||||
|
for (u32 i = 0; i < texCnt && off + 0x40 <= jpcSize; ++i) {
|
||||||
|
const u32 entrySize = *reinterpret_cast<const BE(u32)*>(jpc + off + 4);
|
||||||
|
const char* texName = reinterpret_cast<const char*>(jpc + off + 0x0C);
|
||||||
|
const auto& s = surfaces[i];
|
||||||
|
const Gx2FormatMapping* m = s.baseData.empty() ? nullptr : findFormatMapping(s.format);
|
||||||
|
if (m != nullptr && std::strncmp(texName, "dummy", 5) != 0) {
|
||||||
|
auto* timg = reinterpret_cast<ResTIMG*>(jpc + off + 0x20);
|
||||||
|
const s32 stored = timg->imageOffset;
|
||||||
|
const s32 imgOff = stored ? stored : 0x20;
|
||||||
|
applyTimgAttributes(timg, s, imgOff);
|
||||||
|
const void* pixelPtr = reinterpret_cast<const u8*>(timg) + imgOff;
|
||||||
|
registerHdSurface(*m, s, pixelPtr, jpcStem, i, true);
|
||||||
|
++reg;
|
||||||
|
}
|
||||||
|
off += entrySize ? entrySize : 0x40;
|
||||||
|
}
|
||||||
|
HdLog.info("registerHdParticle[{}]: {}/{} textures registered", jpcStem, reg, texCnt);
|
||||||
|
}
|
||||||
|
|
||||||
void set_hd_content_path(std::filesystem::path contentPath) {
|
void set_hd_content_path(std::filesystem::path contentPath) {
|
||||||
g_contentPath = std::move(contentPath);
|
g_contentPath = std::move(contentPath);
|
||||||
std::lock_guard lk{g_cacheMutex};
|
std::lock_guard lk{g_cacheMutex};
|
||||||
@@ -940,9 +1027,13 @@ std::optional<std::vector<u8>*> try_load_hd_archive(std::string_view gcPath) {
|
|||||||
auto hdFiles = parseRarcFiles(std::span<const u8>(
|
auto hdFiles = parseRarcFiles(std::span<const u8>(
|
||||||
hdBytesOpt->data(), hdBytesOpt->size()));
|
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 hdPackPath = hd_pack_path_for_arc(resPath);
|
||||||
auto hdPack = load_pack_cached(hdPackPath);
|
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::list keeps element addresses stable for aurora's pointer map.
|
||||||
std::vector<u8>* mountBytes;
|
std::vector<u8>* mountBytes;
|
||||||
@@ -959,22 +1050,25 @@ std::optional<std::vector<u8>*> try_load_hd_archive(std::string_view gcPath) {
|
|||||||
mountBytes->size(), hdPack ? "yes" : "no");
|
mountBytes->size(), hdPack ? "yes" : "no");
|
||||||
|
|
||||||
if (hdPack != nullptr) {
|
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;
|
return mountBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_mounted_hd_archive(s32 entryNum, void* arcBytes, size_t arcSize) {
|
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;
|
if (entryNum < 0 || arcBytes == nullptr || arcSize == 0) return;
|
||||||
|
|
||||||
std::filesystem::path packPath;
|
std::filesystem::path packPath;
|
||||||
|
std::filesystem::path gcPackPath;
|
||||||
std::string label;
|
std::string label;
|
||||||
{
|
{
|
||||||
std::lock_guard lk{g_cacheMutex};
|
std::lock_guard lk{g_cacheMutex};
|
||||||
auto it = g_entryNumToOverlay().find(entryNum);
|
auto it = g_entryNumToOverlay().find(entryNum);
|
||||||
if (it == g_entryNumToOverlay().end()) return;
|
if (it == g_entryNumToOverlay().end()) return;
|
||||||
packPath = it->second->packPath;
|
packPath = it->second->packPath;
|
||||||
|
gcPackPath = it->second->gcPackPath;
|
||||||
label = it->second->arcPath.filename().string();
|
label = it->second->arcPath.filename().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,13 +1082,18 @@ void register_mounted_hd_archive(s32 entryNum, void* arcBytes, size_t arcSize) {
|
|||||||
if (hdPack == nullptr) {
|
if (hdPack == nullptr) {
|
||||||
return;
|
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()));
|
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,
|
void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, void* buffer,
|
||||||
size_t resourceSize) {
|
size_t resourceSize) {
|
||||||
|
if (g_contentPath.empty()) return;
|
||||||
if (entryNum < 0 || buffer == nullptr || resourceSize < 0x20) return;
|
if (entryNum < 0 || buffer == nullptr || resourceSize < 0x20) return;
|
||||||
|
|
||||||
const bool isBti = endsWithSuffixCI(resourceName, ".bti");
|
const bool isBti = endsWithSuffixCI(resourceName, ".bti");
|
||||||
@@ -1003,6 +1102,7 @@ void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, vo
|
|||||||
if (!isBti && !isBmd) return;
|
if (!isBti && !isBmd) return;
|
||||||
|
|
||||||
std::filesystem::path packPath;
|
std::filesystem::path packPath;
|
||||||
|
std::filesystem::path gcPackPath;
|
||||||
{
|
{
|
||||||
std::lock_guard lk{g_cacheMutex};
|
std::lock_guard lk{g_cacheMutex};
|
||||||
auto it = g_entryNumToOverlay().find(entryNum);
|
auto it = g_entryNumToOverlay().find(entryNum);
|
||||||
@@ -1010,17 +1110,22 @@ void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, vo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
packPath = it->second->packPath;
|
packPath = it->second->packPath;
|
||||||
|
gcPackPath = it->second->gcPackPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hdPack = load_pack_cached(packPath);
|
auto hdPack = load_pack_cached(packPath);
|
||||||
if (hdPack == nullptr) {
|
if (hdPack == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<const TphdPack> gcPack;
|
||||||
|
if (!gcPackPath.empty()) {
|
||||||
|
gcPack = load_pack_cached(gcPackPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (isBti) {
|
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 {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ void register_mounted_hd_archive(s32 entryNum, void* arcBytes, size_t arcSize);
|
|||||||
void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, void* buffer,
|
void register_copied_hd_resource(s32 entryNum, std::string_view resourceName, void* buffer,
|
||||||
size_t resourceSize);
|
size_t resourceSize);
|
||||||
|
|
||||||
|
// Register HD particle textures which live in a loose `tex/Particle/<jpcStem>.jpc.gtx.gz` sidecar
|
||||||
|
void register_hd_particle_textures(std::string_view jpcStem, void* jpcBuffer, size_t jpcSize);
|
||||||
|
|
||||||
// Returns bytes remaining in a registered HD archive range that contains ptr.
|
// Returns bytes remaining in a registered HD archive range that contains ptr.
|
||||||
// Used for debug heap accounting because some HD buffers are not JKR-owned.
|
// Used for debug heap accounting because some HD buffers are not JKR-owned.
|
||||||
std::optional<size_t> find_registered_hd_archive_remaining(const void* ptr);
|
std::optional<size_t> find_registered_hd_archive_remaining(const void* ptr);
|
||||||
|
|||||||
Reference in New Issue
Block a user