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/OTRGlobals.h"
#include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/UIWidgets.hpp"
#include "soh/SohGui/SohGui.hpp" #include "soh/SohGui/SohGui.hpp"
#include "soh/SaveManager.h"
#include <spdlog/fmt/fmt.h> #include <spdlog/fmt/fmt.h>
#include <array> #include <array>
@@ -27,6 +28,11 @@ extern PlayState* gPlayState;
#include "textures/parameter_static/parameter_static.h" #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 // Maps entries in the GS flag array to the area name it represents
std::vector<const char*> gsMapping = { std::vector<const char*> gsMapping = {
"Deku Tree", "Deku Tree",
@@ -106,6 +112,48 @@ char z2ASCII(int code) {
return char(ret); 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 }; enum MagicLevel { MAGIC_LEVEL_NONE, MAGIC_LEVEL_SINGLE, MAGIC_LEVEL_DOUBLE };
std::unordered_map<int8_t, const char*> magicLevelMap = { std::unordered_map<int8_t, const char*> magicLevelMap = {
@@ -144,6 +192,17 @@ std::unordered_map<int32_t, const char*> fileNumMap = {
{ 2, "File 3" }, { 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() { void DrawInfoTab() {
if (gSaveContext.gameMode == GAMEMODE_TITLE_SCREEN) { if (gSaveContext.gameMode == GAMEMODE_TITLE_SCREEN) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "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. // TODO Needs a better method for name changing but for now this will work.
std::string name; std::string name;
ImU16 one = 1; ImU16 one = 1;
for (int i = 0; i < 8; i++) {
char letter = z2ASCII(gSaveContext.playerName[i]); if (gSaveContext.ship.filenameLanguage == NAME_LANGUAGE_PAL) {
name += letter; 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::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"); Tooltip("Player Name");
std::string nameID; std::string nameID;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
@@ -181,6 +255,25 @@ void DrawInfoTab() {
PopStyleInput(); 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) // Use an intermediary to keep the health from updating (and potentially killing the player)
// until it is done being edited // until it is done being edited
int16_t healthIntermediary = gSaveContext.healthCapacity; int16_t healthIntermediary = gSaveContext.healthCapacity;

View File

@@ -435,13 +435,14 @@ void OTRGlobals::Initialize() {
previousImGuiScaleIndex = -1; previousImGuiScaleIndex = -1;
previousImGuiScale = defaultImGuiScale; previousImGuiScale = defaultImGuiScale;
fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf"); fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf"); fontMono = CreateFontWithSize(16.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf"); fontMonoLarger = CreateFontWithSize(20.0f, "fonts/Inconsolata-Regular.ttf", false);
fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf"); fontMonoLargest = CreateFontWithSize(24.0f, "fonts/Inconsolata-Regular.ttf", false);
fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf"); fontStandard = CreateFontWithSize(16.0f, "fonts/Montserrat-Regular.ttf", false);
fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf"); fontStandardLarger = CreateFontWithSize(20.0f, "fonts/Montserrat-Regular.ttf", false);
fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf"); fontStandardLargest = CreateFontWithSize(24.0f, "fonts/Montserrat-Regular.ttf", false);
fontJapanese = CreateFontWithSize(24.0f, "fonts/NotoSansJP-Regular.ttf", true);
ImGui::GetIO().FontDefault = fontStandardLarger; ImGui::GetIO().FontDefault = fontStandardLarger;
ScaleImGui(); 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(); auto mImGuiIo = &ImGui::GetIO();
ImFont* font; ImFont* font;
if (fontPath == "") { if (fontPath == "") {
@@ -1706,7 +1707,8 @@ ImFont* OTRGlobals::CreateFontWithSize(float size, std::string fontPath) {
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData)); Ship::Context::GetInstance()->GetResourceManager()->LoadResource(fontPath, false, initData));
ImFontConfig fontConf; ImFontConfig fontConf;
fontConf.FontDataOwnedByAtlas = false; 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 // 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; float iconFontSize = size * 2.0f / 3.0f;

View File

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