diff --git a/gflib/string_util.c b/gflib/string_util.c index b7fd0c49a9..9aef8d543a 100644 --- a/gflib/string_util.c +++ b/gflib/string_util.c @@ -29,7 +29,8 @@ static const s32 sPowersOfTen[] = // Tries to determine whether `str` is safe to prepend a ctrl char to // gStringVarX are always safe, as well as stack allocated IWRAM // (if `length mod 4` is 1 or 2) -bool32 IsStringAddrSafe(u8 *str, u32 length) { +bool32 IsStringAddrSafe(u8 *str, u32 length) +{ if (((u32)str) >> 24 == 3) return (str >= gStackBase && (length & 3) && (length & 3) <= 2); return (str >= gStringVar1 && str < sUnknownStringVar); @@ -40,7 +41,8 @@ u8 *StringCopy_Nickname(u8 *dest, const u8 *src) u32 i; u32 limit = POKEMON_NAME_LENGTH; - if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (DECAP_ENABLED && !DECAP_NICKNAMES) + { if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) *dest++ = CHAR_FIXED_CASE; else if (*src == CHAR_FIXED_CASE) @@ -64,10 +66,8 @@ u8 *StringGet_Nickname(u8 *str) u8 i; u32 limit = POKEMON_NAME_LENGTH; - if (DECAP_ENABLED && !DECAP_NICKNAMES) { - if (*str == CHAR_FIXED_CASE) - str++; - } + if (DECAP_ENABLED && !DECAP_NICKNAMES && *str == CHAR_FIXED_CASE) + str++; for (i = 0; i < limit; i++) if (str[i] == EOS) @@ -82,10 +82,8 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src) s32 i; s32 limit = PLAYER_NAME_LENGTH; - if (DECAP_ENABLED && !DECAP_NICKNAMES) { - if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) - *dest++ = CHAR_FIXED_CASE; - } + if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; for (i = 0; i < limit; i++) { @@ -101,11 +99,10 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src) u8 *StringCopy(u8 *dest, const u8 *src) { - if (DECAP_ENABLED && DECAP_MIRRORING) { - // If `src` is mirrored, prepend fixed-case char - if (IsMirrorPtr(src) && *src != CHAR_FIXED_CASE) - *dest++ = CHAR_FIXED_CASE; - } + // If `src` is mirrored, prepend fixed-case char + if (DECAP_ENABLED && DECAP_MIRRORING && IsMirrorPtr(src) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + while (*src != EOS) { *dest = *src; @@ -156,7 +153,8 @@ u16 StringLength(const u8 *str) s32 StringCompare(const u8 *str1, const u8 *str2) { // Ignore leading fixed-case char - if (DECAP_ENABLED) { + if (DECAP_ENABLED) + { if (*str1 == CHAR_FIXED_CASE) str1++; if (*str2 == CHAR_FIXED_CASE) @@ -176,7 +174,8 @@ s32 StringCompare(const u8 *str1, const u8 *str2) s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n) { // Ignore leading fixed-case char - if (DECAP_ENABLED) { + if (DECAP_ENABLED) + { if (*str1 == CHAR_FIXED_CASE) str1++; if (*str2 == CHAR_FIXED_CASE) @@ -391,9 +390,11 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { case PLACEHOLDER_BEGIN: placeholderId = *src++; - if (DECAP_ENABLED) { + if (DECAP_ENABLED) + { // Handle fixed-case versions of placeholders - if (!fixedCase && (placeholderId & PLACEHOLDER_FIXED_MASK || placeholderId == PLACEHOLDER_ID_PLAYER)) { + if (!fixedCase && (placeholderId & PLACEHOLDER_FIXED_MASK || placeholderId == PLACEHOLDER_ID_PLAYER)) + { *dest++ = CHAR_FIXED_CASE; expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); dest = StringExpandPlaceholders(dest, expandedString); @@ -429,11 +430,11 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) } break; case EOS: - #if DECAP_ENABLED - if (fixedCase) + if (DECAP_ENABLED && fixedCase) *dest++ = CHAR_UNFIX_CASE; *dest = EOS; return dest; + #if DECAP_ENABLED case CHAR_UNFIX_CASE: fixedCase = FALSE; *dest++ = c; @@ -441,9 +442,6 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) case CHAR_FIXED_CASE: fixedCase = TRUE; // fallthrough - #else - *dest = EOS; - return dest; #endif case CHAR_PROMPT_SCROLL: case CHAR_PROMPT_CLEAR: @@ -770,11 +768,9 @@ static const u8 *SkipExtCtrlCode(const u8 *s) s++; s += GetExtCtrlCodeLength(*s); } - if (DECAP_ENABLED) { - while (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE) - s++; - } + while (DECAP_ENABLED && (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE)) + s++; return s; } diff --git a/gflib/text.c b/gflib/text.c index a543c51774..139fd5ee1a 100644 --- a/gflib/text.c +++ b/gflib/text.c @@ -241,23 +241,30 @@ static void SetFontsPointer(const struct FontInfo *fonts) gFonts = fonts; } -void * MirrorPtr(const void *ptr) { - if (((u32)ptr) >> 27) // ROM +// Any ROM address must have bit 27 set (0x8000000) +// so this is used to check if a pointer points to ROM +#define IS_ROM_PTR(ptr) (((u32)ptr) >> 27) + +void * MirrorPtr(const void *ptr) +{ + if (IS_ROM_PTR(ptr)) // ROM return ROM_MIRROR_PTR(ptr); else // RAM return RAM_MIRROR_PTR(ptr); } -void * UnmirrorPtr(const void *ptr) { +void * UnmirrorPtr(const void *ptr) +{ u32 value = (u32) ptr; - if (value >> 27) // ROM + if (IS_ROM_PTR(ptr)) // ROM return (void*)(value & ~ROM_MIRROR_MASK); else // RAM return (void*)(value & ~RAM_MIRROR_MASK); } -bool32 IsMirrorPtr(const void *ptr) { - if (((u32)ptr) >> 27) +bool32 IsMirrorPtr(const void *ptr) +{ + if (IS_ROM_PTR(ptr)) return ((u32)ptr) & ROM_MIRROR_MASK; else return ((u32)ptr) & RAM_MIRROR_MASK; @@ -290,7 +297,8 @@ u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 return AddTextPrinter(&printerTemplate, speed, callback); } -u16 AddTextPrinterFixedCaseParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { +u16 AddTextPrinterFixedCaseParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) +{ return AddTextPrinterParameterized(windowId, fontId, MirrorPtr(str), x, y, speed, callback); } @@ -307,16 +315,14 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, voi sTempTextPrinter.textSpeed = speed; sTempTextPrinter.delayCounter = 0; sTempTextPrinter.scrollDistance = 0; - if (DECAP_ENABLED) { - if (DECAP_MIRRORING) { - // string address is mirrored; treat it as a fixed-case string - if (IsMirrorPtr(printerTemplate->currentChar)) { - sTempTextPrinter.lastChar = CHAR_FIXED_CASE; - // Techhnically, unmirroring isn't necessary; - // but older emulators may not support mirroring - // printerTemplate->currentChar = UnmirrorPtr(printerTemplate->currentChar); - } - } + if (DECAP_ENABLED) + { + // string address is mirrored; treat it as a fixed-case string + // Technically, unmirroring isn't necessary; + // but older emulators may not support mirroring + // printerTemplate->currentChar = UnmirrorPtr(printerTemplate->currentChar); + if (DECAP_MIRRORING && IsMirrorPtr(printerTemplate->currentChar)) + sTempTextPrinter.lastChar = CHAR_FIXED_CASE; sTempTextPrinter.lastChar = 0; } @@ -976,8 +982,8 @@ const u16 gLowercaseDiffTable[] = { [CHAR_SPACER] = 0, [CHAR_A ... CHAR_Z] = CHAR_a - CHAR_A, // é treated as uppercase so POKéDEX, POKéMON, etc. decapped - [CHAR_e_ACUTE] = 0x100, - [CHAR_SGL_QUOTE_RIGHT] = 0x100, + [CHAR_e_ACUTE] = 0 | MARK_UPPER_FLAG, + [CHAR_SGL_QUOTE_RIGHT] = 0 | MARK_UPPER_FLAG, // International [CHAR_A_GRAVE ... CHAR_A_ACUTE] = CHAR_a_GRAVE - CHAR_A_GRAVE, [CHAR_A_CIRCUMFLEX] = CHAR_a_CIRCUMFLEX, @@ -1020,7 +1026,8 @@ static u16 RenderText(struct TextPrinter *textPrinter) currChar = *textPrinter->printerTemplate.currentChar; textPrinter->printerTemplate.currentChar++; - if (DECAP_ENABLED) { + if (DECAP_ENABLED) + { lastChar = textPrinter->lastChar; if (lastChar != CHAR_FIXED_CASE) textPrinter->lastChar = currChar; @@ -1184,7 +1191,7 @@ static u16 RenderText(struct TextPrinter *textPrinter) // Clear fixed case textPrinter->lastChar = currChar; return RENDER_FINISH; - #if DECAP_ENABLED + #if DECAP_ENABLED // Disable/enable decapitalization // In vanilla these are 1-2 pixel spaces case CHAR_FIXED_CASE: @@ -1212,14 +1219,15 @@ static u16 RenderText(struct TextPrinter *textPrinter) if (lastChar == CHAR_P) // PC, PP, PM lastChar = 0; break; + #endif } // If not Japanese or fixed case, try to decap - if (!textPrinter->japanese && lastChar != CHAR_FIXED_CASE) { + if (DECAP_ENABLED && !textPrinter->japanese && lastChar != CHAR_FIXED_CASE) + { // Two consecutive uppercase chars; lowercase this one if (IS_UPPER(currChar) && IS_UPPER(lastChar)) currChar = TO_LOWER(currChar); - #endif } switch (subStruct->fontId) diff --git a/gflib/text.h b/gflib/text.h index 09f72a4634..18af356f3d 100644 --- a/gflib/text.h +++ b/gflib/text.h @@ -7,20 +7,7 @@ // loaded at once but not copied to vram yet. #define TEXT_SKIP_DRAW 0xFF -/* -Enable automatic decapitalization of *all* text -Exceptions: -- Several bigrams: TV, TM, HP, HM, PC, PP, PM -- Player names, nicknames, box names -- Strings beginning with {FIXED_CASE}: - - C strings that use `_C` or `__C` - - ASM strings that use `.fixstr` -- If mirroring enabled, string addresses passed through MirrorPtr -*/ -#define DECAP_ENABLED TRUE -// Enables signaling that a string's case should be preserved -// by *mirroring* its address: i.e 08xxxxxx to 0Axxxxxx -#define DECAP_MIRRORING TRUE +// See include/config/decap.h for decap configuration #if DECAP_MIRRORING #define ROM_MIRROR_MASK (0x02000000) #define RAM_MIRROR_MASK (0x00800000) @@ -28,20 +15,6 @@ Exceptions: #define RAM_MIRROR_PTR(x) ((void*)(((u32)(x)) | RAM_MIRROR_MASK)) #endif -// If TRUE, *all* Pokemon nicknames and player names will be decapitalized. -// Otherwise, their case will be preserved. Default FALSE -#define DECAP_NICKNAMES FALSE - -#define DECAP_MAIN_MENU TRUE // main menu options -#define DECAP_OPTION_MENU TRUE // Option menu texts -#define DECAP_START_MENU TRUE // Start menu options/save menu text -#define DECAP_PARTY_MENU TRUE // Party menu texts -#define DECAP_MAP_NAMES TRUE // Map/location names -#define DECAP_EASY_CHAT TRUE // Both words and interface -#define DECAP_FIELD_MSG TRUE // Field messages (including scripts!) -#define DECAP_SUMMARY TRUE // Summary interface -#define DECAP_ITEM_NAMES TRUE // Via ItemId_GetName - enum { FONT_SMALL, FONT_NORMAL, @@ -172,8 +145,12 @@ extern u8 gDisableTextPrinters; extern struct TextGlyph gCurGlyph; extern const u16 gLowercaseDiffTable[]; -#define IS_UPPER(x) (gLowercaseDiffTable[(x) & 0xFF]) -#define TO_LOWER(x) (((x) + gLowercaseDiffTable[(x)]) & 0xFF) +// in gLowercaseDiffTable, 0x100 represents a character treated as uppercase, +// but that maps to itself; only the lower 8 bits are used for mapping +#define MARK_UPPER_FLAG 0x100 +#define LOWERCASE_DIFF_MASK 0xFF +#define IS_UPPER(x) (gLowercaseDiffTable[(x) & LOWERCASE_DIFF_MASK]) +#define TO_LOWER(x) (((x) + gLowercaseDiffTable[(x)]) & LOWERCASE_DIFF_MASK) void * UnmirrorPtr(const void * ptr); void * MirrorPtr(const void * ptr); diff --git a/include/config/decap.h b/include/config/decap.h new file mode 100644 index 0000000000..45755bbde9 --- /dev/null +++ b/include/config/decap.h @@ -0,0 +1,35 @@ +#ifndef GUARD_CONFIG_DECAP_H +#define GUARD_CONFIG_DECAP_H + +/* +Enable automatic decapitalization of *all* text +Exceptions: +- Several bigrams: TV, TM, HP, HM, PC, PP, PM +- Player names, nicknames, box names +- Strings beginning with {FIXED_CASE} +- C strings that use `_C` or `__C` +- ASM strings that use `.fixstr` +- If mirroring enabled, string addresses passed through MirrorPtr +*/ +#define DECAP_ENABLED TRUE +// Enables signaling that a string's case should be preserved +// by *mirroring* its address: i.e 08xxxxxx to 0Axxxxxx +// Unless you are targeting a different platform than the GBA, +// there aren't many reasons to disable this +#define DECAP_MIRRORING TRUE + +// If TRUE, *all* Pokemon nicknames and player names will be decapitalized. +// Otherwise, their case will be preserved. Default FALSE +#define DECAP_NICKNAMES FALSE + +#define DECAP_MAIN_MENU TRUE // Main menu text. +#define DECAP_OPTION_MENU TRUE // Option menu text. +#define DECAP_START_MENU TRUE // Start menu options & Save menu text. +#define DECAP_PARTY_MENU TRUE // Party menu text. +#define DECAP_MAP_NAMES TRUE // Map and location names. +#define DECAP_EASY_CHAT TRUE // Easy Chat words and interface. +#define DECAP_FIELD_MSG TRUE // Field messages (including scripts!). +#define DECAP_SUMMARY TRUE // Summary interface text. +#define DECAP_ITEM_NAMES TRUE // Item names (obtained via ItemId_GetName). + +#endif // GUARD_CONFIG_DECAP_H diff --git a/include/constants/global.h b/include/constants/global.h index cb023efab3..60a8b676d3 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -6,6 +6,7 @@ #include "config/item.h" #include "config/pokemon.h" #include "config/overworld.h" +#include "config/decap.h" // Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. // In Gens 6 and 7, invalid versions instead show "a distant land" in the summary screen. diff --git a/src/battle_message.c b/src/battle_message.c index 88cf2f7ed8..4f81560264 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3198,12 +3198,11 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battler) break; } - if (DECAP_ENABLED && !DECAP_NICKNAMES) { - if (toCpy != text && *toCpy != CHAR_FIXED_CASE) { - *text = CHAR_FIXED_CASE; - StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); - toCpy = text; - } + if (DECAP_ENABLED && !DECAP_NICKNAMES && toCpy != text && *toCpy != CHAR_FIXED_CASE) + { + *text = CHAR_FIXED_CASE; + StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); + toCpy = text; } return toCpy; @@ -3637,13 +3636,15 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) if (toCpy != NULL) { - if (DECAP_ENABLED) { + if (DECAP_ENABLED) + { bool32 fixedCase = *src & PLACEHOLDER_FIXED_MASK; if (fixedCase) dst[dstID++] = CHAR_FIXED_CASE; - while (*toCpy != EOS) { + while (*toCpy != EOS) + { if (*toCpy == CHAR_FIXED_CASE) fixedCase = TRUE; else if (*toCpy == CHAR_UNFIX_CASE) @@ -3652,7 +3653,9 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) } if (fixedCase) dst[dstID++] = CHAR_UNFIX_CASE; - } else { + } + else + { while (*toCpy != EOS) dst[dstID++] = *toCpy++; } diff --git a/src/field_message_box.c b/src/field_message_box.c index 55b019a345..46f0f2898d 100755 --- a/src/field_message_box.c +++ b/src/field_message_box.c @@ -117,10 +117,13 @@ bool8 ShowFieldMessageFromBuffer(void) static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress) { - if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_FIELD_MSG) { + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_FIELD_MSG) + { gStringVar4[0] = CHAR_FIXED_CASE; StringExpandPlaceholders(gStringVar4+1, str); - } else { + } + else + { StringExpandPlaceholders(gStringVar4, str); } AddTextPrinterForMessage(allowSkippingDelayWithButtonPress); diff --git a/src/naming_screen.c b/src/naming_screen.c index 6db93f4394..848e5b1a48 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -1857,16 +1857,16 @@ static void SaveInputText(void) if (sNamingScreen->textBuffer[i] != CHAR_SPACE && sNamingScreen->textBuffer[i] != EOS) { // If there is space, prepend fixed-case character - #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) - if ((sNamingScreen->templateNum == NAMING_SCREEN_PLAYER + if (DECAP_ENABLED && !DECAP_NICKNAMES + && (sNamingScreen->templateNum == NAMING_SCREEN_PLAYER || sNamingScreen->templateNum == NAMING_SCREEN_NICKNAME - || sNamingScreen->templateNum == NAMING_SCREEN_CAUGHT_MON - ) && sNamingScreen->textBuffer[GetTextEntryPosition()] == EOS) + || sNamingScreen->templateNum == NAMING_SCREEN_CAUGHT_MON) + && sNamingScreen->textBuffer[GetTextEntryPosition()] == EOS) { *sNamingScreen->destBuffer = CHAR_FIXED_CASE; StringCopyN(sNamingScreen->destBuffer + 1, sNamingScreen->textBuffer, sNamingScreen->template->maxChars + 0); - } else - #endif + } + else StringCopyN(sNamingScreen->destBuffer, sNamingScreen->textBuffer, sNamingScreen->template->maxChars + 1); break; } diff --git a/src/pokemon.c b/src/pokemon.c index d42d191279..1c705311b9 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2154,10 +2154,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } else { - if (DECAP_ENABLED && !DECAP_NICKNAMES) { - if (IsStringAddrSafe(data, POKEMON_NAME_LENGTH)) + if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, POKEMON_NAME_LENGTH)) *data++ = CHAR_FIXED_CASE; - } retVal = 0; while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH)) { @@ -2520,10 +2518,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_OT_NAME: { - if (DECAP_ENABLED && !DECAP_NICKNAMES) { - if (IsStringAddrSafe(data, PLAYER_NAME_LENGTH)) - *data++ = CHAR_FIXED_CASE; - } + if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, PLAYER_NAME_LENGTH)) + *data++ = CHAR_FIXED_CASE; retVal = 0; while (retVal < PLAYER_NAME_LENGTH) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 27f34fe86a..043e93b427 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1186,12 +1186,12 @@ static s32 TryMessage(s32 i, s32 n, const u8 *string) if (string[j] == CHAR_PROMPT_CLEAR) j++; } - if (DECAP_ENABLED) { - // ignore case-fixing characters in string - if (string[j] == CHAR_FIXED_CASE || string[j] == CHAR_UNFIX_CASE) { - k--; // will be incremented in 'continue' - continue; - } + if (DECAP_ENABLED && (string[j] == CHAR_FIXED_CASE || string[j] == CHAR_UNFIX_CASE)) + { + // Ignores case-fixing characters in string + // k will be incremented in 'continue' + k--; + continue; } if (string[j] != event->pattern[k]) {