From 7a3da27e3e6da8560ecc220b0865eaf6bfffd73e Mon Sep 17 00:00:00 2001 From: Nessy Date: Sun, 1 Feb 2026 18:26:37 +0000 Subject: [PATCH 1/2] Adjusted custom message system to support multiple languages --- Z3DR | 2 +- source/custom_messages.cpp | 226 ++++++++++++++++++++++++----- source/fill.cpp | 4 +- source/hints.cpp | 32 ++-- source/include/custom_messages.hpp | 16 +- source/main.cpp | 2 - source/patch.cpp | 28 ++++ 7 files changed, 248 insertions(+), 62 deletions(-) diff --git a/Z3DR b/Z3DR index e8ed000..0a003fb 160000 --- a/Z3DR +++ b/Z3DR @@ -1 +1 @@ -Subproject commit e8ed00008af25bbb3e5999f43ad6f290f11f4657 +Subproject commit 0a003fbee908947c20518e660a35c2649c65a1be diff --git a/source/custom_messages.cpp b/source/custom_messages.cpp index 19b0e58..8f73202 100644 --- a/source/custom_messages.cpp +++ b/source/custom_messages.cpp @@ -12,22 +12,24 @@ class UnformattedMessageComp { } }; -// Pack data less than 8 bits long into a char array without wasting bits -void packData(u8 size, u8 idx, char* data, u8 unpacked) { - for (u8 i = size; i--;) { - if (unpacked & 1) - data[(idx * size + i) / 8] |= 0x80 >> ((idx * size + i) % 8); - else - data[(idx * size + i) / 8] &= ~(0x80 >> ((idx * size + i) % 8)); - unpacked >>= 1; - } -} - std::set messageEntries; std::vector arrangedMessageEntries; +std::vector textData; +std::vector colData; +std::vector iconData; +std::vector delayData; +u8 colParity, iconParity, delayParity; + +u32 pushText(const char* data) { + u32 offset = textData.size(); + for (u32 idx = 0; data[idx]; idx++) + textData.push_back(data[idx]); + textData.push_back(0); + return offset; +} -void CreateMessage(u16 textId, u16 field_2, u32 field_4, u32 flags, const char* NAEnglishText, - const std::vector& cols, const std::vector& icons, const std::vector& delays, +void CreateMessage(u16 textId, u16 field_2, u32 field_4, u32 flags, const Language& text, + const std::vector& cols, const std::vector& icons, const std::vector& delays, u16 sfx, bool instant, bool repeatSfx) { #ifdef ENABLE_DEBUG static std::vector usedTextIds; @@ -47,118 +49,238 @@ void CreateMessage(u16 textId, u16 field_2, u32 field_4, u32 flags, const char* newEntry.field_2 = field_2; newEntry.field_4 = field_4; newEntry.flags = flags; - std::strcpy(newEntry.text, NAEnglishText); - for (u16 idx = 0; idx < std::min(8, (int)cols.size()); idx++) - packData(4, idx, newEntry.cols, cols.at(idx)); - for (u16 idx = 0; idx < std::min(8, (int)icons.size()); idx++) - packData(6, idx, newEntry.icons, icons.at(idx)); - for (u16 idx = 0; idx < std::min(8, (int)delays.size()); idx++) - packData(6, idx, newEntry.delays, delays.at(idx)); newEntry.sfxAndFlags = ((instant) ? 0x8000 : 0x0000) | ((repeatSfx) ? 0x4000 : 0x0000) | sfx; + u32 offsetEn = 0, offsetFr = 0, offsetEs = 0, offsetDe = 0, offsetIt = 0, offsetNl = 0; + u32 offsetCol = 0, offsetIcon = 0, offsetDelay = 0; + + if (text.English) offsetEn = pushText(text.English); + if (text.French) offsetFr = pushText(text.French); + if (text.Spanish) offsetEs = pushText(text.Spanish); + if (text.German) offsetDe = pushText(text.German); + if (text.Italian) offsetIt = pushText(text.Italian); + if (text.Dutch) offsetNl = pushText(text.Dutch); + + if (cols.size()) { + offsetCol = colData.size() * 2 + colParity; + char temp = (colParity) ? colData.back() : 0; + if (colParity) colData.pop_back(); + for (auto& col: cols) { + if (colParity) + colData.push_back((col << 4) | temp); + else + temp = col; + colParity = 1 - colParity; + } + colData.push_back((colParity) ? (0xF0 | temp) : 0x0F); + colParity = 1 - colParity; + } + + if (icons.size()) { + offsetIcon = (iconData.size() << 2) / 3 - ((iconParity == 3) ? 1 : 0); + char temp = (iconParity) ? iconData.back() : 0; + if (iconParity) iconData.pop_back(); + for (auto& icon: icons) { + switch (iconParity) { + case 0: + temp = icon; + break; + case 1: + iconData.push_back((icon << 6) | temp); + temp = icon >> 2; + break; + case 2: + iconData.push_back((icon << 4) | temp); + temp = icon >> 4; + break; + case 3: + iconData.push_back((icon << 2) | temp); + break; + } + iconParity = (iconParity + 1) % 4; + } + switch (iconParity) { + case 0: + iconData.push_back(0x3F); + break; + case 1: + iconData.push_back(0xC0 | temp); + iconData.push_back(0x0F); + break; + case 2: + iconData.push_back(0xF0 | temp); + iconData.push_back(0x03); + break; + case 3: + iconData.push_back(0xFC | temp); + break; + } + iconParity = (iconParity + 1) % 4; + } + + if (delays.size()) { + offsetDelay = (delayData.size() << 2) / 3 - ((delayParity == 3) ? 1 : 0); + char temp = (delayParity) ? delayData.back() : 0; + if (delayParity) delayData.pop_back(); + for (auto& delay: delays) { + switch (delayParity) { + case 0: + temp = delay; + break; + case 1: + delayData.push_back((delay << 6) | temp); + temp = delay >> 2; + break; + case 2: + delayData.push_back((delay << 4) | temp); + temp = delay >> 4; + break; + case 3: + delayData.push_back((delay << 2) | temp); + break; + } + delayParity = (delayParity + 1) % 4; + } + switch (delayParity) { + case 0: + delayData.push_back(0x3F); + break; + case 1: + delayData.push_back(0xC0 | temp); + delayData.push_back(0x0F); + break; + case 2: + delayData.push_back(0xF0 | temp); + delayData.push_back(0x03); + break; + case 3: + delayData.push_back(0xFC | temp); + break; + } + delayParity = (delayParity + 1) % 4; + } + + newEntry.offsets[0] = offsetEn | (offsetFr << 18); + newEntry.offsets[1] = (offsetFr >> 14) | (offsetEs << 4) | (offsetDe << 22); + newEntry.offsets[2] = (offsetDe >> 10) | (offsetIt << 8) | (offsetNl << 26); + newEntry.offsets[3] = (offsetNl >> 6) | (offsetCol << 12) | (offsetIcon << 22); + newEntry.delayOffset= (u16)offsetDelay; + messageEntries.insert(newEntry); } void CreateBaselineCustomMessages() { + messageEntries.clear(); + textData.clear(); + colData.clear(); + iconData.clear(); + delayData.clear(); + + pushText("ERROR&&No message data"); + colData.push_back(0xF); + iconData.push_back(0x3F); + delayData.push_back(0x3F); + colParity = iconParity = delayParity = 1; + // Small Keys // Woodfall CreateMessage(0x6133, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got a #small key# for the #Woodfall Temple#! Use it to open a locked door in that temple.", + {"You got a #small key# for the #Woodfall Temple#! Use it to open a locked door in that temple."}, {QM_GREEN, QM_GREEN}, {}, {}, 0x0, false, false); // Snowhead CreateMessage(0x6134, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got a #small key# for the #Snowhead Temple#! Use it to open a locked door in that temple.", + {"You got a #small key# for the #Snowhead Temple#! Use it to open a locked door in that temple."}, {QM_GREEN, QM_MAGENTA}, {}, {}, 0x0, false, false); // Great Bay CreateMessage(0x6135, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got a #small key# for the #Great Bay Temple#! Use it to open a locked door in that temple.", + {"You got a #small key# for the #Great Bay Temple#! Use it to open a locked door in that temple."}, {QM_GREEN, QM_CYAN}, {}, {}, 0x0, false, false); // Stone Tower CreateMessage(0x6136, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got a #small key# for the #Stone Tower Temple#! Use it to open a locked door in that temple.", + {"You got a #small key# for the #Stone Tower Temple#! Use it to open a locked door in that temple."}, {QM_GREEN, QM_YELLOW}, {}, {}, 0x0, false, false); // Maps // Woodfall CreateMessage(0x6137, 0x003E, 0x3FFFFFFF, 0xFF0000, - "You found the #dungeon map# for the #Woodfall#!", + {"You found the #dungeon map# for the #Woodfall#!"}, {QM_GREEN, QM_GREEN}, {}, {}, 0x0, false, false); // Snowhead CreateMessage(0x6138, 0x003E, 0x3FFFFFFF, 0xFF0000, - "You found the #dungeon map# for the #Snowhead#!", + {"You found the #dungeon map# for the #Snowhead#!"}, {QM_GREEN, QM_MAGENTA}, {}, {}, 0x0, false, false); // Great Bay CreateMessage(0x6139, 0x003E, 0x3FFFFFFF, 0xFF0000, - "You found the #dungeon map# for the #Great Bay#!", + {"You found the #dungeon map# for the #Great Bay#!"}, {QM_GREEN, QM_CYAN}, {}, {}, 0x0, false, false); // Stone Tower CreateMessage(0x613A, 0x003E, 0x3FFFFFFF, 0xFF0000, - "You found the #dungeon map# for the #Stone Tower#!", + {"You found the #dungeon map# for the #Stone Tower#!"}, {QM_GREEN, QM_YELLOW}, {}, {}, 0x0, false, false); // Compasses // Woodfall CreateMessage(0x613B, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #compass# for the #Woodfall Temple#! Now many of the dungeon's hidden things will appear on the map!", + {"You got the #compass# for the #Woodfall Temple#! Now many of the dungeon's hidden things will appear on the map!"}, {QM_GREEN, QM_GREEN}, {}, {}, 0x0, false, false); // Snowhead CreateMessage(0x613C, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #compass# for the #Snowhead Temple#! Now many of the dungeon's hidden things will appear on the map!", + {"You got the #compass# for the #Snowhead Temple#! Now many of the dungeon's hidden things will appear on the map!"}, {QM_GREEN, QM_MAGENTA}, {}, {}, 0x0, false, false); // Great Bay CreateMessage(0x613D, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #compass# for the #Great Bay Temple#! Now many of the dungeon's hidden things will appear on the map!", + {"You got the #compass# for the #Great Bay Temple#! Now many of the dungeon's hidden things will appear on the map!"}, {QM_GREEN, QM_CYAN}, {}, {}, 0x0, false, false); // Stone Tower CreateMessage(0x613E, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #compass# for the #Stone Tower Temple#! Now many of the dungeon's hidden things will appear on the map!", + {"You got the #compass# for the #Stone Tower Temple#! Now many of the dungeon's hidden things will appear on the map!"}, {QM_GREEN, QM_YELLOW}, {}, {}, 0x0, false, false); // Boss Keys // Woodfall CreateMessage(0x613F, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #boss key# for the #Woodfall Temple#! Now you can enter the chamber where the boss lurks!", + {"You got the #boss key# for the #Woodfall Temple#! Now you can enter the chamber where the boss lurks!"}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); // Snowhead CreateMessage(0x6140, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #boss key# for the #Snowhead Temple#! Now you can enter the chamber where the boss lurks!", + {"You got the #boss key# for the #Snowhead Temple#! Now you can enter the chamber where the boss lurks!"}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); // Great Bay CreateMessage(0x6141, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #boss key# for the #Great Bay Temple#! Now you can enter the chamber where the boss lurks!", + {"You got the #boss key# for the #Great Bay Temple#! Now you can enter the chamber where the boss lurks!"}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); // Stone Tower CreateMessage(0x6142, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #boss key# for the #Stone Tower Temple#! Now you can enter the chamber where the boss lurks!", + {"You got the #boss key# for the #Stone Tower Temple#! Now you can enter the chamber where the boss lurks!"}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); // Kokiri Sword CreateMessage(0x0037, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got the #Kokiri Sword!# The trusty sword you're familiar with. A treasure from Kokiri Forest.", + {"You got the #Kokiri Sword!# The trusty sword you're familiar with. A treasure from Kokiri Forest."}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); // Ice Trap CreateMessage(0x0012, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - " #FOOL!#", + {" #FOOL!#"}, {QM_RED}, {}, {}, 0x0, false, false); //Swamp Skulltula Tokens CreateMessage(0x0052, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got a #Swamp Skulltula Token#! &You have collected #=SSH#.", + {"You got a #Swamp Skulltula Token#! &You have collected #=SSH#."}, {QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); //Ocean Skulltula Tokens CreateMessage(0x6143, 0xFFFF, 0x3FFFFFFF, 0xFF0000, - "You got an #Ocean Skulltula Token#! &You have collected #=OSH#.", + {"You got an #Ocean Skulltula Token#! &You have collected #=OSH#."}, {QM_BLUE, QM_RED}, {}, {}, 0x0, false, false); } @@ -173,5 +295,29 @@ std::pair RawMessageData() { u32 size = arrangedMessageEntries.size() * sizeof(UnformattedMessage); return { data, size }; } +const char* conv = "0123456789ABCDEF"; +std::pair RawMessageTextData() { + const char* data = (const char*)textData.data(); + u32 size = textData.size(); + return { data, size }; +} + +std::pair RawMessageColData() { + const char* data = (const char*)colData.data(); + u32 size = colData.size(); + return { data, size }; +} + +std::pair RawMessageIconData() { + const char* data = (const char*)iconData.data(); + u32 size = iconData.size(); + return { data, size }; +} + +std::pair RawMessageDelayData() { + const char* data = (const char*)delayData.data(); + u32 size = delayData.size(); + return { data, size }; +} } diff --git a/source/fill.cpp b/source/fill.cpp index b85d965..0dbba12 100644 --- a/source/fill.cpp +++ b/source/fill.cpp @@ -1,7 +1,7 @@ #include "fill.hpp" -//#include "custom_messages.hpp" #include "dungeon.hpp" +#include "custom_messages.hpp" #include "item_location.hpp" #include "item_pool.hpp" #include "location_access.hpp" @@ -843,6 +843,8 @@ int NoLogicFill() { int Fill() { + CustomMessages::CreateBaselineCustomMessages(); + int retries = 0; while (retries < 5) { placementFailure = false; diff --git a/source/hints.cpp b/source/hints.cpp index ed01f21..da624a0 100644 --- a/source/hints.cpp +++ b/source/hints.cpp @@ -168,7 +168,7 @@ static void AddHint(Text hint, const LocationKey gossipStone, const std::vector< if (hint.GetEnglish().find("$")) { icons.push_back(B_BUTTON); } - CustomMessages::CreateMessage(messageId, 0xFFFF, 0x3FFFFFFF, 0xFF0020, hint.GetEnglish().c_str(), colors, icons, {}, 0x0, false, false); + CustomMessages::CreateMessage(messageId, 0xFFFF, 0x3FFFFFFF, 0xFF0020, {hint.GetEnglish().c_str()}, colors, icons, {}, 0x0, false, false); //CreateMessageFromTextObject(messageId, 0, 2, 3, AddColorsAndFormat(hint, colors)); //CreateMessageFromTextObject(sariaMessageId, 0, 2, 3, AddColorsAndFormat(hint + EVENT_TRIGGER(), colors)); } @@ -449,37 +449,37 @@ void CreateTingleHintText() { std::string stoneTowerMap = ItemTable(Location(TINGLE_GBC_ST)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D11, 0xFFFF, 0x3FF0A005, 0xFF1001, - clockTownMap.insert(0, "#").append("# #5 Rupees#&").append(woodfallMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {clockTownMap.insert(0, "#").append("# #5 Rupees#&").append(woodfallMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); clockTownMap = ItemTable(Location(TINGLE_N_CLOCK_TOWN_CT)->GetPlacedItemKey()).GetName().GetEnglish(); woodfallMap = ItemTable(Location(TINGLE_N_CLOCK_TOWN_WF)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D12, 0xFFFF, 0x3FF0A014, 0xFF1001, - woodfallMap.insert(0, "#").append("# #20 Rupees#&").append(snowHeadMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {woodfallMap.insert(0, "#").append("# #20 Rupees#&").append(snowHeadMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); woodfallMap = ItemTable(Location(TINGLE_N_CLOCK_TOWN_WF)->GetPlacedItemKey()).GetName().GetEnglish(); snowHeadMap = ItemTable(Location(TINGLE_TWIN_ISLANDS_SH)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D13, 0xFFFF, 0x3FF0A014, 0xFF1001, - snowHeadMap.insert(0, "#").append("# #20 Rupees#&").append(romaniMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {snowHeadMap.insert(0, "#").append("# #20 Rupees#&").append(romaniMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); snowHeadMap = ItemTable(Location(TINGLE_TWIN_ISLANDS_SH)->GetPlacedItemKey()).GetName().GetEnglish(); romaniMap = ItemTable(Location(TINGLE_TWIN_ISLANDS_RR)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D14, 0xFFFF, 0x3FF0A014, 0xFF1001, - romaniMap.insert(0, "#").append("# #20 Rupees#&").append(greatBayMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {romaniMap.insert(0, "#").append("# #20 Rupees#&").append(greatBayMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); romaniMap = ItemTable(Location(TINGLE_TWIN_ISLANDS_RR)->GetPlacedItemKey()).GetName().GetEnglish(); greatBayMap = ItemTable(Location(TINGLE_GBC_GB)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D15, 0xFFFF, 0x3FF0A014, 0xFF1001, - greatBayMap.insert(0, "#").append("# #20 Rupees#&").append(stoneTowerMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {greatBayMap.insert(0, "#").append("# #20 Rupees#&").append(stoneTowerMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); greatBayMap = ItemTable(Location(TINGLE_GBC_GB)->GetPlacedItemKey()).GetName().GetEnglish(); stoneTowerMap = ItemTable(Location(TINGLE_GBC_ST)->GetPlacedItemKey()).GetName().GetEnglish(); CustomMessages::CreateMessage(0x1D16, 0xFFFF, 0x3FF0A014, 0xFF1001, - stoneTowerMap.insert(0, "#").append("# #20 Rupees#&").append(clockTownMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str(), + {stoneTowerMap.insert(0, "#").append("# #20 Rupees#&").append(clockTownMap.insert(0, "#").c_str()).append("# #40 Rupees#").append("&#No thanks#").c_str()}, {QM_GREEN, QM_RED, QM_GREEN, QM_RED, QM_GREEN}, {}, {}, 0x0, false, false); } } @@ -520,20 +520,20 @@ void CreateClockTowerDoorHints() { } CustomMessages::CreateMessage(0x0630, (StartingOcarina.Value() == 0) ? 0x8000 : 0x8002, 0x3FFFFFFF, 0x0FF0211, - "Rooftop access strictly prohibited!&(Enforceable until #midnight# on the&#eve# of the carnival.)^" - "#Notice of carnival activities:#&Musical Performance Contest&Unique Mask Contest&#Prizes available!#", + {"Rooftop access strictly prohibited!&(Enforceable until #midnight# on the&#eve# of the carnival.)^" + "#Notice of carnival activities:#&Musical Performance Contest&Unique Mask Contest&#Prizes available!#"}, {QM_RED, QM_RED, QM_RED, QM_MAGENTA}, {}, {}, 0x0, false, false); - CustomMessages::CreateMessage(0x8000, 0x8001, 0x3FFFFFFF, 0x1000000, ocarinaHint.c_str(), {QM_BLUE, QM_RED}, {}, {}, 0x037C, false, false); + CustomMessages::CreateMessage(0x8000, 0x8001, 0x3FFFFFFF, 0x1000000, {ocarinaHint.c_str()}, {QM_BLUE, QM_RED}, {}, {}, 0x037C, false, false); CustomMessages::CreateMessage(0x8001, 0x8003, 0x3FFFFFFF, 0x1FF0000, - "Also, that #mask competition# sounds interesting! I've heard rumours of some pretty #rare masks# around here, truly one of a kind stuff!", + {"Also, that #mask competition# sounds interesting! I've heard rumours of some pretty #rare masks# around here, truly one of a kind stuff!"}, {QM_RED, QM_RED}, {}, {}, 0x0, false, false); CustomMessages::CreateMessage(0x8002, 0x8003, 0x3FFFFFFF, 0x1FF0000, - "Hey, that #mask competition# sounds interesting! I've heard rumours of some pretty #rare masks# around here, truly one of a kind stuff!", + {"Hey, that #mask competition# sounds interesting! I've heard rumours of some pretty #rare masks# around here, truly one of a kind stuff!"}, {QM_RED, QM_RED}, {}, {}, 0x037C, false, false); - CustomMessages::CreateMessage(0x8003, 0x8004, 0x3FFFFFFF, 0x15D0000, odolwaHint.c_str(), {QM_GREEN, QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); - CustomMessages::CreateMessage(0x8004, 0x8005, 0x3FFFFFFF, 0x15E0000, gohtHint.c_str(), {QM_MAGENTA, QM_MAGENTA, QM_RED}, {}, {}, 0x0, false, false); - CustomMessages::CreateMessage(0x8005, 0x8006, 0x3FFFFFFF, 0x15F0000, gyorgHint.c_str(), {QM_CYAN, QM_CYAN, QM_RED}, {}, {}, 0x0, false, false); - CustomMessages::CreateMessage(0x8006, 0xFFFF, 0x3FFFFFFF, 0x0600000, twinmoldHint.c_str(), {QM_YELLOW, QM_YELLOW, QM_RED}, {}, {}, 0x0, false, false); + CustomMessages::CreateMessage(0x8003, 0x8004, 0x3FFFFFFF, 0x15D0000, {odolwaHint.c_str()}, {QM_GREEN, QM_GREEN, QM_RED}, {}, {}, 0x0, false, false); + CustomMessages::CreateMessage(0x8004, 0x8005, 0x3FFFFFFF, 0x15E0000, {gohtHint.c_str()}, {QM_MAGENTA, QM_MAGENTA, QM_RED}, {}, {}, 0x0, false, false); + CustomMessages::CreateMessage(0x8005, 0x8006, 0x3FFFFFFF, 0x15F0000, {gyorgHint.c_str()}, {QM_CYAN, QM_CYAN, QM_RED}, {}, {}, 0x0, false, false); + CustomMessages::CreateMessage(0x8006, 0xFFFF, 0x3FFFFFFF, 0x0600000, {twinmoldHint.c_str()}, {QM_YELLOW, QM_YELLOW, QM_RED}, {}, {}, 0x0, false, false); } //insert the required number into the hint and set the singular/plural form diff --git a/source/include/custom_messages.hpp b/source/include/custom_messages.hpp index d7f1b27..1c96360 100644 --- a/source/include/custom_messages.hpp +++ b/source/include/custom_messages.hpp @@ -6,8 +6,16 @@ namespace CustomMessages { -void CreateMessage(u16 textId, u16 field_2, u32 field_4, u32 flags, const char* NAEnglishText, - const std::vector& cols, const std::vector& icons, const std::vector& delays, +typedef struct { + const char* English; + const char* French; + const char* Spanish; + const char* German; + const char* Italian; + const char* Dutch; +} Language; +void CreateMessage(u16 textId, u16 field_2, u32 field_4, u32 flags, const Language& text, + const std::vector& cols, const std::vector& icons, const std::vector& delays, u16 sfx, bool instant, bool repeatSfx); u32 NumMessages(); @@ -15,5 +23,9 @@ u32 NumMessages(); void CreateBaselineCustomMessages(); std::pair RawMessageData(); +std::pair RawMessageTextData(); +std::pair RawMessageColData(); +std::pair RawMessageIconData(); +std::pair RawMessageDelayData(); } diff --git a/source/main.cpp b/source/main.cpp index bdfb2ad..084bb28 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -35,8 +35,6 @@ int main() { 0x0000, true, false );*/ - CustomMessages::CreateBaselineCustomMessages(); - u64 initialHoldTime = svcGetSystemTick(); u64 intervalTime = initialHoldTime; while (aptMainLoop()) { diff --git a/source/patch.cpp b/source/patch.cpp index e7c2c66..54ba8e2 100644 --- a/source/patch.cpp +++ b/source/patch.cpp @@ -328,6 +328,34 @@ bool WriteAllPatches() { return false; } + messageEntriesInfo = CustomMessages::RawMessageTextData(); + messageEntriesOffset = V_TO_P(RCUSTOMMESSAGETEXTDATA_ADDR); + messageEntriesSize = messageEntriesInfo.second; + if (!WritePatch(messageEntriesOffset, messageEntriesSize, (char*)messageEntriesInfo.first, code, bytesWritten, totalRW, buf)) { + return false; + } + + messageEntriesInfo = CustomMessages::RawMessageColData(); + messageEntriesOffset = V_TO_P(RCUSTOMMESSAGECOLDATA_ADDR); + messageEntriesSize = messageEntriesInfo.second; + if (!WritePatch(messageEntriesOffset, messageEntriesSize, (char*)messageEntriesInfo.first, code, bytesWritten, totalRW, buf)) { + return false; + } + + messageEntriesInfo = CustomMessages::RawMessageIconData(); + messageEntriesOffset = V_TO_P(RCUSTOMMESSAGEICONDATA_ADDR); + messageEntriesSize = messageEntriesInfo.second; + if (!WritePatch(messageEntriesOffset, messageEntriesSize, (char*)messageEntriesInfo.first, code, bytesWritten, totalRW, buf)) { + return false; + } + + messageEntriesInfo = CustomMessages::RawMessageDelayData(); + messageEntriesOffset = V_TO_P(RCUSTOMMESSAGEDELAYDATA_ADDR); + messageEntriesSize = messageEntriesInfo.second; + if (!WritePatch(messageEntriesOffset, messageEntriesSize, (char*)messageEntriesInfo.first, code, bytesWritten, totalRW, buf)) { + return false; + } + // Write numCustomMessageEntries to code patchOffset = V_TO_P(NUMCUSTOMMESSAGEENTRIES_ADDR); patchSize = 4; From 6e1d44dc511ab9f065fe73a9f104f9dba9513b91 Mon Sep 17 00:00:00 2001 From: Nessy Date: Sun, 1 Feb 2026 18:45:10 +0000 Subject: [PATCH 2/2] Removed debugging line --- source/custom_messages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/custom_messages.cpp b/source/custom_messages.cpp index 8f73202..b867b21 100644 --- a/source/custom_messages.cpp +++ b/source/custom_messages.cpp @@ -295,7 +295,7 @@ std::pair RawMessageData() { u32 size = arrangedMessageEntries.size() * sizeof(UnformattedMessage); return { data, size }; } -const char* conv = "0123456789ABCDEF"; + std::pair RawMessageTextData() { const char* data = (const char*)textData.data(); u32 size = textData.size();