#include "d/d_message.h" #include "common.h" #include "d/d_sc_game.h" #include "d/d_tag_processor.h" #include "d/d_textunk.h" #include "egg/core/eggHeap.h" #include "f/f_base.h" #include "f/f_profile.h" #include "f/f_profile_name.h" #include "libms/libms.h" #include "libms/msgfile.h" #include "sized_string.h" #include "toBeSorted/arc_managers/oarc_manager.h" #include "toBeSorted/event_manager.h" #include "toBeSorted/global_fi_context.h" SPECIAL_BASE_PROFILE(MESSAGE, dMessage_c, fProfile::MESSAGE, 0x2A8, 0); static const char *sMsbtFileNames[82] = { "000-Test.msbt", "001-Action.msbt", "002-System.msbt", "003-ItemGet.msbt", "004-Object.msbt", "005-Tutorial.msbt", "006-KenseiNormal.msbt", "006-1KenseiNormal.msbt", "006-2KenseiNormal.msbt", "006-3KenseiNormal.msbt", "006-4KenseiNormal.msbt", "006-5KenseiNormal.msbt", "006-6KenseiNormal.msbt", "006-7KenseiNormal.msbt", "006-8KenseiNormal.msbt", "006-9KenseiNormal.msbt", "007-MapText.msbt", "008-Hint.msbt", "word.msbt", "100-Town.msbt", "101-Shop.msbt", "102-Zelda.msbt", "103-DaiShinkan.msbt", "104-Rival.msbt", "105-Terry.msbt", "106-DrugStore.msbt", "107-Kanban.msbt", "108-ShinkanA.msbt", "109-TakeGoron.msbt", "110-DivingGame.msbt", "111-FortuneTeller.msbt", "112-Trustee.msbt", "113-RemodelStore.msbt", "114-Friend.msbt", "115-Town2.msbt", "116-InsectGame.msbt", "117-Pumpkin.msbt", "118-Town3.msbt", "119-Captain.msbt", "120-Nushi.msbt", "121-AkumaKun.msbt", "122-Town4.msbt", "123-Town5.msbt", "124-Town6.msbt", "125-D3.msbt", "150-Siren.msbt", "198-Test.msbt", "199-Demo.msbt", "200-Forest.msbt", "201-ForestD1.msbt", "202-ForestD2.msbt", "203-ForestF2.msbt", "204-ForestF3.msbt", "250-ForestSiren.msbt", "251-Salvage.msbt", "299-Demo.msbt", "300-Mountain.msbt", "301-MountainD1.msbt", "302-Anahori.msbt", "303-MountainF2.msbt", "304-MountainD2.msbt", "305-MountainF3.msbt", "350-MountainSiren.msbt", "351-Salvage.msbt", "399-Demo.msbt", "400-Desert.msbt", "401-DesertD2.msbt", "402-DesertF2.msbt", "403-DesertD1.msbt", "404-DesertF3.msbt", "405-DesertD2Clear.msbt", "406-TrolleyRace.msbt", "450-DesertSiren.msbt", "451-Salvage.msbt", "460-RairyuMinigame.msbt", "499-Demo.msbt", "500-CenterField.msbt", "501-Inpa.msbt", "502-CenterFieldBack.msbt", "503-Goron.msbt", "510-Salvage.msbt", "599-Demo.msbt", }; static char *sMsbfFileNames[80] = { "000-Test.msbf", "001-Action.msbf", "002-System.msbf", "003-ItemGet.msbf", "004-Object.msbf", "005-Tutorial.msbf", "006-KenseiNormal.msbf", "006-1KenseiNormal.msbf", "006-2KenseiNormal.msbf", "006-3KenseiNormal.msbf", "006-4KenseiNormal.msbf", "006-5KenseiNormal.msbf", "006-6KenseiNormal.msbf", "006-7KenseiNormal.msbf", "006-8KenseiNormal.msbf", "006-9KenseiNormal.msbf", "008-Hint.msbf", "100-Town.msbf", "101-Shop.msbf", "102-Zelda.msbf", "103-DaiShinkan.msbf", "104-Rival.msbf", "105-Terry.msbf", "106-DrugStore.msbf", "107-Kanban.msbf", "108-ShinkanA.msbf", "109-TakeGoron.msbf", "110-DivingGame.msbf", "111-FortuneTeller.msbf", "112-Trustee.msbf", "113-RemodelStore.msbf", "114-Friend.msbf", "115-Town2.msbf", "116-InsectGame.msbf", "117-Pumpkin.msbf", "118-Town3.msbf", "119-Captain.msbf", "120-Nushi.msbf", "121-AkumaKun.msbf", "122-Town4.msbf", "123-Town5.msbf", "124-Town6.msbf", "125-D3.msbf", "150-Siren.msbf", "198-Test.msbf", "199-Demo.msbf", "200-Forest.msbf", "201-ForestD1.msbf", "202-ForestD2.msbf", "203-ForestF2.msbf", "204-ForestF3.msbf", "250-ForestSiren.msbf", "251-Salvage.msbf", "299-Demo.msbf", "300-Mountain.msbf", "301-MountainD1.msbf", "302-Anahori.msbf", "303-MountainF2.msbf", "304-MountainD2.msbf", "305-MountainF3.msbf", "350-MountainSiren.msbf", "351-Salvage.msbf", "399-Demo.msbf", "400-Desert.msbf", "401-DesertD2.msbf", "402-DesertF2.msbf", "403-DesertD1.msbf", "404-DesertF3.msbf", "405-DesertD2Clear.msbf", "406-TrolleyRace.msbf", "450-DesertSiren.msbf", "451-Salvage.msbf", "460-RairyuMinigame.msbf", "499-Demo.msbf", "500-CenterField.msbf", "501-Inpa.msbf", "502-CenterFieldBack.msbf", "503-Goron.msbf", "510-Salvage.msbf", "599-Demo.msbf", }; static char *sArcNames[] = { "0-Common", "1-Town", "2-Forest", "3-Mountain", "4-Desert", "5-CenterField", }; dMessage_c *dMessage_c::sInstance; dTagProcessor_c *dMessage_c::sTagProcessor; static void *msbAlloc(size_t size) { if (UnkTextThing::getInstance()->getShouldHookAllocations() == false) { return EGG::Heap::alloc(size, 0x20, nullptr); } else { return UnkTextThing::getInstance()->allocUnk(size, 0x20); } } static void msbFree(void *ptr) { if (UnkTextThing::getInstance()->getShouldHookAllocations() == false) { EGG::Heap::free(ptr, nullptr); } else { UnkTextThing::getInstance()->destroyUnk(ptr); } } dMessage_c::dMessage_c() { sInstance = this; } int dMessage_c::create() { mLanguage = getLanguageIdentifier(); mCurrentTextFileNumber = -1; field_0x2FC = 0; field_0x344 = 0; LMS_SetMemFuncs(msbAlloc, msbFree); setZevFromMsbArc(); for (s32 i = 0; i < 82; i++) { SizedString<0x40> fileName = getMsbtFileName(i); s32 num = getMsbtNumberByIndex(i); void *data = getDataFromMsbArc(num, fileName, true); if (data == nullptr) { mpMsgs[i] = nullptr; } else { mpMsgs[i] = LMS_InitMessage(data); } } for (s32 i = 0; i < 80; i++) { SizedString<0x40> fileName = getMsbfFileName(i); s32 num = getMsbfNumberByIndex(i); void *data = getDataFromMsbArc(num, fileName, true); if (data == nullptr) { mpFlows[i] = nullptr; } else { mpFlows[i] = LMS_InitFlow(data); } } sTagProcessor = new dTagProcessor_c(); createGlobalFiContext(); reset(); return SUCCEEDED; } int dMessage_c::doDelete() { UnkTextThing::getInstance()->destroy(); if (sTagProcessor != nullptr) { delete sTagProcessor; sTagProcessor = nullptr; } for (int i = 0; i < 82; i++) { if (mpMsgs[i] != nullptr) { LMS_CloseMessage(mpMsgs[i]); } } for (int i = 0; i < 80; i++) { if (mpFlows[i] != nullptr) { LMS_CloseFlow(mpFlows[i]); } } return SUCCEEDED; } int dMessage_c::execute() { if (field_0x2FC != 0 && !EventManager::isInEvent()) { field_0x2FC = 0; } if (field_0x2FC > 0) { field_0x2FC--; } executeMinigame(); sTagProcessor->execute(); return SUCCEEDED; } int dMessage_c::draw() { return SUCCEEDED; } const wchar_t *dMessage_c::getTextMessageByLabel(const char *label, bool global, wchar_t *dstBuf, u32 maxLen) { return sInstance->getTextMessageByLabelInternal(label, nullptr, global, dstBuf, maxLen); } const wchar_t *dMessage_c::getTextMessageByLabel( const char *label, dTagProcessor_c *pTagProcessor, bool global, wchar_t *dstBuf, u32 maxLen ) { return sInstance->getTextMessageByLabelInternal(label, pTagProcessor, global, dstBuf, maxLen); } const wchar_t *dMessage_c::formatText(const wchar_t *text) { return sInstance->formatTextInternal(text); } const wchar_t *dMessage_c::getTextMessageByLabelInternal( const char *label, dTagProcessor_c *pTagProcessor, bool global, wchar_t *dstBuf, u32 maxLen ) { s32 fileIndex = mCurrentTextFileNumber; MsbtInfo *info = nullptr; if (global) { fileIndex = getMsbtIndexForLabelInternal(label); } if (fileIndex >= 0) { info = getMsbtInfoForIndex(fileIndex); } const wchar_t *text = LMS_GetTextByLabel(info, label); if (pTagProcessor == nullptr) { pTagProcessor = sTagProcessor; } u32 outLen = 0; s32 textIdx = LMS_GetTextIndexByLabel(info, label); MsbtAttrInfo *att = LMS_GetAttribute(info, textIdx); pTagProcessor->setMsgWindowSubtype(att->c_0x00); pTagProcessor->setField_0x90D(att->c_0x01); // Strip trailing newline wchar_t *end; s32 i; if (dstBuf != nullptr) { pTagProcessor->format(nullptr, text, dstBuf, maxLen, &outLen, nullptr); i = outLen - 1; end = &dstBuf[i]; for (; i > 0; i--) { if (*end != L'\n') { break; } *end-- = L'\0'; } // no return - might be intentional since dstBuf already has the result } else { static wchar_t sBuf[0x400] = {}; pTagProcessor->format(nullptr, text, sBuf, ARRAY_LENGTH(sBuf) - 1, &outLen, nullptr); i = outLen - 1; end = &sBuf[i]; for (; i > 0; i--) { if (*end != L'\n') { break; } *end = L'\0'; end--; } return sBuf; } return nullptr; } const wchar_t *dMessage_c::formatTextInternal(const wchar_t *text) { u32 outLen = 0; static wchar_t sBuf[0x200] = {}; sTagProcessor->format(nullptr, text, sBuf, ARRAY_LENGTH(sBuf), &outLen, nullptr); s32 i = outLen - 1; wchar_t *end = &sBuf[i]; for (; i > 0; i--) { if (*end != L'\n') { break; } *end = L'\0'; end--; } return sBuf; } bool dMessage_c::isValidTextLabel(const char *name) { return sInstance->checkIsValidTextLabel(name); } bool dMessage_c::checkIsValidTextLabel(const char *name) { if (name == nullptr) { return false; } return getTextIndexForLabel(name) >= 0; } const char *dMessage_c::getMsbtFileName(s32 idx) { static SizedString<128> sPath; s32 arcIdx = getArcIndexForFile(sMsbtFileNames[idx]); const char *arcName = getArcNameByIndex(arcIdx, true); sPath.sprintf("%s/%s", arcName, sMsbtFileNames[idx]); return sPath; } const char *dMessage_c::getMsbfFileName(s32 idx) { static SizedString<128> sPath; s32 arcIdx = getArcIndexForFile(sMsbfFileNames[idx]); const char *arcName = getArcNameByIndex(arcIdx, true); sPath.sprintf("%s/%s", arcName, sMsbfFileNames[idx]); return sPath; } s32 dMessage_c::getMsbtNumberByIndex(s32 index) { return atoi(getMsbtFileName(index)); } s32 dMessage_c::getMsbfNumberByIndex(s32 index) { return atoi(getMsbfFileName(index)); } s32 dMessage_c::getTextIndexForLabel(const char *label) { s32 idx = getMsbtIndexForLabelInternal(label); MsbtInfo *info = nullptr; if (idx >= 0) { info = getMsbtInfoForIndex(idx); } return LMS_GetTextIndexByLabel(info, label); } s32 dMessage_c::getMsbtIndexForLabel(const char *label) { return sInstance->getMsbtIndexForLabelInternal(label); } s32 dMessage_c::getMsbtIndexForLabelInternal(const char *label) { if (mCurrentTextFileNumber <= 81) { MsbtInfo *info = getMsbtInfoForIndex(mCurrentTextFileNumber); if (LMS_GetTextIndexByLabel(info, label) >= 0) { return mCurrentTextFileNumber; } } for (s32 i = 0; i < 82; i++) { if (getMsbtInfoForIndex(i) != nullptr) { if (LMS_GetTextIndexByLabel(getMsbtInfoForIndex(i), label) >= 0) { return i; } } } return -1; } MsbtInfo *dMessage_c::getMsbtInfoForIndex(s32 index) { return sInstance->getMsbtInfoForIndexInternal(index); } MsbtInfo *dMessage_c::getMsbtInfoForIndexInternal(s32 index) { UnkTextThing *thing = UnkTextThing::getInstance(); MsbtInfo *overrideMsbt = thing->getOverrideMsbtInfo(index); if (overrideMsbt != nullptr && thing->getShouldOverrideData() == true) { return overrideMsbt; } return mpMsgs[index]; } MsbfInfo *dMessage_c::getMsbfInfoForIndex(s32 index) { return sInstance->getMsbfInfoForIndexInternal(index); } MsbfInfo *dMessage_c::getMsbfInfoForIndexInternal(s32 index) { UnkTextThing *thing = UnkTextThing::getInstance(); MsbfInfo *overrideMsbf = thing->getOverrideMsbfInfo(index); if (overrideMsbf != nullptr && thing->getShouldOverrideData() == true) { return overrideMsbf; } return mpFlows[index]; } extern "C" u8 fn_80054F30(); static SizedString<8> sCurrentLanguage; const char *dMessage_c::getLanguageIdentifier() { u8 lang = fn_80054F30(); if (lang == 3) { sCurrentLanguage = "fr_US"; } else if (lang == 4) { sCurrentLanguage = "es_US"; } else { sCurrentLanguage = "en_US"; } return sCurrentLanguage; } const char *dMessage_c::getArcNameByIndex(s32 idx, bool global) { return getArcNameByIndexInternal(idx, global); } const char *dMessage_c::getArcNameByIndexInternal(s32 idx, bool global) { return sArcNames[idx]; } void *dMessage_c::getDataFromMsbArc(s32 number, const char *fileName, bool global) { SizedString<128> path; path.sprintf("%s/%s", getLanguageIdentifier(), getArcNameByIndex(number, global)); return OarcManager::GetInstance()->getData(path, fileName); } void dMessage_c::setZevFromMsbArc() { SizedString<128> path; path.sprintf("%s/dat/zev.dat", getArcNameByIndex(0, false)); sZev0 = getDataFromMsbArc(0, path, false); sZevStage = nullptr; } void dMessage_c::setStageZevFromMsbArc() { SizedString<32> stage = dScGame_c::currentSpawnInfo.stageName; char buf[2]; buf[0] = stage[1]; buf[1] = '\0'; SizedString<128> path; int i = atoi(buf) + 1; // TODO figure out what this is if (stage == "t_tkm24" || stage == "t_tkm26") { i = 2; } else if (i < 1 || 6 <= i) { i = 0; } path.sprintf("%s/dat/zev.dat", getArcNameByIndex(i, false)); sZevStage = getDataFromMsbArc(i, path, false); } void *dMessage_c::sZev0; void *dMessage_c::sZevStage; void *dMessage_c::getZev0Internal() { UnkTextThing *thing = UnkTextThing::getInstance(); void *overrideZev = thing->getOverrideZev0(); if (overrideZev != nullptr && thing->getShouldOverrideData() == true) { return overrideZev; } return sZev0; } void *dMessage_c::getZevStageInternal() { UnkTextThing *thing = UnkTextThing::getInstance(); void *overrideZev = thing->getOverrideStageZev(); if (overrideZev != nullptr && thing->getShouldOverrideData() == true) { return overrideZev; } return sZevStage; } s32 dMessage_c::getArcIndexForFile(const char *fileName) { char buf[2]; buf[0] = fileName[0]; buf[1] = '\0'; return atoi(buf); }