d_dylink for the most part

This commit is contained in:
robojumper
2024-05-26 22:55:38 +02:00
parent 71f3416cb5
commit e6df362f88
10 changed files with 364 additions and 55 deletions
+308 -11
View File
@@ -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;
}