Add some NTSC Player Name decoding in Save Editor (#5867)

This adds a new font, Noto Sans JP
This commit is contained in:
nclok1405
2026-01-04 23:02:06 +09:00
committed by GitHub
parent a134d2c59a
commit 2ed9c56ca3
4 changed files with 111 additions and 15 deletions

Binary file not shown.

View File

@@ -4,6 +4,7 @@
#include "soh/OTRGlobals.h"
#include "soh/SohGui/UIWidgets.hpp"
#include "soh/SohGui/SohGui.hpp"
#include "soh/SaveManager.h"
#include <spdlog/fmt/fmt.h>
#include <array>
@@ -27,6 +28,11 @@ extern PlayState* gPlayState;
#include "textures/parameter_static/parameter_static.h"
}
#include "message_data_static.h"
extern "C" MessageTableEntry* sGerMessageEntryTablePtr;
extern "C" MessageTableEntry* sFraMessageEntryTablePtr;
extern "C" MessageTableEntry* sJpnMessageEntryTablePtr;
// Maps entries in the GS flag array to the area name it represents
std::vector<const char*> gsMapping = {
"Deku Tree",
@@ -106,6 +112,48 @@ char z2ASCII(int code) {
return char(ret);
}
std::string decodeNTSCPlayerNameChar(int code) {
const std::string charmap[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", // 10
"", "", "", "", "", "", "", "", "", "", // 20
"", "", "", "", "", "", "", "", "", "", // 30
"", "", "", "", "", "", "", "", "", "", // 40
"", "", "", "", "", "", "", "", "", "", // 50
"", "", "", "", "", "", "", "", "", "", // 60
"", "", "", "", "", "", "", "", "", "", // 70
"", "", "", "", "", "", "", "", "", "", // 80
"", "", "", "", "", "", "", "", "", "", // 90
"", "", "", "", "", "", "", "", "", "", // 100
"", "", "", "", "", "", "", "", "", "", // 110
"", "", "", "", "", "", "", "", "", "", // 120
"", "", "", "", "", "", "", "", "", "", // 130
"", "", "", "", "", "", "", "", "", "", // 140
"", "", "", "", "", "", "", "", "", "", // 150
"", "", "", "", "", "", "", "", "", "", // 160
"", "", "", "", "", "", "", "", "", "", // 170
"",
};
std::string ret;
if (code < 171) { // Digits and Japanese
ret = charmap[code];
} else if (code >= 171 && code < 197) { // Uppercase letters
ret.assign(1, (char)(code - 171 + 65));
} else if (code >= 197 && code < 223) { // Lowercase letters
ret.assign(1, (char)(code - 197 + 97));
} else if (code == 223) { // Space
ret = " ";
} else if (code == 228) { // -
ret = "-";
} else if (code == 234) { // .
ret = ".";
} else {
ret = "?";
}
return ret;
}
enum MagicLevel { MAGIC_LEVEL_NONE, MAGIC_LEVEL_SINGLE, MAGIC_LEVEL_DOUBLE };
std::unordered_map<int8_t, const char*> magicLevelMap = {
@@ -144,6 +192,17 @@ std::unordered_map<int32_t, const char*> fileNumMap = {
{ 2, "File 3" },
};
std::unordered_map<uint8_t, const char*> filenameLanguageMap = {
{ NAME_LANGUAGE_PAL, "PAL" },
{ NAME_LANGUAGE_NTSC_JPN, "NTSC JPN" },
{ NAME_LANGUAGE_NTSC_ENG, "NTSC ENG" },
};
std::unordered_map<uint8_t, const char*> filenameLanguageMapNTSCOnly = {
{ NAME_LANGUAGE_NTSC_JPN, "NTSC JPN" },
{ NAME_LANGUAGE_NTSC_ENG, "NTSC ENG" },
};
void DrawInfoTab() {
if (gSaveContext.gameMode == GAMEMODE_TITLE_SCREEN) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Title Screen");
@@ -160,15 +219,30 @@ void DrawInfoTab() {
// TODO Needs a better method for name changing but for now this will work.
std::string name;
ImU16 one = 1;
for (int i = 0; i < 8; i++) {
char letter = z2ASCII(gSaveContext.playerName[i]);
name += letter;
if (gSaveContext.ship.filenameLanguage == NAME_LANGUAGE_PAL) {
for (int i = 0; i < 8; i++) {
char letter = z2ASCII(gSaveContext.playerName[i]);
name += letter;
}
name += '\0';
} else {
for (int i = 0; i < 8; i++) {
name += decodeNTSCPlayerNameChar(gSaveContext.playerName[i]);
}
name += '\0';
}
name += '\0';
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
ImGui::Text("Name: %s", name.c_str());
if (gSaveContext.ship.filenameLanguage == NAME_LANGUAGE_PAL) {
ImGui::Text("Name: %s", name.c_str());
} else {
ImGui::PushFont(OTRGlobals::Instance->fontJapanese);
ImGui::Text("Name: %s", name.c_str());
ImGui::PopFont();
}
Tooltip("Player Name");
std::string nameID;
for (int i = 0; i < 8; i++) {
@@ -181,6 +255,25 @@ void DrawInfoTab() {
PopStyleInput();
}
// Filename encoding
const bool hasPAL = (sGerMessageEntryTablePtr != nullptr) && (sFraMessageEntryTablePtr != nullptr);
const bool hasNTSC = (sJpnMessageEntryTablePtr != nullptr);
if (hasPAL && hasNTSC) {
// Full
Combobox("Player Name Language", &gSaveContext.ship.filenameLanguage, filenameLanguageMap,
comboboxOptionsBase.Tooltip("Encoding used for Player Name"));
} else if (hasNTSC && (gSaveContext.ship.filenameLanguage != NAME_LANGUAGE_PAL)) {
// NTSC only
Combobox("Player Name Language", &gSaveContext.ship.filenameLanguage, filenameLanguageMapNTSCOnly,
comboboxOptionsBase.Tooltip("Encoding used for Player Name"));
} else {
// PAL only (read only)
ImGui::BeginDisabled();
Combobox("Player Name Language", &gSaveContext.ship.filenameLanguage, filenameLanguageMap,
comboboxOptionsBase.Tooltip("Encoding used for Player Name"));
ImGui::EndDisabled();
}
// Use an intermediary to keep the health from updating (and potentially killing the player)
// until it is done being edited
int16_t healthIntermediary = gSaveContext.healthCapacity;

View File

@@ -435,13 +435,14 @@ void OTRGlobals::Initialize() {
previousImGuiScaleIndex = -1;
previousImGuiScale = defaultImGuiScale;
fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf");
fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf");
fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf");
fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf");
fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf");
fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf");
fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf");
fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf", false);
fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf", false);
fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf", false);
fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf", false);
fontJapanese = CreateFontWithSize(24.0f, "fonts/NotoSansJP-Regular.ttf", true);
ImGui::GetIO().FontDefault = fontStandardLarger;
ScaleImGui();
@@ -1687,7 +1688,7 @@ extern "C" SoundFontSample* ReadCustomSample(const char* path) {
*/
}
ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath) {
ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath, bool isJapaneseFont) {
auto mImGuiIo = &ImGui::GetIO();
ImFont* font;
if (fontPath == "") {
@@ -1706,7 +1707,8 @@ ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath) {
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData));
ImFontConfig fontConf;
fontConf.FontDataOwnedByAtlas = false;
font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size, &fontConf);
const ImWchar* glyph_ranges = isJapaneseFont ? mImGuiIo->Fonts->GetGlyphRangesJapanese() : nullptr;
font = mImGuiIo->Fonts->AddFontFromMemoryTTF(fontData->Data, fontData->DataSize, size, &fontConf, glyph_ranges);
}
// FontAwesome fonts need to have their sizes reduced by 2.0f/3.0f in order to align correctly
float iconFontSize = size * 2.0f / 3.0f;

View File

@@ -66,6 +66,7 @@ class OTRGlobals {
ImFont* fontMono;
ImFont* fontMonoLarger;
ImFont* fontMonoLargest;
ImFont* fontJapanese;
OTRGlobals();
~OTRGlobals();
@@ -82,7 +83,7 @@ class OTRGlobals {
bool hasMasterQuest;
bool hasOriginal;
ImFont* CreateDefaultFontWithSize(float size);
ImFont* CreateFontWithSize(float size, std::string fontPath);
ImFont* CreateFontWithSize(float size, std::string fontPath, bool isJapaneseFont);
};
#endif