Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
6bab68e
Copy NTSC GC to PAL GC
inspectredc Jul 22, 2025
6f0e38c
Update code affected xml offsets and add configs for OTRExporter and …
inspectredc Jul 23, 2025
58eef1d
Update file list, and make changes to parameter and title. Remove mes…
inspectredc Jul 24, 2025
0e5c199
Asset Extraction
inspectredc Jul 24, 2025
01115ad
SPA -> ESP
inspectredc Jul 24, 2025
94c09ed
Add PAL GC Version, Fix file choose textures and get z_en_mag and z_f…
inspectredc Jul 25, 2025
353fb54
more file choose stuff
inspectredc Jul 28, 2025
01d76d1
keyboard and font
inspectredc Jul 28, 2025
6595d54
pal supported
inspectredc Jul 30, 2025
5005cde
format
inspectredc Jul 30, 2025
1896513
Copy GC_PAL -> N64_PAL_11, Add PAL 1.1 Extraction, Edit Code Offsets …
inspectredc Aug 1, 2025
be187c7
Phase 1
PurpleHato Aug 17, 2025
713c952
Spanish reverted exclamation mark and message rework
PurpleHato Aug 17, 2025
ea0c8a9
put function comment back, oppsie :p
PurpleHato Aug 17, 2025
307c3d6
French item list - done
PurpleHato Aug 17, 2025
2f45011
Shop translations + misc fixes
PurpleHato Aug 17, 2025
a649052
clang, I don't think there's a time where i'll not forget about
PurpleHato Aug 17, 2025
8fb3ec5
remove the _ replacement to space, in the end it looks better with th…
PurpleHato Aug 17, 2025
195fefe
Moved LOCALIZED macro to CustomMessage header
PurpleHato Aug 18, 2025
5f5a0ea
Base localization support for actors
PurpleHato Aug 18, 2025
cad24ed
Articles and name support + french translations
PurpleHato Aug 18, 2025
a95a9a8
clang, as usual Hato, get it memorized already :derp:
PurpleHato Aug 18, 2025
6a57298
Hex escape sequence
PurpleHato Aug 18, 2025
22ecb66
french gold dust
PurpleHato Aug 18, 2025
7f8dd7d
additional misc tweaks
PurpleHato Aug 18, 2025
8e8ab0f
more small tweak
PurpleHato Aug 18, 2025
b41a528
clang, again, as usual
PurpleHato Aug 18, 2025
c7a79c2
German Items list + additionnal article case
PurpleHato Aug 19, 2025
cbef3a2
clang, it's a meme at this point
PurpleHato Aug 19, 2025
2fe1189
Checking if UTF8 without BOM would solve compilation issue since it's…
PurpleHato Aug 19, 2025
428f17d
curcly brace ???
PurpleHato Aug 19, 2025
a442efd
maybe this ?
PurpleHato Aug 19, 2025
defe28e
Now?
PurpleHato Aug 19, 2025
dfee083
German hints phase1 and other small tweaks
PurpleHato Aug 20, 2025
8b3d7a0
[Rando] Add Frog Shuffle (#1115)
balloondude2 Aug 20, 2025
f19b55e
german typo
PurpleHato Aug 20, 2025
9b0af05
frog shuffle item list
PurpleHato Aug 20, 2025
788bf4d
frog localization support + french and german translations
PurpleHato Aug 20, 2025
f00cc4d
german articles
PurpleHato Aug 20, 2025
76abc5c
clang
PurpleHato Aug 20, 2025
6c4a537
[Rando] Add setting for minimum Stray Fairies (#1224)
Eblo Aug 20, 2025
0a9e296
Merge branch 'develop' into PalRando
PurpleHato Aug 20, 2025
904f4af
French grammar and adjective helpers and handling
PurpleHato Aug 21, 2025
4c34ab1
Tweak notification system to not parse the you got item
PurpleHato Aug 21, 2025
a904ecf
TWEAK: Textbox width to have a more coherent auto linebreak + tiny fr…
PurpleHato Aug 21, 2025
fca9469
Woohooooo guess what? It's clang time!
PurpleHato Aug 21, 2025
5baa669
Consistancy for tags + french l' article handling + some german tweaks
PurpleHato Aug 21, 2025
3dcdbae
tweak size a bit more again
PurpleHato Aug 21, 2025
53787fa
german typo
PurpleHato Aug 21, 2025
829dac2
Oopsie
PurpleHato Aug 21, 2025
06d54ab
article and fix
PurpleHato Aug 21, 2025
c3b3eaf
german colour tweak
PurpleHato Aug 21, 2025
b85a198
mostly german tweak
PurpleHato Aug 22, 2025
de99e3d
german wording
PurpleHato Aug 22, 2025
793c497
Do you know what time it is? Yes! clang very gooooood!
PurpleHato Aug 22, 2025
f560695
French l' article handling for GI case
PurpleHato Aug 22, 2025
1fc326d
french tweak
PurpleHato Aug 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion OTRExporter
2 changes: 1 addition & 1 deletion ZAPDTR
Submodule ZAPDTR updated 2 files
+29 −2 ZAPD/ZRom.cpp
+43 −5 ZAPD/ZTextMM.cpp
15 changes: 15 additions & 0 deletions mm/2s2h/BenGui/BenMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ static const std::unordered_map<int32_t, const char*> menuThemeOptions = {
{ UIWidgets::Colors::DarkGray, "Dark Gray" },
};

static const std::unordered_map<int32_t, const char*> languageOptions = {
{ LANGUAGE_JPN, "Japanese" }, { LANGUAGE_ENG, "English" }, { LANGUAGE_GER, "German" },
{ LANGUAGE_FRE, "French" }, { LANGUAGE_SPA, "Spanish" },
};

static const std::vector<const char*> alwaysWinDoggyraceOptions = {
"Off", // ALWAYS_WIN_DOGGY_RACE_OFF
"When owning Mask of Truth", // ALWAYS_WIN_DOGGY_RACE_MASKOFTRUTH
Expand Down Expand Up @@ -362,6 +367,16 @@ void BenMenu::AddSettings() {
ImGui::EndChild();
});

// Language Settings
path.sidebarName = "Language";
AddSidebarEntry("Settings", "Language", 1);
AddWidget(path, "Language Select", WIDGET_CVAR_COMBOBOX)
.CVar("gSettings.Language")
.Options(ComboboxOptions()
.Tooltip("Sets the language for the game.")
.ComboMap(&languageOptions)
.DefaultIndex(LANGUAGE_ENG));

// Audio Settings
path.sidebarName = "Audio";
path.column = SECTION_COLUMN_1;
Expand Down
5 changes: 4 additions & 1 deletion mm/2s2h/BenPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ OTRGlobals::OTRGlobals() {

archiveFiles.insert(archiveFiles.end(), patchFiles.begin(), patchFiles.end());

std::unordered_set<uint32_t> validHashes = { MM_NTSC_US_10, MM_NTSC_US_GC };
std::unordered_set<uint32_t> validHashes = { MM_NTSC_US_10, MM_NTSC_US_GC, MM_NTSC_PAL_GC };

context = Ship::Context::CreateUninitializedInstance("2 Ship 2 Harkinian", appShortName, "2ship2harkinian.json");
context->InitFileDropMgr();
Expand Down Expand Up @@ -1064,6 +1064,7 @@ extern "C" uint32_t ResourceMgr_GetGamePlatform(int index) {
case MM_NTSC_US_10:
return GAME_PLATFORM_N64;
case MM_NTSC_US_GC:
case MM_NTSC_PAL_GC:
return GAME_PLATFORM_GC;
}
}
Expand All @@ -1076,6 +1077,8 @@ extern "C" uint32_t ResourceMgr_GetGameRegion(int index) {
case MM_NTSC_US_10:
case MM_NTSC_US_GC:
return GAME_REGION_NTSC;
case MM_NTSC_PAL_GC:
return GAME_REGION_PAL;
}
}

Expand Down
1 change: 1 addition & 0 deletions mm/2s2h/BenPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#define MM_NTSC_US_10 0x5354631C
#define MM_NTSC_US_GC 0xB443EB08
#define MM_NTSC_PAL_GC 0x6AECEC4F

#ifdef __cplusplus
#include <Context.h>
Expand Down
85 changes: 83 additions & 2 deletions mm/2s2h/CustomMessage/CustomMessage.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@


#include "CustomMessage.h"
#include "2s2h/GameInteractor/GameInteractor.h"

Expand Down Expand Up @@ -40,8 +40,58 @@ void CustomMessage::ReplaceColorChars(std::string* msg) {
CustomMessage::Replace(msg, "%p", "\x06");
}

void CustomMessage::ReplaceSpecialChars(std::string* msg) {
CustomMessage::Replace(msg, "\n", "\x11");
CustomMessage::Replace(msg, "À", "\x80");
CustomMessage::Replace(msg, "Á", "\x81");
CustomMessage::Replace(msg, "Â", "\x82");
CustomMessage::Replace(msg, "Ä", "\x83");
CustomMessage::Replace(msg, "Ç", "\x84");
CustomMessage::Replace(msg, "È", "\x85");
CustomMessage::Replace(msg, "É", "\x86");
CustomMessage::Replace(msg, "Ê", "\x87");
CustomMessage::Replace(msg, "Ë", "\x88");
CustomMessage::Replace(msg, "Ì", "\x89");
CustomMessage::Replace(msg, "Í", "\x8A");
CustomMessage::Replace(msg, "Î", "\x8B");
CustomMessage::Replace(msg, "Ï", "\x8C");
CustomMessage::Replace(msg, "Ñ", "\x8D");
CustomMessage::Replace(msg, "Ò", "\x8E");
CustomMessage::Replace(msg, "Ó", "\x8F");
CustomMessage::Replace(msg, "Ô", "\x90");
CustomMessage::Replace(msg, "Ö", "\x91");
CustomMessage::Replace(msg, "Ù", "\x92");
CustomMessage::Replace(msg, "Ú", "\x93");
CustomMessage::Replace(msg, "Û", "\x94");
CustomMessage::Replace(msg, "Ü", "\x95");
CustomMessage::Replace(msg, "ß", "\x96");
CustomMessage::Replace(msg, "à", "\x97");
CustomMessage::Replace(msg, "á", "\x98");
CustomMessage::Replace(msg, "â", "\x99");
CustomMessage::Replace(msg, "ä", "\x9A");
CustomMessage::Replace(msg, "ç", "\x9B");
CustomMessage::Replace(msg, "è", "\x9C");
CustomMessage::Replace(msg, "é", "\x9D");
CustomMessage::Replace(msg, "ê", "\x9E");
CustomMessage::Replace(msg, "ë", "\x9F");
CustomMessage::Replace(msg, "ì", "\xA0");
CustomMessage::Replace(msg, "í", "\xA1");
CustomMessage::Replace(msg, "î", "\xA2");
CustomMessage::Replace(msg, "ï", "\xA3");
CustomMessage::Replace(msg, "ñ", "\xA4");
CustomMessage::Replace(msg, "ò", "\xA5");
CustomMessage::Replace(msg, "ó", "\xA6");
CustomMessage::Replace(msg, "ô", "\xA7");
CustomMessage::Replace(msg, "ö", "\xA8");
CustomMessage::Replace(msg, "ù", "\xA9");
CustomMessage::Replace(msg, "ú", "\xAA");
CustomMessage::Replace(msg, "û", "\xAB");
CustomMessage::Replace(msg, "ü", "\xAC");
CustomMessage::Replace(msg, "¡", "\xAD");
}

void CustomMessage::AddLineBreaks(std::string* msg) {
const float MAX_TEXTBOX_WIDTH = 300.0f;
const float MAX_TEXTBOX_WIDTH = 280.0f;
const int MAX_LINES_PER_PAGE = 4;

float currentLineWidth = 0.0f;
Expand Down Expand Up @@ -137,6 +187,7 @@ void CustomMessage::LoadCustomMessageIntoFont(CustomMessage::Entry entry) {

if (entry.autoFormat) {
CustomMessage::ReplaceColorChars(&entry.msg);
CustomMessage::ReplaceSpecialChars(&entry.msg);
CustomMessage::Replace(&entry.msg, "\n", "\x11");
CustomMessage::AddLineBreaks(&entry.msg);
CustomMessage::EnsureMessageEnd(&entry.msg);
Expand All @@ -154,6 +205,36 @@ void CustomMessage::LoadCustomMessageIntoFont(CustomMessage::Entry entry) {
memcpy(&font->msgBuf, buff, msgCtx->msgLength);
}

// Helper function to get the correct French adjective based on article gender
std::string CustomMessage::GetFrenchAdjectiveAgreement(const std::string& article, const std::string& masculineForm,
const std::string& feminineForm) {
std::string cleanArticle = article;
// Clean the article by removing trailing spaces
if (!cleanArticle.empty() && cleanArticle.back() == ' ') {
cleanArticle.pop_back();
}

// Check for feminine articles
if (cleanArticle == "une" || cleanArticle == "la") {
return feminineForm;
}
return masculineForm;
}

// Helper function to handle "que" -> "qu'" elision before vowels
std::string CustomMessage::GetFrenchQueForm(const std::string& article) {
std::string cleanArticle = article;
// Clean the article by removing trailing spaces
if (!cleanArticle.empty() && cleanArticle.back() == ' ') {
cleanArticle.pop_back();
}

if (cleanArticle == "un" || cleanArticle == "une") {
return "qu'";
}
return "que ";
}

void CustomMessage::RegisterHooks() {
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnOpenText>(
CUSTOM_MESSAGE_ID, [](u16* textId, bool* loadFromMessageTable) {
Expand Down
14 changes: 14 additions & 0 deletions mm/2s2h/CustomMessage/CustomMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
#define BUFFER_SIZE 1280
#define MESSAGE_HEADER_SIZE 11

#define LOCALIZED(eng, fre, ger, jpn, spa) \
(gSaveContext.options.language == LANGUAGE_FRE ? (fre) \
: gSaveContext.options.language == LANGUAGE_GER ? (ger) \
: gSaveContext.options.language == LANGUAGE_JPN ? (jpn) \
: gSaveContext.options.language == LANGUAGE_SPA ? (spa) \
: (eng))

#ifdef __cplusplus

extern "C" {
Expand Down Expand Up @@ -35,9 +42,16 @@ void SetActiveCustomMessage(std::string msg, Entry options = {});
void Replace(std::string* msg, const std::string& placeholder, const std::string& value);
void AddLineBreaks(std::string* msg);
void ReplaceColorChars(std::string* msg);
void ReplaceSpecialChars(std::string* msg);
void EnsureMessageEnd(std::string* msg);
Entry LoadVanillaMessageTableEntry(u16 textId);
void LoadCustomMessageIntoFont(Entry entry);
// French grammar helpers
std::string GetFrenchAdjectiveAgreement(const std::string& article, const std::string& masculineForm,
const std::string& feminineForm);
std::string GetFrenchQueForm(const std::string& article);
// German grammar helper
void ReplaceGermanSpecialCases(std::string* msg);
} // namespace CustomMessage

#endif // __cplusplus
Expand Down
4 changes: 2 additions & 2 deletions mm/2s2h/DeveloperTools/SaveEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,12 +891,12 @@ void DrawItemsAndMasksTab() {
std::string riFilterString(riFilter.InputBuf);

for (auto& [randoItemId, randoStaticItem] : Rando::StaticData::Items) {
if (!riFilter.PassFilter(randoStaticItem.name)) {
if (!riFilter.PassFilter(randoStaticItem.nameEng)) {
continue;
}

std::string buttonLabel = "Give ";
buttonLabel += randoStaticItem.name;
buttonLabel += randoStaticItem.nameEng;
if (UIWidgets::Button(buttonLabel.c_str())) {
GameInteractor::Instance->events.emplace_back(GIEventGiveItem{
.showGetItemCutscene =
Expand Down
53 changes: 53 additions & 0 deletions mm/2s2h/Enhancements/Language/LanguageHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "public/bridge/consolevariablebridge.h"
#include "2s2h/GameInteractor/GameInteractor.h"
#include "2s2h/ShipInit.hpp"

extern "C" {
#include "variables.h"
extern PlayState* gPlayState;
extern SaveContext gSaveContext;
void Message_SetTables(PlayState* play);
}

extern "C" MessageTableEntry* sMessageTableNES;
extern "C" MessageTableEntry* sMessageTableJPN;
extern "C" MessageTableEntry* sMessageTableGER;
extern "C" MessageTableEntry* sMessageTableFRA;
extern "C" MessageTableEntry* sMessageTableESP;

#define CVAR_NAME "gSettings.Language"
#define CVAR CVarGetInteger(CVAR_NAME, LANGUAGE_ENG)

void RegisterLanguageHandler() {

COND_HOOK(OnGameStateUpdate, true, []() {
uint8_t oldLanguage = gSaveContext.options.language;
gSaveContext.options.language = CVAR;

if (gSaveContext.options.language == LANGUAGE_GER && sMessageTableGER == NULL) {
gSaveContext.options.language = LANGUAGE_ENG;
CVarSetInteger(CVAR_NAME, LANGUAGE_ENG);
} else if (gSaveContext.options.language == LANGUAGE_FRE && sMessageTableFRA == NULL) {
gSaveContext.options.language = LANGUAGE_ENG;
CVarSetInteger(CVAR_NAME, LANGUAGE_ENG);
} else if (gSaveContext.options.language == LANGUAGE_SPA && sMessageTableESP == NULL) {
gSaveContext.options.language = LANGUAGE_ENG;
CVarSetInteger(CVAR_NAME, LANGUAGE_ENG);
}

if (gSaveContext.options.language == LANGUAGE_ENG && sMessageTableNES == NULL) {
gSaveContext.options.language = LANGUAGE_JPN;
CVarSetInteger(CVAR_NAME, LANGUAGE_JPN);
} else if (gSaveContext.options.language == LANGUAGE_JPN && sMessageTableJPN == NULL) {
gSaveContext.options.language = LANGUAGE_ENG;
CVarSetInteger(CVAR_NAME, LANGUAGE_ENG);
}

// TODO: Figure out what condition to put this in (GameInteractor_ExecuteOnOpenText?)
if (gPlayState != nullptr && gSaveContext.options.language != oldLanguage) {
Message_SetTables(gPlayState);
}
});
}

static RegisterShipInitFunc initFunc(RegisterLanguageHandler, { CVAR_NAME });
12 changes: 11 additions & 1 deletion mm/2s2h/Extractor/Extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,23 @@
extern "C" uint32_t CRC32C(unsigned char* data, size_t dataSize);

static constexpr uint32_t MM_US_10 = 0x5354631C;
static constexpr uint32_t MM_PAL_11 = 0x0A5D8F83;
static constexpr uint32_t MM_US_GC = 0xB443EB08;
static constexpr uint32_t MM_PAL_GC = 0x6AECEC4F;

static const std::unordered_map<uint32_t, const char*> verMap = {
{ MM_US_10, "US 1.0" },
{ MM_PAL_11, "PAL 1.1" },
{ MM_US_GC, "US GC" },
{ MM_PAL_GC, "PAL GC" },
};

// TODO only check the first 54MB of the rom.
static constexpr std::array<const uint32_t, 10> goodCrcs = {
0x96F49400, // MM US 1.0 32MB
0xBB434787, // MM GC
0xE3038C1C, // MM PAL 1.1
0xBB434787, // MM US GC
0xB82EC0E9, // MM PAL GC
};

enum class ButtonId : int {
Expand Down Expand Up @@ -495,8 +501,12 @@ const char* Extractor::GetZapdVerStr() const {
switch (GetRomVerCrc()) {
case MM_US_10:
return "N64_US";
case MM_PAL_11:
return "N64_PAL_11";
case MM_US_GC:
return "GC_US";
case MM_PAL_GC:
return "GC_PAL";
default:
// We should never be in a state where this path happens.
UNREACHABLE;
Expand Down
23 changes: 18 additions & 5 deletions mm/2s2h/Rando/ActorBehavior/DmChar05.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "ActorBehavior.h"
#include "ActorBehavior.h"
#include "2s2h/CustomMessage/CustomMessage.h"

// These come from corresponding entries in z_message.c.
Expand All @@ -11,13 +11,26 @@
void replaceGetItemText(RandoCheckId randoCheckId, u16* textId, bool* loadFromMessageTable) {
auto randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId];
auto randoStaticItem = Rando::StaticData::Items[randoSaveCheck.randoItemId];

auto entry = CustomMessage::LoadVanillaMessageTableEntry(*textId);
entry.autoFormat = false;
entry.msg.assign("You received " + std::string(randoStaticItem.article) + " " + std::string(randoStaticItem.name) +
"!\x1C\x02\x10");
entry.icon = 0xFE; // No icon

std::string message;
if (gSaveContext.options.language == LANGUAGE_FRE) {
message = "Vous obtenez " + std::string(randoStaticItem.articleFre) + " " + "%r" +
std::string(randoStaticItem.nameFre) + "%w!\x1C\x02\x10";
} else if (gSaveContext.options.language == LANGUAGE_GER) {
message = "Du hast " + std::string(randoStaticItem.articleGer) + " " + "%r" +
std::string(randoStaticItem.nameGer) + " %werhalten!\x1C\x02\x10";
} else if (gSaveContext.options.language == LANGUAGE_SPA) {
message = "¡Obtiene " + std::string(randoStaticItem.articleSpa) + " " + "%r" +
std::string(randoStaticItem.nameSpa) + "%w!\x1C\x02\x10";
} else {
message = "You received " + std::string(randoStaticItem.articleEng) + " " + "%r" +
std::string(randoStaticItem.nameEng) + "%w!\x1C\x02\x10";
}

entry.msg.assign(message);
entry.icon = 0xFE; // No icon
CustomMessage::LoadCustomMessageIntoFont(entry);
*loadFromMessageTable = false;
}
Expand Down
Loading
Loading