From b5348a2e425f713258d45d2b74207e692ab025de Mon Sep 17 00:00:00 2001 From: shibbo Date: Tue, 29 Oct 2024 21:03:48 -0400 Subject: [PATCH] start some work on `RVLFaceLib` --- configure.py | 11 +- src/RVLFaceLib/RFL_Controller.c | 346 ++++++++ src/RVLFaceLib/RFL_DataUtility.c | 299 +++++++ src/RVLFaceLib/RFL_Database.c | 805 ++++++++++++++++++ src/RVLFaceLib/RFL_DefaultDatabase.c | 55 ++ src/RVLFaceLib/RFL_Format.c | 37 + src/RVLFaceLib/RFL_HiddenDatabase.c | 509 ++++++++++++ src/RVLFaceLib/RFL_Icon.c | 157 ++++ src/RVLFaceLib/RFL_MakeRandomFace.c | 508 ++++++++++++ src/RVLFaceLib/RFL_MakeTex.c | 914 +++++++++++++++++++++ src/RVLFaceLib/RFL_MiddleDatabase.c | 655 +++++++++++++++ src/RVLFaceLib/RFL_Model.c | 1135 ++++++++++++++++++++++++++ src/RVLFaceLib/RFL_NANDAccess.c | 1135 ++++++++++++++++++++++++++ src/RVLFaceLib/RFL_NANDLoader.c | 438 ++++++++++ src/RVLFaceLib/RFL_System.c | 275 +++++++ 15 files changed, 7274 insertions(+), 5 deletions(-) create mode 100644 src/RVLFaceLib/RFL_Controller.c create mode 100644 src/RVLFaceLib/RFL_DataUtility.c create mode 100644 src/RVLFaceLib/RFL_Database.c create mode 100644 src/RVLFaceLib/RFL_DefaultDatabase.c create mode 100644 src/RVLFaceLib/RFL_Format.c create mode 100644 src/RVLFaceLib/RFL_HiddenDatabase.c create mode 100644 src/RVLFaceLib/RFL_Icon.c create mode 100644 src/RVLFaceLib/RFL_MakeRandomFace.c create mode 100644 src/RVLFaceLib/RFL_MakeTex.c create mode 100644 src/RVLFaceLib/RFL_MiddleDatabase.c create mode 100644 src/RVLFaceLib/RFL_Model.c create mode 100644 src/RVLFaceLib/RFL_NANDAccess.c create mode 100644 src/RVLFaceLib/RFL_NANDLoader.c create mode 100644 src/RVLFaceLib/RFL_System.c diff --git a/configure.py b/configure.py index 38d96487..74cc0bc0 100644 --- a/configure.py +++ b/configure.py @@ -311,6 +311,7 @@ "-i libs/MetroTRK/include", "-i libs/RVL_SDK/include", "-i libs/Runtime/include", + "-i libs/RVLFaceLib/include", f"-i build/{config.version}/include", f"-DVERSION={version_num}", ] @@ -2759,11 +2760,11 @@ def JSysLib(lib_name: str, objects: List[Object]) -> Dict[str, Any]: Object(NonMatching, "RVLFaceLib/RFL_MakeTex.c"), Object(NonMatching, "RVLFaceLib/RFL_Icon.c"), Object(NonMatching, "RVLFaceLib/RFL_HiddenDatabase.c"), - Object(NonMatching, "RVLFaceLib/RFL_Database.c"), - Object(NonMatching, "RVLFaceLib/RFL_Controller.c"), - Object(NonMatching, "RVLFaceLib/RFL_MiddleDatabase.c"), - Object(NonMatching, "RVLFaceLib/RFL_DefaultDatabase.c"), - Object(NonMatching, "RVLFaceLib/RFL_DataUtility.c"), + Object(Matching, "RVLFaceLib/RFL_Database.c"), + Object(Matching, "RVLFaceLib/RFL_Controller.c"), + Object(Matching, "RVLFaceLib/RFL_MiddleDatabase.c"), + Object(Matching, "RVLFaceLib/RFL_DefaultDatabase.c"), + Object(Matching, "RVLFaceLib/RFL_DataUtility.c"), Object(NonMatching, "RVLFaceLib/RFL_Format.c") ] ), diff --git a/src/RVLFaceLib/RFL_Controller.c b/src/RVLFaceLib/RFL_Controller.c new file mode 100644 index 00000000..dd82abb6 --- /dev/null +++ b/src/RVLFaceLib/RFL_Controller.c @@ -0,0 +1,346 @@ +#include +#include +#include + +#define MAGIC_CONTROLLER_DATA 'RNCD' +#define DB_REMOTE_MEM_ADDR 0 +#define READ_RETRY_MAX 3 + +static void readcallback_(s32 chan, s32 result); + +static void clearDatabase_(RFLiCtrlBuf* buf) { + int i; + + buf->identifier = MAGIC_CONTROLLER_DATA; + buf->secretFlag = 0; + buf->deleted = 0; + buf->checksum = 0; + + for (i = 0; i < RFL_CTRL_CHAR_MAX; i++) { + memset(&buf->data[i], 0, sizeof(RFLiCharData)); + } +} + +void RFLiInitCtrlBuf(MEMiHeapHead* heap) { + RFLiCtrlBufManager* mgr; + void* buffer; + int i; + + mgr = RFLiGetCtrlBufManager(); + + for (i = 0; i < 4; i++) { + mgr->buffer[i] = MEMAllocFromExpHeapEx(heap, sizeof(RFLiCtrlBuf), 32); + clearDatabase_(mgr->buffer[i]); + mgr->loaded[i] = FALSE; + } + + mgr->replaceBuf[0] = MEMAllocFromExpHeapEx(heap, sizeof(RFLiCtrlBuf), 32); + clearDatabase_(mgr->replaceBuf[0]); + + mgr->replaceBuf[1] = MEMAllocFromExpHeapEx(heap, sizeof(RFLiCtrlBuf), 32); + clearDatabase_(mgr->replaceBuf[1]); + + mgr->readBuffer = NULL; + mgr->writeBuffer = NULL; + mgr->writeChan = -1; + mgr->read = TRUE; + mgr->verifyBuffer = NULL; + + buffer = MEMAllocFromExpHeapEx( + heap, RFLGetMiddleDBBufferSize(RFL_CTRL_CHAR_MAX), 32); + + RFLInitMiddleDB(&mgr->hiddenMDB, RFLMiddleDBType_HiddenRandom, buffer, + RFL_CTRL_CHAR_MAX); +} + +static BOOL RFLiCheckCtrlBufferCore(const RFLiCtrlBuf* buf, u8 index, + RFLiHiddenType type) { + u16 mask; + + mask = 1 << index; + + if (type == RFLiHiddenType_No) { + if (buf->secretFlag & mask) { + return FALSE; + } + } else if (type == RFLiHiddenType_Yes) { + if (!(buf->secretFlag & mask)) { + return FALSE; + } + } + + return RFLiIsValidID(&buf->data[index].createID); +} + +static void validBufferFound_(RFLiCtrlBuf* buf, s32 chan) { + RFLiCtrlBufManager* mgr; + RFLiCharInfo info; + int i; + + mgr = RFLiGetCtrlBufManager(); + memcpy(mgr->buffer[chan], buf, sizeof(RFLiCtrlBuf)); + + for (i = 0; i < RFL_CTRL_CHAR_MAX; i++) { + if (RFLiIsValidID(&mgr->buffer[chan]->data[i].createID)) { + RFLiConvertRaw2Info(&mgr->buffer[chan]->data[i], &info); + + if (!RFLiCheckValidInfo(&info) || !RFLiIsValidOnNAND(&info)) { + memset(&mgr->buffer[chan]->data[i], 0, sizeof(RFLiCharData)); + } + } + } + + mgr->loaded[chan] = TRUE; + + RFLiFree(mgr->tempBuffer); + mgr->tempBuffer = NULL; + + if (RFLiGetIsolation()) { + RFLiEndWorking(RFLErrcode_Success); + } else { + RFLiWriteCtrlToHiddenDB(mgr->buffer[chan], + RFLiGetCtrlBufManager()->readIsChMode); + } +} + +static BOOL checkCRC_(const RFLiCtrlBuf* buf) { + if (buf->identifier == MAGIC_CONTROLLER_DATA) { + return RFLiCalculateCRC(buf, sizeof(RFLiCtrlBuf)) == 0; + } + + return FALSE; +} + +static BOOL errorAndReRead_(s32 chan) { + RFLiCtrlBufManager* mgr; + BOOL retried; + s32 reason; + + mgr = RFLiGetCtrlBufManager(); + retried = FALSE; + + if (++mgr->retry < READ_RETRY_MAX) { + reason = + WPADReadFaceData(chan, mgr->tempBuffer, + sizeof(RFLiCtrlBuf) * RFLi_CTRL_REPLACE_BUF_NUM, + DB_REMOTE_MEM_ADDR, readcallback_); + if (reason != WPAD_RESULT_SUCCESS) { + RFLiEndWorkingReason(RFLErrcode_Controllerfail, reason); + } + + retried = TRUE; + } + + return retried; +} + +static void clearDeleted_(RFLiCtrlBuf* buf) { + int i; + u16 mask; + + for (i = 0; i < RFL_CTRL_CHAR_MAX; i++) { + mask = 1 << i; + if (buf->deleted & mask) { + memset(&buf->data[i], 0, sizeof(RFLiCharData)); + } + } +} + +static void readcallback_(s32 chan, s32 result) { + RFLiCtrlBufManager* mgr; + RFLiCtrlBuf* buf; + RFLiCtrlBuf* validBuf; + + mgr = RFLiGetCtrlBufManager(); + + switch (result) { + case WPAD_RESULT_SUCCESS: + validBuf = NULL; + buf = mgr->tempBuffer; + + // First block + if (checkCRC_(buf)) { + clearDeleted_(buf); + memcpy(mgr->replaceBuf[0], buf, sizeof(RFLiCtrlBuf)); + + if (validBuf == NULL) { + validBuf = buf; + } + } + + buf++; + + // Second block + if (checkCRC_(buf)) { + clearDeleted_(buf); + memcpy(mgr->replaceBuf[1], buf, sizeof(RFLiCtrlBuf)); + + if (validBuf == NULL) { + validBuf = buf; + } + } + + if (validBuf != NULL) { + validBufferFound_(validBuf, chan); + } else if (!errorAndReRead_(chan)) { + RFLiFree(mgr->tempBuffer); + mgr->tempBuffer = NULL; + + RFLiEndWorking(RFLErrcode_Loadfail); + mgr->loaded[chan] = FALSE; + } + break; + case WPAD_RESULT_ERR_3: + if (!errorAndReRead_(chan)) { + RFLiFree(mgr->tempBuffer); + mgr->tempBuffer = NULL; + + RFLiEndWorkingReason(RFLErrcode_Controllerfail, WPAD_RESULT_ERR_3); + mgr->loaded[chan] = FALSE; + } + break; + default: + RFLiFree(mgr->tempBuffer); + mgr->tempBuffer = NULL; + RFLiEndWorkingReason(RFLErrcode_Controllerfail, WPAD_RESULT_ERR_1); + break; + } +} + +static void readbuffer_(s32 chan, RFLiCtrlBuf* dst, BOOL ch) { + RFLiCtrlBufManager* mgr; + RFLiCtrlBuf* buf; + s32 reason; + u32 type; + + mgr = RFLiGetCtrlBufManager(); + + reason = WPADProbe(chan, &type); + if (reason != WPAD_RESULT_SUCCESS) { + RFLiEndWorkingReason(RFLErrcode_Controllerfail, reason); + return; + } + + buf = RFLiAlloc32(sizeof(RFLiCtrlBuf) * RFLi_CTRL_REPLACE_BUF_NUM); + mgr->tempBuffer = buf; + mgr->readBuffer = dst; + mgr->readIsChMode = ch; + mgr->retry = FALSE; + + reason = WPADReadFaceData(chan, buf, + sizeof(RFLiCtrlBuf) * RFLi_CTRL_REPLACE_BUF_NUM, + DB_REMOTE_MEM_ADDR, readcallback_); + if (reason != WPAD_RESULT_SUCCESS) { + RFLiFree(mgr->tempBuffer); + mgr->tempBuffer = NULL; + RFLiEndWorkingReason(RFLErrcode_Controllerfail, reason); + } +} + +static RFLErrcode RFLiLoadControllerAsync(s32 chan, BOOL ch) { + RFLiCtrlBufManager* mgr; + + if (chan < 0 || chan >= 4) { + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + if (RFLiIsWorking()) { + return RFLErrcode_NotAvailable; + } + + mgr = RFLiGetCtrlBufManager(); + if (mgr == NULL) { + return RFLErrcode_NotAvailable; + } + + RFLiStartWorking(); + memset(mgr->buffer[chan], 0, sizeof(RFLiCtrlBuf)); + memset(mgr->replaceBuf[0], 0, sizeof(RFLiCtrlBuf)); + memset(mgr->replaceBuf[1], 0, sizeof(RFLiCtrlBuf)); + readbuffer_(chan, mgr->buffer[chan], ch); + return RFLGetAsyncStatus(); +} + +static RFLErrcode RFLLoadControllerAsync(s32 chan) { + return RFLiLoadControllerAsync(chan, FALSE); +} + +BOOL RFLiGetControllerData(RFLiCharInfo* info, s32 chan, u16 index, + BOOL allowHidden) { + RFLiCtrlBufManager* mgr; + RFLiCtrlBuf* buf; + u16 mask; + + if (chan < 0 || chan >= 4) { + return FALSE; + } + + if (index >= RFL_CTRL_CHAR_MAX) { + return FALSE; + } + + mgr = RFLiGetCtrlBufManager(); + if (mgr == NULL) { + return FALSE; + } + + if (!mgr->loaded[chan]) { + return FALSE; + } + + buf = mgr->buffer[chan]; + mask = 1 << index; + + if (!RFLiIsValidID(&buf->data[index].createID)) { + return FALSE; + } + + if (!allowHidden && (buf->secretFlag & mask)) { + return FALSE; + } + + RFLiConvertRaw2Info(&buf->data[index], info); + return TRUE; +} + +static BOOL RFLIsAvailableControllerData(s32 chan, u16 index) { + RFLiCtrlBufManager* mgr; + RFLiCtrlBuf* buf; + u16 mask; + + if (chan < 0 || chan >= 4) { + // @bug Will be interpreted as TRUE + return RFLErrcode_WrongParam; + } + + if (index >= RFL_CTRL_CHAR_MAX) { + // @bug Will be interpreted as TRUE + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return FALSE; + } + + mgr = RFLiGetCtrlBufManager(); + if (mgr == NULL) { + return FALSE; + } + + if (!mgr->loaded[chan]) { + return FALSE; + } + + buf = mgr->buffer[chan]; + mask = 1 << index; + + if (buf->secretFlag & mask) { + return FALSE; + } + + return RFLiIsValidID(&buf->data[index].createID); +} diff --git a/src/RVLFaceLib/RFL_DataUtility.c b/src/RVLFaceLib/RFL_DataUtility.c new file mode 100644 index 00000000..ad6b128d --- /dev/null +++ b/src/RVLFaceLib/RFL_DataUtility.c @@ -0,0 +1,299 @@ +#include +#include + +// For masking out padding when comparing fields +#define PADMASK(t, size) ((t)(~((1 << size) - 1))) +// Padding bitmask constants (see RFLiCharInfo structure) +#define FACELINE_PAD_MASK (PADMASK(u16, 6)) +#define HAIR_PAD_MASK (PADMASK(u16, 5)) +#define EYE_PAD_MASK (PADMASK(u32, 5)) +#define EYEBROW_PAD_MASK (PADMASK(u32, 6)) +#define NOSE_PAD_MASK (PADMASK(u16, 3)) +#define MOUTH_PAD_MASK (PADMASK(u16, 0)) +#define BEARD_PAD_MASK (PADMASK(u16, 0)) +#define GLASS_PAD_MASK (PADMASK(u16, 1)) +#define MOLE_PAD_MASK (PADMASK(u16, 1)) + +BOOL RFLiCheckValidInfo(const RFLiCharInfo* info) { + if (info->faceline.type > RFLi_MAX_FACELINE_TYPE) { + return FALSE; + } + if (info->faceline.color > RFLi_MAX_FACELINE_COLOR) { + return FALSE; + } + if (info->faceline.texture > RFLi_MAX_FACELINE_TEXTURE) { + return FALSE; + } + + if (info->hair.type > RFLi_MAX_HAIR_TYPE) { + return FALSE; + } + if (info->hair.color > RFLi_MAX_HAIR_COLOR) { + return FALSE; + } + if (info->hair.flip > TRUE) { + return FALSE; + } + + if (info->eye.type > RFLi_MAX_EYE_TYPE) { + return FALSE; + } + if (info->eye.color > RFLi_MAX_EYE_COLOR) { + return FALSE; + } + if (info->eye.scale > RFLi_MAX_EYE_SCALE) { + return FALSE; + } + if (info->eye.rotate > RFLi_MAX_EYE_ROTATE) { + return FALSE; + } + if (info->eye.x > RFLi_MAX_EYE_X) { + return FALSE; + } + if (info->eye.y > RFLi_MAX_EYE_Y) { + return FALSE; + } + + if (info->eyebrow.type > RFLi_MAX_EYEBROW_TYPE) { + return FALSE; + } + if (info->eyebrow.color > RFLi_MAX_EYEBROW_COLOR) { + return FALSE; + } + if (info->eyebrow.scale > RFLi_MAX_EYEBROW_SCALE) { + return FALSE; + } + if (info->eyebrow.rotate > RFLi_MAX_EYEBROW_ROTATE) { + return FALSE; + } + if (info->eyebrow.x > RFLi_MAX_EYEBROW_X) { + return FALSE; + } + if (info->eyebrow.y < RFLi_MIN_EYEBROW_Y) { + return FALSE; + } + if (info->eyebrow.y > RFLi_MAX_EYEBROW_Y) { + return FALSE; + } + + if (info->nose.type > RFLi_MAX_NOSE_TYPE) { + return FALSE; + } + if (info->nose.scale > RFLi_MAX_NOSE_SCALE) { + return FALSE; + } + if (info->nose.y > RFLi_MAX_NOSE_Y) { + return FALSE; + } + + if (info->mouth.type > RFLi_MAX_MOUTH_TYPE) { + return FALSE; + } + if (info->mouth.color > RFLi_MAX_MOUTH_COLOR) { + return FALSE; + } + if (info->mouth.scale > RFLi_MAX_MOUTH_SCALE) { + return FALSE; + } + if (info->mouth.y > RFLi_MAX_MOUTH_Y) { + return FALSE; + } + + if (info->beard.mustache > RFLi_MAX_BEARD_MUSTACHE) { + return FALSE; + } + if (info->beard.type > RFLi_MAX_BEARD_TYPE) { + return FALSE; + } + if (info->beard.color > RFLi_MAX_BEARD_COLOR) { + return FALSE; + } + if (info->beard.scale > RFLi_MAX_BEARD_SCALE) { + return FALSE; + } + if (info->beard.y > RFLi_MAX_BEARD_Y) { + return FALSE; + } + + if (info->glass.type > RFLi_MAX_GLASS_TYPE) { + return FALSE; + } + if (info->glass.color > RFLi_MAX_GLASS_COLOR) { + return FALSE; + } + if (info->glass.scale > RFLi_MAX_GLASS_SCALE) { + return FALSE; + } + if (info->glass.y > RFLi_MAX_GLASS_Y) { + return FALSE; + } + + if (info->mole.type > RFLi_MAX_MOLE_TYPE) { + return FALSE; + } + if (info->mole.scale > RFLi_MAX_MOLE_SCALE) { + return FALSE; + } + if (info->mole.x > RFLi_MAX_MOLE_X) { + return FALSE; + } + if (info->mole.y > RFLi_MAX_MOLE_Y) { + return FALSE; + } + + if (!RFLiIsValidName2(info)) { + return FALSE; + } + + if (info->body.height > RFLi_MAX_BODY_HEIGHT) { + return FALSE; + } + if (info->body.build > RFLi_MAX_BODY_BUILD) { + return FALSE; + } + + if (info->personal.sex > RFLSex_Female) { + return FALSE; + } + if (!RFLiCheckBirthday(info->personal.bmonth, info->personal.bday)) { + return FALSE; + } + if (info->personal.color >= RFLFavoriteColor_Max) { + return FALSE; + } + + if (RFLiIsSpecialID(&info->createID) && !info->personal.localOnly) { + return FALSE; + } + + return TRUE; +} + +BOOL RFLiIsValidOnNAND(const RFLiCharInfo* info) { + return !RFLiIsTemporaryID(&info->createID); +} + +RFLErrcode RFLiPickupCharInfo(RFLiCharInfo* info, RFLDataSource source, + RFLMiddleDB* db, u16 index) { + RFLErrcode err; + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + switch (source) { + case RFLDataSource_Official: + err = RFLiGetCharInfo(info, index); + break; + case RFLDataSource_Controller1: + case RFLDataSource_Controller2: + case RFLDataSource_Controller3: + case RFLDataSource_Controller4: + if (RFLiGetControllerData(info, source - RFLDataSource_Controller1, + (u8)index, FALSE)) { + err = RFLErrcode_Success; + } else { + err = RFLErrcode_Broken; + } + break; + case RFLDataSource_Middle: + if (RFLiGetCharInfoMiddleDB(info, db, index)) { + err = RFLErrcode_Success; + } else { + err = RFLErrcode_Broken; + } + break; + case RFLDataSource_Default: + RFLiGetDefaultData(info, index); + err = RFLErrcode_Success; + break; + } + + if (err == RFLErrcode_Success) { + if (!RFLiIsValidID(&info->createID)) { + err = RFLErrcode_Broken; + } else if (!RFLiCheckValidInfo(info)) { + err = RFLErrcode_Broken; + } + } + + return err; +} + +static void copyChar2Additional_(RFLAdditionalInfo* dst, + const RFLiCharInfo* src) { + u8 height; + u8 build; + + memcpy(dst->name, src->personal.name, sizeof(wchar_t) * RFL_NAME_LEN); + dst->name[RFL_NAME_LEN] = '\0'; + + memcpy(dst->creator, src->personal.creator, + sizeof(wchar_t) * RFL_CREATOR_LEN); + // @bug Copy paste error + dst->name[RFL_CREATOR_LEN] = '\0'; + + dst->sex = src->personal.sex; + + if (RFLiCheckBirthday(src->personal.bmonth, src->personal.bday)) { + dst->bmonth = src->personal.bmonth; + dst->bday = src->personal.bday; + } else { + dst->bmonth = 0; + dst->bday = 0; + } + + dst->color = src->personal.color; + dst->favorite = src->personal.favorite; + + height = src->body.height; + build = src->body.build; + + // @bug Max height is 128, not 127 + if (height > RFLi_MAX_BODY_HEIGHT - 1) { + height = RFLi_MAX_BODY_HEIGHT - 1; + } + // @bug Max build is 128, not 127 + if (build > RFLi_MAX_BODY_BUILD - 1) { + build = RFLi_MAX_BODY_BUILD - 1; + } + + dst->height = height; + dst->build = build; + memcpy(&dst->createID, &src->createID, sizeof(RFLCreateID)); + dst->reserved = 0; + dst->skinColor = RFLiGetFacelineColor(src); +} + +RFLErrcode RFLGetAdditionalInfo(RFLAdditionalInfo* addi, RFLDataSource source, + RFLMiddleDB* db, u16 index) { + RFLiCharInfo info; + RFLErrcode err; + + err = RFLiPickupCharInfo(&info, source, db, index); + if (err == RFLErrcode_Success) { + copyChar2Additional_(addi, &info); + } + + return err; +} + +static BOOL RFLiCheckBirthday(u8 month, u8 day) { + // (One-indexed) + static const u8 scDayMax[12 + 1] = {0, 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31}; + + if (month == 0 && day != 0) { + return FALSE; + } + + if (month != 0 && day == 0) { + return FALSE; + } + + if (month > 12 || day > 31) { + return FALSE; + } + + return day <= scDayMax[month]; +} diff --git a/src/RVLFaceLib/RFL_Database.c b/src/RVLFaceLib/RFL_Database.c new file mode 100644 index 00000000..7a84757d --- /dev/null +++ b/src/RVLFaceLib/RFL_Database.c @@ -0,0 +1,805 @@ +#include + +#define ASYNC_CRC_STEP 0x1400 + +void RFLiInitDatabase(MEMiHeapHead* heap) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + mgr->database = + (RFLiDatabase*)MEMAllocFromExpHeapEx(heap, sizeof(RFLiDatabase), 32); + mgr->saveCb = NULL; + mgr->formatCb = NULL; + mgr->bootloadCb = NULL; + + RFLiClearDBBuffer(); +} + +static void initBrokenDatabase_(void) { + RFLiClearDBBuffer(); +} + +static void bootloadCheckCRCCb_(u32 crc) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + if (crc != 0) { + RFLiGetManager()->lastErrCode = RFLErrcode_Loadfail; + RFLiSetFileBroken(RFLiFileBrokenType_DBBroken); + } else { + RFLiGetManager()->lastErrCode = RFLErrcode_Success; + } + + RFLiCloseAsync(RFLiFileType_Database, mgr->bootloadCb); +} + +static void bootloadDBcallback_(void) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + switch (RFLGetAsyncStatus()) { + case RFLErrcode_Success: + RFLiCheckHeaderCRCAsync(bootloadCheckCRCCb_); + break; + case RFLErrcode_NANDCommandfail: + switch (RFLiGetLastReason()) { + case NAND_RESULT_CORRUPT: + RFLiSetFileBroken(RFLiFileBrokenType_Corrupt); + break; + case NAND_RESULT_AUTHENTICATION: + case NAND_RESULT_ECC_CRIT: + default: + RFLiGetManager()->lastErrCode = RFLErrcode_Loadfail; + RFLiSetFileBroken(RFLiFileBrokenType_DBBroken); + break; + } + RFLiCloseAsync(RFLiFileType_Database, mgr->bootloadCb); + break; + case RFLErrcode_NotAvailable: + default: + RFLiSetFileBroken(RFLiFileBrokenType_DBBroken); + RFLiCloseAsync(RFLiFileType_Database, mgr->bootloadCb); + break; + } +} + +static void bootloadDBopencallback_(void) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + switch (RFLGetAsyncStatus()) { + case RFLErrcode_Success: + switch (RFLiReadAsync(RFLiFileType_Database, mgr->database, + sizeof(RFLiDatabase), bootloadDBcallback_, 0)) { + case RFLErrcode_Busy: + case RFLErrcode_Success: + break; + default: + if (mgr->bootloadCb != NULL) { + mgr->bootloadCb(); + } + break; + } + break; + case RFLErrcode_NANDCommandfail: + switch (RFLiGetLastReason()) { + case NAND_RESULT_NOEXISTS: + RFLiSetFileBroken(RFLiFileBrokenType_DBNotFound); + RFLiGetManager()->lastErrCode = RFLErrcode_Success; + if (mgr->bootloadCb != NULL) { + mgr->bootloadCb(); + } + break; + case NAND_RESULT_CORRUPT: + RFLiSetFileBroken(RFLiFileBrokenType_Corrupt); + break; + case NAND_RESULT_AUTHENTICATION: + case NAND_RESULT_ECC_CRIT: + RFLiSetFileBroken(RFLiFileBrokenType_DBBroken); + if (mgr->bootloadCb != NULL) { + mgr->bootloadCb(); + } + break; + default: + break; + } + break; + case RFLErrcode_NotAvailable: + default: + break; + } +} + +RFLErrcode RFLiBootLoadDatabaseAsync(RFLiCallback callback) { + RFLiDBManager* mgr; + + if (!RFLAvailable()) { + return RFLErrcode_Fatal; + } + + if (RFLiIsWorking()) { + return RFLErrcode_Fatal; + } + + mgr = RFLiGetDBManager(); + if (mgr == NULL) { + return RFLErrcode_Fatal; + } + + mgr->bootloadCb = callback; + + switch (RFLiOpenAsync(RFLiFileType_Database, 1, + bootloadDBopencallback_)) { + case RFLErrcode_NANDCommandfail: + if (RFLiGetLastReason() == NAND_RESULT_NOEXISTS) { + initBrokenDatabase_(); + RFLiGetManager()->lastErrCode = RFLErrcode_Success; + + if (mgr->bootloadCb != NULL) { + mgr->bootloadCb(); + } + } + break; + case RFLErrcode_Success: + case RFLErrcode_Busy: + default: + break; + } + + return RFLGetAsyncStatus(); +} + +static void saveDBcallback_(void) { + RFLiCloseAsync(RFLiFileType_Database, RFLiGetDBManager()->saveCb); +} + +static RFLErrcode saveCore_(RFLiCallback callback) { + return RFLiWriteAsync(RFLiFileType_Database, RFLiGetDBManager()->database, + sizeof(RFLiDatabase), callback, 0); +} + +static void saveDBopencallback_(void) { + (void)RFLiGetDBManager(); + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + switch (saveCore_(saveDBcallback_)) { + case RFLErrcode_Success: + case RFLErrcode_Busy: + break; + default: + RFLiCloseAsync(RFLiFileType_Database, NULL); + break; + } + } +} + +static void saveDBmultiopencallback_(void) { + (void)RFLiGetDBManager(); + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + switch (saveCore_(RFLiGetDBManager()->saveCb)) { + case RFLErrcode_Success: + case RFLErrcode_Busy: + break; + default: + RFLiCloseAsync(RFLiFileType_Database, NULL); + break; + } + } +} + +static void saveDatabase_(u32 arg) { + switch (RFLiGetDBManager()->saveType) { + case RFLiOpenType_Multi: + RFLiOpenAsync(RFLiFileType_Database, 2, + saveDBmultiopencallback_); + break; + case RFLiOpenType_Single: + RFLiOpenAsync(RFLiFileType_Database, 2, + saveDBopencallback_); + break; + case RFLiOpenType_Opened: + saveCore_(RFLiGetDBManager()->saveCb); + break; + default: + break; + } +} + +static RFLErrcode RFLiSaveDatabaseAsync(RFLiCallback callback) { + RFLiDBManager* mgr; + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + mgr = RFLiGetDBManager(); + if (mgr == NULL || !RFLiDBIsLoaded()) { + return RFLErrcode_NotAvailable; + } + + mgr->saveCb = callback; + mgr->saveType = RFLiOpenType_Opened; + + RFLiCreateHeaderCRCAsync(saveDatabase_); + return RFLGetAsyncStatus(); +} + +RFLiCharData* RFLiGetCharData(u16 index) { + RFLiDBManager* mgr; + RFLiCharData* data; + RFLiCharInfo info; + + if (index >= RFL_DB_CHAR_MAX) { + return NULL; + } + + if (!RFLAvailable()) { + return NULL; + } + + mgr = RFLiGetDBManager(); + if (mgr == NULL || !RFLiDBIsLoaded()) { + return NULL; + } + + data = &mgr->database->rawData[index]; + + if (!RFLiIsValidID(&data->createID)) { + return NULL; + } + + RFLiConvertRaw2Info(data, &info); + if (!RFLiCheckValidInfo(&info)) { + return NULL; + } + + if (!RFLiIsValidOnNAND(&info)) { + return NULL; + } + + return data; +} + +static void convertRaw2InfoCore_(const RFLiCharData* data, RFLiCharInfo* info) { + info->faceline.type = data->faceType; + info->faceline.color = data->faceColor; + info->faceline.texture = data->faceTex; + + info->hair.type = data->hairType; + info->hair.color = data->hairColor; + info->hair.flip = data->hairFlip; + + info->eye.type = data->eyeType; + info->eye.color = data->eyeColor; + info->eye.scale = data->eyeScale; + info->eye.rotate = data->eyeRotate; + info->eye.x = data->eyeX; + info->eye.y = data->eyeY; + + info->eyebrow.type = data->eyebrowType; + info->eyebrow.color = data->eyebrowColor; + info->eyebrow.scale = data->eyebrowScale; + info->eyebrow.rotate = data->eyebrowRotate; + info->eyebrow.x = data->eyebrowX; + info->eyebrow.y = data->eyebrowY; + + info->nose.type = data->noseType; + info->nose.scale = data->noseScale; + info->nose.y = data->noseY; + + info->mouth.type = data->mouthType; + info->mouth.color = data->mouthColor; + info->mouth.scale = data->mouthScale; + info->mouth.y = data->mouthY; + + info->beard.mustache = data->mustacheType; + info->beard.type = data->beardType; + info->beard.color = data->beardColor; + info->beard.scale = data->beardScale; + info->beard.y = data->beardY; + + info->glass.type = data->glassType; + info->glass.color = data->glassColor; + info->glass.scale = data->glassScale; + info->glass.y = data->glassY; + + info->mole.type = data->moleType; + info->mole.scale = data->moleScale; + info->mole.x = data->moleX; + info->mole.y = data->moleY; + + info->body.height = data->height; + info->body.build = data->build; + + memcpy(info->personal.name, data->name, RFL_NAME_LEN * sizeof(wchar_t)); + info->personal.name[RFL_NAME_LEN] = L'\0'; + + memcpy(&info->createID, &data->createID, sizeof(RFLCreateID)); + + info->personal.sex = data->sex; + info->personal.bmonth = data->birthMonth; + info->personal.bday = data->birthDay; + info->personal.color = data->favoriteColor; + info->personal.favorite = data->favorite; + info->personal.localOnly = data->localonly; +} + +void RFLiConvertRaw2Info(const RFLiCharData* data, RFLiCharInfo* out) { + convertRaw2InfoCore_(data, out); + + memcpy(out->personal.creator, data->creatorName, + RFL_CREATOR_LEN * sizeof(wchar_t)); + out->personal.creator[RFL_CREATOR_LEN] = '\0'; +} + +void RFLiConvertHRaw2Info(const RFLiHiddenCharData* data, RFLiCharInfo* out) { + convertRaw2InfoCore_((RFLiCharData*)data, out); + + memset(out->personal.creator, 0, RFL_CREATOR_LEN * sizeof(wchar_t)); + out->personal.creator[RFL_CREATOR_LEN] = '\0'; + + out->personal.bmonth = 0; + out->personal.bday = 0; +} + +static void convertInfo2RawCore_(const RFLiCharInfo* info, RFLiCharData* data) { + data->faceType = info->faceline.type; + data->faceColor = info->faceline.color; + data->faceTex = info->faceline.texture; + + data->hairType = info->hair.type; + data->hairColor = info->hair.color; + data->hairFlip = info->hair.flip; + + data->eyeType = info->eye.type; + data->eyeColor = info->eye.color; + data->eyeScale = info->eye.scale; + data->eyeRotate = info->eye.rotate; + data->eyeX = info->eye.x; + data->eyeY = info->eye.y; + + data->eyebrowType = info->eyebrow.type; + data->eyebrowColor = info->eyebrow.color; + data->eyebrowScale = info->eyebrow.scale; + data->eyebrowRotate = info->eyebrow.rotate; + data->eyebrowX = info->eyebrow.x; + data->eyebrowY = info->eyebrow.y; + + data->noseType = info->nose.type; + data->noseScale = info->nose.scale; + data->noseY = info->nose.y; + + data->mouthType = info->mouth.type; + data->mouthColor = info->mouth.color; + data->mouthScale = info->mouth.scale; + data->mouthY = info->mouth.y; + + data->mustacheType = info->beard.mustache; + data->beardType = info->beard.type; + data->beardColor = info->beard.color; + data->beardScale = info->beard.scale; + data->beardY = info->beard.y; + + data->glassType = info->glass.type; + data->glassColor = info->glass.color; + data->glassScale = info->glass.scale; + data->glassY = info->glass.y; + + data->moleType = info->mole.type; + data->moleScale = info->mole.scale; + data->moleX = info->mole.x; + data->moleY = info->mole.y; + + data->height = info->body.height; + data->build = info->body.build; + + memcpy(data->name, info->personal.name, RFL_NAME_LEN * sizeof(wchar_t)); + memcpy(&data->createID, &info->createID, sizeof(RFLCreateID)); + + data->sex = info->personal.sex; + data->birthMonth = info->personal.bmonth; + data->birthDay = info->personal.bday; + data->favoriteColor = info->personal.color; + data->favorite = info->personal.favorite; + data->localonly = info->personal.localOnly; +} + +static void RFLiConvertInfo2Raw(const RFLiCharInfo* info, RFLiCharData* out) { + convertInfo2RawCore_(info, out); + memcpy(out->creatorName, info->personal.creator, + RFL_CREATOR_LEN * sizeof(wchar_t)); +} + +static void RFLiConvertInfo2HRaw(const RFLiCharInfo* info, RFLiHiddenCharData* out) { + convertInfo2RawCore_(info, (RFLiCharData*)out); + out->birthPadding = 0; +} + +static void RFLiConvertRaw2HRaw(const RFLiCharData* data, RFLiHiddenCharData* out) { + memset(out, 0, sizeof(RFLiHiddenCharData)); + // Copy everything that also exists in hidden (just no creator name) + memcpy(out, data, sizeof(RFLiCharData) - RFL_CREATOR_LEN * sizeof(wchar_t)); + out->birthPadding = 0; +} + +static RFLErrcode RFLiGetCharRawData(RFLiCharData* out, u16 index) { + RFLiCharData* data; + + if (out == NULL) { + return RFLErrcode_WrongParam; + } + + if (index >= RFL_DB_CHAR_MAX) { + return RFLErrcode_WrongParam; + } + + data = RFLiGetCharData(index); + if (data == NULL) { + return RFLErrcode_Broken; + } + + memcpy(out, data, sizeof(RFLiCharData)); + return RFLErrcode_Success; +} + +RFLErrcode RFLiGetCharInfo(RFLiCharInfo* out, u16 index) { + RFLiCharData* data; + + if (out == NULL) { + return RFLErrcode_WrongParam; + } + + if (index >= RFL_DB_CHAR_MAX) { + return RFLErrcode_WrongParam; + } + + data = RFLiGetCharData(index); + if (data == NULL) { + // Shouldn't this return RFLErrcode_Broken? + return RFLErrcode_NotAvailable; + } + + RFLiConvertRaw2Info(data, out); + if (!RFLiCheckValidInfo(out)) { + return RFLErrcode_Broken; + } + + return RFLErrcode_Success; +} + +BOOL RFLIsAvailableOfficialData(u16 index) { + if (index >= RFL_DB_CHAR_MAX) { + return FALSE; + } + + return RFLiGetCharData(index) != NULL; +} + +static void RFLiSetTemporaryID(RFLiCharInfo* info) { + u32* dst = (u32*)&info->createID.data; + dst[0] = RFLi_CREATE_ID_MASK_TEMPORARY; + dst[1] = 0; +} + +BOOL RFLiIsSameID(const RFLCreateID* id1, const RFLCreateID* id2) { + const u32* ptr1 = (u32*)id1->data; + const u32* ptr2 = (u32*)id2->data; + + if (id1 == NULL) { + return FALSE; + } + + if (id2 == NULL) { + return FALSE; + } + + if (!RFLiIsValidID(id1)) { + return FALSE; + } + + if (!RFLiIsValidID(id2)) { + return FALSE; + } + + if (RFLiIsTemporaryID(id1)) { + return FALSE; + } + + if (RFLiIsTemporaryID(id2)) { + return FALSE; + } + + if (ptr1[0] != ptr2[0]) { + return FALSE; + } + + if (ptr1[1] != ptr2[1]) { + return FALSE; + } + + return TRUE; +} + +BOOL RFLiIsValidID(const RFLCreateID* id) { + const u32* ptr = (u32*)id->data; + + if (id == NULL) { + return FALSE; + } + + return ptr[0] != 0 || ptr[1] != 0; +} + +BOOL RFLiIsSpecialID(const RFLCreateID* id) { + const u32* ptr = (u32*)id->data; + + if (id == NULL) { + return FALSE; + } + + if (!RFLiIsValidID(id)) { + return FALSE; + } + + if (RFLiIsTemporaryID(id)) { + return FALSE; + } + + return (*ptr & RFLi_CREATE_ID_MASK_NOT_SPECIAL) == 0 ? TRUE : FALSE; +} + +BOOL RFLiIsTemporaryID(const RFLCreateID* id) { + const u32* ptr = (u32*)id->data; + + if (id == NULL) { + return FALSE; + } + + if (!RFLiIsValidID(id)) { + return FALSE; + } + + return (*ptr & RFLi_CREATE_ID_MASK_TEMPORARY) != 0 ? TRUE : FALSE; +} + +BOOL RFLSearchOfficialData(const RFLCreateID* id, u16* index) { + u16 i; + BOOL success; + RFLiDBManager* mgr; + RFLiCharData* head; + + success = FALSE; + + if (id == NULL) { + return FALSE; + } + + if (!RFLAvailable()) { + return FALSE; + } + + mgr = RFLiGetDBManager(); + if (mgr == NULL) { + return FALSE; + } + + head = mgr->database->rawData; + for (i = 0; i < RFL_DB_CHAR_MAX; i++) { + if (RFLIsAvailableOfficialData(i) && + RFLiIsSameID(&head[i].createID, id)) { + success = TRUE; + break; + } + } + + if (success == TRUE && index != NULL) { + *index = i; + } + + return success; +} + +static BOOL RFLiIsValidName(const RFLiCharData* data) { + if (data == NULL) { + return FALSE; + } + + return data->name[0] != L'\0'; +} + +BOOL RFLiIsValidName2(const RFLiCharInfo* info) { + RFLiCharData data; + + if (info == NULL) { + return FALSE; + } + + RFLiConvertInfo2Raw(info, &data); + return RFLiIsValidName(&data); +} + +static BOOL RFLiGetIsolation(void) { + return !RFLiDBIsLoaded() ? TRUE : RFLiGetDBManager()->database->isolation; +} + +static RFLiHiddenDB* RFLiGetHiddenHeader(void) { + if (!RFLiDBIsLoaded()) { + return NULL; + } + + return &RFLiGetDBManager()->database->hidden; +} + +static BOOL RFLiDBIsLoaded(void) { + if (!RFLAvailable()) { + return FALSE; + } + + if (RFLiGetDBManager() == NULL) { + return FALSE; + } + + return !RFLiNeedRepairError() && !RFLiNotFoundError(); +} + +static u16 RFLiCalculateCRC(const void* p, u32 len) { + int i = 0; + u16 crc = 0; + const u8* current = (u8*)p; + + for (; i < len; current++, i++) { + u8 data = *current; + int j; + + for (j = 0; j < 8; j++, data <<= 1) { + if (crc & 0x8000) { + crc <<= 1; + crc ^= 0x1021; + } else { + crc <<= 1; + } + + if (data & 0x80) { + crc ^= 0x1; + } + } + } + + return crc; +} + +static void alarmCreateCb_(OSAlarm* alarm, OSContext* ctx) { +#pragma unused(alarm) +#pragma unused(ctx) + + RFLiDBManager* mgr = RFLiGetDBManager(); + u8* current = mgr->crcInfo.current; + u32 count = mgr->crcInfo.count; + u16 crc = mgr->crcInfo.crc; + u32 size = mgr->crcInfo.size; + u32 curCount = 0; + + while (count < size) { + u8 data = *current; + int i; + + if (curCount >= ASYNC_CRC_STEP) { + mgr->crcInfo.current = current; + mgr->crcInfo.count = count; + mgr->crcInfo.crc = crc; + + OSCancelAlarm(&mgr->crcInfo.alarm); + OSSetAlarmUserData(&mgr->crcInfo.alarm, &mgr->crcInfo); + OSSetAlarm(&mgr->crcInfo.alarm, OSMillisecondsToTicks(19), + alarmCreateCb_); + return; + } + + for (i = 0; i < 8; i++, data <<= 1) { + if (crc & 0x8000) { + crc <<= 1; + crc ^= 0x1021; + } else { + crc <<= 1; + } + + if (data & 0x80) { + crc ^= 0x1; + } + } + + current++; + count++; + curCount++; + } + + OSCancelAlarm(&mgr->crcInfo.alarm); + mgr->crcInfo.callback(crc); + mgr->database->hidden.crc = crc; +} + +static void RFLiCreateHeaderCRCAsync(RFLiExCallback callback) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + if (!RFLiIsWorking()) { + RFLiStartWorking(); + } + + mgr->database->hidden.crc = 0; + + mgr->crcInfo.head = mgr->database; + mgr->crcInfo.size = sizeof(RFLiDatabase); + mgr->crcInfo.current = mgr->crcInfo.head; + mgr->crcInfo.count = 0; + mgr->crcInfo.crc = 0; + mgr->crcInfo.callback = callback; + + OSCancelAlarm(&mgr->crcInfo.alarm); + OSSetAlarmUserData(&mgr->crcInfo.alarm, &mgr->crcInfo); + OSSetAlarm(&mgr->crcInfo.alarm, OSMillisecondsToTicks(19), alarmCreateCb_); +} + +static void alarmCheckCb_(OSAlarm* alarm, OSContext* ctx) { +#pragma unused(alarm) +#pragma unused(ctx) + + RFLiDBManager* mgr = RFLiGetDBManager(); + u8* current = mgr->crcInfo.current; + u32 count = mgr->crcInfo.count; + u16 crc = mgr->crcInfo.crc; + u32 size = mgr->crcInfo.size; + u32 curCount = 0; + + while (count < size) { + u8 data = *current; + int i; + + if (curCount >= ASYNC_CRC_STEP) { + mgr->crcInfo.current = current; + mgr->crcInfo.count = count; + mgr->crcInfo.crc = crc; + + OSCancelAlarm(&mgr->crcInfo.alarm); + OSSetAlarmUserData(&mgr->crcInfo.alarm, &mgr->crcInfo); + OSSetAlarm(&mgr->crcInfo.alarm, OSMillisecondsToTicks(19), + alarmCheckCb_); + return; + } + + for (i = 0; i < 8; i++, data <<= 1) { + if (crc & 0x8000) { + crc <<= 1; + crc ^= 0x1021; + } else { + crc <<= 1; + } + + if (data & 0x80) { + crc ^= 0x1; + } + } + + current++; + count++; + curCount++; + } + + OSCancelAlarm(&mgr->crcInfo.alarm); + mgr->crcInfo.callback(crc); +} + +static void RFLiCheckHeaderCRCAsync(RFLiExCallback callback) { + RFLiDBManager* mgr = RFLiGetDBManager(); + + if (!RFLiIsWorking()) { + RFLiStartWorking(); + } + + mgr->crcInfo.head = mgr->database; + mgr->crcInfo.size = sizeof(RFLiDatabase); + mgr->crcInfo.current = mgr->crcInfo.head; + mgr->crcInfo.count = 0; + mgr->crcInfo.crc = 0; + mgr->crcInfo.callback = callback; + + OSCancelAlarm(&mgr->crcInfo.alarm); + OSSetAlarmUserData(&mgr->crcInfo.alarm, &mgr->crcInfo); + OSSetAlarm(&mgr->crcInfo.alarm, OSMillisecondsToTicks(19), alarmCheckCb_); +} diff --git a/src/RVLFaceLib/RFL_DefaultDatabase.c b/src/RVLFaceLib/RFL_DefaultDatabase.c new file mode 100644 index 00000000..cebf2cb1 --- /dev/null +++ b/src/RVLFaceLib/RFL_DefaultDatabase.c @@ -0,0 +1,55 @@ +#include + +static const u8 scDefaultData[][sizeof(RFLiCharData)] = { + // "Guest A" + {0x00, 0x08, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0xEC, 0xFF, 0x82, 0xD2, 0x10, + 0x04, 0x88, 0x00, 0x31, 0x80, 0x08, 0xA2, 0x08, 0x8C, 0x08, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // "Guest B" + {0x00, 0x0A, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x01, 0xEC, 0xFF, 0x82, 0xD2, 0x00, + 0x04, 0x6F, 0x80, 0x31, 0x80, 0xC8, 0xA2, 0x08, 0x8C, 0x88, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // "Guest C" + {0x00, 0x00, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x02, 0xEC, 0xFF, 0x82, 0xD2, 0x04, + 0x04, 0x42, 0x40, 0x31, 0x80, 0x28, 0xA2, 0x08, 0x8C, 0x08, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // "Guest D" + {0x40, 0x04, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x03, 0xEC, 0xFF, 0x82, 0xD2, 0x08, + 0x04, 0x30, 0x00, 0x01, 0x80, 0x08, 0xA2, 0x10, 0x6C, 0x08, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // "Guest E" + {0x40, 0x0C, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x04, 0xEC, 0xFF, 0x82, 0xD2, 0x00, + 0x04, 0x1D, 0xC0, 0x01, 0x80, 0xE8, 0xA2, 0x10, 0x6C, 0xA8, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // "Guest F" + {0x40, 0x0E, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6E, 0x00, + 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x80, 0x00, 0x00, 0x05, 0xEC, 0xFF, 0x82, 0xD2, 0x00, + 0x04, 0x18, 0x40, 0x01, 0x80, 0x28, 0xA2, 0x10, 0x6C, 0x08, 0x58, + 0x14, 0x4A, 0xB8, 0x8D, 0x00, 0x8A, 0x00, 0x8A, 0x25, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +void RFLiGetDefaultData(RFLiCharInfo* info, u16 index) { + RFLiConvertRaw2Info((RFLiCharData*)&scDefaultData[index], info); +} diff --git a/src/RVLFaceLib/RFL_Format.c b/src/RVLFaceLib/RFL_Format.c new file mode 100644 index 00000000..65a3083b --- /dev/null +++ b/src/RVLFaceLib/RFL_Format.c @@ -0,0 +1,37 @@ +#include +#include + +#define MAGIC_OFFICIAL_DB 'RNOD' +#define MAGIC_HIDDEN_DB 'RNHD' + +#define TABLE_DATA_STEP 1250 +// TODO: How is this size calculated? +#define TEMP_BUFFER_SIZE (TABLE_DATA_STEP * sizeof(RFLiHiddenCharData) + 0x30E0) + +static void RFLiClearTableData(RFLiTableData* data) { + memset(data, 0, sizeof(RFLiTableData)); + data->next = -1; + data->prev = -1; +} + +void RFLiClearDBBuffer(void) { + RFLiDatabase* db; + RFLiHiddenDB* header; + int i; + + db = RFLiGetDBManager()->database; + header = &db->hidden; + + // @bug Four-byte buffer overrun + memset((u8*)db + sizeof(db->identifier), 0, sizeof(RFLiDatabase)); + db->identifier = MAGIC_OFFICIAL_DB; + db->isolation = TRUE; + + header->identifier = MAGIC_HIDDEN_DB; + header->head = -1; + header->tail = -1; + + for (i = 0; i < RFLi_HDB_DATA_MAX; i++) { + RFLiClearTableData(&header->data[i]); + } +} diff --git a/src/RVLFaceLib/RFL_HiddenDatabase.c b/src/RVLFaceLib/RFL_HiddenDatabase.c new file mode 100644 index 00000000..611871fd --- /dev/null +++ b/src/RVLFaceLib/RFL_HiddenDatabase.c @@ -0,0 +1,509 @@ +#include +#include + +static void writeData_(RFLiHiddenCharData* data) NO_INLINE; + +static void initWritableList_(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + mgr->writeCb = NULL; + mgr->list.num = 0; + mgr->list.current = 0; +} + +void RFLiInitHiddenDatabase(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + if (mgr != NULL) { + mgr->cachedDB = NULL; + mgr->cached = FALSE; + } +} + +static void loadclosecallback_(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + if (mgr->loadCb != NULL) { + mgr->loadCb(mgr->loadArg); + } +} + +static void loadcallback_(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + RFLiHiddenCharData* data = mgr->loadTmp; + RFLiCharInfo info; + + if (RFLiIsSameID( + &data->createID, + &RFLiGetHiddenHeader()->data[mgr->loadIndex].createID)) { + RFLiConvertHRaw2Info(data, &info); + + if (RFLiCheckValidInfo(&info) && RFLiIsValidOnNAND(&info)) { + memcpy(mgr->loadDst, data, sizeof(RFLiHiddenCharData)); + } else { + RFLiGetManager()->lastErrCode = RFLErrcode_Broken; + } + } + } + + RFLiFree(mgr->loadTmp); + mgr->loadTmp = NULL; + + RFLiCloseAsync(RFLiFileType_Database, loadclosecallback_); +} + +static void loadopencallback_(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + s32 offset = mgr->loadIndex * sizeof(RFLiHiddenCharData); + mgr->loadTmp = RFLiAlloc32(sizeof(RFLiHiddenCharData)); + + switch (RFLiReadAsync(RFLiFileType_Database, mgr->loadTmp, + sizeof(RFLiHiddenCharData), loadcallback_, + offset + sizeof(RFLiDatabase))) { + case RFLErrcode_Success: + case RFLErrcode_Busy: + break; + default: + RFLiFree(mgr->loadTmp); + mgr->loadTmp = NULL; + RFLiCloseAsync(RFLiFileType_Database, NULL); + } + } else { + mgr->loadDst = NULL; + mgr->loadTmp = NULL; + mgr->loadIndex = 0; + mgr->loadCb = NULL; + } +} + +RFLErrcode RFLiLoadHiddenDataAsync(RFLiHiddenCharData* data, u16 index, + RFLiExCallback cb, u32 arg) { + RFLiHDBManager* mgr; + + if (index >= RFLi_HDB_DATA_MAX) { + return RFLErrcode_WrongParam; + } + + if (data == NULL) { + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return RFLErrcode_NotAvailable; + } + + mgr->loadDst = data; + mgr->loadIndex = index; + mgr->loadCb = cb; + mgr->loadArg = arg; + + return RFLiOpenAsync(RFLiFileType_Database, 1, + loadopencallback_); +} + +RFLErrcode RFLiLoadCachedHiddenData(RFLiHiddenCharData* data, u16 index) { + RFLiHDBManager* mgr; + RFLiCharInfo info; + + if (index >= RFLi_HDB_DATA_MAX) { + return RFLErrcode_WrongParam; + } + + if (data == NULL) { + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return RFLErrcode_NotAvailable; + } + + if (RFLiIsCachedHDB()) { + if (RFLiIsSameID(&mgr->cachedDB[index].createID, + &RFLiGetHiddenHeader()->data[index].createID)) { + RFLiConvertHRaw2Info(&mgr->cachedDB[index], &info); + + if (RFLiCheckValidInfo(&info)) { + memcpy(data, &mgr->cachedDB[index], sizeof(RFLiHiddenCharData)); + return RFLErrcode_Success; + } else { + return RFLErrcode_Broken; + } + } else { + return RFLErrcode_Broken; + } + } else { + return RFLErrcode_NotAvailable; + } +} + +static void writeCtrl2HDBCallback_(void) { + RFLiCloseAsync(RFLiFileType_Database, NULL); +} + +static int overwrite_(const RFLiHiddenCharData* data) { + RFLiHiddenDB* hdb = RFLiGetHiddenHeader(); + s16 head = hdb->head; + + RFLiTableData* it = &hdb->data[head]; + s16 next = it->next; + s16 prev = hdb->tail; + + RFLiTableData* itnext = &hdb->data[next]; + RFLiTableData* itprev = &hdb->data[prev]; + + if (head < 0 || head >= RFLi_HDB_DATA_MAX) { + return -1; + } + + RFLiClearTableData(it); + hdb->head = next; + itnext->prev = -1; + itprev->next = head; + it->prev = prev; + hdb->tail = head; + it->next = -1; + + memcpy(&it->createID, &data->createID, sizeof(RFLCreateID)); + it->sex = data->sex; + + return head; +} + +static void create_(const RFLiHiddenCharData* data, s16 index) { + RFLiHiddenDB* hdb = RFLiGetHiddenHeader(); + RFLiTableData* it = &hdb->data[index]; + + if (index < 0 || index >= RFLi_HDB_DATA_MAX) { + return; + } + + RFLiClearTableData(it); + + if (hdb->tail >= 0) { + hdb->data[hdb->tail].next = index; + it->prev = hdb->tail; + hdb->tail = index; + it->next = -1; + } else { + hdb->tail = index; + hdb->head = index; + it->prev = -1; + it->next = -1; + } + + memcpy(&it->createID, &data->createID, sizeof(RFLCreateID)); + it->sex = data->sex; +} + +static void writeHeader_(void) { + RFLiSaveDatabaseAsync(RFLiGetHDBManager()->writeCb); +} + +static void writeCallback_(void) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + RFLiHDBList* list = &RFLiGetHDBManager()->list; + + if (RFLiIsCachedHDB()) { + memcpy(&mgr->cachedDB[mgr->writeIndex], mgr->writeTmp, + sizeof(RFLiHiddenCharData)); + } + + list->current++; + if (list->current < list->num) { + writeData_(&list->data[list->current]); + } else { + RFLiFree(mgr->writeTmp); + mgr->writeTmp = NULL; + writeHeader_(); + } + } else { + RFLiFree(mgr->writeTmp); + mgr->writeTmp = NULL; + RFLiCloseAsync(RFLiFileType_Database, NULL); + } +} + +static BOOL checkCtrlWritableData_(const RFLiCtrlBuf* buf, BOOL ch, u8 index) { + if (RFLiCheckCtrlBufferCore(buf, index, + ch ? RFLiHiddenType_Yes : RFLiHiddenType_Any)) { + RFLiHiddenCharData data; + RFLiConvertRaw2HRaw(&buf->data[index], &data); + + if (!data.localonly && !RFLSearchOfficialData(&data.createID, NULL)) { + return TRUE; + } + } + + return FALSE; +} + +static u8 getCtrlWritableCount_(const RFLiCtrlBuf* buf, volatile s64 ch) { + u8 i; + u8 count = 0; + RFLiHDBManager* mgr = RFLiGetHDBManager(); + RFLiHiddenCharData* dst; + + for (i = 0; i < RFL_CTRL_CHAR_MAX; i++) { + if (checkCtrlWritableData_(buf, ch, i)) { + + RFLiHiddenCharData temp; + RFLiConvertRaw2HRaw(&buf->data[i], &temp); + + dst = &mgr->list.data[mgr->list.num]; + memcpy(dst, &temp, sizeof(RFLiHiddenCharData)); + dst->favorite = FALSE; + + count++; + mgr->list.num++; + } + } + + return count; +} + +static s32 getFirstBlank_(void) { + RFLiTableData* head = RFLiGetHiddenHeader()->data; + int i; + + for (i = 0; i < RFLi_HDB_DATA_MAX; i++) { + if (!RFLiIsValidID(&head[i].createID)) { + return i; + } + } + + return -1; +} + +static void writeData_(RFLiHiddenCharData* data) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + s32 blank = getFirstBlank_(); + s32 index = RFLiSearchHiddenData(&data->createID); + + if ((s16)index != -1) { + blank = index; + } else if ((s16)blank < 0) { + blank = overwrite_(data); + } else { + create_(data, blank); + } + + memcpy(mgr->writeTmp, data, sizeof(RFLiHiddenCharData)); + mgr->writeIndex = blank; + RFLiWriteAsync(RFLiFileType_Database, mgr->writeTmp, + sizeof(RFLiHiddenCharData), writeCallback_, + (s16)blank * sizeof(RFLiHiddenCharData) + + sizeof(RFLiDatabase)); +} + +static void openForWriteCallback_(void) { + RFLiHDBManager* mgr; + RFLiHDBList* list; + + if (RFLGetAsyncStatus() != RFLErrcode_Success) { + return; + } + + mgr = RFLiGetHDBManager(); + list = &mgr->list; + + if (list->current < list->num) { + mgr->writeTmp = RFLiAlloc32(sizeof(RFLiHiddenCharData)); + writeData_(&list->data[list->current]); + } +} + +static void writeDataStart_(void) { + RFLiOpenAsync(RFLiFileType_Database, 2, + openForWriteCallback_); +} + +static void writeHeaderStart_(void) { + RFLiOpenAsync(RFLiFileType_Database, 2, writeHeader_); +} + +static void writeCtrl2HDB_(RFLiCallback formatCb, RFLiCallback writeCb) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + mgr->writeCb = writeCtrl2HDBCallback_; + + if (!RFLiDBIsLoaded()) { + RFLiFormatAsync(formatCb); + } else { + RFLiOpenAsync(RFLiFileType_Database, 2, writeCb); + } +} + +RFLErrcode RFLiWriteCtrlToHiddenDB(const RFLiCtrlBuf* buf, BOOL ch) { + initWritableList_(); + + if (getCtrlWritableCount_(buf, ch) > 0) { + writeCtrl2HDB_(writeDataStart_, openForWriteCallback_); + } else { + writeCtrl2HDB_(writeHeaderStart_, writeHeader_); + } + + return RFLGetAsyncStatus(); +} + +s32 RFLiSearchHiddenData(const RFLCreateID* id) { + RFLiHDBManager* mgr; + RFLiTableData* head; + int i; + s32 index = -1; + + if (!RFLAvailable()) { + return -1; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return -1; + } + + head = RFLiGetHiddenHeader()->data; + for (i = 0; i < RFLi_HDB_DATA_MAX; i++) { + if (RFLiIsSameID(&head[i].createID, id)) { + index = i; + break; + } + } + + return index; +} + +u16 RFLiCountupHiddenDataNum(RFLSex sex) { + RFLiHDBManager* mgr; + RFLiHiddenDB* header; + u16 count = 0; + u16 i; + + if (!RFLAvailable()) { + return 0; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return 0; + } + + if (!RFLiDBIsLoaded()) { + return 0; + } + + header = RFLiGetHiddenHeader(); + + for (i = 0; i < RFLi_HDB_DATA_MAX; i++) { + if (RFLiIsValidHiddenData(i, sex)) { + count++; + } + } + + return count; +} + +s16 RFLiGetHiddenNext(u16 index) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + RFLiTableData* data; + + if (index >= RFLi_HDB_DATA_MAX) { + return -1; + } + + if (mgr == NULL) { + return -1; + } + + data = &RFLiGetHiddenHeader()->data[index]; + + if (RFLiIsValidID(&data->createID)) { + return data->next; + } + + return -1; +} + +s16 RFLiGetHiddenPrev(u16 index) { + RFLiHDBManager* mgr = RFLiGetHDBManager(); + RFLiTableData* data; + + if (index >= RFLi_HDB_DATA_MAX) { + return -1; + } + + if (mgr == NULL) { + return -1; + } + + data = &RFLiGetHiddenHeader()->data[index]; + + if (RFLiIsValidID(&data->createID)) { + return data->prev; + } + + return -1; +} + +BOOL RFLiIsValidHiddenData(u16 index, RFLSex sex) { + RFLiHDBManager* mgr; + RFLiHiddenDB* header; + u16 count; + + if (!RFLAvailable()) { + return FALSE; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return FALSE; + } + + header = RFLiGetHiddenHeader(); + + if (!RFLiIsValidID(&header->data[index].createID)) { + return FALSE; + } + + if (sex == RFLSex_All) { + return TRUE; + } + + switch (header->data[index].sex) { + case RFLSex_Male: + return sex == RFLSex_Male; + case RFLSex_Female: + return sex == RFLSex_Female; + } + + return FALSE; +} + +void RFLiClearCacheHDB(RFLiHiddenCharData* cache) { + memset(cache, 0, sizeof(RFLiHiddenCharData) * RFLi_HDB_DATA_MAX); +} + +BOOL RFLiIsCachedHDB(void) { + RFLiHDBManager* mgr; + + if (!RFLAvailable()) { + return FALSE; + } + + mgr = RFLiGetHDBManager(); + if (mgr == NULL) { + return FALSE; + } + + return mgr->cached; +} diff --git a/src/RVLFaceLib/RFL_Icon.c b/src/RVLFaceLib/RFL_Icon.c new file mode 100644 index 00000000..3d122cb6 --- /dev/null +++ b/src/RVLFaceLib/RFL_Icon.c @@ -0,0 +1,157 @@ +#include + +double atan2(double y, double x); + +inline float atan2f(float x, float y) { + return atan2(x, y); +} + +RFLErrcode RFLMakeIcon(void *buf, RFLDataSource source, RFLMiddleDB *middleDB, u16 index, RFLExpression expression, const RFLIconSetting *setting) { + RFLiCharInfo info; + RFLErrcode err = RFLiPickupCharInfo(&info, source, middleDB, index); + + if (err == RFLErrcode_Success) { + RFLiMakeIcon(buf, &info, expression, setting); + } + + return err; +} + +void RFLiMakeIcon(void* buf, RFLiCharInfo* info, RFLExpression expression, + const RFLIconSetting* setting) { + RFLCharModel model; // sp+218 + void* modelBuf; // sp+84 + f32 vp[6]; // sp+150 + u32 byteSize; // sp+80 + Mtx viewMtx; // sp+1e8 + u32 scissorOffsetX; // sp+7C + u32 scissorOffsetY; // sp+78 + u32 scissorWidth; // sp+74 + u32 scissorHeight; // sp+70 + RFLiCoordinateData iconCoordData; // sp+118 + RFLiCoordinateData coordData; // sp+128 + u32 bufSize; // sp+6C + RFLResolution resolution; // sp+68 + RFLExpFlag expFlag; // sp+64 + GXColor backColor; // sp+60 + Mtx44 projMtx; // sp+1A8 + f32 fovy; // f31 + f32 aspect; // f30 + Vec cameraPos; // sp+10C + Vec target; // sp+100 + Vec cameraUp; // sp+F4 + GXLightObj light; // sp+168 + Vec pos; // sp+E8 + RFLDrawSetting drawSetting; // sp+138 + + // buf -> r28 + // info -> r29 + // expression -> r27 + // setting -> r30 + + iconCoordData = (RFLiCoordinateData){1, 2, 0, FALSE, FALSE, FALSE}; + byteSize = setting->width * setting->height * sizeof(u16); + coordData = *RFLiGetCoordinateData(); + RFLiSetCoordinateData(&iconCoordData); + + if ((setting->width > 128 || setting->height > 128) && RFLiGetUseDeluxTex()) { + resolution = RFLResolution_256; + } else if (setting->width > 64 || setting->height > 64) { + resolution = RFLResolution_128; + } else { + resolution = RFLResolution_64; + } + + expFlag = 1 << expression; + bufSize = RFLGetModelBufferSize(resolution, expFlag); + modelBuf = RFLiAlloc32(bufSize); + RFLiInitCharModel(&model, info, modelBuf, resolution, expFlag); + RFLSetExpression(&model, expression); + + if (setting->bgType == RFLIconBG_Direct) { + backColor = setting->bgColor; + } else { + backColor = RFLGetFavoriteColor(info->personal.color); + } + backColor.a = 0; + + GXGetScissor(&scissorOffsetX, &scissorOffsetY, &scissorWidth, + &scissorHeight); + GXSetScissor(0, 0, setting->width, setting->height); + + RFLiSetupCopyTex(GX_RGBA8, setting->width, setting->height, buf, backColor); + + GXGetViewportv(vp); + GXSetViewport(0.0f, 0.0f, setting->width, setting->height, 0.0f, 1.0f); + + aspect = (f32)setting->width / (f32)setting->height; + if (setting->width < setting->height) { + fovy = 2 * ((180 / 3.141592653589793f) * atan2f(43.2f / aspect, 500.0f)); + } else { + fovy = 2 * ((180 / 3.141592653589793f) * atan2f(43.2f, 500.0f)); + } + + C_MTXPerspective(projMtx, fovy, aspect, 500.0f, 700.0f); + GXSetProjection(projMtx, GX_PERSPECTIVE); + + cameraPos = (Vec){0.0f, 34.5f, 600.0f}; + target = (Vec){0.0f, 34.5f, 0.0f}; + cameraUp = (Vec){0.0f, 1.0f, 0.0f}; + + C_MTXLookAt(viewMtx, &cameraPos, &cameraUp, &target); + GXInitLightColor(&light, (GXColor){255, 255, 255, 255}); + + pos = (Vec){1600.0f, 1500.0f, 6000.0f}; + + PSMTXMultVec(viewMtx, &pos, &pos); + GXInitLightPos(&light, pos.x, pos.y, pos.z); + GXLoadLightObjImm(&light, GX_LIGHT0); + RFLSetMtx(&model, viewMtx); + + drawSetting.lightEnable = TRUE; + drawSetting.lightMask = GX_LIGHT0; + drawSetting.diffuse = GX_DF_CLAMP; + drawSetting.attn = GX_AF_NONE; + drawSetting.ambColor = (GXColor){160, 160, 160, 255}; + drawSetting.compLoc = 0; + RFLLoadDrawSetting(&drawSetting); + + if (!setting->drawXluOnly) { + GXSetColorUpdate(TRUE); + GXSetAlphaUpdate(TRUE); + GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, + GX_LO_COPY); + RFLDrawOpa(&model); + } + + GXSetZMode(TRUE, GX_LEQUAL, FALSE); + GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); + GXSetColorUpdate(TRUE); + GXSetAlphaUpdate(FALSE); + RFLDrawXlu(&model); + + GXSetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_INVSRCALPHA, GX_LO_COPY); + GXSetAlphaUpdate(TRUE); + GXSetColorUpdate(FALSE); + RFLDrawXlu(&model); + + GXSetZMode(TRUE, GX_LEQUAL, TRUE); + GXSetColorUpdate(TRUE); + GXCopyTex(buf, TRUE); + GXPixModeSync(); + + if (RFLiGetManager()->iconDrawCb == NULL) { + GXDrawDone(); + } else { + RFLiGetManager()->iconDrawCb(); + } + + RFLiFree(modelBuf); + GXSetViewportv(vp); + GXSetScissor(scissorOffsetX, scissorOffsetY, scissorWidth, scissorHeight); + RFLiSetCoordinateData(&coordData); +} + +void RFLSetIconDrawDoneCallback(RFLCallback callback) { + RFLiGetManager()->iconDrawCb = callback; +} \ No newline at end of file diff --git a/src/RVLFaceLib/RFL_MakeRandomFace.c b/src/RVLFaceLib/RFL_MakeRandomFace.c new file mode 100644 index 00000000..82aa26d3 --- /dev/null +++ b/src/RVLFaceLib/RFL_MakeRandomFace.c @@ -0,0 +1,508 @@ +#include +#include + +void RFLi_MakeRandomFace(RFLiCharInfo* info, RFLSex sex, RFLAge age, + RFLRace race) { + RFLSex sex_; + RFLAge age_; + RFLRace race_; + u32 rand; + + if (sex == RFLSex_All) { + if (RFLi_GetRandU32(RFLSex_All) == RFLSex_Male) { + sex_ = RFLSex_Male; + } else { + sex_ = RFLSex_Female; + } + } else { + sex_ = sex; + } + + if (age == RFLAge_All) { + rand = RFLi_GetRandU32(10); + + if (rand < 4) { + age_ = RFLAge_Child; + } else if (rand < 8) { + age_ = RFLAge_Adult; + } else { + age_ = RFLAge_Elder; + } + } else { + age_ = age; + } + + switch (race) { + case RFLRace_Black: + race_ = RFLRace_Black; + break; + case RFLRace_White: + race_ = RFLRace_White; + break; + case RFLRace_Asian: + race_ = RFLRace_Asian; + break; + case RFLRace_All: + rand = RFLi_GetRandU32(10); + + if (rand < 4) { + race_ = RFLRace_Asian; + } else if (rand < 8) { + race_ = RFLRace_White; + } else { + race_ = RFLRace_Black; + } + break; + } + + RFLi_MakeRandomFace_Core(info, sex_, age_, race_); +} + +void RFLi_MakeRandomFace_Core(RFLiCharInfo* info, RFLSex sex, RFLAge age, + RFLRace race) { + int sex_ = sex; + int age_ = age; + int race_ = race; + u8 y = 0; + int i; + + if (sex_ == RFLSex_Female) { + y += RFLi_GetRandU32(3); + } + + if (age_ == RFLAge_Child) { + y += RFLi_GetRandU32(3); + } + + info->faceline.type = RFLi_GetFacelineType(sex_, age_, race_); + info->faceline.color = RFLi_GetFaceColor(sex_, race_); + info->faceline.texture = RFLi_GetFaceTexType(sex_, age_, race_); + + info->hair.type = RFLi_GetHairType(sex_, age_, race_); + info->hair.color = RFLi_GetHairColor(age_, race_); + info->hair.flip = RFLi_GetRandU32(TRUE + 1); + + info->eye.type = RFLi_GetEyeType(sex_, age_, race_); + info->eye.color = RFLi_GetEyeColor(race_); + info->eye.scale = 4; + if (sex_ == RFLSex_Male) { + info->eye.rotate = 4; + info->eye.rotate += + (RFLi_EYE_ROT_OFFSET[2] - RFLi_EYE_ROT_OFFSET[info->eye.type]); + } else { + info->eye.rotate = 3; + info->eye.rotate += + (RFLi_EYE_ROT_OFFSET[4] - RFLi_EYE_ROT_OFFSET[info->eye.type]); + } + info->eye.x = 2; + info->eye.y = y + 12; + + info->eyebrow.type = RFLi_GetEyebrowType(sex_, age_, race_); + info->eyebrow.color = info->hair.color; + info->eyebrow.scale = 4; + info->eyebrow.rotate = 6; + info->eyebrow.x = 2; + if (race_ == RFLRace_Asian) { + info->eyebrow.y = y + 9; + info->eyebrow.rotate += (RFLi_EYEBROW_ROT_OFFSET[6] - + RFLi_EYEBROW_ROT_OFFSET[info->eyebrow.type]); + } else { + info->eyebrow.y = y + 10; + info->eyebrow.rotate += (RFLi_EYEBROW_ROT_OFFSET[0] - + RFLi_EYEBROW_ROT_OFFSET[info->eyebrow.type]); + } + + info->nose.type = RFLi_GetNoseType(sex_, age_, race_); + if (sex_ == RFLSex_Male) { + info->nose.scale = 4; + } else { + info->nose.scale = 3; + } + info->nose.y = y + 9; + + info->mouth.type = RFLi_GetMouthType(sex_, age_, race_); + if (sex_ == RFLSex_Male) { + info->mouth.color = 0; + } else { + info->mouth.color = RFLi_GetRandU32(RFLi_MAX_MOUTH_COLOR + 1); + } + info->mouth.scale = 4; + info->mouth.y = y + 13; + + if (sex_ == RFLSex_Male && (age_ == RFLAge_Adult || age_ == RFLAge_Elder) && + RFLi_GetRandU32(10) < 2) { + u32 r = RFLi_GetRandU32(3); + + if (r == 0) { + info->beard.mustache = 0; + info->beard.type = RFLi_GetRandU32(RFLi_MAX_BEARD_TYPE) + 1; + } else if (r == 1) { + info->beard.mustache = RFLi_GetRandU32(RFLi_MAX_BEARD_MUSTACHE) + 1; + info->beard.type = 0; + } else if (r == 2) { + info->beard.mustache = RFLi_GetRandU32(RFLi_MAX_BEARD_MUSTACHE) + 1; + info->beard.type = RFLi_GetRandU32(RFLi_MAX_BEARD_TYPE) + 1; + } + + info->beard.color = info->hair.color; + info->beard.scale = 4; + info->beard.y = 10; + } else { + info->beard.mustache = 0; + info->beard.type = 0; + info->beard.color = 0; + info->beard.scale = 0; + info->beard.y = 0; + } + + info->glass.type = RFLi_GetGlassType(age_); + info->glass.color = 0; + info->glass.scale = 4; + info->glass.y = y + 10; + + info->mole.type = 0; + info->mole.scale = 4; + info->mole.x = RFLi_GetRandU32(RFLi_MAX_MOLE_X + 1); + info->mole.y = RFLi_GetRandU32(RFLi_MAX_MOLE_Y + 1); + + info->body.height = RFLi_MAX_BODY_HEIGHT / 2; + info->body.build = RFLi_MAX_BODY_BUILD / 2; + + for (i = 0; i < RFL_NAME_LEN + 1; i++) { + info->personal.name[i] = L'\0'; + } + wcsncpy(info->personal.name, L"no name", + sizeof(L"no name") / sizeof(wchar_t) - 1); + + for (i = 0; i < RFL_CREATOR_LEN + 1; i++) { + info->personal.creator[i] = L'\0'; + } + + info->personal.sex = sex_; + info->personal.bmonth = 0; + info->personal.bday = 0; + info->personal.color = RFLi_GetRandU32(RFLFavoriteColor_Max); + info->personal.favorite = FALSE; + info->personal.localOnly = FALSE; +} + +u8 RFLi_GetFacelineType(RFLSex sex, RFLAge age, RFLRace race) { + static const u8 + facelineTypeTable[RFLSex_All * RFLAge_All * RFLRace_All][10] = { + // clang-format off + /* Male, Child, Black */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Male, Child, White */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Male, Child, Asian */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Male, Adult, Black */ {0, 0, 1, 2, 2, 3, 4, 5, 6, 7}, + /* Male, Adult, White */ {0, 1, 2, 2, 3, 4, 5, 6, 6, 7}, + /* Male, Adult, Asian */ {0, 0, 1, 2, 2, 3, 4, 5, 6, 7}, + /* Male, Elder, Black */ {0, 0, 1, 2, 2, 3, 4, 5, 6, 7}, + /* Male, Elder, White */ {0, 1, 2, 2, 3, 4, 5, 6, 7, 7}, + /* Male, Elder, Asian */ {0, 0, 1, 2, 2, 3, 4, 5, 6, 7}, + /* Female, Child, Black */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Female, Child, White */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Female, Child, Asian */ {0, 0, 0, 1, 1, 1, 2, 3, 4, 5}, + /* Female, Adult, Black */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5}, + /* Female, Adult, White */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5}, + /* Female, Adult, Asian */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5}, + /* Female, Elder, Black */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5}, + /* Female, Elder, White */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5}, + /* Female, Elder, Asian */ {0, 0, 0, 1, 1, 2, 2, 3, 4, 5} + // clang-format on + }; + + return facelineTypeTable[(sex * 9) + (age * 3) + race] + [RFLi_GetRandU32(LENGTHOF(facelineTypeTable[0]))]; +} + +u8 RFLi_GetHairType(RFLSex sex, RFLAge age, RFLRace race) { + const u8 hair_parts[RFLSex_All * RFLAge_All * RFLRace_All][72] = { + // clang-format off + /* Male, Child, Black */ {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Child, White */ {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Child, Asian */ {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, Black */ {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, White */ {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, Asian */ {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, Black */ {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, White */ {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, Asian */ {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Black */ { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, White */ { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Asian */ { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50, 58, 60, 62, 63, 64, 69, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 60, 62, 63, 64, 69, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 60, 62, 63, 64, 69, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Black */ { 0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, White */ { 0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Asian */ { 0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + // clang-format on + }; + + const u8 hair_parts_num[RFLSex_All * RFLAge_All * RFLRace_All] = { + 26, 26, 26, 34, 34, 34, 16, 17, 17, 33, 33, 33, 37, 36, 36, 22, 22, 22}; + + u8 index = (sex * 9) + (age * 3) + race; + return hair_parts[index][RFLi_GetRandU32(hair_parts_num[index])]; +} + +u8 RFLi_GetEyeType(RFLSex sex, RFLAge age, RFLRace race) { + const u8 eye_parts[RFLSex_All * RFLAge_All * RFLRace_All][48] = { + // clang-format off + /* Male, Child, Black */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Child, White */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Child, Asian */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27, 29, 32, 34, 36, 38, 39, 41, 43, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, Black */ { 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, White */ { 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, Asian */ { 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29, 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, Black */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 31, 32, 34, 36, 37, 39, 41, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, White */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 31, 32, 34, 36, 37, 39, 41, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Elder, Asian */ { 2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22, 26, 31, 32, 34, 36, 37, 39, 41, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Black */ { 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, White */ { 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Asian */ { 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Black */ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, White */ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Asian */ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Black */ { 1, 2, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 32, 34, 37, 39, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, White */ { 1, 2, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 32, 34, 37, 39, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Asian */ { 1, 2, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 26, 32, 34, 37, 39, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + // clang-format on + }; + + const u8 eye_parts_num[RFLSex_All * RFLAge_All * RFLRace_All] = { + 22, 22, 23, 29, 29, 29, 24, 24, 24, 34, 34, 35, 40, 40, 40, 20, 20, 20}; + + u8 index = (sex * 9) + (age * 3) + race; + return eye_parts[index][RFLi_GetRandU32(eye_parts_num[index])]; +} + +u8 RFLi_GetEyebrowType(RFLSex sex, RFLAge age, RFLRace race) { + static const u8 eyebrow_parts[RFLSex_All * RFLAge_All * RFLRace_All][24] = { + // clang-format off + /* Male, Child, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20, 0, 0, 0, 0, 0, 0}, + /* Male, Child, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20, 0, 0, 0, 0, 0, 0}, + /* Male, Child, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20, 0, 0, 0, 0, 0, 0}, + /* Male, Adult, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0}, + /* Male, Adult, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0}, + /* Male, Adult, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0}, + /* Male, Elder, Black */ { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 0, 0, 0}, + /* Male, Elder, White */ { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 0, 0, 0}, + /* Male, Elder, Asian */ { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 0, 0, 0}, + /* Female, Child, Black */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, White */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Asian */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Black */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, White */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Asian */ { 0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Black */ { 0, 3, 7, 8, 9, 10, 11, 13, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, White */ { 0, 3, 7, 8, 9, 10, 11, 13, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Female, Elder, Asian */ { 0, 3, 7, 8, 9, 10, 11, 13, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + // clang-format on + }; + + const u8 eyebrow_parts_num[RFLSex_All * RFLAge_All * RFLRace_All] = { + 18, 18, 18, 23, 23, 23, 21, 21, 21, 9, 9, 9, 11, 11, 11, 9, 9, 9}; + + u8 index = (sex * 9) + (age * 3) + race; + return eyebrow_parts[index][RFLi_GetRandU32(eyebrow_parts_num[index])]; +} + +u8 RFLi_GetNoseType(RFLSex sex, RFLAge age, RFLRace race) { + const u8 nose_parts[RFLSex_All * RFLAge_All * RFLRace_All][12] = { + // clang-format off + /* Male, Child, Black */ { 0, 1, 2, 3, 4, 5, 7, 8, 10, 0, 0, 0}, + /* Male, Child, White */ { 0, 1, 2, 3, 4, 5, 7, 8, 10, 0, 0, 0}, + /* Male, Child, Asian */ { 0, 1, 2, 3, 4, 5, 7, 8, 10, 0, 0, 0}, + /* Male, Adult, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + /* Male, Adult, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + /* Male, Adult, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 0}, + /* Male, Elder, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + /* Male, Elder, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + /* Male, Elder, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 0}, + /* Female, Child, Black */ { 0, 1, 3, 4, 8, 10, 0, 0, 0, 0, 0, 0}, + /* Female, Child, White */ { 0, 1, 3, 4, 8, 10, 0, 0, 0, 0, 0, 0}, + /* Female, Child, Asian */ { 0, 1, 3, 4, 8, 10, 0, 0, 0, 0, 0, 0}, + /* Female, Adult, Black */ { 0, 1, 3, 4, 6, 8, 9, 10, 11, 0, 0, 0}, + /* Female, Adult, White */ { 0, 1, 3, 4, 6, 8, 9, 10, 11, 0, 0, 0}, + /* Female, Adult, Asian */ { 0, 1, 3, 4, 6, 8, 10, 11, 0, 0, 0, 0}, + /* Female, Elder, Black */ { 0, 1, 3, 4, 6, 8, 9, 10, 11, 0, 0, 0}, + /* Female, Elder, White */ { 0, 1, 3, 4, 6, 8, 9, 10, 11, 0, 0, 0}, + /* Female, Elder, Asian */ { 0, 1, 3, 4, 6, 8, 10, 11, 0, 0, 0, 0} + // clang-format on + }; + + const u8 nose_parts_num[RFLSex_All * RFLAge_All * RFLRace_All] = { + 9, 9, 9, 12, 12, 11, 12, 12, 11, 6, 6, 6, 9, 9, 8, 9, 9, 8}; + + u8 index = (sex * 9) + (age * 3) + race; + return nose_parts[index][RFLi_GetRandU32(nose_parts_num[index])]; +} + +u8 RFLi_GetMouthType(RFLSex sex, RFLAge age, RFLRace race) { + const u8 mouth_parts[RFLSex_All * RFLAge_All * RFLRace_All][24] = { + // clang-format off + /* Male, Child, Black */ { 0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0}, + /* Male, Child, White */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Male, Child, Asian */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Male, Adult, Black */ { 0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Male, Adult, White */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0}, + /* Male, Adult, Asian */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0}, + /* Male, Elder, Black */ { 0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Male, Elder, White */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0}, + /* Male, Elder, Asian */ { 0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0}, + /* Female, Child, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Female, Child, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0}, + /* Female, Child, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0}, + /* Female, Adult, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0, 0}, + /* Female, Adult, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0}, + /* Female, Adult, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0}, + /* Female, Elder, Black */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 16, 17, 18, 19, 21, 22, 23, 0, 0, 0, 0}, + /* Female, Elder, White */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 0, 0, 0}, + /* Female, Elder, Asian */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 0, 0, 0} + // clang-format on + }; + + const u8 mouth_parts_num[RFLSex_All * RFLAge_All * RFLRace_All] = { + 17, 19, 19, 19, 21, 21, 19, 21, 21, 19, 20, 20, 19, 20, 20, 20, 21, 21}; + + u8 index = (sex * 9) + (age * 3) + race; + return mouth_parts[index][RFLi_GetRandU32(mouth_parts_num[index])]; +} + +u8 RFLi_GetFaceTexType(RFLSex sex, RFLAge age, RFLRace race) { + static const u8 + faceTexTypeTable[RFLSex_All * RFLAge_All * RFLRace_All][20] = { + // clang-format off + /* Male, Child, Black */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3}, + /* Male, Child, White */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 8, 8}, + /* Male, Child, Asian */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3}, + /* Male, Adult, Black */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10}, + /* Male, Adult, White */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10}, + /* Male, Adult, Asian */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 6, 7, 9, 10}, + /* Male, Elder, Black */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + /* Male, Elder, White */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + /* Male, Elder, Asian */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + /* Female, Child, Black */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, + /* Female, Child, White */ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4}, + /* Female, Child, Asian */ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4}, + /* Female, Adult, Black */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, + /* Female, Adult, White */ { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 8, 8}, + /* Female, Adult, Asian */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4}, + /* Female, Elder, Black */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + /* Female, Elder, White */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, + /* Female, Elder, Asian */ {10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11} + // clang-format on + }; + + return faceTexTypeTable[(sex * 9) + (age * 3) + race] + [RFLi_GetRandU32(LENGTHOF(faceTexTypeTable[0]))]; +} + +u8 RFLi_GetGlassType(RFLAge age) { + u32 rand = RFLi_GetRandU32(100); + + switch (age) { + case RFLAge_Child: + if (rand < 90) { + return 0; + } + if (rand < 94) { + return 1; + } + if (rand < 96) { + return 2; + } + return 3; + case RFLAge_Adult: + if (rand < 83) { + return 0; + } + if (rand < 86) { + return 1; + } + if (rand < 90) { + return 2; + } + if (rand < 93) { + return 3; + } + if (rand < 94) { + return 4; + } + if (rand < 96) { + return 5; + } + if (rand < 98) { + return 6; + } + return 7; + case RFLAge_Elder: + if (rand < 78) { + return 0; + } + if (rand < 83) { + return 1; + } + if (rand < 93) { + return 3; + } + if (rand < 98) { + return 6; + } + return 7; + default: + return 0; + } +} + +u8 RFLi_GetFaceColor(RFLSex sex, RFLRace race) { + static const u8 faceColorTable[RFLSex_All * RFLRace_All][10] = { + // clang-format off + /* Male, Black */ {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, + /* Male, White */ {0, 0, 0, 0, 1, 1, 2, 3, 3, 3}, + /* Male, Asian */ {0, 0, 1, 1, 1, 1, 1, 1, 1, 2}, + /* Female, Black */ {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, + /* Female, White */ {0, 0, 0, 0, 0, 0, 0, 0, 1, 3}, + /* Female, Asian */ {0, 0, 0, 0, 0, 0, 0, 0, 1, 1} + // clang-format on + }; + + return faceColorTable[(sex * 3) + race] + [RFLi_GetRandU32(LENGTHOF(faceColorTable[0]))]; +} + +u8 RFLi_GetHairColor(RFLAge age, RFLRace race) { + static const u8 hairColorTable[RFLAge_All * RFLRace_All][20] = { + // clang-format off + /* Black, Child */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Black, Adult */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Black, Elder */ {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + /* White, Child */ {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7}, + /* White, Adult */ {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, + /* White, Elder */ {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, + /* Asian, Child */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, + /* Asian, Adult */ {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3}, + /* Asian, Elder */ {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4} + // clang-format on + }; + + return hairColorTable[(race * 4 - race) + age] + [RFLi_GetRandU32(LENGTHOF(hairColorTable[0]))]; +} + +u8 RFLi_GetEyeColor(RFLRace race) { + static const u8 eyeColorTable[RFLRace_All][10] = { + // clang-format off + /* Black */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + /* White */ {0, 1, 1, 2, 3, 3, 4, 4, 4, 5}, + /* Asian */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + // clang-format on + }; + + return eyeColorTable[race][RFLi_GetRandU32(LENGTHOF(eyeColorTable[0]))]; +} + +u32 RFLi_GetRandU32(u32 max) { + return OSGetTick() % max; +} diff --git a/src/RVLFaceLib/RFL_MakeTex.c b/src/RVLFaceLib/RFL_MakeTex.c new file mode 100644 index 00000000..3c03107a --- /dev/null +++ b/src/RVLFaceLib/RFL_MakeTex.c @@ -0,0 +1,914 @@ +#include +#include + +void RFLiSetupCopyTex(GXTexFmt fmt, u16 width, u16 height, void* buffer, + GXColor clearColor) { + GXSetFog(GX_FOG_NONE, 1.0f, 1.0f, 0.0f, 0.0f, (GXColor){0, 0, 0, 0}); + GXSetColorUpdate(TRUE); + GXSetAlphaUpdate(TRUE); + GXSetDstAlpha(FALSE, 0); + GXSetZMode(TRUE, GX_LEQUAL, TRUE); + GXSetPixelFmt(GX_PF_RGBA6_Z24, GX_ZC_LINEAR); + GXSetCopyFilter(FALSE, NULL, FALSE, NULL); + GXSetCopyClamp(3); + GXSetTexCopySrc(0, 0, width, height); + GXSetTexCopyDst(width, height, fmt, FALSE); + GXSetCopyClear(clearColor, 0xFFFFFF); + DCInvalidateRange(buffer, 2 * width * height); + GXCopyTex(buffer, TRUE); + GXPixModeSync(); +} + +void RFLiMakeTexture(const RFLiCharInfo* info, u8** buffers, + RFLResolution resolution) { + int i; + RFLiCharInfo infos[RFLExp_Max]; + RFLiFaceParts parts[RFLExp_Max]; + u8** eyeBuf[RFLExp_Max]; + u8** mouthBuf[RFLExp_Max]; + RFLiTexture** eyeTex[RFLExp_Max]; + RFLiTexture** mouthTex[RFLExp_Max]; + + u32 eyeSize; + u32 eyebrowSize; + u32 mouthSize; + u32 mustacheSize; + u32 moleSize; + + u8* eyeNormal; + u8* eyebrowNormal; + u8* mouthNormal; + u8* mustacheNormal; + u8* moleNormal; + + u8* eyeSmile; + u8* mouthAnger; + u8* eyeSorrow; + u8* mouthSorrow; + u8* eyeSurprise; + u8* eyeBlink; + u8* mouthOpen; + + RFLiTexture* eyeNormalTex; + RFLiTexture* eyebrowNormalTex; + RFLiTexture* mouthNormalTex; + RFLiTexture* mustacheNormalTex; + RFLiTexture* moleNormalTex; + RFLiTexture* eyeSmileTex; + RFLiTexture* mouthAngerTex; + RFLiTexture* eyeSadTex; + RFLiTexture* mouthSadTex; + RFLiTexture* eyeSurpriseTex; + RFLiTexture* eyeBlinkTex; + RFLiTexture* mouthOpenTex; + + u32 sx; + u32 sy; + u32 sw; + u32 sh; + + RFLi_MASKRSL max; + u32 size; + u8* ptr; + + eyeSize = RFLiGetTexSize(RFLiPartsTex_Eye, 0); + eyebrowSize = RFLiGetTexSize(RFLiPartsTex_Eyebrow, 0); + mouthSize = RFLiGetTexSize(RFLiPartsTex_Mouth, 0); + mustacheSize = RFLiGetTexSize(RFLiPartsTex_Mustache, 0); + moleSize = RFLiGetTexSize(RFLiPartsTex_Mole, 0); + + eyeNormal = NULL; + eyebrowNormal = NULL; + mouthNormal = NULL; + mustacheNormal = NULL; + moleNormal = NULL; + + eyeSmile = NULL; + mouthAnger = NULL; + eyeSorrow = NULL; + mouthSorrow = NULL; + eyeSurprise = NULL; + eyeBlink = NULL; + mouthOpen = NULL; + + eyeBuf[RFLExp_Normal] = &eyeNormal; + eyeBuf[RFLExp_Smile] = &eyeSmile; + eyeBuf[RFLExp_Anger] = &eyeNormal; + eyeBuf[RFLExp_Sorrow] = &eyeSorrow; + eyeBuf[RFLExp_Surprise] = &eyeSurprise; + eyeBuf[RFLExp_Blink] = &eyeBlink; + eyeBuf[RFLExp_OpenMouth] = &eyeNormal; + + mouthBuf[RFLExp_Normal] = &mouthNormal; + mouthBuf[RFLExp_Smile] = &mouthNormal; + mouthBuf[RFLExp_Anger] = &mouthAnger; + mouthBuf[RFLExp_Sorrow] = &mouthSorrow; + mouthBuf[RFLExp_Surprise] = &mouthNormal; + mouthBuf[RFLExp_Blink] = &mouthNormal; + mouthBuf[RFLExp_OpenMouth] = &mouthOpen; + + eyeNormalTex = NULL; + eyebrowNormalTex = NULL; + mouthNormalTex = NULL; + mustacheNormalTex = NULL; + moleNormalTex = NULL; + eyeSmileTex = NULL; + mouthAngerTex = NULL; + eyeSadTex = NULL; + mouthSadTex = NULL; + eyeSurpriseTex = NULL; + eyeBlinkTex = NULL; + mouthOpenTex = NULL; + + eyeTex[RFLExp_Normal] = &eyeNormalTex; + eyeTex[RFLExp_Smile] = &eyeSmileTex; + eyeTex[RFLExp_Anger] = &eyeNormalTex; + eyeTex[RFLExp_Sorrow] = &eyeSadTex; + eyeTex[RFLExp_Surprise] = &eyeSurpriseTex; + eyeTex[RFLExp_Blink] = &eyeBlinkTex; + eyeTex[RFLExp_OpenMouth] = &eyeNormalTex; + + mouthTex[RFLExp_Normal] = &mouthNormalTex; + mouthTex[RFLExp_Smile] = &mouthNormalTex; + mouthTex[RFLExp_Anger] = &mouthAngerTex; + mouthTex[RFLExp_Sorrow] = &mouthSadTex; + mouthTex[RFLExp_Surprise] = &mouthNormalTex; + mouthTex[RFLExp_Blink] = &mouthNormalTex; + mouthTex[RFLExp_OpenMouth] = &mouthOpenTex; + + if (buffers[RFLExp_Normal] != NULL) { + infos[RFLExp_Normal] = *info; + } + + if (buffers[RFLExp_Smile] != NULL) { + int changeEyeRot = 0; + RFLExpression expr = RFLExp_Smile; + + infos[expr] = *info; + infos[expr].eye.type = 48; + + changeEyeRot = RFLi_EYE_ROT_OFFSET[info->eye.type] - + RFLi_EYE_ROT_OFFSET[infos[expr].eye.type]; + if (changeEyeRot + (int)infos[expr].eye.rotate < 0) { + infos[expr].eye.rotate = 0; + } else if (changeEyeRot + (int)infos[expr].eye.rotate > + RFLi_MAX_EYE_ROTATE) { + infos[expr].eye.rotate = RFLi_MAX_EYE_ROTATE; + } else { + infos[expr].eye.rotate += changeEyeRot; + } + } + + if (buffers[RFLExp_Anger] != NULL) { + int changeEyebrowRot = 0; + int changeEyeRot = 0; + RFLExpression expr = RFLExp_Anger; + + infos[expr] = *info; + + changeEyebrowRot = 2; + if (changeEyebrowRot + (int)infos[expr].eyebrow.rotate < 0) { + infos[expr].eyebrow.rotate = 0; + } else if (changeEyebrowRot + (int)infos[expr].eyebrow.rotate > + RFLi_MAX_EYEBROW_ROTATE) { + infos[expr].eyebrow.rotate = RFLi_MAX_EYEBROW_ROTATE; + } else { + infos[expr].eyebrow.rotate += changeEyebrowRot; + } + + changeEyeRot = 2; + if (changeEyeRot + (int)infos[expr].eye.rotate < 0) { + infos[expr].eye.rotate = 0; + } else if (changeEyeRot + (int)infos[expr].eye.rotate > + RFLi_MAX_EYE_ROTATE) { + infos[expr].eye.rotate = RFLi_MAX_EYE_ROTATE; + } else { + infos[expr].eye.rotate += changeEyeRot; + } + + infos[expr].mouth.type = 10; + } + + if (buffers[RFLExp_Sorrow] != NULL) { + int changeEyebrowRot = 0; + int changeEyeRot = 0; + RFLExpression expr = RFLExp_Sorrow; + + infos[expr] = *info; + + changeEyebrowRot = -2; + if (changeEyebrowRot + (int)infos[expr].eyebrow.rotate < 0) { + infos[expr].eyebrow.rotate = 0; + } else if (changeEyebrowRot + (int)infos[expr].eyebrow.rotate > + RFLi_MAX_EYEBROW_ROTATE) { + infos[expr].eyebrow.rotate = RFLi_MAX_EYEBROW_ROTATE; + } else { + infos[expr].eyebrow.rotate += changeEyebrowRot; + } + + changeEyeRot = -2; + if (changeEyeRot + (int)infos[expr].eye.rotate < 0) { + infos[expr].eye.rotate = 0; + } else if (changeEyeRot + (int)infos[expr].eye.rotate > + RFLi_MAX_EYE_ROTATE) { + infos[expr].eye.rotate = RFLi_MAX_EYE_ROTATE; + } else { + infos[expr].eye.rotate += changeEyeRot; + } + + infos[expr].mouth.type = 12; + } + + if (buffers[RFLExp_Surprise] != NULL) { + int changeEyeRot = 0; + RFLExpression expr = RFLExp_Surprise; + + infos[expr] = *info; + infos[expr].eyebrow.y -= 2; + infos[expr].eye.type = 49; + + changeEyeRot = RFLi_EYE_ROT_OFFSET[info->eye.type] - + RFLi_EYE_ROT_OFFSET[infos[expr].eye.type]; + if (changeEyeRot + (int)infos[expr].eye.rotate < 0) { + infos[expr].eye.rotate = 0; + } else if (changeEyeRot + (int)infos[expr].eye.rotate > + RFLi_MAX_EYE_ROTATE) { + infos[expr].eye.rotate = RFLi_MAX_EYE_ROTATE; + } else { + infos[expr].eye.rotate += changeEyeRot; + } + } + + if (buffers[RFLExp_Blink] != NULL) { + int changeEyeRot = 0; + RFLExpression expr = RFLExp_Blink; + + infos[expr] = *info; + infos[expr].eye.type = 26; + + changeEyeRot = RFLi_EYE_ROT_OFFSET[info->eye.type] - + RFLi_EYE_ROT_OFFSET[infos[expr].eye.type]; + if (changeEyeRot + (int)infos[expr].eye.rotate < 0) { + infos[expr].eye.rotate = 0; + } else if (changeEyeRot + (int)infos[expr].eye.rotate > + RFLi_MAX_EYE_ROTATE) { + infos[expr].eye.rotate = RFLi_MAX_EYE_ROTATE; + } else { + infos[expr].eye.rotate += changeEyeRot; + } + } + + if (buffers[RFLExp_OpenMouth] != NULL) { + RFLExpression expr = RFLExp_OpenMouth; + + infos[expr] = *info; + infos[expr].mouth.type = 24; + } + + for (i = 0; i < RFLExp_Max; i++) { + if (buffers[i] == NULL) { + continue; + } + + if (*eyeBuf[i] == NULL) { + *eyeBuf[i] = RFLiAlloc32(eyeSize); + } + + if (*mouthBuf[i] == NULL) { + *mouthBuf[i] = RFLiAlloc32(mouthSize); + } + + if (eyebrowNormal == NULL) { + eyebrowNormal = RFLiAlloc32(eyebrowSize); + } + + if (mustacheNormal == NULL) { + mustacheNormal = RFLiAlloc32(mustacheSize); + } + + if (moleNormal == NULL) { + moleNormal = RFLiAlloc32(moleSize); + } + + if (*eyeTex[i] == NULL) { + *eyeTex[i] = RFLiLoadTexture(RFLiPartsTex_Eye, infos[i].eye.type, + *eyeBuf[i]); + DCStoreRange(*eyeBuf[i], eyeSize); + } + + if (*mouthTex[i] == NULL) { + *mouthTex[i] = RFLiLoadTexture(RFLiPartsTex_Mouth, + infos[i].mouth.type, *mouthBuf[i]); + DCStoreRange(*mouthBuf[i], mouthSize); + } + + if (eyebrowNormalTex == NULL) { + eyebrowNormalTex = RFLiLoadTexture( + RFLiPartsTex_Eyebrow, infos[i].eyebrow.type, eyebrowNormal); + DCStoreRange(eyebrowNormal, eyebrowSize); + } + + if (mustacheNormalTex == NULL) { + mustacheNormalTex = RFLiLoadTexture( + RFLiPartsTex_Mustache, infos[i].beard.mustache, mustacheNormal); + DCStoreRange(mustacheNormal, mustacheSize); + } + + if (moleNormalTex == NULL) { + moleNormalTex = RFLiLoadTexture(RFLiPartsTex_Mole, + infos[i].mole.type, moleNormal); + DCStoreRange(moleNormal, moleSize); + } + + parts[i].eyeR.ngtobj.texture = *eyeTex[i]; + parts[i].eyeL.ngtobj.texture = *eyeTex[i]; + + parts[i].eyebrowR.ngtobj.texture = eyebrowNormalTex; + parts[i].eyebrowL.ngtobj.texture = eyebrowNormalTex; + + parts[i].mouth.ngtobj.texture = *mouthTex[i]; + + parts[i].mustacheR.ngtobj.texture = mustacheNormalTex; + parts[i].mustacheL.ngtobj.texture = mustacheNormalTex; + + parts[i].mole.ngtobj.texture = moleNormalTex; + + RFLiInitRFLTexture(&parts[i].eyeR.ngtobj); + RFLiInitRFLTexture(&parts[i].eyeL.ngtobj); + + RFLiInitRFLTexture(&parts[i].eyebrowR.ngtobj); + RFLiInitRFLTexture(&parts[i].eyebrowL.ngtobj); + + RFLiInitRFLTexture(&parts[i].mouth.ngtobj); + + RFLiInitRFLTexture(&parts[i].mustacheR.ngtobj); + RFLiInitRFLTexture(&parts[i].mustacheL.ngtobj); + + RFLiInitRFLTexture(&parts[i].mole.ngtobj); + } + + max = RFLiGetMaxMaskRsl(resolution); + + for (i = 0; i < RFLExp_Max; i++) { + if (buffers[i] != NULL) { + break; + } + } + + RFLiSetupCopyTex(GX_TF_RGB5A3, max, max, buffers[i], (GXColor){0, 0, 0, 0}); + + size = RFLiGetMaskBufSize(resolution); + for (i = 0; i < RFLExp_Max; i++) { + if (buffers[i] != NULL) { + DCInvalidateRange(buffers[i], size); + } + } + + GXGetScissor(&sx, &sy, &sw, &sh); + for (i = 0; i < RFLExp_Max; i++) { + if (buffers[i] == NULL) { + continue; + } + + ptr = buffers[i]; + + if (resolution & 256) { + GXSetTexCopySrc(0, 0, 256, 256); + GXSetTexCopyDst(256, 256, GX_TF_RGB5A3, FALSE); + GXSetScissor(0, 0, 256, 256); + RFLiSetFaceParts(&infos[i], &parts[i], RFLi_MASKRSL_256); + RFLiCapture(ptr, &infos[i], &parts[i], RFLi_MASKRSL_256); + ptr += RFLiGetMaskSize(RFLi_MASKRSL_256); + } + + if (resolution & 128) { + GXSetTexCopySrc(0, 0, 128, 128); + GXSetTexCopyDst(128, 128, GX_TF_RGB5A3, FALSE); + GXSetScissor(0, 0, 128, 128); + RFLiSetFaceParts(&infos[i], &parts[i], RFLi_MASKRSL_128); + RFLiCapture(ptr, &infos[i], &parts[i], RFLi_MASKRSL_128); + ptr += RFLiGetMaskSize(RFLi_MASKRSL_128); + } + + if (resolution & 64) { + GXSetTexCopySrc(0, 0, 64, 64); + GXSetTexCopyDst(64, 64, GX_TF_RGB5A3, FALSE); + GXSetScissor(0, 0, 64, 64); + RFLiSetFaceParts(&infos[i], &parts[i], RFLi_MASKRSL_64); + RFLiCapture(ptr, &infos[i], &parts[i], RFLi_MASKRSL_64); + ptr += RFLiGetMaskSize(RFLi_MASKRSL_64); + } + + if (resolution & 32) { + GXSetTexCopySrc(0, 0, 32, 32); + GXSetTexCopyDst(32, 32, GX_TF_RGB5A3, FALSE); + GXSetScissor(0, 0, 32, 32); + RFLiSetFaceParts(&infos[i], &parts[i], RFLi_MASKRSL_32); + RFLiCapture(ptr, &infos[i], &parts[i], RFLi_MASKRSL_32); + ptr += RFLiGetMaskSize(RFLi_MASKRSL_32); + } + } + + if (eyeNormal != NULL) { + RFLiFree(eyeNormal); + } + + if (eyebrowNormal != NULL) { + RFLiFree(eyebrowNormal); + } + + if (mouthNormal != NULL) { + RFLiFree(mouthNormal); + } + + if (mustacheNormal != NULL) { + RFLiFree(mustacheNormal); + } + + if (moleNormal != NULL) { + RFLiFree(moleNormal); + } + + if (eyeSmile != NULL) { + RFLiFree(eyeSmile); + } + + if (mouthAnger != NULL) { + RFLiFree(mouthAnger); + } + + if (eyeSorrow != NULL) { + RFLiFree(eyeSorrow); + } + + if (mouthSorrow != NULL) { + RFLiFree(mouthSorrow); + } + + if (eyeSurprise != NULL) { + RFLiFree(eyeSurprise); + } + + if (eyeBlink != NULL) { + RFLiFree(eyeBlink); + } + + if (mouthOpen != NULL) { + RFLiFree(mouthOpen); + } + + GXSetScissor(sx, sy, sw, sh); +} + +void RFLiInitRFLTexture(RFLiTexObj* tobj) { + RFLiTexture* tex = tobj->texture; + + GXInitTexObj(&tobj->tobj, RFLiGetTexImage(tex), tex->width, tex->height, + tex->format, GX_CLAMP, GX_CLAMP, FALSE); + GXInitTexObjLOD(&tobj->tobj, tex->minFilt, tex->magFilt, tex->minLOD, + tex->maxLOD, tex->lodBias, tex->enableBiasClamp, + tex->enableEdgeLOD, tex->enableMaxAniso); +} + +void RFLiSetup2DCameraAndParam(void) { + Mtx44 proj; + + C_MTXOrtho(proj, 0.0f, 448.0f, 0.0f, 608.0f, 0.0f, 1.0f); + GXSetProjection(proj, GX_ORTHOGRAPHIC); + GXSetViewport(0.0f, 0.0f, 608.0f, 448.0f, 0.0f, 1.0f); + GXSetZScaleOffset(1.0f, 0.0f); + GXSetCullMode(GX_CULL_BACK); + GXSetZMode(FALSE, GX_LEQUAL, FALSE); + GXSetZCompLoc(FALSE); + GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_NEVER, 0); + GXSetAlphaUpdate(TRUE); + GXSetDither(FALSE); + GXClearVtxDesc(); + GXInvalidateVtxCache(); + GXSetVtxDesc(GX_VA_POS, GX_DIRECT); + GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 8); + GXSetNumChans(1); + GXSetChanCtrl(GX_COLOR0A0, FALSE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, + GX_DF_NONE, 2); + GXSetNumTexGens(1); + GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 60, 0, 0x7D); + GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, + GX_CH_ALPHA); + GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, + GX_CH_ALPHA); + GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, + GX_CH_ALPHA); + GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, + GX_CH_ALPHA); +} + +void RFLiSetTev4Mole(void) { + GXSetNumTevStages(1); + + GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevColor(GX_TEVREG0, RFLi_MOLE_COLOR0); +} + +void RFLiSetTev4Mouth(u32 color) { + GXSetNumTevStages(3); + + GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP1, GX_TEV_SWAP1); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_C0, GX_CC_TEXC, GX_CC_ZERO); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + + GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP3, GX_TEV_SWAP2); + GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_C1, GX_CC_TEXC, + GX_CC_CPREV); + GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_APREV); + GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + + GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP2, GX_TEV_SWAP3); + GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_ONE, GX_CC_TEXC, + GX_CC_CPREV); + GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_APREV); + GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + + GXSetTevColor(GX_TEVREG0, RFLi_MOUTH_COLOR0[color]); + GXSetTevColor(GX_TEVREG1, RFLi_MOUTH_COLOR1[color]); +} + +void RFLiSetTev4Eye(u32 color, u32 type) { + GXColor color0; + GXColor color1; + + GXSetNumTevStages(3); + GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP1, GX_TEV_SWAP1); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_C0, GX_CC_TEXC, GX_CC_ZERO); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP3, GX_TEV_SWAP3); + GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_C1, GX_CC_TEXC, + GX_CC_CPREV); + GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_APREV); + GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP2, GX_TEV_SWAP2); + GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_ONE, GX_CC_TEXC, + GX_CC_CPREV); + GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_APREV); + GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + + switch (type) { + case 9: + color0 = (GXColor){255, 130, 0, 255}; + break; + case 20: + color0 = (GXColor){0, 255, 255, 255}; + break; + default: + color0 = (GXColor){0, 0, 0, 255}; + break; + } + + color1 = RFLi_EYE_COLOR1[color]; + + GXSetTevColor(GX_TEVREG0, color0); + GXSetTevColor(GX_TEVREG1, color1); +} + +void RFLiSetTev4Eyebrow(u32 color) { + GXSetNumTevStages(1); + GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevColor(GX_TEVREG0, RFLi_EYEBROW_COLOR0[color]); +} + +void RFLiSetTev4Mustache(u32 color) { + GXSetNumTevStages(1); + GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevColor(GX_TEVREG0, RFLi_BEARD_COLOR0[color]); +} + +void RFLiSetFaceParts(const RFLiCharInfo* info, RFLiFaceParts* face, + RFLi_MASKRSL resolution) { + f32 eyeX = RFLi_TEX_EYE_BASE_X + RFLi_TEX_SCALE_X * info->eye.x; + f32 eyeY = + RFLi_TEX_EYE_BASE_Y + 1.1600001f * RFLi_TEX_SCALE_Y * info->eye.y; + f32 eyeW = RFLi_TEX_EYE_BASE_W * RFLi_TEX_SCALE2DIM(info->eye.scale); + f32 eyeH = RFLi_TEX_EYE_BASE_H * RFLi_TEX_SCALE2DIM(info->eye.scale); + f32 eyeA = RFLi_TEX_ROTATE2ANG(info->eye.rotate + + RFLi_EYE_ROT_OFFSET[info->eye.type]); + + f32 eyebrowX = RFLi_TEX_EYEBROW_BASE_X + RFLi_TEX_SCALE_X * info->eyebrow.x; + f32 eyebrowY = RFLi_TEX_EYEBROW_BASE_Y + + 1.1600001f * RFLi_TEX_SCALE_Y * info->eyebrow.y; + f32 eyebrowW = + RFLi_TEX_EYEBROW_BASE_W * RFLi_TEX_SCALE2DIM(info->eyebrow.scale); + f32 eyebrowH = + RFLi_TEX_EYEBROW_BASE_H * RFLi_TEX_SCALE2DIM(info->eyebrow.scale); + f32 eyebrowA = RFLi_TEX_ROTATE2ANG( + info->eyebrow.rotate + RFLi_EYEBROW_ROT_OFFSET[info->eyebrow.type]); + + f32 mouthY = + RFLi_TEX_MOUTH_BASE_Y + 1.1600001f * RFLi_TEX_SCALE_Y * info->mouth.y; + f32 mouthW = RFLi_TEX_MOUTH_BASE_W * RFLi_TEX_SCALE2DIM(info->mouth.scale); + f32 mouthH = RFLi_TEX_MOUTH_BASE_H * RFLi_TEX_SCALE2DIM(info->mouth.scale); + + f32 mustacheY = RFLi_TEX_MUSTACHE_BASE_Y + + 1.1600001f * RFLi_TEX_SCALE_Y * info->beard.y; + f32 mustacheW = + RFLi_TEX_MUSTACHE_BASE_W * RFLi_TEX_SCALE2DIM(info->beard.scale); + f32 mustacheH = + RFLi_TEX_MUSTACHE_BASE_H * RFLi_TEX_SCALE2DIM(info->beard.scale); + + f32 moleX = RFLi_TEX_MOLE_BASE_X + 2.0f * RFLi_TEX_SCALE_X * info->mole.x; + f32 moleY = + RFLi_TEX_MOLE_BASE_Y + 1.1600001f * RFLi_TEX_SCALE_Y * info->mole.y; + f32 moleW = RFLi_TEX_MOLE_BASE_W + RFLi_TEX_SCALE2DIM(info->mole.scale); + f32 moleH = RFLi_TEX_MOLE_BASE_H + RFLi_TEX_SCALE2DIM(info->mole.scale); + + f32 resolution_ = RFLi_TEX_UNIT(resolution); + + face->eyeR.x = resolution_ * (32.0f - eyeX); + face->eyeR.y = eyeY * resolution_; + face->eyeR.width = eyeW * resolution_; + face->eyeR.height = eyeH * resolution_; + face->eyeR.angle = eyeA; + face->eyeR.origin = RFL_ORIGIN_RIGHT; + + face->eyeL.x = resolution_ * (32.0f + eyeX); + face->eyeL.y = eyeY * resolution_; + face->eyeL.width = eyeW * resolution_; + face->eyeL.height = eyeH * resolution_; + face->eyeL.angle = 360.0f - eyeA; + face->eyeL.origin = RFL_ORIGIN_LEFT; + + face->eyebrowR.x = resolution_ * (32.0f - eyebrowX); + face->eyebrowR.y = eyebrowY * resolution_; + face->eyebrowR.width = eyebrowW * resolution_; + face->eyebrowR.height = eyebrowH * resolution_; + face->eyebrowR.angle = eyebrowA; + face->eyebrowR.origin = RFL_ORIGIN_RIGHT; + + face->eyebrowL.x = resolution_ * (32.0f + eyebrowX); + face->eyebrowL.y = eyebrowY * resolution_; + face->eyebrowL.width = eyebrowW * resolution_; + face->eyebrowL.height = eyebrowH * resolution_; + face->eyebrowL.angle = 360.0f - eyebrowA; + face->eyebrowL.origin = RFL_ORIGIN_LEFT; + + face->mouth.x = 32.0f * resolution_; + face->mouth.y = mouthY * resolution_; + face->mouth.width = mouthW * resolution_; + face->mouth.height = mouthH * resolution_; + face->mouth.angle = 0.0f; + face->mouth.origin = RFL_ORIGIN_CENTER; + + face->mustacheR.x = 32.0f * resolution_; + face->mustacheR.y = mustacheY * resolution_; + face->mustacheR.width = mustacheW * resolution_; + face->mustacheR.height = mustacheH * resolution_; + face->mustacheR.angle = 0.0f; + face->mustacheR.origin = RFL_ORIGIN_RIGHT; + + face->mustacheL.x = 32.0f * resolution_; + face->mustacheL.y = mustacheY * resolution_; + face->mustacheL.width = mustacheW * resolution_; + face->mustacheL.height = mustacheH * resolution_; + face->mustacheL.angle = 0.0f; + face->mustacheL.origin = RFL_ORIGIN_LEFT; + + face->mole.x = moleX * resolution_; + face->mole.y = moleY * resolution_; + face->mole.width = moleW * resolution_; + face->mole.height = moleH * resolution_; + face->mole.angle = 0.0f; + face->mole.origin = RFL_ORIGIN_CENTER; +} + +void RFLiCapture(u8* buffer, const RFLiCharInfo* info, RFLiFaceParts* face, + RFLi_MASKRSL resolution) { + f32 vp[6]; + + GXInvalidateTexAll(); + GXGetViewportv(vp); + RFLiSetup2DCameraAndParam(); + + GXSetTevDirect(GX_TEVSTAGE0); + GXSetTevDirect(GX_TEVSTAGE1); + GXSetTevDirect(GX_TEVSTAGE2); + GXSetBlendMode(GX_BM_BLEND, GX_BL_INVDSTALPHA, GX_BL_DSTALPHA, GX_LO_SET); + GXSetColorUpdate(TRUE); + + RFLiSetTev4Mustache(info->beard.color); + RFLiDrawFaceParts(&face->mustacheR); + RFLiDrawFaceParts(&face->mustacheL); + + RFLiSetTev4Mouth(info->mouth.color); + RFLiDrawFaceParts(&face->mouth); + + RFLiSetTev4Eyebrow(info->eyebrow.color); + RFLiDrawFaceParts(&face->eyebrowR); + RFLiDrawFaceParts(&face->eyebrowL); + + RFLiSetTev4Eye(info->eye.color, info->eye.type); + RFLiDrawFaceParts(&face->eyeR); + RFLiDrawFaceParts(&face->eyeL); + + RFLiSetTev4Mole(); + RFLiDrawFaceParts(&face->mole); + + GXSetColorUpdate(FALSE); + GXCopyTex(buffer, TRUE); + GXPixModeSync(); + GXSetBlendMode(GX_BM_BLEND, GX_BL_INVDSTALPHA, GX_BL_ONE, GX_LO_SET); + + RFLiSetTev4Mustache(info->beard.color); + RFLiDrawFaceParts(&face->mustacheR); + RFLiDrawFaceParts(&face->mustacheL); + + RFLiSetTev4Mouth(info->mouth.color); + RFLiDrawFaceParts(&face->mouth); + + RFLiSetTev4Eyebrow(info->eyebrow.color); + RFLiDrawFaceParts(&face->eyebrowR); + RFLiDrawFaceParts(&face->eyebrowL); + + RFLiSetTev4Eye(info->eye.color, info->eye.type); + RFLiDrawFaceParts(&face->eyeR); + RFLiDrawFaceParts(&face->eyeL); + + RFLiSetTev4Mole(); + RFLiDrawFaceParts(&face->mole); + + GXSetColorUpdate(TRUE); + + if (RFLiGetManager()->modelDrawCb == NULL) { + GXDrawDone(); + } + else { + RFLiGetManager()->modelDrawCb(); + } + + DCInvalidateRange(buffer, RFLiGetMaskSize(resolution)); + GXCopyTex(buffer, 1); + GXPixModeSync(); + GXSetViewportv(vp); +} + +void RFLiDrawFaceParts(RFLiPart* part) { + GXLoadTexObj(&part->ngtobj.tobj, GX_TEXMAP0); + RFLiDrawQuad(part->x, part->y, part->width, part->height, part->angle, + part->origin); +} + +void RFLiDrawQuad(f32 x, f32 y, f32 width, f32 height, f32 rotZ, + RFL_ORIGIN origin) { + Mtx rot; + Mtx pos; + f32 baseX; + s16 s0; + s16 s1; + + PSMTXIdentity(pos); + PSMTXScaleApply(pos, pos, width, height, 1.0f); + PSMTXRotRad(rot, (3.141592653589793f / 180.0f) * rotZ, 'z'); + PSMTXConcat(rot, pos, pos); + PSMTXScaleApply(pos, pos, RFLi_TEX_SCALE_X, RFLi_TEX_SCALE_Y, 1.0f); + PSMTXTransApply(pos, pos, x, y, 0.0f); + + GXLoadPosMtxImm(pos, GX_PNMTX0); + GXSetCurrentMtx(GX_PNMTX0); + + switch (origin) { + case RFL_ORIGIN_CENTER: + baseX = -0.5f; + s0 = 256; + s1 = 0; + break; + case RFL_ORIGIN_RIGHT: + baseX = -1.0f; + s0 = 256; + s1 = 0; + break; + case RFL_ORIGIN_LEFT: + baseX = 0.0f; + s0 = 0; + s1 = 256; + break; + } + + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + { + GXPosition2f32(1.0 + baseX, -0.5f); + GXColor1u32(0x000000FF); + GXTexCoord2s16(s0, 0); + + GXPosition2f32(1.0 + baseX, 0.5f); + GXColor1u32(0x000000FF); + GXTexCoord2s16(s0, 256); + + GXPosition2f32(baseX, 0.5f); + GXColor1u32(0x000000FF); + GXTexCoord2s16(s1, 256); + + GXPosition2f32(baseX, -0.5f); + GXColor1u32(0x000000FF); + GXTexCoord2s16(s1, 0); + } + GXEnd(); +} + +RFLi_MASKRSL RFLiGetMaxMaskRsl(RFLResolution resolution) { + switch (resolution) { + case RFLResolution_64: + case RFLResolution_64M: + return RFLi_MASKRSL_64; + case RFLResolution_128: + case RFLResolution_128M: + return RFLi_MASKRSL_128; + case RFLResolution_256: + case RFLResolution_256M: + return RFLi_MASKRSL_256; + default: + return RFLi_MASKRSL_64; + } +} + +u32 RFLiGetMaskSize(RFLi_MASKRSL resolution) { + return 2 * (resolution * resolution); +} + +u32 RFLiGetMaskBufSize(RFLResolution resolution) { + u32 size = 0; + + if (resolution & 32) { + size += RFLiGetMaskSize(RFLi_MASKRSL_32); + } + + if (resolution & 64) { + size += RFLiGetMaskSize(RFLi_MASKRSL_64); + } + + if (resolution & 128) { + size += RFLiGetMaskSize(RFLi_MASKRSL_128); + } + + if (resolution & 256) { + size += RFLiGetMaskSize(RFLi_MASKRSL_256); + } + + return size; +} \ No newline at end of file diff --git a/src/RVLFaceLib/RFL_MiddleDatabase.c b/src/RVLFaceLib/RFL_MiddleDatabase.c new file mode 100644 index 00000000..985456c0 --- /dev/null +++ b/src/RVLFaceLib/RFL_MiddleDatabase.c @@ -0,0 +1,655 @@ +#include + +typedef struct RandomParam { + u8 sex; // at 0x0 + u8 age; // at 0x1 + u8 race; // at 0x2 + u8 padding; // at 0x3 +} RandomParam; + +typedef struct HiddenRandomParam { + u16 dstIdx; // at 0x0 + u8 sex; // at 0x2 + u8 padding; // at 0x3 +} HiddenRandomParam; + +typedef struct HiddenNewOldParam { + s32 sex : 2; + s32 srcIdx : 15; + s32 dstIdx : 15; +} HiddenNewOldParam; + +typedef struct Param2 { + s16 lastSrcIdx; // at 0x0 + u16 padding; // at 0x2 +} Param2; + +u32 RFLGetMiddleDBBufferSize(u16 size) { + return size * sizeof(RFLiHiddenCharData); +} + +void RFLInitMiddleDB(RFLMiddleDB* db, RFLMiddleDBType type, void* buffer, + u16 size) { + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + HiddenNewOldParam* nparam; + HiddenRandomParam* hparam; + RandomParam* rparam; + + if (db == NULL) { + return; + } + + if (buffer == NULL) { + return; + } + + idb->type = type; + idb->callback = NULL; + idb->size = size; + idb->data = (RFLiHiddenCharData*)buffer; + idb->storedSize = 0; + idb->userData1 = 0; + idb->userData2 = 0; + + switch (type) { + case RFLMiddleDBType_Random: + rparam = (RandomParam*)&idb->userData1; + + if (size > RFL_DB_CHAR_MAX) { + return; + } + + rparam->sex = RFLSex_All; + rparam->age = RFLAge_All; + rparam->race = RFLRace_All; + break; + case RFLMiddleDBType_HiddenNewer: + case RFLMiddleDBType_HiddenOlder: + nparam = (HiddenNewOldParam*)&idb->userData1; + + if (size > RFLi_HDB_DATA_MAX) { + return; + } + + nparam->srcIdx = -1; + nparam->dstIdx = 0; + break; + case RFLMiddleDBType_HiddenRandom: + hparam = (HiddenRandomParam*)&idb->userData1; + hparam->sex = RFLSex_All; + hparam->dstIdx = 0; + break; + } + + memset(idb->data, 0, RFLGetMiddleDBBufferSize(idb->size)); +} + +static BOOL checkHiddenData_(RFLiHiddenCharData* data) { + RFLiCharInfo info; + + if (!RFLiIsValidID(&data->createID)) { + return FALSE; + } + + RFLiConvertHRaw2Info(data, &info); + return RFLiCheckValidInfo(&info) ? TRUE : FALSE; +} + +static void updateHDBcallback_(u32 arg) { + RFLErrcode err; + HiddenNewOldParam* nparam; + RFLiMiddleDB* db; + + db = (RFLiMiddleDB*)arg; + nparam = (HiddenNewOldParam*)&db->userData1; + + if (RFLGetAsyncStatus() == RFLErrcode_Success || + RFLGetAsyncStatus() == RFLErrcode_Broken) { + s16 src = -1; + + if (db->type == RFLMiddleDBType_HiddenOlder) { + src = RFLiGetHiddenNext(nparam->srcIdx); + } else { + src = RFLiGetHiddenPrev(nparam->srcIdx); + } + + if (RFLGetAsyncStatus() != RFLErrcode_Broken && + checkHiddenData_(&db->data[db->storedSize])) { + db->storedSize++; + } + + nparam->dstIdx++; + + if (src >= 0 && nparam->dstIdx < db->size) { + nparam->srcIdx = src; + + err = RFLiLoadHiddenDataAsync(&db->data[db->storedSize], + nparam->srcIdx, updateHDBcallback_, + (u32)db); + + if (err != RFLErrcode_Busy) { + RFLiEndWorking(err); + } + } else { + RFLiGetManager()->lastErrCode = db->storedSize < db->size + ? RFLErrcode_DBNodata + : RFLErrcode_Success; + } + } + + if (!RFLiIsWorking()) { + if (RFLGetAsyncStatus() == RFLErrcode_NANDCommandfail && + RFLGetLastReason() == NAND_RESULT_BUSY) { + Param2* param2 = (Param2*)&db->userData2; + nparam->srcIdx = param2->lastSrcIdx; + db->storedSize = 0; + nparam->dstIdx = 0; + } + + if (db->callback != NULL) { + db->callback(); + } + } +} + +static s16 stepOne_(s16 srcIdx, BOOL oldIsHead) { + s16 ret; + + (void)RFLiGetHDBManager(); + + ret = -1; + + if (oldIsHead) { + if (srcIdx < 0) { + ret = RFLiGetHiddenHeader()->head; + } else { + ret = RFLiGetHiddenNext(srcIdx); + } + } else { + if (srcIdx < 0) { + ret = RFLiGetHiddenHeader()->tail; + } else { + ret = RFLiGetHiddenPrev(srcIdx); + } + } + + return ret; +} + +static void loadHiddenDataSync_(RFLiMiddleDB* db) { + s16 src = -1; + BOOL running = TRUE; + HiddenNewOldParam* nparam = (HiddenNewOldParam*)&db->userData1; + + while (running) { + RFLiLoadCachedHiddenData(&db->data[db->storedSize], nparam->srcIdx); + + if (db->type == RFLMiddleDBType_HiddenOlder) { + src = RFLiGetHiddenNext(nparam->srcIdx); + } else { + src = RFLiGetHiddenPrev(nparam->srcIdx); + } + + if (checkHiddenData_(&db->data[db->storedSize])) { + db->storedSize++; + } + + nparam->dstIdx++; + + if (src >= 0 && nparam->dstIdx < db->size) { + nparam->srcIdx = src; + } else { + running = FALSE; + } + } + + RFLiEndWorking(nparam->srcIdx == -1 ? RFLErrcode_DBNodata + : RFLErrcode_Success); +} + +static void updateHiddenOld_(RFLiMiddleDB* db, BOOL oldIsHead, BOOL cache) { + HiddenNewOldParam* nparam; + s16 src; + + if (!RFLiDBIsLoaded()) { + RFLiEndWorking(RFLErrcode_DBNodata); + return; + } + + if (db->data == NULL || db->size <= 0 || !RFLiDBIsLoaded()) { + RFLiEndWorking(RFLErrcode_NotAvailable); + return; + } + + nparam = (HiddenNewOldParam*)&db->userData1; + src = stepOne_(nparam->srcIdx, oldIsHead); + + if (src >= 0) { + nparam->srcIdx = src; + + if (cache) { + loadHiddenDataSync_(db); + } else { + RFLErrcode err = RFLiLoadHiddenDataAsync( + &db->data[nparam->dstIdx], nparam->srcIdx, updateHDBcallback_, + (u32)db); + + if (err != RFLErrcode_Busy) { + RFLiEndWorking(err); + } + } + } else { + RFLiEndWorking(RFLErrcode_DBNodata); + } +} + +static void loadHiddenRandomSync_(RFLiMiddleDB* db) { + BOOL running = TRUE; + HiddenRandomParam* hparam = (HiddenRandomParam*)&db->userData1; + + while (running) { + // Accesses gender/color/favorite bitfield. Is this done to be random? + u32 src = *(u32*)&db->data[hparam->dstIdx]; + + if (src > 0) { + u16 srcIdx = src - 1; + RFLiLoadCachedHiddenData(&db->data[db->storedSize], srcIdx); + + if (checkHiddenData_(&db->data[db->storedSize])) { + db->storedSize++; + } + + hparam->dstIdx++; + + if (src > 0 && hparam->dstIdx < db->size) { + if (src >= RFLi_HDB_DATA_MAX) { + running = FALSE; + } + } else { + running = FALSE; + } + + } else { + running = FALSE; + } + } + + RFLiGetManager()->lastErrCode = + db->storedSize < db->size ? RFLErrcode_DBNodata : RFLErrcode_Success; +} + +static void updateHDBRandcallback_(u32 arg) { + RFLiMiddleDB* db = (RFLiMiddleDB*)arg; + HiddenRandomParam* hparam = (HiddenRandomParam*)&db->userData1; + u32* src; + u16 srcIdx; + + if (RFLGetAsyncStatus() == RFLErrcode_Success || + RFLGetAsyncStatus() == RFLErrcode_Broken) { + if (RFLGetAsyncStatus() != RFLErrcode_Broken && + checkHiddenData_(&db->data[db->storedSize])) { + db->storedSize++; + } + + hparam->dstIdx++; + // Accesses gender/color/favorite bitfield. Is this done to be random? + src = (u32*)&db->data[hparam->dstIdx]; + + if (*src > 0 && hparam->dstIdx < db->size) { + if (*src < RFLi_HDB_DATA_MAX) { + u16 srcIdx = *src - 1; + RFLErrcode err = + RFLiLoadHiddenDataAsync(&db->data[db->storedSize], srcIdx, + updateHDBRandcallback_, (u32)db); + + if (err != RFLErrcode_Busy) { + RFLiEndWorking(err); + } + } else { + RFLiGetManager()->lastErrCode = RFLErrcode_Broken; + } + } else { + RFLiGetManager()->lastErrCode = db->storedSize < db->size + ? RFLErrcode_DBNodata + : RFLErrcode_Success; + } + } + + if (!RFLiIsWorking() && db->callback != NULL) { + if (RFLGetAsyncStatus() == RFLErrcode_NANDCommandfail && + RFLGetLastReason() == NAND_RESULT_BUSY) { + db->storedSize = 0; + hparam->dstIdx = 0; + } + + db->callback(); + } +} + +static void updateHiddenRandom_(RFLiMiddleDB* db, BOOL cache) { + u16 max; + u16* array; + HiddenRandomParam* hparam; + RFLSex sex; + u16 count; + u32 rand; + u16 aidx; + u16 i; + + hparam = (HiddenRandomParam*)&db->userData1; + sex = (RFLSex)hparam->sex; + max = db->size; + rand = OSGetTick(); + + RFLiStartWorking(); + + if (!RFLiDBIsLoaded()) { + RFLiEndWorking(RFLErrcode_DBNodata); + return; + } + + count = RFLiCountupHiddenDataNum(sex); + if (count <= 0) { + RFLiEndWorking(RFLErrcode_DBNodata); + return; + } + + if (count < db->size) { + max = count; + } + + array = (u16*)RFLiAlloc(count * sizeof(u16)); + if (array == NULL) { + RFLiEndWorking(RFLErrcode_Fatal); + return; + } + + aidx = 0; + for (i = 0; i < RFLi_HDB_DATA_MAX; i++) { + if (RFLiIsValidHiddenData(i, sex)) { + array[aidx++] = i; + } + } + + for (i = 0; i < count - 1; i++) { + u16 tmp; + + u16 target = (((rand >> 16) + rand) & 0xFFFF) % (count - 1); + if (target >= i) { + target++; + } + + tmp = array[target]; + array[target] = array[i]; + array[i] = tmp; + + rand = 0x04F8BB63 * (rand + 0x046AC055); + } + + for (i = 0; i < max; i++) { + u32* src = (u32*)&db->data[i]; + *src = array[i] + 1; + } + + RFLiFree(array); + + { + u16 srcIdx = 0; + u32* src = (u32*)&db->data[hparam->dstIdx]; + + if (*src == 0) { + RFLiEndWorking(RFLErrcode_DBNodata); + } else { + srcIdx = *src - 1; + + if (cache) { + loadHiddenRandomSync_(db); + } else { + RFLErrcode err = + RFLiLoadHiddenDataAsync(&db->data[hparam->dstIdx], srcIdx, + updateHDBRandcallback_, (u32)db); + + if (err != RFLErrcode_Busy) { + RFLiEndWorking(err); + } + } + } + } +} + +static void updateRandom_(RFLiMiddleDB* db) { + int count = 0; + RandomParam* rparam = (RandomParam*)&db->userData1; + int j; + RFLiCharInfo info; + + RFLiStartWorking(); + + while (db->storedSize < db->size) { + BOOL isSame = FALSE; + + RFLi_MakeRandomFace(&info, (RFLSex)rparam->sex, (RFLAge)rparam->age, + (RFLRace)rparam->race); + RFLiSetTemporaryID(&info); + + for (j = 0; j < db->storedSize; j++) { + RFLiCharInfo temp; + RFLiConvertHRaw2Info(&db->data[j], &temp); + + if (RFLiIsSameFaceCore(&info, &temp)) { + isSame = TRUE; + break; + } + } + + if (!isSame) { + RFLiConvertInfo2HRaw(&info, &db->data[db->storedSize]); + db->storedSize++; + } + } + + RFLiEndWorking(RFLErrcode_Success); +} + +static void startUpdateDB_(RFLiMiddleDB* db) { + Param2* param2; + HiddenNewOldParam* nparam; + HiddenRandomParam* hparam; + + db->storedSize = 0; + memset(db->data, 0, RFLGetMiddleDBBufferSize(db->size)); + + switch (db->type) { + case RFLMiddleDBType_HiddenNewer: + case RFLMiddleDBType_HiddenOlder: + nparam = (HiddenNewOldParam*)&db->userData1; + param2 = (Param2*)&db->userData2; + + nparam->dstIdx = 0; + param2->lastSrcIdx = nparam->srcIdx; + break; + case RFLMiddleDBType_HiddenRandom: + hparam = (HiddenRandomParam*)&db->userData1; + param2 = (Param2*)&db->userData2; + + hparam->dstIdx = 0; + param2->lastSrcIdx = 0; + break; + } +} + +static RFLErrcode RFLUpdateMiddleDBAsync(RFLMiddleDB* db) { + if (db == NULL) { + return RFLErrcode_WrongParam; + } + + if (RFLAvailable()) { + return RFLiUpdateMiddleDBAsync(db, NULL, FALSE); + } + + return RFLErrcode_NotAvailable; +} + +static RFLErrcode RFLiUpdateMiddleDBAsync(RFLMiddleDB* db, RFLiCallback cb, + BOOL cache) { + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + + if (db == NULL) { + return RFLErrcode_WrongParam; + } + + idb->callback = cb; + startUpdateDB_(idb); + + switch (idb->type) { + case RFLMiddleDBType_HiddenRandom: + updateHiddenRandom_(idb, cache); + break; + case RFLMiddleDBType_HiddenNewer: + updateHiddenOld_(idb, FALSE, cache); + break; + case RFLMiddleDBType_HiddenOlder: + updateHiddenOld_(idb, TRUE, cache); + break; + case RFLMiddleDBType_Random: + updateRandom_(idb); + break; + } + + if (!RFLiIsWorking() && idb->callback != NULL) { + idb->callback(); + } + + return RFLGetAsyncStatus(); +} + +static RFLMiddleDBType RFLGetMiddleDBType(const RFLMiddleDB* db) { + return ((RFLiMiddleDB*)db)->type; +} + +static u16 RFLGetMiddleDBStoredSize(const RFLMiddleDB* db) { + return ((RFLiMiddleDB*)db)->storedSize; +} + +BOOL RFLiGetCharInfoMiddleDB(RFLiCharInfo* info, const RFLMiddleDB* db, + u16 index) { + RFLiHiddenCharData* data; + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + + if (idb == NULL) { + return FALSE; + } + + if (index >= idb->size) { + return FALSE; + } + + if (idb->storedSize <= 0) { + return FALSE; + } + + data = &idb->data[index]; + if (!RFLiIsValidID(&data->createID)) { + return FALSE; + } + + RFLiConvertHRaw2Info(data, info); + return TRUE; +} + +static void RFLSetMiddleDBRandomMask(RFLMiddleDB* db, RFLSex sex, RFLAge age, + RFLRace race) { + RandomParam* rparam; + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + + if (RFLGetMiddleDBType(db) != RFLMiddleDBType_Random) { + return; + } + + rparam = (RandomParam*)&idb->userData1; + rparam->sex = sex; + rparam->age = age; + rparam->race = race; +} + +static void RFLSetMiddleDBHiddenMask(RFLMiddleDB* db, RFLSex sex) { + HiddenRandomParam* hparam; + HiddenNewOldParam* nparam; + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + + switch (RFLGetMiddleDBType(db)) { + case RFLMiddleDBType_HiddenRandom: + hparam = (HiddenRandomParam*)&idb->userData1; + hparam->sex = sex; + break; + case RFLMiddleDBType_HiddenNewer: + case RFLMiddleDBType_HiddenOlder: + nparam = (HiddenNewOldParam*)&idb->userData1; + nparam->sex = sex; + break; + } +} + +static RFLErrcode RFLiAddMiddleDBUserData(RFLMiddleDB* db, RFLiCharData* raw) { + RFLiHiddenCharData hraw; + RFLiCharInfo info; + RFLiMiddleDB* idb = (RFLiMiddleDB*)db; + + if (idb == NULL) { + return RFLErrcode_WrongParam; + } + + if (raw == NULL) { + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + RFLiConvertRaw2HRaw(raw, &hraw); + + if (idb->type != RFLMiddleDBType_UserSet) { + return RFLErrcode_NotAvailable; + } + + if (!RFLiIsValidID(&hraw.createID)) { + return RFLErrcode_Broken; + } + + RFLiConvertRaw2Info(raw, &info); + if (!RFLiCheckValidInfo(&info)) { + return RFLErrcode_Broken; + } + + if (idb->storedSize >= idb->size) { + return RFLErrcode_DBFull; + } + + memcpy(&idb->data[idb->storedSize], &hraw, sizeof(RFLiHiddenCharData)); + idb->storedSize++; + + return RFLErrcode_Success; +} + +static RFLErrcode RFLAddMiddleDBStoreData(RFLMiddleDB* db, const RFLStoreData* data) { + RFLiStoreData* idata = (RFLiStoreData*)data; + + if (db == NULL) { + return RFLErrcode_WrongParam; + } + + if (data == NULL) { + return RFLErrcode_WrongParam; + } + + if (!RFLAvailable()) { + return RFLErrcode_NotAvailable; + } + + if (RFLiCalculateCRC(data, sizeof(RFLStoreData)) != 0) { + return RFLErrcode_Broken; + } + + return RFLiAddMiddleDBUserData(db, &idata->data); +} diff --git a/src/RVLFaceLib/RFL_Model.c b/src/RVLFaceLib/RFL_Model.c new file mode 100644 index 00000000..0d66f485 --- /dev/null +++ b/src/RVLFaceLib/RFL_Model.c @@ -0,0 +1,1135 @@ +#include +#include +#include +#include + +#define NUM_VTX_POS(size) ((size) / VTX_POS_SIZE) +#define SIZE_VTX_POS(count) ((count) * VTX_POS_SIZE) + +#define NUM_VTX_NRM(size) ((size) / VTX_NRM_SIZE) +#define SIZE_VTX_NRM(count) ((count) * VTX_NRM_SIZE) + +#define NUM_VTX_TXC(size) ((size) / VTX_TXC_SIZE) +#define SIZE_VTX_TXC(count) ((count) * VTX_TXC_SIZE) + +RFLiCoordinateData coordinateData = {1, 2, 0, FALSE, FALSE, FALSE}; + +const RFLDrawCoreSetting cDefaultDrawCoreSetting2Tev = { + 1, GX_TEXCOORD0, GX_TEXMAP0, 2, GX_TEV_SWAP0, + GX_KCOLOR0, GX_TEVPREV, GX_PNMTX0, FALSE}; + +const RFLDrawCoreSetting cDefaultDrawCoreSetting1Tev = { + 1, GX_TEXCOORD0, GX_TEXMAP0, 1, GX_TEV_SWAP0, + GX_KCOLOR0, GX_TEVPREV, GX_PNMTX0, FALSE}; + +static const GXColor cFacelineColor[RFLi_MAX_FACELINE_COLOR + 1] = { + // clang-format off + {240, 216, 196, 255}, + {255, 188, 128, 255}, + {216, 136, 80, 255}, + {255, 176, 144, 255}, + {152, 80, 48, 255}, + {82, 46, 28, 255}, + // clang-format on +}; + +static const GXColor cHairColor[RFLi_MAX_HAIR_COLOR + 1] = { + // clang-format off + {30, 26, 24, 255}, + {56, 32, 21, 255}, + {85, 38, 23, 255}, + {112, 64, 36, 255}, + {114, 114, 120, 255}, + {73, 54, 26, 255}, + {122, 89, 40, 255}, + {193, 159, 100, 255}, + // clang-format on +}; + +static const GXColor cBeardColor[RFLi_MAX_BEARD_COLOR + 1] = { + // clang-format off + {30, 26, 24, 255}, + {56, 32, 21, 255}, + {85, 38, 23, 255}, + {112, 64, 36, 255}, + {114, 114, 120, 255}, + {73, 54, 26, 255}, + {122, 89, 40, 255}, + {193, 159, 100, 255}, + // clang-format on +}; + +static const GXColor cGlassColor[RFLi_MAX_GLASS_COLOR + 1] = { + // clang-format off + {16, 16, 16, 255}, + {96, 56, 16, 255}, + {152, 24, 16, 255}, + {32, 48, 96, 255}, + {144, 88, 0, 255}, + {96, 88, 80, 255}, + // clang-format on +}; + +static const GXColor cFavoriteColor[RFLFavoriteColor_Max] = { + // clang-format off + {184, 64, 48, 255}, //!< RFLFavoriteColor_Red + {240, 120, 40, 255}, //!< RFLFavoriteColor_Orange + {248, 216, 32, 255}, //!< RFLFavoriteColor_Yellow + {128, 200, 40, 255}, //!< RFLFavoriteColor_YellowGreen + {0, 116, 40, 255}, //!< RFLFavoriteColor_Green + {32, 72, 152, 255}, //!< RFLFavoriteColor_Blue + {64, 160, 216, 255}, //!< RFLFavoriteColor_SkyBlue + {232, 96, 120, 255}, //!< RFLFavoriteColor_Pink + {112, 44, 168, 255}, //!< RFLFavoriteColor_Purple + {72, 56, 24, 255}, //!< RFLFavoriteColor_Brown + {224, 224, 224, 255}, //!< RFLFavoriteColor_White + {24, 24, 20, 255}, //!< RFLFavoriteColor_Black + // clang-format on +}; + +static const GXColor cWhite = {255, 255, 255, 255}; + +static void RFLSetCoordinate(RFLCoordinateType u, RFLCoordinateType f) { + RFLCoordinateType r; + + union { + RFLCoordinateType c; + u8 b[4]; + } uu; + union { + RFLCoordinateType c; + u8 b[4]; + } uf; + union { + RFLCoordinateType c; + u8 b[4]; + } ur; + + uu.c = u; + uf.c = f; + + ur.b[0] = (uu.b[1] * uf.b[2]) - (uu.b[2] * uf.b[1]); + ur.b[1] = (uu.b[2] * uf.b[0]) - (uu.b[0] * uf.b[2]); + ur.b[2] = (uu.b[0] * uf.b[1]) - (uu.b[1] * uf.b[0]); + + r = ur.c; + + if (u & RFLCoordinateType_X) { + coordinateData.uOff = 0; // U -> X + } else if (u & RFLCoordinateType_Y) { + coordinateData.uOff = 1; // U -> Y + } else { + coordinateData.uOff = 2; // U -> Z + } + + if (f & RFLCoordinateType_X) { + coordinateData.fOff = 0; // F -> X + } else if (f & RFLCoordinateType_Y) { + coordinateData.fOff = 1; // F -> Y + } else { + coordinateData.fOff = 2; // F -> Z + } + + if (r & RFLCoordinateType_X) { + coordinateData.rOff = 0; // R -> X + } else if (r & RFLCoordinateType_Y) { + coordinateData.rOff = 1; // R -> Y + } else { + coordinateData.rOff = 2; // R -> Z + } + + coordinateData.uRev = (u & RFLCoordinateType_RevMask) != 0; + coordinateData.fRev = (f & RFLCoordinateType_RevMask) != 0; + coordinateData.rRev = (r & RFLCoordinateType_RevMask) != 0; +} + +static u32 RFLiGetExpressionNum(u32 exprFlags) { + int i; + u32 num = 0; + + for (i = 0; i < RFLExp_Max; i++) { + if (exprFlags & (1 << i)) { + num++; + } + } + + return num; +} + +u32 RFLGetModelBufferSize(RFLResolution res, u32 exprFlags) { + const u32 exprNum = RFLiGetExpressionNum(exprFlags); + const s32 texSize = RFLiGetMaskBufSize(res); + + return ROUND_UP(exprNum * sizeof(GXTexObj), 32) + + ROUND_UP(sizeof(RFLiCharModelRes), 32) + + ROUND_UP(texSize * exprNum, 32); +} + +RFLErrcode RFLInitCharModel(RFLCharModel* model, RFLDataSource src, + RFLMiddleDB* db, u16 id, void* work, + RFLResolution res, u32 exprFlags) { + RFLiCharInfo info; + + RFLErrcode err = RFLiPickupCharInfo(&info, src, db, id); + if (err == RFLErrcode_Success) { + RFLiInitCharModel(model, &info, work, res, exprFlags); + } + + return err; +} + +void RFLiInitCharModel(RFLCharModel* model, RFLiCharInfo* info, void* work, + RFLResolution res, u32 exprFlags) { + RFLiCharModel* imodel; + u32 maskSize; + u8* maskImages[RFLExp_Max]; + u32 maskRes; + GXTexObj* exprTexObj; + u8* image; + BOOL setExpr; + f32 maxLod; + u8 mipmap; + int i; + + imodel = (RFLiCharModel*)model; + + imodel->resolution = res; + maskSize = RFLiGetMaskBufSize(res); + maskRes = RFLiGetMaxMaskRsl(res); + imodel->res = work; + + // Expression texobjs + exprTexObj = + (GXTexObj*)ROUND_UP_PTR((u8*)work + sizeof(RFLiCharModelRes), 32); + for (i = 0; i < RFLExp_Max; i++) { + if (exprFlags & (1 << i)) { + imodel->maskTexObj[i] = exprTexObj; + exprTexObj++; + } else { + imodel->maskTexObj[i] = NULL; + } + } + + // Expression images + image = (u8*)ROUND_UP_PTR(exprTexObj, 32); + for (i = 0; i < RFLExp_Max; i++) { + if (exprFlags & (1 << i)) { + maskImages[i] = image; + image = (u8*)image + maskSize; + } else { + maskImages[i] = NULL; + } + } + + RFLiInitCharModelRes(imodel->res, info); + + setExpr = FALSE; + mipmap = FALSE; + maxLod = 0.0f; + + switch (res) { + case RFLResolution_64M: + maxLod = 1.0f; + mipmap = TRUE; + break; + case RFLResolution_128M: + maxLod = 2.0f; + mipmap = TRUE; + break; + case RFLResolution_256M: + maxLod = 3.0f; + mipmap = TRUE; + break; + } + + for (i = 0; i < RFLExp_Max; i++) { + if (imodel->maskTexObj[i] != NULL) { + GXInitTexObj(imodel->maskTexObj[i], maskImages[i], maskRes, maskRes, + GX_TF_RGB5A3, GX_CLAMP, GX_CLAMP, mipmap); + + if (mipmap) { + GXInitTexObjLOD(imodel->maskTexObj[i], GX_LIN_MIP_LIN, + GX_LINEAR, 0.0f, maxLod, 0.0f, FALSE, FALSE, + GX_ANISO_1); + } else { + GXInitTexObjLOD(imodel->maskTexObj[i], GX_LINEAR, GX_LINEAR, + 0.0f, 0.0f, 0.0f, FALSE, FALSE, GX_ANISO_1); + } + + if (!setExpr) { + imodel->expression = (RFLExpression)i; + setExpr = TRUE; + } + } + } + + RFLiMakeTexture(info, maskImages, res); +} + +void RFLSetMtx(RFLCharModel* model, const Mtx mvMtx) { + RFLiCharModel* imodel = (RFLiCharModel*)model; + PSMTXCopy(mvMtx, imodel->posMtx); + PSMTXInvXpose(mvMtx, imodel->nrmMtx); +} + +void RFLSetExpression(RFLCharModel* model, RFLExpression expr) { + RFLiCharModel* imodel = (RFLiCharModel*)model; + imodel->expression = expr; +} + +static RFLExpression RFLGetExpression(const RFLCharModel* model) { + RFLiCharModel* imodel = (RFLiCharModel*)model; + return imodel->expression; +} + +GXColor RFLGetFavoriteColor(RFLFavoriteColor color) { + return cFavoriteColor[color]; +} + +GXColor RFLiGetFacelineColor(const RFLiCharInfo* info) { + int color = 0; + + if (info->faceline.color < 6) { + color = info->faceline.color; + } + + return cFacelineColor[color]; +} + +void RFLLoadDrawSetting(const RFLDrawSetting* setting) { + GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_NEVER, 0); + GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); + GXSetZMode(TRUE, GX_LEQUAL, TRUE); + GXSetZCompLoc(setting->compLoc); + GXSetColorUpdate(TRUE); + GXSetAlphaUpdate(TRUE); + GXSetDither(FALSE); + GXSetDstAlpha(FALSE, 0); + + if (setting->lightEnable) { + GXSetTevDirect(GX_TEVSTAGE1); + GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); + GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, + GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, + GX_CC_ZERO); + GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_APREV); + GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + GX_TEVPREV); + + RFLLoadMaterialSetting(&cDefaultDrawCoreSetting2Tev); + RFLLoadVertexSetting(&cDefaultDrawCoreSetting2Tev); + GXSetNumChans(1); + + GXSetChanCtrl(GX_COLOR0, TRUE, GX_SRC_REG, GX_SRC_REG, + setting->lightMask, setting->diffuse, setting->attn); + GXSetChanCtrl(GX_ALPHA0, FALSE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, + GX_DF_NONE, GX_AF_NONE); + GXSetChanAmbColor(GX_COLOR0, setting->ambColor); + GXSetChanMatColor(GX_COLOR0, cWhite); + } else { + RFLLoadMaterialSetting(&cDefaultDrawCoreSetting1Tev); + RFLLoadVertexSetting(&cDefaultDrawCoreSetting1Tev); + GXSetNumChans(0); + } +} + +void RFLDrawOpa(const RFLCharModel* model) { + RFLDrawOpaCore(model, &cDefaultDrawCoreSetting2Tev); +} + +void RFLDrawXlu(const RFLCharModel* model) { + RFLDrawXluCore(model, &cDefaultDrawCoreSetting2Tev); +} + +void RFLLoadVertexSetting(const RFLDrawCoreSetting* setting) { + GXClearVtxDesc(); + GXSetVtxDesc(GX_VA_POS, GX_VA_TEX1MTXIDX); + GXSetVtxDesc(GX_VA_NRM, GX_VA_TEX1MTXIDX); + GXSetVtxDesc(GX_VA_TEX0, GX_VA_TEX1MTXIDX); + GXSetVtxAttrFmt(0, GX_VA_POS, 1, 3, 8); + GXSetVtxAttrFmt(0, GX_VA_NRM, 0, 3, 14); + GXSetVtxAttrFmt(0, GX_VA_TEX0, 1, 3, 13); + GXSetNumTexGens(setting->txcGenNum); +} + +void RFLLoadMaterialSetting(const RFLDrawCoreSetting* setting) { + GXSetTevSwapModeTable(setting->tevSwapTable, GX_CH_RED, GX_CH_GREEN, + GX_CH_BLUE, GX_CH_ALPHA); + GXSetTevSwapModeTable(setting->tevSwapTable + 1, GX_CH_RED, GX_CH_ALPHA, + GX_CH_BLUE, GX_CH_GREEN); + GXSetNumTevStages(setting->tevStageNum); + GXSetTevDirect(GX_TEVSTAGE0); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + setting->tevOutRegID); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + setting->tevOutRegID); + GXSetTevKColorSel(GX_TEVSTAGE0, + (GXTevKColorSel)(setting->tevKColorID + GX_TEV_KCSEL_K0)); + GXSetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_8_8); +} + +void RFLDrawOpaCore(const RFLCharModel* model, + const RFLDrawCoreSetting* setting) { + RFLiCharModel* imodel = (RFLiCharModel*)model; + RFLiCharModelRes* res = imodel->res; + + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_KONST); + + // @bug Copy-paste error + GXSetTevSwapMode(GX_TEVSTAGE0, setting->tevSwapTable, + setting->tevSwapTable); + GXSetTevSwapMode(GX_TEVSTAGE0, setting->tevSwapTable, + setting->tevSwapTable); + + GXSetCullMode(setting->reverseCulling ? GX_CULL_FRONT : GX_CULL_BACK); + + GXLoadPosMtxImm(imodel->posMtx, setting->posNrmMtxID); + GXLoadNrmMtxImm(imodel->nrmMtx, setting->posNrmMtxID); + GXSetCurrentMtx(setting->posNrmMtxID); + + GXSetTexCoordGen2(setting->txcID, GX_TG_MTX2x4, GX_TG_POS, 60, 0, 0x7D); + GXSetVtxDesc(GX_VA_TEX0, GX_VA_PNMTXIDX); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, + GX_COLOR_NULL); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, + GX_CC_KONST); + + if (res->beardDlSize > 0) { + GXSetTevKColor(setting->tevKColorID, cBeardColor[res->beardColor]); + GXSetArray(GX_VA_POS, res->beardVtxPos, 6); + GXSetArray(GX_VA_NRM, res->beardVtxNrm, 6); + GXCallDisplayList(res->beardDl, res->beardDlSize); + } + + GXSetTevKColor(setting->tevKColorID, cFacelineColor[res->facelineColor]); + + if (res->noseDlSize > 0) { + GXSetArray(GX_VA_POS, res->noseVtxPos, 6); + GXSetArray(GX_VA_NRM, res->noseVtxNrm, 6); + GXCallDisplayList(res->noseDl, res->noseDlSize); + } + + if (res->flipHair) { + GXSetCullMode(setting->reverseCulling ? GX_CULL_BACK : GX_CULL_FRONT); + } + + if (res->foreheadDlSize > 0) { + GXSetArray(GX_VA_POS, res->foreheadVtxPos, 6); + GXSetArray(GX_VA_NRM, res->foreheadVtxNrm, 6); + GXCallDisplayList(res->foreheadDl, res->foreheadDlSize); + } + + if (res->hairDlSize > 0) { + GXSetTevKColor(setting->tevKColorID, cHairColor[res->hairColor]); + GXSetArray(GX_VA_POS, res->hairVtxPos, 6); + GXSetArray(GX_VA_NRM, res->hairVtxNrm, 6); + GXCallDisplayList(res->hairDl, res->hairDlSize); + } + + GXSetTevOrder(GX_TEVSTAGE0, setting->txcID, setting->texMapID, + GX_COLOR_NULL); + GXSetTexCoordGen2(setting->txcID, GX_TG_MTX2x4, GX_TG_TEX0, 60, 0, 0x7D); + GXSetVtxDesc(GX_VA_TEX0, GX_VA_TEX1MTXIDX); + + if (res->capDlSize > 0) { + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, + GX_CC_KONST); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_DIVIDE_2, 1, + setting->tevOutRegID); + GXSetTevKColor(setting->tevKColorID, + cFavoriteColor[res->favoriteColor]); + + GXLoadTexObj(&res->capTexObj, setting->texMapID); + GXSetArray(GX_VA_POS, res->capVtxPos, 6); + GXSetArray(GX_VA_NRM, res->capVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->capVtxTxc, 4); + GXCallDisplayList(res->capDl, res->capDlSize); + + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, + setting->tevOutRegID); + } + + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_KONST, GX_CC_TEXC, GX_CC_TEXA, + GX_CC_ZERO); + GXSetTevKColor(setting->tevKColorID, cFacelineColor[res->facelineColor]); + GXSetTevSwapMode(GX_TEVSTAGE0, setting->tevSwapTable, + (GXTevSwapSel)(setting->tevSwapTable + 1)); + + if (res->flipHair) { + GXSetCullMode(setting->reverseCulling ? GX_CULL_FRONT : GX_CULL_BACK); + } + + GXLoadTexObj(&res->faceTexObj, setting->texMapID); + GXSetArray(GX_VA_POS, res->faceVtxPos, 6); + GXSetArray(GX_VA_NRM, res->faceVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->faceVtxTxc, 4); + GXCallDisplayList(res->faceDl, res->faceDlSize); +} + +void RFLDrawXluCore(const RFLCharModel* model, + const RFLDrawCoreSetting* setting) { + RFLiCharModel* imodel = (RFLiCharModel*)model; + RFLiCharModelRes* res = imodel->res; + + GXSetTevOrder(GX_TEVSTAGE0, setting->txcID, setting->texMapID, + GX_COLOR_NULL); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, + GX_CA_TEXA); + GXSetTevSwapMode(GX_TEVSTAGE0, setting->tevSwapTable, + setting->tevSwapTable); + + GXLoadPosMtxImm(imodel->posMtx, setting->posNrmMtxID); + GXLoadNrmMtxImm(imodel->nrmMtx, setting->posNrmMtxID); + GXSetCurrentMtx(setting->posNrmMtxID); + + GXSetTexCoordGen2(setting->txcID, GX_TG_MTX2x4, GX_TG_TEX0, 60, 0 , 0x7D); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, + GX_CC_TEXC); + GXSetCullMode(setting->reverseCulling ? GX_CULL_FRONT : GX_CULL_BACK); + + GXLoadTexObj(imodel->maskTexObj[imodel->expression], setting->texMapID); + GXSetArray(GX_VA_POS, res->maskVtxPos, 6); + GXSetArray(GX_VA_NRM, res->maskVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->maskVtxTxc, 4); + GXCallDisplayList(res->maskDl, res->maskDlSize); + + if (res->noselineDlSize > 0) { + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, + GX_CC_ZERO); + GXLoadTexObj(&res->noseTexObj, setting->texMapID); + GXSetArray(GX_VA_POS, res->noselineVtxPos, 6); + GXSetArray(GX_VA_NRM, res->noselineVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->noselineVtxTxc, 4); + GXCallDisplayList(res->noselineDl, res->noselineDlSize); + } + + if (res->glassesDlSize > 0) { + GXSetTevKColor(setting->tevKColorID, cGlassColor[res->glassesColor]); + GXSetCullMode(GX_CULL_NONE); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, + GX_CC_ZERO); + GXLoadTexObj(&res->glassesTexObj, setting->texMapID); + GXSetArray(GX_VA_POS, res->glassesVtxPos, 6); + GXSetArray(GX_VA_NRM, res->glassesVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->glassesVtxTxc, 4); + GXCallDisplayList(res->glassesDl, res->glassesDlSize); + } +} + +void RFLiInitCharModelRes(RFLiCharModelRes* res, const RFLiCharInfo* info) { + Vec noseTrans; + Vec beardTrans; + Vec hairTrans; + + GXSetMisc(1, 0); + GXSetMisc(2, 1); + + /** + * Faceline shape + */ + { + RFLiShapeRes arg; + arg.part = RFLiPartsShp_Faceline; + arg.file = info->faceline.type; + arg.vtxPosBuf = res->faceVtxPos; + arg.vtxNrmBuf = res->faceVtxNrm; + arg.vtxTxcBuf = res->faceVtxTxc; + arg.dlBuf = res->faceDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->faceVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->faceVtxNrm)); + arg.vtxTxcBufSize = NUM_VTX_TXC(sizeof(res->faceVtxTxc)); + arg.dlBufSize = sizeof(res->faceDl); + arg.noseTrans = &noseTrans; + arg.beardTrans = &beardTrans; + arg.hairTrans = &hairTrans; + arg.flipX = FALSE; + arg.transform = FALSE; + RFLiInitShapeRes(&arg); + + res->faceDlSize = arg.dlSize; + } + + /** + * Cap shape + */ + { + RFLiShapeRes arg; + arg.part = RFLiPartsShp_Cap; + arg.file = info->hair.type; + arg.vtxPosBuf = res->capVtxPos; + arg.vtxNrmBuf = res->capVtxNrm; + arg.vtxTxcBuf = res->capVtxTxc; + arg.dlBuf = res->capDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->capVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->capVtxNrm)); + arg.vtxTxcBufSize = NUM_VTX_TXC(sizeof(res->capVtxTxc)); + arg.dlBufSize = sizeof(res->capDl); + arg.flipX = info->hair.flip; + arg.transform = TRUE; + arg.posScale = 1.0f; + arg.posTrans = &hairTrans; + RFLiInitShapeRes(&arg); + + res->capDlSize = arg.dlSize; + + res->hairVtxPos = (s16*)((u8*)res->capVtxPos + + (arg.numVtxPos * 4 - arg.numVtxPos) * 2); + res->hairVtxNrm = (s16*)((u8*)res->capVtxNrm + + (arg.numVtxNrm * 4 - arg.numVtxNrm) * 2); + + res->hairDl = res->noseDl + ROUND_UP(arg.dlSize, 32) + + offsetof(RFLiCharModelRes, capDl); + } + + /** + * Hair shape + */ + { + RFLiShapeRes arg; + int temp = 4; + + arg.part = RFLiPartsShp_Hair; + arg.file = info->hair.type; + arg.vtxPosBuf = res->hairVtxPos; + arg.vtxNrmBuf = res->hairVtxNrm; + arg.dlBuf = res->hairDl; + + arg.vtxPosBufSize = + NUM_VTX_POS(sizeof(res->capVtxPos)) - + (((uintptr_t)res->hairVtxPos - (uintptr_t)res->capVtxPos) / + VTX_COORD_SIZE) / + VTX_COORDS_IN_POS; + + arg.vtxNrmBufSize = + NUM_VTX_POS(sizeof(res->capVtxNrm)) - + (((uintptr_t)res->hairVtxNrm - (uintptr_t)res->capVtxNrm) / + VTX_COORD_SIZE) / + VTX_COORDS_IN_NRM; + + arg.dlBufSize = sizeof(res->capDl) - + ((uintptr_t)res->hairDl - (uintptr_t)res->capDl); + + arg.flipX = info->hair.flip; + arg.transform = TRUE; + arg.posScale = 1.0f; + arg.posTrans = &hairTrans; + RFLiInitShapeRes(&arg); + + res->hairDlSize = arg.dlSize; + + res->foreheadVtxPos = + (s16*)((u8*)res->hairVtxPos + + (arg.numVtxPos * temp - arg.numVtxPos) * 2); + + res->foreheadVtxNrm = + (s16*)((u8*)res->hairVtxNrm + + (arg.numVtxNrm * temp - arg.numVtxNrm) * 2); + + res->foreheadDl = res->hairDl + ROUND_UP(arg.dlSize, 32); + + res->flipHair = info->hair.flip; + } + + /** + * Forehead shape + */ + { + RFLiShapeRes arg; + arg.part = RFLiPartsShp_Forehead; + arg.file = info->hair.type; + arg.vtxPosBuf = res->foreheadVtxPos; + arg.vtxNrmBuf = res->foreheadVtxNrm; + arg.dlBuf = res->foreheadDl; + + arg.vtxPosBufSize = + NUM_VTX_POS(sizeof(res->capVtxPos)) - + (((uintptr_t)res->foreheadVtxPos - (uintptr_t)res->capVtxPos) / + VTX_COORD_SIZE) / + VTX_COORDS_IN_POS; + + arg.vtxNrmBufSize = + NUM_VTX_POS(sizeof(res->capVtxNrm)) - + (((uintptr_t)res->foreheadVtxNrm - (uintptr_t)res->capVtxNrm) / + VTX_COORD_SIZE) / + VTX_COORDS_IN_NRM; + + arg.dlBufSize = sizeof(res->capDl) - + ((uintptr_t)res->foreheadDl - (uintptr_t)res->capDl); + + arg.flipX = info->hair.flip; + arg.transform = TRUE; + arg.posScale = 1.0f; + arg.posTrans = &hairTrans; + RFLiInitShapeRes(&arg); + + res->foreheadDlSize = arg.dlSize; + } + + /** + * Beard shape + */ + { + RFLiShapeRes arg; + arg.part = RFLiPartsShp_Beard; + arg.file = info->beard.type; + arg.vtxPosBuf = res->beardVtxPos; + arg.vtxNrmBuf = res->beardVtxNrm; + arg.dlBuf = res->beardDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->beardVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->beardVtxNrm)); + arg.dlBufSize = sizeof(res->beardDl); + arg.flipX = FALSE; + arg.transform = TRUE; + arg.posScale = 1.0f; + arg.posTrans = &beardTrans; + RFLiInitShapeRes(&arg); + + res->beardDlSize = arg.dlSize; + } + + { + f32 scale; + Vec trans; + + /** + * Nose shape + */ + { + RFLiShapeRes arg; + + scale = 0.4f + 0.175f * info->nose.scale; + + trans.x = noseTrans.x; + trans.y = +noseTrans.y + -1.5f * (info->nose.y - 8); + trans.z = noseTrans.z; + + arg.part = RFLiPartsShp_Nose; + arg.file = info->nose.type; + arg.vtxPosBuf = res->noseVtxPos; + arg.vtxNrmBuf = res->noseVtxNrm; + arg.dlBuf = res->noseDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->noseVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->noseVtxNrm)); + arg.dlBufSize = sizeof(res->noseDl); + arg.flipX = FALSE; + arg.transform = TRUE; + arg.posScale = scale; + arg.posTrans = &trans; + RFLiInitShapeRes(&arg); + + res->noseDlSize = arg.dlSize; + } + + /** + * Noseline shape + */ + { + RFLiShapeRes arg; + + arg.part = RFLiPartsShp_Noseline; + arg.file = info->nose.type; + arg.vtxPosBuf = res->noselineVtxPos; + arg.vtxNrmBuf = res->noselineVtxNrm; + arg.vtxTxcBuf = res->noselineVtxTxc; + arg.dlBuf = res->noselineDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->noselineVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->noselineVtxNrm)); + arg.vtxTxcBufSize = NUM_VTX_TXC(sizeof(res->noselineVtxTxc)); + arg.dlBufSize = sizeof(res->noselineDl); + arg.flipX = FALSE; + arg.transform = TRUE; + arg.posScale = scale; + arg.posTrans = &trans; + RFLiInitShapeRes(&arg); + + res->noselineDlSize = arg.dlSize; + } + } + + /** + * Mask shape + */ + { + RFLiShapeRes arg; + arg.part = RFLiPartsShp_Mask; + arg.file = info->faceline.type; + arg.vtxPosBuf = res->maskVtxPos; + arg.vtxNrmBuf = res->maskVtxNrm; + arg.vtxTxcBuf = res->maskVtxTxc; + arg.dlBuf = res->maskDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->maskVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->maskVtxNrm)); + arg.vtxTxcBufSize = NUM_VTX_TXC(sizeof(res->maskVtxTxc)); + arg.dlBufSize = sizeof(res->maskDl); + arg.flipX = FALSE; + arg.transform = FALSE; + RFLiInitShapeRes(&arg); + + res->maskDlSize = arg.dlSize; + } + + /** + * Glasses shape + */ + { + RFLiShapeRes arg; + f32 scale; + Vec trans; + + scale = 0.15f * info->glass.scale + 0.4f; + + trans.x = noseTrans.x; + trans.y = 5.0f + noseTrans.y + -1.5f * (info->glass.y - 11); + trans.z = 2.0f + noseTrans.z; + + arg.part = RFLiPartsShp_Glass; + arg.file = 0; + arg.vtxPosBuf = res->glassesVtxPos; + arg.vtxNrmBuf = res->glassesVtxNrm; + arg.vtxTxcBuf = res->glassesVtxTxc; + arg.dlBuf = res->glassesDl; + arg.vtxPosBufSize = NUM_VTX_POS(sizeof(res->glassesVtxPos)); + arg.vtxNrmBufSize = NUM_VTX_NRM(sizeof(res->glassesVtxNrm)); + arg.vtxTxcBufSize = NUM_VTX_TXC(sizeof(res->glassesVtxTxc)); + arg.dlBufSize = sizeof(res->glassesDl); + arg.flipX = FALSE; + arg.transform = TRUE; + arg.posScale = scale; + arg.posTrans = &trans; + RFLiInitShapeRes(&arg); + + res->glassesDlSize = arg.dlSize; + } + + RFLiInitTexRes(&res->faceTexObj, RFLiPartsShpTex_Face, + info->faceline.texture, res->faceTex); + + if (res->capDlSize > 0) { + RFLiInitTexRes(&res->capTexObj, RFLiPartsShpTex_Cap, info->hair.type, + res->capTex); + } + + if (res->noselineDlSize > 0) { + RFLiInitTexRes(&res->noseTexObj, RFLiPartsShpTex_Noseline, + info->nose.type, res->noseTex); + } + + RFLiInitTexRes(&res->glassesTexObj, RFLiPartsShpTex_Glass, info->glass.type, + res->glassesTex); + + res->facelineColor = info->faceline.color; + res->hairColor = info->hair.color; + res->beardColor = info->beard.color; + res->glassesColor = info->glass.color; + res->favoriteColor = info->personal.color; + + DCFlushRange(res, sizeof(RFLiCharModelRes)); +} + +void RFLiInitShapeRes(RFLiShapeRes* shape) { + static const u32 csHeader[RFLiPartsShp_Max] = { + 'nose', 'frhd', 'face', 'hair', 'cap_', 'berd', 'nsln', 'mask', 'glas'}; + + void* res; + u8* ptr8; + + // Not all shapes have texture coordinates + BOOL skipTxc = shape->part == RFLiPartsShp_Forehead || + shape->part == RFLiPartsShp_Hair || + shape->part == RFLiPartsShp_Beard || + shape->part == RFLiPartsShp_Nose; + + u32 fileSize = RFLiGetShapeSize(shape->part, shape->file); + res = RFLiAlloc32(fileSize); + RFLiLoadShape(shape->part, shape->file, res); + + ptr8 = (u8*)res; + ptr8 += sizeof(u32); + + // Faceline extra data + if (shape->part == RFLiPartsShp_Faceline) { + memcpy(shape->noseTrans, ptr8, sizeof(Vec)); + ptr8 += sizeof(Vec); + memcpy(shape->beardTrans, ptr8, sizeof(Vec)); + ptr8 += sizeof(Vec); + memcpy(shape->hairTrans, ptr8, sizeof(Vec)); + ptr8 += sizeof(Vec); + } + + /** + * Vertex positions + */ + + if (*(u16*)ptr8 == 0) { + shape->numVtxPos = 0; + shape->numVtxNrm = 0; + shape->numVtxTxc = 0; + shape->dlSize = 0; + RFLiFree(res); + return; + } + + shape->numVtxPos = *(u16*)ptr8; + ptr8 += sizeof(u16); + + { + u32 byteSize = SIZE_VTX_POS(shape->numVtxPos); + s16* ptr16 = (s16*)ptr8; + int i; + + if (shape->transform) { + s32 s = 256.0f * shape->posScale; + s32 tx = 256.0f * shape->posTrans->x; + s32 ty = 256.0f * shape->posTrans->y; + s32 tz = 256.0f * shape->posTrans->z; + + for (i = 0; i < shape->numVtxPos; i++) { + s16 temp[3]; + + if (shape->flipX) { + temp[0] = tx + ((-ptr16[0] * s) >> 8); + } else { + temp[0] = tx + ((ptr16[0] * s) >> 8); + } + + temp[1] = ty + ((ptr16[1] * s) >> 8); + temp[2] = tz + ((ptr16[2] * s) >> 8); + + RFLiTransformCoordinate( + &shape->vtxPosBuf[i * VTX_COORDS_IN_POS], temp); + ptr16 += VTX_COORDS_IN_POS; + } + + } else if (shape->flipX != 0) { + for (i = 0; i < shape->numVtxPos; i++) { + s16 temp[3]; + + temp[0] = -ptr16[0]; + temp[1] = ptr16[1]; + temp[2] = ptr16[2]; + + RFLiTransformCoordinate( + &shape->vtxPosBuf[i * VTX_COORDS_IN_POS], temp); + ptr16 += VTX_COORDS_IN_POS; + } + } else { + for (i = 0; i < shape->numVtxPos; i++) { + RFLiTransformCoordinate( + &shape->vtxPosBuf[i * VTX_COORDS_IN_POS], ptr16); + ptr16 += VTX_COORDS_IN_POS; + } + } + + ptr8 += byteSize; + } + + /** + * Vertex normals + */ + + shape->numVtxNrm = *(u16*)ptr8; + ptr8 += sizeof(u16); + + { + s16* ptr16 = (s16*)ptr8; + u32 byteSize = SIZE_VTX_NRM(shape->numVtxNrm); + + if (shape->flipX != 0) { + int i; + for (i = 0; i < shape->numVtxNrm; i++) { + s16 temp[3]; + + temp[0] = -ptr16[0]; + temp[1] = ptr16[1]; + temp[2] = ptr16[2]; + + RFLiTransformCoordinate( + &shape->vtxNrmBuf[i * VTX_COORDS_IN_NRM], temp); + ptr16 += VTX_COORDS_IN_NRM; + } + } else { + int i; + for (i = 0; i < shape->numVtxNrm; i++) { + RFLiTransformCoordinate( + &shape->vtxNrmBuf[i * VTX_COORDS_IN_NRM], ptr16); + ptr16 += VTX_COORDS_IN_NRM; + } + } + + ptr8 += byteSize; + } + + /** + * Vertex texcoords + */ + + { + u32 byteSize; + + if (skipTxc) { + shape->numVtxTxc = 0; + } else { + shape->numVtxTxc = *(u16*)ptr8; + ptr8 += sizeof(u16); + + byteSize = SIZE_VTX_TXC(shape->numVtxTxc); + memcpy(shape->vtxTxcBuf, ptr8, byteSize); + ptr8 += byteSize; + } + } + + /** + * Display list + */ + + { + int i; + int j; + s32 primitiveNum = *ptr8++; + + DCInvalidateRange(shape->dlBuf, shape->dlBufSize); + GXBeginDisplayList(shape->dlBuf, shape->dlBufSize); + + for (i = 0; i < primitiveNum; i++) { + u16 vtxNum = *ptr8++; + GXPrimitive prim = *ptr8++; + + GXBegin(prim, 0, vtxNum); + { + for (j = 0; j < vtxNum; j++) { + GXPosition1x8(*ptr8++); + GXNormal1x8(*ptr8++); + + if (!skipTxc) { + GXTexCoord1x8(*ptr8++); + } + } + } + GXEnd(); + } + } + + shape->dlSize = GXEndDisplayList(); + RFLiFree(res); +} + +void RFLiInitTexRes(GXTexObj* texObj, RFLiPartsShpTex part, u16 file, + void* buffer) { + u32 texSize; + RFLiTexture* tex; + + tex = (RFLiTexture*)RFLiAlloc32(RFLiGetShpTexSize(part, file)); + RFLiLoadShpTexture(part, file, tex); + + switch (part) { + case RFLiPartsShpTex_Face: + texSize = tex->height * tex->width * 2; + break; + case RFLiPartsShpTex_Cap: + case RFLiPartsShpTex_Noseline: + texSize = tex->height * tex->width / 2; + break; + case RFLiPartsShpTex_Glass: + texSize = tex->height * tex->width; + break; + } + + memcpy(buffer, RFLiGetTexImage(tex), texSize); + GXInitTexObj(texObj, buffer, tex->width, tex->height, tex->format, + tex->wrapS, tex->wrapT, FALSE); + GXInitTexObjLOD(texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, FALSE, + FALSE, GX_ANISO_1); + RFLiFree(tex); +} + +static void RFLiTransformCoordinate(s16* to, const s16* from) { + to[coordinateData.rOff] = coordinateData.rRev ? -from[0] : from[0]; + to[coordinateData.uOff] = coordinateData.uRev ? -from[1] : from[1]; + to[coordinateData.fOff] = coordinateData.fRev ? -from[2] : from[2]; +} + +static void RFLDrawShape(const RFLCharModel* model) { + GXCullMode cullMode; + RFLiCharModel* imodel = (RFLiCharModel*)model; + RFLiCharModelRes* res = imodel->res; + + GXClearVtxDesc(); + GXSetVtxDesc(GX_VA_POS, GX_VA_TEX1MTXIDX); + GXSetVtxDesc(GX_VA_NRM, GX_VA_TEX1MTXIDX); + GXSetVtxAttrFmt(0, GX_VA_POS, 1, 3, 8); + GXSetVtxAttrFmt(0, GX_VA_NRM, 0, 3, 14); + GXSetVtxAttrFmt(0, GX_VA_TEX0, 1, 3, 13); + + GXLoadPosMtxImm(imodel->posMtx, 0); + GXLoadNrmMtxImm(imodel->nrmMtx, 0); + GXSetCurrentMtx(0); + + if (res->beardDlSize > 0) { + GXSetArray(GX_VA_POS, res->beardVtxPos, 6); + GXSetArray(GX_VA_NRM, res->beardVtxNrm, 6); + GXCallDisplayList(res->beardDl, res->beardDlSize); + } + + if (res->noseDlSize > 0) { + GXSetArray(GX_VA_POS, res->noseVtxPos, 6); + GXSetArray(GX_VA_NRM, res->noseVtxNrm, 6); + GXCallDisplayList(res->noseDl, res->noseDlSize); + } + + if (res->flipHair) { + GXGetCullMode(&cullMode); + switch (cullMode) { + case GX_CULL_BACK: + GXSetCullMode(GX_CULL_FRONT); + break; + case GX_CULL_FRONT: + GXSetCullMode(GX_CULL_BACK); + break; + } + } + + if (res->foreheadDlSize > 0) { + GXSetArray(GX_VA_POS, res->foreheadVtxPos, 6); + GXSetArray(GX_VA_NRM, res->foreheadVtxNrm, 6); + GXCallDisplayList(res->foreheadDl, res->foreheadDlSize); + } + + if (res->hairDlSize > 0) { + GXSetArray(GX_VA_POS, res->hairVtxPos, 6); + GXSetArray(GX_VA_NRM, res->hairVtxNrm, 6); + GXCallDisplayList(res->hairDl, res->hairDlSize); + } + + GXSetVtxDesc(GX_VA_TEX0, GX_VA_TEX1MTXIDX); + if (res->capDlSize > 0) { + GXSetArray(GX_VA_POS, res->capVtxPos, 6); + GXSetArray(GX_VA_NRM, res->capVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->capVtxTxc, 4); + GXCallDisplayList(res->capDl, res->capDlSize); + } + + if (res->flipHair) { + switch (cullMode) { + case GX_CULL_BACK: + GXSetCullMode(GX_CULL_BACK); + break; + case GX_CULL_FRONT: + GXSetCullMode(GX_CULL_FRONT); + break; + } + } + + GXSetArray(GX_VA_POS, res->faceVtxPos, 6); + GXSetArray(GX_VA_NRM, res->faceVtxNrm, 6); + GXSetArray(GX_VA_TEX0, res->faceVtxTxc, 4); + GXCallDisplayList(res->faceDl, res->faceDlSize); +} + +void RFLSetModelDrawDoneCallback(RFLCallback cb) { + RFLiGetManager()->modelDrawCb = cb; +} diff --git a/src/RVLFaceLib/RFL_NANDAccess.c b/src/RVLFaceLib/RFL_NANDAccess.c new file mode 100644 index 00000000..1a8d863b --- /dev/null +++ b/src/RVLFaceLib/RFL_NANDAccess.c @@ -0,0 +1,1135 @@ +#include +#include +#include +#include + +#define ACC_SAFE_BUFFER_SIZE 0x2000 +#define MAX_RETRY_COUNT 30 + +static const char* scFileNames[RFLiFileType_Max] = { + "/shared2/menu/FaceLib/RFL_DB.dat", //!< RFLiFileType_Database + "/shared2/menu/FaceLib/RFL_Res.dat" //!< RFLiFileType_Resource +}; + +static const char* scFirstDirectory = "/shared2/menu"; +static const char* scSecondDirectory = "/shared2/menu/FaceLib"; + +static const u8 scFilePermissions[RFLiFileType_Max] = { + NAND_PERM_RWALL, // RFLiFileType_Database + NAND_PERM_RALL // RFLiFileType_Resource +}; + +static const u8 scFileAttributes[RFLiFileType_Max] = { + 0, //!< RFLiFileType_Database + 0 //!< RFLiFileType_Resource +}; + +static void opencallback_(s32 result, NANDCommandBlock* block); +static void createcallback_(s32 result, NANDCommandBlock* block); +static void close2opencallback_(s32 result, NANDCommandBlock* block); +static void readcallback_(s32 result, NANDCommandBlock* block); +static void writecallback_(s32 result, NANDCommandBlock* block); +static void readseekcallback_(s32 result, NANDCommandBlock* block); +static void writecallback_(s32 result, NANDCommandBlock* block); +static void writeseekcallback_(s32 result, NANDCommandBlock* block); +static void closecallback_(s32 result, NANDCommandBlock* block); +static void getlengthcallback_(s32 result, NANDCommandBlock* block); +static void deletecallback_(s32 result, NANDCommandBlock* block); +static void createdircallback1_(s32 result, NANDCommandBlock* block); +static void createdircallback2_(s32 result, + NANDCommandBlock* block) NO_INLINE; + +void RFLiInitAccessInfo(MEMiHeapHead* heap) { + u16 i; + + for (i = 0; i < RFLiFileType_Max; i++) { + RFLiAccessInfo* info = RFLiGetAccInfo((RFLiFileType)i); + memset(info, 0, sizeof(RFLiAccessInfo)); + info->safeBuffer = + MEMAllocFromExpHeapEx(heap, ACC_SAFE_BUFFER_SIZE, 32); + OSCreateAlarm(&info->alarm); + } +} + +void RFLiExitAccessInfo(void) { + u16 i; + + for (i = 0; i < RFLiFileType_Max; i++) { + RFLiAccessInfo* info = RFLiGetAccInfo((RFLiFileType)i); + OSCancelAlarm(&info->alarm); + } +} + +BOOL RFLiIsWorking(void) { + return RFLiGetWorking(); +} + +void RFLiStartWorking(void) { + BOOL enabled; + + enabled = OSDisableInterrupts(); + + RFLiSetWorking(TRUE); + RFLiGetManager()->lastErrCode = RFLErrcode_Busy; + + OSRestoreInterrupts(enabled); +} + +static void startWorkingClose_(void) { + BOOL enabled; + + enabled = OSDisableInterrupts(); + + RFLiSetWorking(TRUE); + RFLiGetManager()->beforeCloseErr = RFLiGetManager()->lastErrCode; + RFLiGetManager()->beforeCloseReason = RFLiGetManager()->lastReason; + RFLiGetManager()->lastErrCode = RFLErrcode_Busy; + + OSRestoreInterrupts(enabled); +} + +static void endWorkingCloseReason_(RFLErrcode err, s32 reason) { + BOOL enabled; + + enabled = OSDisableInterrupts(); + + RFLiSetWorking(FALSE); + if (err == RFLErrcode_Success) { + RFLiGetManager()->lastErrCode = RFLiGetManager()->beforeCloseErr; + // @bug Should be beforeCloseReason + RFLiGetManager()->lastReason = RFLiGetManager()->beforeCloseErr; + } else { + RFLiGetManager()->lastErrCode = err; + RFLiGetManager()->lastReason = reason; + } + + OSRestoreInterrupts(enabled); +} + +static void endWorkingClose_(RFLErrcode err) { + endWorkingCloseReason_(err, NAND_RESULT_OK); +} + +void RFLiEndWorkingReason(RFLErrcode err, s32 reason) { + BOOL enabled; + + switch (RFLiGetManager()->lastErrCode) { + case RFLErrcode_Busy: + case RFLErrcode_Success: + enabled = OSDisableInterrupts(); + + RFLiSetWorking(FALSE); + RFLiGetManager()->lastErrCode = err; + RFLiGetManager()->lastReason = reason; + + OSRestoreInterrupts(enabled); + break; + default: + break; + } +} + +void RFLiEndWorking(RFLErrcode err) { + RFLiEndWorkingReason(err, NAND_RESULT_OK); +} + +NANDCommandBlock* RFLiSetCommandBlock(RFLiFileType type, RFLiAsyncTag tag) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + RFLiCallbackTag* data; + + block = &RFLiGetAccInfo(type)->block; + info = RFLiGetAccInfo(type); + + data = &info->tag; + data->tag = tag; + data->type = type; + + NANDSetUserData(block, data); + return block; +} + +RFLiFileType RFLiGetType(NANDCommandBlock* block) { + const RFLiCallbackTag* data; + data = (const RFLiCallbackTag*)NANDGetUserData(block); + + return data->type; +} + +NANDFileInfo* RFLiGetWorkingFile(RFLiFileType type) { + return RFLAvailable() ? &RFLiGetAccInfo(type)->file : NULL; +} + +static void alarmCallback_(OSAlarm* alarm, OSContext* ctx) { +#pragma unused(ctx) + + const RFLiFileType* userData; + userData = (const RFLiFileType*)OSGetAlarmUserData(alarm); + + if (RFLAvailable()) { + const RFLiExCallback alarmCallback = + RFLiGetAccInfo(*userData)->retryCallback; + alarmCallback(*userData); + } +} + +static void retry_(u32 arg, u8 retryCount, RFLiExCallback callback) { + RFLiAccessInfo* info; + + if (retryCount < MAX_RETRY_COUNT) { + info = RFLiGetAccInfo(arg); + info->retryCallback = callback; + info->alarmData = arg; + info->retryCount = retryCount; + + OSCancelAlarm(&info->alarm); + OSSetAlarmUserData(&info->alarm, &info->alarmData); + OSSetAlarm(&info->alarm, OS_MSEC_TO_TICKS(50), alarmCallback_); + } else { + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, NAND_RESULT_BUSY); + } +} + +static void opencallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + BOOL doCallback; + + doCallback = TRUE; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + info->opened = TRUE; + RFLiEndWorking(RFLErrcode_Success); + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_MAXFD: + case NAND_RESULT_MAXBLOCKS: + case NAND_RESULT_AUTHENTICATION: + case NAND_RESULT_ECC_CRIT: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ACCESS: + info->opened = FALSE; + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_NOEXISTS: + info->opened = FALSE; + if (info->openInfo.openMode == 1) { + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + } else { + result = NANDPrivateCreateAsync( + info->openInfo.path, info->openInfo.perm, info->openInfo.attr, + createcallback_, + RFLiSetCommandBlock(type, RFLiAsyncTag_CreateAsync)); + + switch (result) { + case NAND_RESULT_OK: + doCallback = FALSE; + break; + default: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + } + } + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_INVALID: + default: + info->opened = FALSE; + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (doCallback && info->callback != NULL) { + info->callback(); + } +} + +static void createcallback_(s32 result, NANDCommandBlock* block) { + NANDCommandBlock* openBlock; + RFLiAccessInfo* info; + RFLiFileType type; + BOOL doCallback; + s32 reason; + + doCallback = TRUE; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + case NAND_RESULT_EXISTS: + openBlock = RFLiSetCommandBlock(type, RFLiAsyncTag_OpenAsync); + memset(info->safeBuffer, 0, ACC_SAFE_BUFFER_SIZE); + + reason = NANDPrivateSafeOpenAsync( + info->openInfo.path, RFLiGetWorkingFile(type), + info->openInfo.openMode, info->safeBuffer, ACC_SAFE_BUFFER_SIZE, + opencallback_, openBlock); + + switch (reason) { + case NAND_RESULT_OK: + doCallback = FALSE; + break; + case NAND_RESULT_NOEXISTS: + case NAND_RESULT_MAXFD: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + case NAND_RESULT_ECC_CRIT: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (doCallback && info->callback != NULL) { + info->callback(); + } +} + +static void close2opencallback_(s32 result, NANDCommandBlock* block) { + NANDCommandBlock* openBlock; + RFLiAccessInfo* info; + RFLiFileType type; + BOOL doCallback; + s32 reason; + + doCallback = TRUE; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + openBlock = RFLiSetCommandBlock(type, RFLiAsyncTag_OpenAsync); + memset(info->safeBuffer, 0, ACC_SAFE_BUFFER_SIZE); + + reason = NANDPrivateSafeOpenAsync( + info->openInfo.path, RFLiGetWorkingFile(type), + info->openInfo.openMode, info->safeBuffer, ACC_SAFE_BUFFER_SIZE, + opencallback_, openBlock); + + switch (reason) { + case NAND_RESULT_OK: + doCallback = FALSE; + break; + case NAND_RESULT_NOEXISTS: + case NAND_RESULT_MAXFD: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (doCallback && info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiOpenAsync(RFLiFileType type, u8 openMode, + RFLiCallback callback) { + NANDCommandBlock* block; + const char* filename; + NANDFileInfo* file; + RFLiAccessInfo* info; + s32 reason; + + file = RFLiGetWorkingFile(type); + RFLiStartWorking(); + info = RFLiGetAccInfo(type); + filename = scFileNames[type]; + + info->callback = callback; + strncpy(info->openInfo.path, filename, 64); + info->openInfo.path[64] = '\0'; + info->openInfo.openMode = openMode; + info->openInfo.perm = scFilePermissions[type]; + info->openInfo.attr = scFileAttributes[type]; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_OpenAsync); + if (info->opened) { + reason = NANDSafeCloseAsync(file, close2opencallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + } + } else { + memset(info->safeBuffer, 0, ACC_SAFE_BUFFER_SIZE); + reason = NANDPrivateSafeOpenAsync( + filename, file, openMode, info->safeBuffer, ACC_SAFE_BUFFER_SIZE, + opencallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_NOEXISTS: + if (openMode == 1) { + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + } else { + block = RFLiSetCommandBlock(type, RFLiAsyncTag_CreateAsync); + reason = NANDPrivateCreateAsync(filename, info->openInfo.perm, + info->openInfo.attr, + createcallback_, block); + if (reason != NAND_RESULT_OK) { + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + } + } + break; + case NAND_RESULT_MAXFD: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + } + + return RFLGetAsyncStatus(); +} + +static void readcallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + BOOL error; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + error = FALSE; + + // Non-error result = buffer size + if (result >= NAND_RESULT_OK) { + if (result > info->readInfo.size) { + error = TRUE; + RFLiEndWorking(RFLErrcode_Loadfail); + } else if (result == info->readInfo.size) { + RFLiEndWorking(RFLErrcode_Success); + } else { + error = TRUE; + RFLiEndWorking(RFLErrcode_Loadfail); + } + } else { + error = TRUE; + + switch (result) { + case NAND_RESULT_ACCESS: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_BUSY: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ECC_CRIT: + case NAND_RESULT_AUTHENTICATION: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + } + + if (error) { + memset(info->readInfo.dst, 0, info->readInfo.size); + } + + if (info->callback != NULL) { + info->callback(); + } +} + +static void readseekcallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + BOOL doCallback; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + doCallback = TRUE; + + // Non-error result = Seek offset + if (result == info->readInfo.offset) { + NANDCommandBlock* block; + NANDFileInfo* file; + s32 reason; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_ReadAsync); + file = RFLiGetWorkingFile(type); + reason = NANDReadAsync(file, info->readInfo.dst, + ROUND_UP(info->readInfo.size, 32), readcallback_, + block); + switch (reason) { + case NAND_RESULT_OK: + doCallback = FALSE; + break; + case NAND_RESULT_ACCESS: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_BUSY: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ECC_CRIT: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + } else if (result >= NAND_RESULT_OK) { + RFLiEndWorking(RFLErrcode_Loadfail); + } else { + switch (result) { + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + } + + if (doCallback && info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiReadAsync(RFLiFileType type, void* dst, u32 size, + RFLiCallback callback, s32 offset) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + NANDFileInfo* file; + s32 reason; + + file = RFLiGetWorkingFile(type); + info = RFLiGetAccInfo(type); + + RFLiStartWorking(); + + info->callback = callback; + info->readInfo.dst = dst; + info->readInfo.size = size; + info->readInfo.offset = offset; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_SeekAsync); + reason = + NANDSeekAsync(file, offset, 0, readseekcallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + return RFLGetAsyncStatus(); +} + +static void retryWrite_(u32 arg) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + NANDFileInfo* file; + s32 reason; + + info = RFLiGetAccInfo(arg); + block = RFLiSetCommandBlock(arg, RFLiAsyncTag_WriteAsync); + file = RFLiGetWorkingFile(arg); + + reason = NANDWriteAsync(file, info->writeInfo.src, info->writeInfo.size, + writecallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(arg, info->retryCount + 1, retryWrite_); + break; + case NAND_RESULT_MAXBLOCKS: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + if (!RFLiIsWorking() && info->callback != NULL) { + info->callback(); + } +} + +static void writecallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + if (result == info->writeInfo.size) { + RFLiEndWorking(RFLErrcode_Success); + } else if (result >= NAND_RESULT_OK) { + RFLiEndWorking(RFLErrcode_Savefail); + } else { + switch (result) { + case NAND_RESULT_MAXBLOCKS: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(type, info->retryCount + 1, retryWrite_); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + } + + if (info->callback != NULL) { + info->callback(); + } +} + +static void retryWriteSeek_(u32 arg) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + NANDFileInfo* file; + s32 reason; + + info = RFLiGetAccInfo(arg); + block = RFLiSetCommandBlock(arg, RFLiAsyncTag_SeekAsync); + file = RFLiGetWorkingFile(arg); + + reason = NANDSeekAsync(file, info->writeInfo.offset, 0, + writeseekcallback_, block); + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(arg, info->retryCount + 1, retryWriteSeek_); + break; + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } +} + +static void writeseekcallback_inline(u32 arg) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + NANDFileInfo* file; + s32 reason; + + block = RFLiSetCommandBlock(arg, RFLiAsyncTag_WriteAsync); + info = RFLiGetAccInfo(arg); + file = RFLiGetWorkingFile(arg); + reason = NANDWriteAsync(file, info->writeInfo.src, info->writeInfo.size, + writecallback_, block); + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(arg, 0, retryWrite_); + break; + case NAND_RESULT_MAXBLOCKS: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } +} + +static void writeseekcallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + BOOL doCallback; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + doCallback = TRUE; + + if (result == info->writeInfo.offset) { + writeseekcallback_inline(type); + if (RFLiIsWorking()) { + doCallback = FALSE; + } + } else if (result >= NAND_RESULT_OK) { + RFLiEndWorking(RFLErrcode_Savefail); + } else { + switch (result) { + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(type, 0, retryWriteSeek_); + break; + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + } + + if (doCallback && info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiWriteAsync(RFLiFileType type, const void* src, u32 size, + RFLiCallback callback, s32 offset) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + NANDFileInfo* file; + s32 reason; + + info = RFLiGetAccInfo(type); + file = RFLiGetWorkingFile(type); + + RFLiStartWorking(); + + info->callback = callback; + info->writeInfo.src = src; + info->writeInfo.size = size; + info->writeInfo.offset = offset; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_SeekAsync); + reason = + NANDSeekAsync(file, offset, 0, writeseekcallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + retry_(type, 0, retryWriteSeek_); + break; + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + return RFLGetAsyncStatus(); +} + +static s32 closecore_(u32 arg) { + NANDCommandBlock* block; + NANDFileInfo* info; + + block = RFLiSetCommandBlock(arg, RFLiAsyncTag_CloseAsync); + info = RFLiGetWorkingFile(arg); + + return NANDSafeCloseAsync(info, closecallback_, block); +} + +static void closecallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + info->opened = FALSE; + endWorkingClose_(RFLErrcode_Success); + break; + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (!RFLiIsWorking() && info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiCloseAsync(RFLiFileType type, RFLiCallback callback) { + RFLiAccessInfo* info; + s32 reason; + + info = RFLiGetAccInfo(type); + startWorkingClose_(); + info->callback = callback; + reason = closecore_(type); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + return RFLGetAsyncStatus(); +} + +static void getlengthcallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + RFLiEndWorking(RFLErrcode_Success); + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiGetLengthAsync(RFLiFileType type, u32* out, + RFLiCallback callback) { + NANDCommandBlock* block; + NANDFileInfo* file; + RFLiAccessInfo* info; + s32 reason; + + info = RFLiGetAccInfo(type); + RFLiStartWorking(); + info->callback = callback; + info->getLengthInfo.dst = out; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_GetLengthAsync); + file = RFLiGetWorkingFile(type); + reason = NANDGetLengthAsync(file, out, getlengthcallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + return RFLGetAsyncStatus(); +} + +static void deletecallback_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + case NAND_RESULT_NOEXISTS: + RFLiEndWorking(RFLErrcode_Success); + break; + case NAND_RESULT_OPENFD: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiDeleteAsync(RFLiFileType type, RFLiCallback callback) { + NANDCommandBlock* block; + RFLiAccessInfo* info; + s32 reason; + + info = RFLiGetAccInfo(type); + RFLiStartWorking(); + info->callback = callback; + + block = RFLiSetCommandBlock(type, RFLiAsyncTag_DeleteAsync); + reason = NANDPrivateDeleteAsync(scFileNames[type], deletecallback_, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_NOEXISTS: + RFLiEndWorking(RFLErrcode_Success); + break; + case NAND_RESULT_OPENFD: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + case NAND_RESULT_ACCESS: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } + + return RFLGetAsyncStatus(); +} + +static void createDirCommon_(const char* dir, NANDAsyncCallback callback) { + NANDCommandBlock* block; + s32 reason; + + block = + RFLiSetCommandBlock(RFLiFileType_Database, RFLiAsyncTag_CreateDirAsync); + reason = + NANDPrivateCreateDirAsync(dir, NAND_PERM_RWALL, 0, callback, block); + + switch (reason) { + case NAND_RESULT_OK: + break; + case NAND_RESULT_EXISTS: + callback(NAND_RESULT_EXISTS, block); + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, reason); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_ECC_CRIT: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, reason); + break; + } +} + +static void createdircallback2_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + case NAND_RESULT_EXISTS: + RFLiEndWorking(RFLErrcode_Success); + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_ECC_CRIT: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (RFLGetAsyncStatus() != RFLErrcode_Busy && info->callback != NULL) { + info->callback(); + } +} + +static void createdircallback1_(s32 result, NANDCommandBlock* block) { + RFLiAccessInfo* info; + RFLiFileType type; + + type = RFLiGetType(block); + info = RFLiGetAccInfo(type); + + switch (result) { + case NAND_RESULT_OK: + case NAND_RESULT_EXISTS: + createDirCommon_(scSecondDirectory, createdircallback2_); + break; + case NAND_RESULT_MAXFILES: + case NAND_RESULT_CORRUPT: + case NAND_RESULT_BUSY: + case NAND_RESULT_ALLOC_FAILED: + RFLiEndWorkingReason(RFLErrcode_NANDCommandfail, result); + break; + case NAND_RESULT_FATAL_ERROR: + case NAND_RESULT_UNKNOWN: + case NAND_RESULT_INVALID: + case NAND_RESULT_ECC_CRIT: + case NAND_RESULT_ACCESS: + default: + RFLiEndWorkingReason(RFLErrcode_Fatal, result); + break; + } + + if (RFLGetAsyncStatus() != RFLErrcode_Busy && info->callback != NULL) { + info->callback(); + } +} + +RFLErrcode RFLiCreateSaveDirAsync(RFLiCallback callback) { + RFLiAccessInfo* info; + + info = RFLiGetAccInfo(RFLiFileType_Database); + RFLiStartWorking(); + info->callback = callback; + + createDirCommon_(scFirstDirectory, createdircallback1_); + + return RFLGetAsyncStatus(); +} diff --git a/src/RVLFaceLib/RFL_NANDLoader.c b/src/RVLFaceLib/RFL_NANDLoader.c new file mode 100644 index 00000000..9fceb389 --- /dev/null +++ b/src/RVLFaceLib/RFL_NANDLoader.c @@ -0,0 +1,438 @@ +#include +#include +#include + +#define LOADER_HEADER_BUF_1_SIZE 0x100 +#define LOADER_HEADER_BUF_2_SIZE 0x20 +#define TEMP_BUF_SIZE 0x400 + +static const char* scResFileFullPathName = "/shared2/FaceLib/RFL_Res.dat"; + +void RFLiInitLoader(void) { + RFLiLoader* loader; + int i; + + loader = RFLiGetLoader(); + for (i = 0; i < RFLiArcID_Max; i++) { + loader->archives[i].numFiles = 0; + loader->archives[i].biggestSize = 0; + loader->archives[i].offset = 0; + } + + loader->cacheSize = 0; + loader->cache = NULL; + loader->cached = FALSE; + loader->version = 0; + loader->headerBuf1 = NULL; + loader->headerBuf2 = NULL; +} + +static void parseOnmemoryRes_(void) NO_INLINE { + RFLiLoader* loader; + int i; + + loader = RFLiGetLoader(); + loader->version = *(u16*)((u8*)loader->cache + 2); + for (i = 0; i < RFLiArcID_Max; i++) { + // Pointer to section offset in header + u32* offset = (u32*)((u8*)loader->cache + ((i + 1) * 4)); + // Pointer to section + u32* p_section = (u32*)((u8*)loader->cache + *offset); + // Load archive + loader->archives[i].numFiles = ((u16*)p_section)[0]; + loader->archives[i].biggestSize = ((u16*)p_section)[1]; + loader->archives[i].offset = *offset + 4; + } +} + +static void loadResRead2ndcallback_(void) { + RFLiLoader* loader; + BOOL free; + RFLiCallback cb; + u32* headerBuf1; + u32* headerBuf2; + + loader = RFLiGetLoader(); + free = FALSE; + cb = NULL; + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + const u16 arc = loader->numResources; + headerBuf1 = (u32*)loader->headerBuf1; + if (arc < RFLiArcID_Max) { + headerBuf2 = (u32*)loader->headerBuf2; + headerBuf1 = &headerBuf1[arc + 1]; + loader->archives[arc].numFiles = ((u16*)headerBuf2)[0]; + loader->archives[arc].biggestSize = ((u16*)headerBuf2)[1]; + loader->archives[arc].offset = *headerBuf1 + 4; + loader->numResources++; + switch (RFLiReadAsync(RFLiFileType_Resource, loader->headerBuf2, 32, + loadResRead2ndcallback_, *(headerBuf1 + 1))) { + case RFLErrcode_Busy: + break; + case RFLErrcode_Success: + break; + default: + free = TRUE; + } + } else { + free = TRUE; + cb = NULL; + } + } else { + free = TRUE; + RFLiSetFileBroken(RFLiFileBrokenType_ResBroken); + } + + if (free) { + RFLiFree(loader->headerBuf1); + loader->headerBuf1 = NULL; + RFLiFree(loader->headerBuf2); + loader->headerBuf2 = NULL; + RFLiCloseAsync(RFLiFileType_Resource, cb); + } +} + +static void errclosecallback_(void) { + RFLExit(); +} + +static void loadResRead1stcallback_(void) { + RFLiLoader* loader; + BOOL free; + u32* headerBuf1; + void* headerBuf2; + + loader = RFLiGetLoader(); + free = FALSE; + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + headerBuf1 = (u32*)loader->headerBuf1; + loader->version = *(u16*)((u8*)headerBuf1 + 2); + + headerBuf2 = RFLiAlloc32(LOADER_HEADER_BUF_2_SIZE); + loader->headerBuf2 = headerBuf2; + loader->numResources = 0; + + switch (RFLiReadAsync(RFLiFileType_Resource, headerBuf2, + LOADER_HEADER_BUF_2_SIZE, loadResRead2ndcallback_, + *(headerBuf1 + 1))) { + case RFLErrcode_Busy: + break; + case RFLErrcode_Success: + break; + default: + free = TRUE; + } + } else { + free = TRUE; + RFLiSetFileBroken(RFLiFileBrokenType_ResBroken); + } + + if (free) { + RFLiFree(loader->headerBuf1); + loader->headerBuf1 = NULL; + + if (loader->headerBuf2 != NULL) { + RFLiFree(loader->headerBuf2); + loader->headerBuf2 = NULL; + } + + RFLiCloseAsync(RFLiFileType_Resource, errclosecallback_); + } +} + +static void loadResGetlengthcallback_(void) { + RFLiLoader* loader; + void* headerBuf1; + + loader = RFLiGetLoader(); + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + headerBuf1 = RFLiAlloc32(LOADER_HEADER_BUF_1_SIZE); + loader->headerBuf1 = headerBuf1; + switch (RFLiReadAsync(RFLiFileType_Resource, headerBuf1, + LOADER_HEADER_BUF_1_SIZE, loadResRead1stcallback_, + 0)) { + case RFLErrcode_Busy: + break; + case RFLErrcode_Success: + break; + default: + RFLiFree(loader->headerBuf1); + loader->headerBuf1 = NULL; + RFLiCloseAsync(RFLiFileType_Resource, NULL); + } + } else { + RFLiCloseAsync(RFLiFileType_Resource, errclosecallback_); + } +} + +static void loadResOpencallback_(void) { + RFLiLoader* loader; + + if (RFLGetAsyncStatus() == RFLErrcode_Success) { + loader = RFLiGetLoader(); + loader->cacheSize = 0; + switch (RFLiGetLengthAsync(RFLiFileType_Resource, &loader->cacheSize, + loadResGetlengthcallback_)) { + case RFLErrcode_Busy: + break; + case RFLErrcode_Success: + break; + default: + RFLiCloseAsync(RFLiFileType_Resource, NULL); + } + } else { + RFLExit(); + } +} + +RFLErrcode RFLiLoadResourceHeaderAsync(void) { + RFLiLoader* loader; + + loader = RFLiGetLoader(); + if (loader == NULL) { + RFLiEndWorking(RFLErrcode_Fatal); + return RFLErrcode_Fatal; + } + + if (RFLIsResourceCached()) { + parseOnmemoryRes_(); + RFLiEndWorking(RFLErrcode_Success); + return RFLErrcode_Busy; + } + + return RFLiOpenAsync(RFLiFileType_Resource, 1, + loadResOpencallback_); +} + +static u32 getCachedLength_(RFLiLoader* loader, u32 arcIdx, u16 fileIdx) { + RFLiArchive* arc = &loader->archives[arcIdx]; + const void* arcBuf = (u8*)loader->cache + arc->offset; + const u32 next = ((u32*)arcBuf)[fileIdx + 1]; + const u32 self = ((u32*)arcBuf)[fileIdx]; + return next - self; +} + +static u32 getNANDLength_(RFLiLoader* loader, u32 arcIdx, u16 fileIdx) { + NANDFileInfo file; + RFLiArchive* arc; + void* tmpBuf; + u32 length = 0; + + tmpBuf = RFLiAlloc32(TEMP_BUF_SIZE); + arc = &loader->archives[arcIdx]; + + if (NANDPrivateOpen(scResFileFullPathName, &file, 1) == + NAND_RESULT_OK) { + const u32 readSize = ROUND_UP(arc->numFiles * 4 + 4, 32); + NANDSeek(&file, arc->offset, 0); + + if (NANDRead(&file, tmpBuf, readSize) == readSize) { + const u32 self = ((u32*)tmpBuf)[fileIdx]; + const u32 next = ((u32*)tmpBuf)[fileIdx + 1]; + length = next - self; + } + + NANDClose(&file); + } + + RFLiFree(tmpBuf); + return length; +} + +static u32 getLength_(u32 arcIdx, u16 fileIdx) { + RFLiLoader* loader; + + loader = RFLiGetLoader(); + if (loader == NULL) { + return 0; + } + + if (fileIdx >= loader->archives[arcIdx].numFiles) { + return 0; + } + + if (RFLIsResourceCached()) { + return getCachedLength_(loader, arcIdx, fileIdx); + } else { + return getNANDLength_(loader, arcIdx, fileIdx); + } +} + +static void* getCachedFile_(void* dst, RFLiLoader* loader, u32 arcIdx, + u16 fileIdx) { + const u8* cache = (u8*)loader->cache; + RFLiArchive* arc = &loader->archives[arcIdx]; + const void* arcBuf = cache + arc->offset; + const u32 self = ((u32*)arcBuf)[fileIdx]; + const u32 next = ((u32*)arcBuf)[fileIdx + 1]; + const u32 size = next - self; + const u32 src = arc->offset + self + (arc->numFiles * 4); + + memcpy(dst, src + 4 + cache, size); + return dst; +} + +static void* getNANDFile_(void* dst, RFLiLoader* loader, u32 arcIdx, + u16 fileIdx) { + NANDFileInfo file; + RFLiArchive* arc; + void* tmpBuf; + void* arcBuf; + void* ret; + u32 readSize; + + ret = NULL; + arc = &loader->archives[arcIdx]; + tmpBuf = RFLiAlloc32(TEMP_BUF_SIZE); + + if (NANDPrivateOpen(scResFileFullPathName, &file, 1) == + NAND_RESULT_OK) { + u32 arcBufSize; + s32 seekOffset; + u32 arcSize; + u32 arcOffset; + + readSize = ROUND_UP(arc->numFiles * 4 + 4, 32); + arcSize = 0; + arcOffset = 0; + + NANDSeek(&file, arc->offset, 0); + + if (NANDRead(&file, tmpBuf, readSize) == readSize) { + const u32 next = ((u32*)tmpBuf)[fileIdx + 1]; + const u32 self = ((u32*)tmpBuf)[fileIdx]; + arcSize = next - self; + arcOffset = self; + } else { + RFLiSetFileBroken(RFLiFileBrokenType_ResBroken); + RFLiFree(tmpBuf); + return NULL; + } + + seekOffset = arc->offset + arc->numFiles * 4 + 4 + arcOffset; + arcBufSize = ROUND_UP(arcSize, 32); + + arcBuf = RFLiAlloc32(arcBufSize); + NANDSeek(&file, seekOffset, 0); + + if (NANDRead(&file, arcBuf, arcBufSize) == arcBufSize) { + memcpy(dst, arcBuf, arcSize); + ret = dst; + } else { + RFLiSetFileBroken(RFLiFileBrokenType_ResBroken); + RFLiFree(tmpBuf); + RFLiFree(arcBuf); + return NULL; + } + + RFLiFree(arcBuf); + NANDClose(&file); + } else { + RFLiSetFileBroken(RFLiFileBrokenType_ResBroken); + } + + RFLiFree(tmpBuf); + return ret; +} + +static void* getFile_(void* dst, u32 arcIdx, u16 fileIdx) { + RFLiLoader* loader; + + if (!RFLAvailable()) { + return NULL; + } + + loader = RFLiGetLoader(); + if (loader == NULL) { + return NULL; + } + + if (fileIdx >= loader->archives[arcIdx].numFiles) { + return NULL; + } + + if (RFLIsResourceCached()) { + return getCachedFile_(dst, loader, arcIdx, fileIdx); + } else { + return getNANDFile_(dst, loader, arcIdx, fileIdx); + } +} + +u32 RFLiGetTexSize(RFLiPartsTex part, u16 file) { + static const u32 scParts2Arc[] = {RFLiArcID_Eye, RFLiArcID_Eyebrow, + RFLiArcID_Mouth, RFLiArcID_Mustache, + RFLiArcID_Mole}; + return getLength_(scParts2Arc[part], file); +} + +RFLiTexture* RFLiLoadTexture(RFLiPartsTex part, u16 file, void* dst) { + static const u32 scParts2Arc[] = {RFLiArcID_Eye, RFLiArcID_Eyebrow, + RFLiArcID_Mouth, RFLiArcID_Mustache, + RFLiArcID_Mole}; + return (RFLiTexture*)getFile_(dst, scParts2Arc[part], file); +} + +u32 RFLiGetShpTexSize(RFLiPartsShpTex part, u16 file) { + static const u32 scParts2Arc[] = {RFLiArcID_FaceTex, RFLiArcID_CapTex, + RFLiArcID_NlineTex, RFLiArcID_GlassTex}; + return getLength_(scParts2Arc[part], file); +} + +RFLiTexture* RFLiLoadShpTexture(RFLiPartsShpTex part, u16 file, void* dst) { + static const u32 scParts2Arc[] = {RFLiArcID_FaceTex, RFLiArcID_CapTex, + RFLiArcID_NlineTex, RFLiArcID_GlassTex}; + return (RFLiTexture*)getFile_(dst, scParts2Arc[part], file); +} + +u32 RFLiGetShapeSize(RFLiPartsShp part, u16 file) { + static const u32 scParts2Arc[] = { + RFLiArcID_Nose, RFLiArcID_ForeHead, RFLiArcID_Faceline, + RFLiArcID_Hair, RFLiArcID_Cap, RFLiArcID_Beard, + RFLiArcID_Nline, RFLiArcID_Mask, RFLiArcID_Glass}; + return getLength_(scParts2Arc[part], file); +} + +void* RFLiLoadShape(RFLiPartsShp part, u16 file, void* dst) { + static const u32 scParts2Arc[] = { + RFLiArcID_Nose, RFLiArcID_ForeHead, RFLiArcID_Faceline, + RFLiArcID_Hair, RFLiArcID_Cap, RFLiArcID_Beard, + RFLiArcID_Nline, RFLiArcID_Mask, RFLiArcID_Glass}; + return getFile_(dst, scParts2Arc[part], file); +} + +BOOL RFLFreeCachedResource(void) { + RFLiLoader* loader; + + if (!RFLAvailable()) { + return TRUE; + } + + if (RFLiIsWorking()) { + return TRUE; + } + + loader = RFLiGetLoader(); + if (loader == NULL) { + return TRUE; + } + + loader->cached = FALSE; + loader->cache = NULL; + loader->cacheSize = 0; + return FALSE; +} + +BOOL RFLIsResourceCached(void) { + RFLiLoader* loader; + + if (!RFLAvailable()) { + return FALSE; + } + + loader = RFLiGetLoader(); + if (loader == NULL) { + return FALSE; + } + + return loader->cached; +} diff --git a/src/RVLFaceLib/RFL_System.c b/src/RVLFaceLib/RFL_System.c new file mode 100644 index 00000000..54c99941 --- /dev/null +++ b/src/RVLFaceLib/RFL_System.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include + +#define RFL_SYSTEM_HEAP_SIZE 0x24800 +#define RFL_WORK_SIZE 0x4B000 +#define RFL_DELUXE_WORK_SIZE 0x64000 + +/** + * These functions are inlined, but with their conditions inverted(?) from the + * actual functions. To avoid manually inlining them, these defines will hide + * the fake inlines. + */ +#define RFLiGetLoader_() (RFLAvailable() ? &RFLiGetManager()->loader : NULL) +#define RFLiGetLastReason_() (RFLAvailable() ? RFLiGetManager()->lastReason : 0) +#define RFLGetLastReason_() \ + (RFLAvailable() ? RFLiGetLastReason_() : sRFLLastReason) + +const char* __RFLVersion = + "<< RVL_SDK - RFL \trelease build: Mar 6 2008 17:40:04 (0x4199_60831) >>"; + +static const RFLiCoordinateData scCoordinate = {1, 2, 0, FALSE, FALSE, FALSE}; + +static RFLiManager* sRFLiManager = NULL; +static RFLErrcode sRFLLastErrCode = RFLErrcode_NotAvailable; +static u8 sRFLiFileBrokenType; +static s32 sRFLLastReason; + +u32 RFLGetWorkSize(BOOL deluxeTex) { + return deluxeTex ? RFL_DELUXE_WORK_SIZE + sizeof(RFLiManager) + : RFL_WORK_SIZE + sizeof(RFLiManager); +} + +RFLErrcode RFLInitResAsync(void* workBuffer, void* resBuffer, u32 resSize, + BOOL deluxeTex) { + u32 workSize; + u32 heapSize; + void* heapBuffer; + RFLErrcode error; + + if (resBuffer == NULL) { + return RFLErrcode_Fatal; + } + + if (RFLiGetManager() == NULL) { + OSRegisterVersion(__RFLVersion); + + workSize = deluxeTex ? RFL_DELUXE_WORK_SIZE : RFL_WORK_SIZE; + memset(workBuffer, 0, workSize); + + sRFLiManager = (RFLiManager*)workBuffer; + sRFLLastErrCode = RFLErrcode_NotAvailable; + sRFLLastReason = NAND_RESULT_OK; + sRFLiFileBrokenType = RFLiFileBrokenType_DBNotFound; + sRFLiManager->workBuffer = (u8*)workBuffer + sizeof(RFLiManager); + + heapSize = deluxeTex ? RFL_DELUXE_WORK_SIZE - sizeof(RFLiManager) + : RFL_WORK_SIZE - sizeof(RFLiManager); + + RFLiGetManager()->rootHeap = MEMCreateExpHeapEx(RFLiGetManager()->workBuffer, heapSize, 1); + heapBuffer = MEMAllocFromExpHeapEx(RFLiGetManager()->rootHeap, RFL_SYSTEM_HEAP_SIZE, 32); + RFLiGetManager()->systemHeap = MEMCreateExpHeapEx(heapBuffer, RFL_SYSTEM_HEAP_SIZE, 1); + heapSize = MEMGetAllocatableSizeForExpHeapEx(RFLiGetManager()->rootHeap, 4); + heapBuffer = MEMAllocFromExpHeapEx(RFLiGetManager()->rootHeap, heapSize, 8); + RFLiGetManager()->tmpHeap = MEMCreateExpHeapEx(heapBuffer, heapSize, 1); + + RFLiGetManager()->lastErrCode = RFLErrcode_Success; + RFLiGetManager()->beforeCloseErr = RFLErrcode_Success; + RFLiGetManager()->lastReason = 0; + RFLiGetManager()->beforeCloseReason = 0; + RFLiGetManager()->deluxeTex = deluxeTex; + RFLiGetManager()->brokenType = RFLiFileBrokenType_DBNotFound; + + RFLSetIconDrawDoneCallback(NULL); + RFLSetModelDrawDoneCallback(NULL); + RFLiSetWorking(FALSE); + RFLiInitDatabase(RFLiGetManager()->systemHeap); + RFLiInitLoader(); + RFLiInitCtrlBuf(RFLiGetManager()->systemHeap); + RFLiInitHiddenDatabase(); + RFLiInitAccessInfo(RFLiGetManager()->systemHeap); + RFLiSetCoordinateData(&scCoordinate); + + if (resBuffer != NULL) { + RFLiLoader* loader = RFLiGetLoader_(); + loader->cached = TRUE; + loader->cacheSize = resSize; + loader->cache = resBuffer; + } + + error = RFLiBootLoadAsync(); + if (error != RFLErrcode_Busy && error != RFLErrcode_Success) { + RFLExit(); + } + } else { + error = RFLErrcode_Success; + } + + return error; +} + +RFLErrcode RFLInitRes(void* workBuffer, void* resBuffer, u32 resSize, + BOOL deluxeTex) { + RFLInitResAsync(workBuffer, resBuffer, resSize, deluxeTex); + return RFLWaitAsync(); +} + +void RFLExit(void) { + if (RFLiGetManager() == NULL) { + return; + } + + RFLWaitAsync(); + + sRFLLastErrCode = RFLGetAsyncStatus(); + sRFLLastReason = RFLGetLastReason_(); + sRFLiFileBrokenType = RFLiGetManager()->brokenType; + + if (RFLIsResourceCached()) { + RFLFreeCachedResource(); + } + + RFLiExitAccessInfo(); + + MEMDestroyExpHeap(RFLiGetManager()->tmpHeap); + MEMDestroyExpHeap(RFLiGetManager()->systemHeap); + MEMDestroyExpHeap(RFLiGetManager()->rootHeap); + + sRFLiManager = NULL; +} + +static void bootloadDB2Res_(void) { + RFLiLoadResourceHeaderAsync(); + + switch (RFLWaitAsync()) { + case RFLErrcode_Loadfail: + case RFLErrcode_Success: + case RFLErrcode_Busy: + break; + default: + RFLExit(); + } +} + +RFLErrcode RFLiBootLoadAsync(void) { + return RFLiBootLoadDatabaseAsync(bootloadDB2Res_); +} + +BOOL RFLAvailable(void) { + return sRFLiManager != NULL; +} + +static void* allocal_(u32 size, s32 align) { + return MEMAllocFromExpHeapEx(RFLiGetManager()->tmpHeap, size, align); +} + +void* RFLiAlloc(u32 size) { + return allocal_(size, 8); +} + +void* RFLiAlloc32(u32 size) { + return allocal_(size, 32); +} + +void RFLiFree(void* block) { + MEMFreeToExpHeap(RFLiGetManager()->tmpHeap, block); +} + +RFLiDBManager* RFLiGetDBManager(void) { + return !RFLAvailable() ? NULL : &RFLiGetManager()->dbMgr; +} + +RFLiHDBManager* RFLiGetHDBManager(void) { + return !RFLAvailable() ? NULL : &RFLiGetManager()->hdbMgr; +} + +RFLiLoader* RFLiGetLoader(void) { + return !RFLAvailable() ? NULL : &RFLiGetManager()->loader; +} + +BOOL RFLiGetWorking(void) { + return !RFLAvailable() ? FALSE : RFLiGetManager()->working; +} + +void RFLiSetWorking(BOOL working) { + RFLiGetManager()->working = working; +} + +RFLiManager* RFLiGetManager(void) { + return sRFLiManager; +} + +RFLErrcode RFLGetAsyncStatus(void) { + if (!RFLAvailable()) { + return sRFLLastErrCode; + } + + if (RFLiIsWorking()) { + return RFLErrcode_Busy; + } + + if (RFLiCriticalError()) { + return RFLErrcode_Fatal; + } + + return RFLiGetManager()->lastErrCode; +} + +s32 RFLGetLastReason(void) { + return !RFLAvailable() ? sRFLLastReason : RFLiGetLastReason_(); +} + +RFLErrcode RFLWaitAsync(void) { + volatile RFLErrcode status; + + do { + status = RFLGetAsyncStatus(); + } while (status == RFLErrcode_Busy); + + return status; +} + +RFLiAccessInfo* RFLiGetAccInfo(RFLiFileType type) { + return !RFLAvailable() ? NULL : &RFLiGetManager()->info[type]; +} + +RFLiCtrlBufManager* RFLiGetCtrlBufManager(void) { + return !RFLAvailable() ? NULL : &RFLiGetManager()->ctrlMgr; +} + +BOOL RFLiGetUseDeluxTex(void) { + return !RFLAvailable() ? FALSE : RFLiGetManager()->deluxeTex; +} + +s32 RFLiGetLastReason(void) { + return !RFLAvailable() ? FALSE : RFLiGetManager()->lastReason; +} + +void RFLiSetFileBroken(RFLiFileBrokenType type) { + if (RFLAvailable()) { + RFLiGetManager()->brokenType |= (1 << type) & 0xFF; + } +} + +BOOL RFLiNotFoundError(void) { + u8* broken = &sRFLiFileBrokenType; + + if (RFLAvailable()) { + broken = &RFLiGetManager()->brokenType; + } + + return *broken >> RFLiFileBrokenType_DBNotFound & 1; +} + +BOOL RFLiNeedRepairError(void) { + u8* broken = &sRFLiFileBrokenType; + + if (RFLAvailable()) { + broken = &RFLiGetManager()->brokenType; + } + + return *broken >> RFLiFileBrokenType_DBBroken & 1; +} + +BOOL RFLiCriticalError(void) { + u8* broken = &sRFLiFileBrokenType; + + if (RFLAvailable()) { + broken = &RFLiGetManager()->brokenType; + } + + return *broken & 1 << RFLiFileBrokenType_ResBroken || + *broken & 1 << RFLiFileBrokenType_Corrupt; +}