diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/DynamicLink.cpp | 2 | ||||
| -rw-r--r-- | src/d/d_dylink.cpp | 319 | ||||
| -rw-r--r-- | src/m/m_dvd.cpp | 8 | ||||
| -rw-r--r-- | src/m/m_heap.cpp | 4 |
4 files changed, 315 insertions, 18 deletions
diff --git a/src/DynamicLink.cpp b/src/DynamicLink.cpp index 2c7947c0..526e12ce 100644 --- a/src/DynamicLink.cpp +++ b/src/DynamicLink.cpp @@ -129,7 +129,7 @@ void DynamicModuleControl::initialize(EGG::ExpHeap *heap) { sTotalFreeSize = heap->getTotalFreeSize(); } -bool DynamicModuleControl::callback(void *arg) { +u32 DynamicModuleControl::callback(void *arg) { return static_cast<DynamicModuleControl *>(arg)->do_load(); } diff --git a/src/d/d_dylink.cpp b/src/d/d_dylink.cpp index 3bec3f39..6ab8cbdb 100644 --- a/src/d/d_dylink.cpp +++ b/src/d/d_dylink.cpp @@ -1,8 +1,10 @@ -#include <common.h> #include <DynamicLink.h> +#include <common.h> #include <d/d_dylink.h> #include <m/m_dvd.h> #include <m/m_heap.h> +#include <rvl/DVD.h> +#include <rvl/NAND.h> // clang-format off #include <MSL_C/string.h> // clang-format on @@ -12,9 +14,13 @@ struct RelNamePtr { const char *name; }; -static RelNamePtr *pDynamicNameTable; +extern const RelNamePtr DYNAMIC_NAME_TABLE[]; + +static const RelNamePtr *pDynamicNameTable; static int nDynamicNameTable; +DynamicModuleControl g_DMC("d_profile", nullptr); + namespace dDyl { DynamicModuleControl **pDMC; @@ -23,8 +29,21 @@ EGG::Heap *cCc_frmHeap; u32 Initialized; mDvd_callback_c *DVD; -// initDylinkHeap -extern "C" int fn_80052E00(int maxRelId, RelNamePtr *dynNameTable, int dynNameTableNum, EGG::Heap *parentHeap) { +enum NandChunkStatus { + Success = 1, + DVDPending = 2, + NANDPending = 4, + Error = 8, + + FinalizedMask = Success | Error, +}; + +NandChunkStatus status_1 = Success; +NandChunkStatus status_2 = Success; +char relsDir[] = "/rels"; + +/** 80052E00 */ +int initDylinkHeap(int maxRelId, const RelNamePtr *dynNameTable, int dynNameTableNum, EGG::Heap *parentHeap) { cCc_frmHeap = mHeap::createFrmHeap(maxRelId * 0x10 + dynNameTableNum * 0x48, parentHeap, "ダイナミックリンク制御用ヒープ(dDyl::cCc_frmHeap)", 0x20, 0); @@ -38,7 +57,7 @@ extern "C" int fn_80052E00(int maxRelId, RelNamePtr *dynNameTable, int dynNameTa nDynamicNameTable = dynNameTableNum; for (int i = 0; i < nDynamicNameTable; i++) { - RelNamePtr *rel = &pDynamicNameTable[i]; + const RelNamePtr *rel = &pDynamicNameTable[i]; if (rel->name != nullptr) { for (int j = 0; j < nDMC; j++) { DynamicModuleControl **dmc = &pDMC[j]; @@ -59,12 +78,12 @@ extern "C" int fn_80052E00(int maxRelId, RelNamePtr *dynNameTable, int dynNameTa return 1; } -// isLoaded? -extern "C" bool fn_80052FA0(u16 relId) { +/** 80052FA0 */ +bool isLinked(u16 relId) { return pDMC[relId] != nullptr ? pDMC[relId]->isLinked() : true; } -bool dDyl::Unlink(u16 relId) { +BOOL Unlink(u16 relId) { DynamicModuleControl *dmc = pDMC[relId]; if (dmc != nullptr) { return dmc->unlink(); @@ -73,7 +92,7 @@ bool dDyl::Unlink(u16 relId) { return false; } -extern "C" int fn_80052FF0(u16 relId) { +int tryLink(u16 relId) { if (!Initialized) { return 0; } @@ -81,8 +100,7 @@ extern "C" int fn_80052FF0(u16 relId) { DynamicModuleControl *dmc = pDMC[relId]; if (dmc != nullptr) { if (dmc->load_async()) { - // what is going on here? - return (bool)dmc->link() + 2; + return dmc->link() ? 1 : 2; } else { return 0; } @@ -91,4 +109,283 @@ extern "C" int fn_80052FF0(u16 relId) { } } +// TODO prevent moves +struct UnkNandStruct { + /* 0x00 */ NandChunkStatus status; + /* 0x04 */ OSThreadQueue queue; + /* 0x0C */ DVDFileInfo dvdFileInfo; + /* 0x48 */ NANDFileInfo *nandFileInfo; + /* 0x4C */ NANDCommandBlock nandBlock; + + UnkNandStruct() { + status = status_2; + OSInitThreadQueue(&queue); + nandFileInfo = nullptr; + updateStatus(status_1); + nandFileInfo = nullptr; + dvdFileInfo.block.userData = this; + NANDSetUserData(&nandBlock, this); + } + + void waitForFinalization() { + if (!isFinalized()) { + BOOL enabled = OSDisableInterrupts(); + while (!isFinalized()) { + OSSleepThread(&queue); + } + OSRestoreInterrupts(enabled); + } + } + + bool isFinalized() { + return (status & FinalizedMask) != 0; + } + + void updateStatus(NandChunkStatus newStatus) { + if (status != newStatus) { + status = newStatus; + OSWakeupThread(&queue); + } + } + + void open(s32 entryNum, NANDFileInfo *fInfo) { + DVDFastOpen(entryNum, &dvdFileInfo); + nandFileInfo = fInfo; + } + + bool isError() { + return (status & Error) != 0; + } +}; + +void nandCallback(s32 result, NANDCommandBlock *block) { + UnkNandStruct *s = reinterpret_cast<UnkNandStruct *>(NANDGetUserData(block)); + NandChunkStatus status = result >= 0 ? Success : Error; + s->updateStatus(status); +} + +void dvdCallback(s32 result, DVDFileInfo *dvdInfo) { + NandChunkStatus status = Error; + UnkNandStruct *s = reinterpret_cast<UnkNandStruct *>(dvdInfo->block.userData); + u32 length = dvdInfo->block.length; + if (result == length) { + if (!NANDWriteAsync(s->nandFileInfo, dvdInfo->block.addr, length, nandCallback, &s->nandBlock)) { + status = NANDPending; + } + } + s->updateStatus(status); +} + +bool startDvdRead(UnkNandStruct *s, void *dst, s32 size, s32 offset) { + s->waitForFinalization(); + + if ((s->status & 8) != 0) { + return false; + } + + BOOL dvdResult = DVDReadAsyncPrio(&s->dvdFileInfo, dst, size, offset, dvdCallback, 2); + NandChunkStatus status = dvdResult != 0 ? DVDPending : Error; + s->updateStatus(status); + return dvdResult; +} + +extern "C" void fn_802DEFE0(const char *fmt, ...); + +extern "C" bool fn_80053240(char *relPath, char *tmpRelPath, EGG::Heap *heap) { + DVDFileInfo dvdFileInfo; + NANDFileInfo nandFileInfo; + + s32 entryNum = DVDConvertPathToEntrynum(relPath); + if (entryNum < 0 || !DVDFastOpen(entryNum, &dvdFileInfo)) { + goto err; + } + + u32 sizeRead = 0; + + u32 fileSize = ROUND_UP(dvdFileInfo.size, 0x20); + NANDResult deleteResult = NANDDelete(tmpRelPath); + if (deleteResult != NAND_RESULT_OK && deleteResult != NAND_RESULT_NOEXISTS) { + goto end; + } + + if (NANDCreate(tmpRelPath, NAND_PERM_RUSR | NAND_PERM_WUSR, 0) || NANDOpen(tmpRelPath, &nandFileInfo, 2)) { + goto end; + } + + fn_802DEFE0("BufferSize %u KB\n", 0x200); + + void *buf = heap->alloc(0x80000, 0x20); + if (buf == nullptr) { + goto end; + } + + // Weird part start (everything above this is regshuffles) + + // TODO the initialization of this value is a bit + // weird because it essentially computes the signed + // division 0x80000 / 2 + // (which adds +1 to the dividend if the result is negative to + // ensure rounding to 0 behavior) + // and the optimizer fails to constant propagate these through + // to realize that 0x80000 is actually always positive + int totalBufSize = 0x80000; + int chunkSize = 0x80000 / 2; + + // TODO this initalization happens slightly differently + UnkNandStruct nandStructs[2]; + + // Weird part end (most below this is regshuffles) + + nandStructs[0].open(entryNum, &nandFileInfo); + nandStructs[1].open(entryNum, &nandFileInfo); + + // dispatch multiple requests to load parts of the profile REL in parallel + int whichThread = 0; + for (; sizeRead < fileSize; sizeRead += chunkSize) { + if (chunkSize > fileSize - sizeRead) { + chunkSize = fileSize - sizeRead; + } + // NB the whichThread * totalBufSize / 2 match relies on whichThread * totalBufSize being able to overflow + if (!startDvdRead(&nandStructs[whichThread], ((u8 *)buf) + whichThread * totalBufSize / 2, chunkSize, + /* offset */ sizeRead)) { + break; + } + + whichThread ^= 1; + } + + nandStructs[0].waitForFinalization(); + nandStructs[1].waitForFinalization(); + + if (nandStructs[0].isError() || nandStructs[1].isError()) { + sizeRead = 0; + } + + // Yes this condition is extremely weird but in the binary + // this literally checks if a stack address isn't zero + // There's also a bit of a weird addressing going on here + if ((u32)&nandStructs[1]) { + nandStructs[1].waitForFinalization(); + } + nandStructs[0].waitForFinalization(); + + heap->free(buf); + if (NANDClose(&nandFileInfo) != NAND_RESULT_OK) { + sizeRead = 0; + } + +end: + DVDClose(&dvdFileInfo); + return sizeRead >= fileSize; + +err: + return false; +} + +u32 loadRelsArcCallback(void *arg) { + EGG::Archive *arc = nullptr; + EGG::Heap *heap = DynamicModuleControl::sDylinkHeap; + + char rels[] = "/rels.arc"; + char tmpRels[] = "/tmp/rels.arc"; + + if (fn_80053240(rels, tmpRels, heap)) { + arc = EGG::Archive::loadFST(tmpRels, heap, 0x20); + } + if (arc != nullptr) { + DynamicModuleControl::sArchive = arc; + } + return (u32)arc; +} + +u32 dvdCallback(void *arg) { + DynamicModuleControl::initialize(mHeap::g_dylinkHeap); + DynamicModuleControl::sRelsDir = relsDir; + DVDFileInfo info; + + if (DVDOpen("/RVZELDANP.str", &info)) { + if (info.size != 0) { + u32 size = ROUND_UP(info.size, 0x20); + void *destination = DynamicModuleControl::sDylinkHeap->alloc(size, 0x20); + DVDReadPrio(&info, destination, size, 0, 2); + OSSetStringTable(destination); + } + DVDClose(&info); + } + + BOOL result = g_DMC.link(); + do { + } while (!result); + Initialized = 1; + return true; +} + +void initModule() { + initDylinkHeap(0x2bf, DYNAMIC_NAME_TABLE, 0x27c, mHeap::g_dylinkHeap); + DVD = mDvd_callback_c::createOrFail(dvdCallback, mHeap::g_dylinkHeap); +} + +void initRelsArc() { + DVD = mDvd_callback_c::createOrFail(loadRelsArcCallback, nullptr); +} + +bool destroy() { + if (DVD == nullptr) { + return true; + } + + if (DVD->mStatus != 0) { + DVD->do_delete(); + DVD = nullptr; + return true; + } + + return false; +} + } // namespace dDyl + +dDynamicModuleControl::~dDynamicModuleControl() { + if (mPtr != nullptr) { + do_unlink(); + } +} + +void dDynamicModuleControl::set(u16 *ptr, int count) { + mPtr = ptr; + mCount = count; +} + +BOOL dDynamicModuleControl::do_link() const { + if (mPtr == nullptr) { + return true; + } + bool result = true; + u16 *rel = mPtr; + for (int i = 0; i < mCount; i++) { + bool linked = dDyl::isLinked(*rel); + if (!linked && dDyl::tryLink(*rel) != 1) { + result = false; + goto end; + } + rel++; + } +end: + return result; +} + +BOOL dDynamicModuleControl::do_unlink() { + u16 *rel = mPtr; + if (rel == nullptr) { + return true; + } + for (int i = 0; i < mCount; i++) { + bool linked = dDyl::isLinked(*rel); + if (linked && dDyl::Unlink(*rel) == 0) { + return false; + } + rel++; + } + mPtr = nullptr; + return true; +} diff --git a/src/m/m_dvd.cpp b/src/m/m_dvd.cpp index 82fce847..21fed247 100644 --- a/src/m/m_dvd.cpp +++ b/src/m/m_dvd.cpp @@ -330,7 +330,7 @@ mDvd_callback_c::~mDvd_callback_c() {} mDvd_callback_c::mDvd_callback_c(dvdReadCallback cb, void *cbData) { mCallback = cb; mCallbackData = cbData; - mSuccess = false; + mCallbackResult = 0; } /* 802ef5d0 */ @@ -343,7 +343,7 @@ mDvd_callback_c *mDvd_callback_c::create(dvdReadCallback cb, void *cbData) { } /** 802ef650 */ -extern "C" mDvd_callback_c *fn_802EF650(dvdReadCallback cb, void *cbData) { +mDvd_callback_c *mDvd_callback_c::createOrFail(dvdReadCallback cb, void *cbData) { mDvd_callback_c *cmd = mDvd_callback_c::create(cb, cbData); while (!cmd) {} return cmd; @@ -351,9 +351,9 @@ extern "C" mDvd_callback_c *fn_802EF650(dvdReadCallback cb, void *cbData) { /** 802ef680 */ u32 mDvd_callback_c::execute() { - mSuccess = (mCallback)(mCallbackData); + mCallbackResult = (mCallback)(mCallbackData); waitDone(); - return (bool)mSuccess; + return (bool)mCallbackResult; } /** 802ef6e0 */ diff --git a/src/m/m_heap.cpp b/src/m/m_heap.cpp index 6ab9cc12..ca18857f 100644 --- a/src/m/m_heap.cpp +++ b/src/m/m_heap.cpp @@ -11,7 +11,7 @@ EGG::Heap *mHeap::g_gameHeaps[4]; EGG::Heap *mHeap::s_SavedCurrentHeap; EGG::Heap *mHeap::g_archiveHeap; EGG::Heap *mHeap::g_commandHeap; -EGG::Heap *mHeap::g_dylinkHeap; +EGG::ExpHeap *mHeap::g_dylinkHeap; EGG::AssertHeap *mHeap::g_assertHeap; const char *const mHeap::s_GameHeapNames[4] = { 0, @@ -144,7 +144,7 @@ mHeap::~mHeap() { heap->becomeCurrentHeap(); } -EGG::Heap *mHeap::createHeap(size_t size, EGG::Heap *block, const char *name) { +EGG::ExpHeap *mHeap::createHeap(size_t size, EGG::Heap *block, const char *name) { EGG::ExpHeap *heap = EGG::ExpHeap::create(size, block, 4); if (heap != nullptr) { heap->setGroupID(0); |
