From 1ce961768ef8a93538718c26606fd63c1bae67f0 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:52:29 -0500 Subject: [PATCH 1/6] Added automatic runtime decapitalization. --- charmap.txt | 11 ++++ data/text/pc_transfer.inc | 8 +-- data/text/save.inc | 6 +- gflib/characters.h | 7 +++ gflib/string_util.c | 84 ++++++++++++++++++++++++- gflib/string_util.h | 1 + gflib/text.c | 115 ++++++++++++++++++++++++++++++++++- gflib/text.h | 51 ++++++++++++++++ include/global.h | 5 ++ ld_script.ld | 1 + ld_script_modern.ld | 1 + src/battle_message.c | 29 ++++++++- src/easy_chat.c | 7 +++ src/field_message_box.c | 5 ++ src/fonts.c | 10 +-- src/item.c | 4 ++ src/main.c | 5 ++ src/main_menu.c | 5 ++ src/naming_screen.c | 15 ++++- src/option_menu.c | 5 ++ src/party_menu.c | 10 +++ src/pokemon.c | 12 +++- src/pokemon_storage_system.c | 8 +++ src/pokemon_summary_screen.c | 4 ++ src/region_map.c | 4 ++ src/start_menu.c | 4 ++ src/strings.c | 15 +++-- tools/preproc/asm_file.cpp | 2 + tools/preproc/asm_file.h | 1 + tools/preproc/c_file.cpp | 16 ++++- tools/preproc/preproc.cpp | 10 +++ 31 files changed, 435 insertions(+), 26 deletions(-) diff --git a/charmap.txt b/charmap.txt index 477448c9c5..2768a7d132 100644 --- a/charmap.txt +++ b/charmap.txt @@ -61,6 +61,13 @@ UP_ARROW = 79 DOWN_ARROW = 7A LEFT_ARROW = 7B RIGHT_ARROW = 7C +@ Case-fixing/unfixing chars +@ These are 1-2 pixel spaces in vanilla. +@ 7D collides with 'ヲ' in JP, +@ but that kana is practically unused in JP text. +FIXED_CASE = 7D +UNFIX_CASE = 7E + SUPER_E = 84 '<' = 85 '>' = 86 @@ -345,6 +352,10 @@ ARCHIE = FD 0A MAXIE = FD 0B KYOGRE = FD 0C GROUDON = FD 0D +@ Like STR_VAR_X, but prepends CHAR_FIXED_CASE +STR_VAR_1_FIXED = FD 82 +STR_VAR_2_FIXED = FD 83 +STR_VAR_3_FIXED = FD 84 @ battle string placeholders diff --git a/data/text/pc_transfer.inc b/data/text/pc_transfer.inc index 3fa255340b..01fe80a426 100644 --- a/data/text/pc_transfer.inc +++ b/data/text/pc_transfer.inc @@ -1,11 +1,11 @@ gText_PkmnTransferredSomeonesPC:: - .string "{STR_VAR_2} was transferred to\n" + .string "{STR_VAR_2_FIXED} was transferred to\n" .string "SOMEONE'S PC.\p" .string "It was placed in \n" .string "BOX “{STR_VAR_1}.”$" gText_PkmnTransferredLanettesPC:: - .string "{STR_VAR_2} was transferred to\n" + .string "{STR_VAR_2_FIXED} was transferred to\n" .string "LANETTE'S PC.\p" .string "It was placed in \n" .string "BOX “{STR_VAR_1}.”$" @@ -13,13 +13,13 @@ gText_PkmnTransferredLanettesPC:: gText_PkmnTransferredSomeonesPCBoxFull:: .string "BOX “{STR_VAR_3}” on\n" .string "SOMEONE'S PC was full.\p" - .string "{STR_VAR_2} was transferred to\n" + .string "{STR_VAR_2_FIXED} was transferred to\n" .string "BOX “{STR_VAR_1}.”$" gText_PkmnTransferredLanettesPCBoxFull:: .string "BOX “{STR_VAR_3}” on\n" .string "LANETTE'S PC was full.\p" - .string "{STR_VAR_2} was transferred to\n" + .string "{STR_VAR_2_FIXED} was transferred to\n" .string "BOX “{STR_VAR_1}.”$" gText_NoMoreRoomForPokemon:: diff --git a/data/text/save.inc b/data/text/save.inc index 4d17327c9b..4c16fd2fe6 100644 --- a/data/text/save.inc +++ b/data/text/save.inc @@ -7,14 +7,14 @@ gText_AlreadySavedFile:: .string "Is it okay to overwrite it?$" gText_SavingDontTurnOff:: - .string "SAVING…\n" + .fixstr "SAVING…\n" .string "DON'T TURN OFF THE POWER.$" gText_PlayerSavedGame:: .string "{PLAYER} saved the game.$" gText_DifferentSaveFile:: - .string "WARNING!\p" + .string "{FIXED_CASE}WARNING!{UNFIX_CASE}\p" .string "There is a different game file that\n" .string "is already saved.\p" .string "If you save now, the other file's\n" @@ -29,5 +29,5 @@ gText_SaveError:: .string "backup memory.$" gText_SavingDontTurnOffPower:: - .string "SAVING…\n" + .fixstr "SAVING…\n" .string "DON'T TURN OFF THE POWER.$" diff --git a/gflib/characters.h b/gflib/characters.h index 8ed440e63a..3b00fa1eea 100644 --- a/gflib/characters.h +++ b/gflib/characters.h @@ -77,6 +77,9 @@ #define CHAR_DOWN_ARROW 0x7A #define CHAR_LEFT_ARROW 0x7B #define CHAR_RIGHT_ARROW 0x7C +// Case-fixing characters +#define CHAR_FIXED_CASE 0x7D +#define CHAR_UNFIX_CASE 0x7E // #define CHAR_SUPER_E 0x84 #define CHAR_LESS_THAN 0x85 @@ -262,6 +265,10 @@ #define PLACEHOLDER_ID_MAXIE 0xB #define PLACEHOLDER_ID_KYOGRE 0xC #define PLACEHOLDER_ID_GROUDON 0xD +// Fixed-case placeholders +// These behave the same as their (id & 0x7F), +// but prepend CHAR_FIXED_CASE +#define PLACEHOLDER_FIXED_MASK 0x80 // battle placeholders are located in battle_message.h diff --git a/gflib/string_util.c b/gflib/string_util.c index d515c14cda..8924c4928b 100644 --- a/gflib/string_util.c +++ b/gflib/string_util.c @@ -25,11 +25,29 @@ static const s32 sPowersOfTen[] = 1000000000, }; +#if DECAP_ENABLED +// 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) { + if (((u32)str) >> 24 == 3) + return (str >= gStackBase && (length & 3) && (length & 3) <= 2); + return (str >= gStringVar1 && str < sUnknownStringVar); +} +#endif + u8 *StringCopy_Nickname(u8 *dest, const u8 *src) { - u8 i; + u32 i; u32 limit = POKEMON_NAME_LENGTH; + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + else if (*src == CHAR_FIXED_CASE) + *dest++ = *src++; + #endif + for (i = 0; i < limit; i++) { dest[i] = src[i]; @@ -47,6 +65,11 @@ u8 *StringGet_Nickname(u8 *str) u8 i; u32 limit = POKEMON_NAME_LENGTH; + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if (*str == CHAR_FIXED_CASE) + str++; + #endif + for (i = 0; i < limit; i++) if (str[i] == EOS) return &str[i]; @@ -60,6 +83,11 @@ 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; + #endif + for (i = 0; i < limit; i++) { dest[i] = src[i]; @@ -74,6 +102,11 @@ 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; + #endif while (*src != EOS) { *dest = *src; @@ -123,6 +156,13 @@ u16 StringLength(const u8 *str) s32 StringCompare(const u8 *str1, const u8 *str2) { + // Ignore leading fixed-case char + #if DECAP_ENABLED + if (*str1 == CHAR_FIXED_CASE) + str1++; + if (*str2 == CHAR_FIXED_CASE) + str2++; + #endif while (*str1 == *str2) { if (*str1 == EOS) @@ -136,6 +176,13 @@ 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 (*str1 == CHAR_FIXED_CASE) + str1++; + if (*str2 == CHAR_FIXED_CASE) + str2++; + #endif while (*str1 == *str2) { if (*str1 == EOS) @@ -334,6 +381,9 @@ u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { + #if DECAP_ENABLED + bool32 fixedCase = FALSE; + #endif for (;;) { u8 c = *src++; @@ -344,7 +394,18 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { case PLACEHOLDER_BEGIN: placeholderId = *src++; - expandedString = GetExpandedPlaceholder(placeholderId); + #if DECAP_ENABLED + // Handle fixed-case versions of placeholders + if (!fixedCase && (placeholderId & PLACEHOLDER_FIXED_MASK || placeholderId == PLACEHOLDER_ID_PLAYER)) { + *dest++ = CHAR_FIXED_CASE; + expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); + dest = StringExpandPlaceholders(dest, expandedString); + *dest++ = CHAR_UNFIX_CASE; + *dest = EOS; + break; + } + #endif + expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); dest = StringExpandPlaceholders(dest, expandedString); break; case EXT_CTRL_CODE_BEGIN: @@ -371,8 +432,22 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) } break; case EOS: + #if DECAP_ENABLED + if (fixedCase) + *dest++ = CHAR_UNFIX_CASE; *dest = EOS; return dest; + case CHAR_UNFIX_CASE: + fixedCase = FALSE; + *dest++ = c; + break; + case CHAR_FIXED_CASE: + fixedCase = TRUE; + // fallthrough + #else + *dest = EOS; + return dest; + #endif case CHAR_PROMPT_SCROLL: case CHAR_PROMPT_CLEAR: case CHAR_NEWLINE: @@ -698,6 +773,11 @@ static const u8 *SkipExtCtrlCode(const u8 *s) s++; s += GetExtCtrlCodeLength(*s); } + #if DECAP_ENABLED + while (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE) + s++; + #endif + return s; } diff --git a/gflib/string_util.h b/gflib/string_util.h index 0a8a99fb98..a2480a0aec 100644 --- a/gflib/string_util.h +++ b/gflib/string_util.h @@ -13,6 +13,7 @@ enum StringConvertMode STR_CONV_MODE_LEADING_ZEROS }; +bool32 IsStringAddrSafe(u8 *ptr, u32 length); u8 *StringCopy_Nickname(u8 *dest, const u8 *src); u8 *StringGet_Nickname(u8 *str); u8 *StringCopy_PlayerName(u8 *dest, const u8 *src); diff --git a/gflib/text.c b/gflib/text.c index bc917c7ced..2ea298442e 100644 --- a/gflib/text.c +++ b/gflib/text.c @@ -241,6 +241,30 @@ static void SetFontsPointer(const struct FontInfo *fonts) gFonts = fonts; } +#if (DECAP_ENABLED) && (DECAP_MIRRORING) +void * MirrorPtr(const void *ptr) { + if (((u32)ptr) >> 27) // ROM + return ROM_MIRROR_PTR(ptr); + else // RAM + return RAM_MIRROR_PTR(ptr); +} + +void * UnmirrorPtr(const void *ptr) { + u32 value = (u32) ptr; + if (value >> 27) // ROM + return (void*)(value & ~ROM_MIRROR_MASK); + else // RAM + return (void*)(value & ~RAM_MIRROR_MASK); +} + +bool32 IsMirrorPtr(const void *ptr) { + if (((u32)ptr) >> 27) + return ((u32)ptr) & ROM_MIRROR_MASK; + else + return ((u32)ptr) & RAM_MIRROR_MASK; +} +#endif + void DeactivateAllTextPrinters(void) { int printer; @@ -268,6 +292,12 @@ u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 return AddTextPrinter(&printerTemplate, speed, callback); } +#if (DECAP_ENABLED) && (DECAP_MIRRORING) +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); +} +#endif + bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { int i; @@ -281,6 +311,19 @@ 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); + } + else + #endif + sTempTextPrinter.lastChar = 0; + #endif for (i = 0; i < (int)ARRAY_COUNT(sTempTextPrinter.subStructFields); i++) sTempTextPrinter.subStructFields[i] = 0; @@ -931,10 +974,34 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c } } +#if DECAP_ENABLED +// if table[char] & 0xFF == 0, character is not uppercase +const u16 gLowercaseDiffTable[] = { + // English + [CHAR_SPACE] = 0, + [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, + // International + [CHAR_A_GRAVE ... CHAR_A_ACUTE] = CHAR_a_GRAVE - CHAR_A_GRAVE, + [CHAR_A_CIRCUMFLEX] = CHAR_a_CIRCUMFLEX, + [CHAR_C_CEDILLA ... CHAR_I_GRAVE] = CHAR_c_CEDILLA - CHAR_C_CEDILLA, + [CHAR_I_ACUTE] = CHAR_i_ACUTE, + [CHAR_I_CIRCUMFLEX ... CHAR_N_TILDE] = CHAR_i_CIRCUMFLEX - CHAR_I_CIRCUMFLEX, + [CHAR_A_DIAERESIS ... CHAR_U_DIAERESIS] = CHAR_a_DIAERESIS - CHAR_A_DIAERESIS, + [EOS] = 0, +}; +#endif + static u16 RenderText(struct TextPrinter *textPrinter) { struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); - u16 currChar; + u32 currChar; + #if DECAP_ENABLED + u32 lastChar; + #endif s32 width; s32 widthHelper; @@ -962,6 +1029,11 @@ static u16 RenderText(struct TextPrinter *textPrinter) currChar = *textPrinter->printerTemplate.currentChar; textPrinter->printerTemplate.currentChar++; + #if DECAP_ENABLED + lastChar = textPrinter->lastChar; + if (lastChar != CHAR_FIXED_CASE) + textPrinter->lastChar = currChar; + #endif switch (currChar) { @@ -1117,8 +1189,49 @@ static u16 RenderText(struct TextPrinter *textPrinter) textPrinter->printerTemplate.currentX += gCurGlyph.width + textPrinter->printerTemplate.letterSpacing; return RENDER_PRINT; case EOS: + #if DECAP_ENABLED + // Clear fixed case + textPrinter->lastChar = currChar; + return RENDER_FINISH; + // Disable/enable decapitalization + // In vanilla these are 1-2 pixel spaces + case CHAR_FIXED_CASE: + case CHAR_UNFIX_CASE: + textPrinter->lastChar = currChar; + if (!textPrinter->japanese) + return RENDER_REPEAT; + break; + // common decap exceptions + case CHAR_V: + if (lastChar == CHAR_T) // TV + lastChar = 0; + break; + case CHAR_M: + if (lastChar == CHAR_T) { // TM + lastChar = 0; + break; + } + case CHAR_P: + if (lastChar == CHAR_H) { // HP, HM + lastChar = 0; + break; + } + case CHAR_C: + if (lastChar == CHAR_P) // PC, PP, PM + lastChar = 0; + break; + } + + // If not Japanese or fixed case, try to decap + if (!textPrinter->japanese && lastChar != CHAR_FIXED_CASE) { + // Two consecutive uppercase chars; lowercase this one + if (IS_UPPER(currChar) && IS_UPPER(lastChar)) + currChar = TO_LOWER(currChar); + } + #else return RENDER_FINISH; } + #endif switch (subStruct->fontId) { diff --git a/gflib/text.h b/gflib/text.h index ff13efa9a8..0915fff20b 100644 --- a/gflib/text.h +++ b/gflib/text.h @@ -7,6 +7,41 @@ // 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 +#if DECAP_MIRRORING +#define ROM_MIRROR_MASK (0x02000000) +#define RAM_MIRROR_MASK (0x00800000) +#define ROM_MIRROR_PTR(x) ((void*)(((u32)(x)) | ROM_MIRROR_MASK)) +#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, @@ -92,6 +127,9 @@ struct TextPrinter u8 scrollDistance; u8 minLetterSpacing; // 0x20 u8 japanese; + #if DECAP_ENABLED + u8 lastChar; // used to determine whether to decap strings + #endif }; struct FontInfo @@ -135,6 +173,19 @@ extern TextFlags gTextFlags; extern u8 gDisableTextPrinters; extern struct TextGlyph gCurGlyph; +#if DECAP_ENABLED +extern const u16 gLowercaseDiffTable[]; +#define IS_UPPER(x) (gLowercaseDiffTable[(x) & 0xFF]) +#define TO_LOWER(x) (((x) + gLowercaseDiffTable[(x)]) & 0xFF) + +#if DECAP_MIRRORING +void * UnmirrorPtr(const void * ptr); +void * MirrorPtr(const void * ptr); +bool32 IsMirrorPtr(const void *ptr); +u16 AddTextPrinterFixedCaseParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); +#endif +#endif + void DeactivateAllTextPrinters(void); u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); bool16 AddTextPrinter(struct TextPrinterTemplate *template, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); diff --git a/include/global.h b/include/global.h index 1ea3b65d64..b982cb11ff 100644 --- a/include/global.h +++ b/include/global.h @@ -28,6 +28,9 @@ // We define these when using certain IDEs to fool preproc #define _(x) {x} #define __(x) {x} +// Like the above, but prepends a fixed-case character +#define _C(x) {x} +#define __C(x) {x} #define INCBIN(...) {0} #define INCBIN_U8 INCBIN #define INCBIN_U16 INCBIN @@ -1079,4 +1082,6 @@ struct MapPosition s8 elevation; }; +extern u8 gStackBase[]; // Start of stack-allocated IWRAM + #endif // GUARD_GLOBAL_H diff --git a/ld_script.ld b/ld_script.ld index c4abf075f8..f5df978fa4 100644 --- a/ld_script.ld +++ b/ld_script.ld @@ -45,6 +45,7 @@ SECTIONS { /* COMMON starts at 0x30022A8 */ INCLUDE "sym_common.ld" *libc.a:sbrkr.o(COMMON); + gStackBase = .; } > IWRAM /* BEGIN ROM DATA */ diff --git a/ld_script_modern.ld b/ld_script_modern.ld index 4ccbfbaa0f..ad9a03ce90 100644 --- a/ld_script_modern.ld +++ b/ld_script_modern.ld @@ -34,6 +34,7 @@ SECTIONS { gflib/*.o(COMMON); *libc.a:*.o(COMMON); *libnosys.a:*.o(COMMON); + gStackBase = .; } > IWRAM /* BEGIN ROM DATA */ diff --git a/src/battle_message.c b/src/battle_message.c index e50fcff3de..d09d150fa6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2328,7 +2328,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) if (*src == PLACEHOLDER_BEGIN) { src++; - switch (*src) + switch (*src & ~PLACEHOLDER_FIXED_MASK) { case B_TXT_BUFF1: if (gBattleTextBuff1[0] == B_BUFF_PLACEHOLDER_BEGIN) @@ -2590,6 +2590,13 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) toCpy = gLinkPlayers[0].name; else toCpy = gSaveBlock2Ptr->playerName; + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if (toCpy != text && *toCpy != CHAR_FIXED_CASE && !(*src & PLACEHOLDER_FIXED_MASK)) { + *text = CHAR_FIXED_CASE; + StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); + toCpy = text; + } + #endif break; case B_TXT_TRAINER1_LOSE_TEXT: // trainerA lose text if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) @@ -2725,6 +2732,25 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) break; } + #if DECAP_ENABLED + if (toCpy != NULL) + { + bool32 fixedCase = *src & PLACEHOLDER_FIXED_MASK; + + if (fixedCase) + dst[dstID++] = CHAR_FIXED_CASE; + + while (*toCpy != EOS) { + if (*toCpy == CHAR_FIXED_CASE) + fixedCase = TRUE; + else if (*toCpy == CHAR_UNFIX_CASE) + fixedCase = FALSE; + dst[dstID++] = *toCpy++; + } + if (fixedCase) + dst[dstID++] = CHAR_UNFIX_CASE; + } + #else // missing if (toCpy != NULL) check while (*toCpy != EOS) { @@ -2732,6 +2758,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) dstID++; toCpy++; } + #endif if (*src == B_TXT_TRAINER1_LOSE_TEXT || *src == B_TXT_TRAINER2_LOSE_TEXT || *src == B_TXT_TRAINER1_WIN_TEXT || *src == B_TXT_TRAINER2_WIN_TEXT) { diff --git a/src/easy_chat.c b/src/easy_chat.c index 8e93ca310e..f8f2a38737 100644 --- a/src/easy_chat.c +++ b/src/easy_chat.c @@ -3954,6 +3954,9 @@ static void PrintTitle(void) static void PrintEasyChatText(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { + #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_EASY_CHAT) + str = MirrorPtr(str); + #endif AddTextPrinterParameterized(windowId, fontId, str, x, y, speed, callback); } @@ -5240,6 +5243,10 @@ u8 *ConvertEasyChatWordsToString(u8 *dest, const u16 *src, u16 columns, u16 rows u16 i, j; u16 numColumns = columns - 1; + #if (DECAP_ENABLED) && !(DECAP_EASY_CHAT) + *dest++ = CHAR_FIXED_CASE; + #endif + for (i = 0; i < rows; i++) { for (j = 0; j < numColumns; j++) diff --git a/src/field_message_box.c b/src/field_message_box.c index b797e1d353..c5f286eb79 100755 --- a/src/field_message_box.c +++ b/src/field_message_box.c @@ -117,7 +117,12 @@ bool8 ShowFieldMessageFromBuffer(void) static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress) { + #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_FIELD_MSG) + gStringVar4[0] = CHAR_FIXED_CASE; + StringExpandPlaceholders(gStringVar4+1, str); + #else StringExpandPlaceholders(gStringVar4, str); + #endif AddTextPrinterForMessage(allowSkippingDelayWithButtonPress); CreateTask_DrawFieldMessage(); } diff --git a/src/fonts.c b/src/fonts.c index 8ef32bfef2..851357f76a 100644 --- a/src/fonts.c +++ b/src/fonts.c @@ -9,7 +9,7 @@ ALIGNED(4) const u8 gFontSmallNarrowLatinGlyphWidths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 8, 8, 8, 8, 8, 8, 8, 4, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 4, - 3, 3, 3, 3, 3, 3, 3, 5, 3, 8, 8, 8, 8, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 8, 8, 8, 8, 0, 0, 3, 4, 5, 6, 7, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 3, 5, 5, @@ -45,7 +45,7 @@ ALIGNED(4) const u8 gFontSmallLatinGlyphWidths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 4, 7, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 4, - 3, 3, 3, 3, 3, 3, 3, 5, 3, 8, 8, 8, 8, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 8, 8, 8, 8, 0, 0, 3, 4, 5, 6, 7, 5, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, @@ -81,7 +81,7 @@ ALIGNED(4) const u8 gFontNarrowLatinGlyphWidths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 4, 8, 8, 8, 7, 8, 8, 4, 4, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 4, - 3, 3, 3, 3, 3, 3, 3, 5, 3, 7, 7, 7, 7, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 7, 7, 7, 7, 0, 0, 3, 4, 5, 6, 7, 5, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 3, 5, 3, @@ -117,7 +117,7 @@ ALIGNED(4) const u8 gFontShortLatinGlyphWidths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 8, 8, 8, 8, 8, 8, 4, 6, 8, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 3, 3, 3, 3, 3, 3, 6, - 3, 3, 3, 3, 3, 3, 3, 6, 3, 12, 12, 12, 12, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 6, 3, 12, 12, 12, 12, 0, 0, 3, 4, 5, 6, 7, 8, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 5, @@ -153,7 +153,7 @@ ALIGNED(4) const u8 gFontNormalLatinGlyphWidths[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 4, 8, 8, 8, 7, 8, 8, 4, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 3, 3, 3, 3, 3, 3, 6, - 3, 3, 3, 3, 3, 3, 3, 6, 3, 7, 7, 7, 7, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 6, 3, 7, 7, 7, 7, 0, 0, 3, 4, 5, 6, 7, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 3, 6, 3, diff --git a/src/item.c b/src/item.c index b67cf5f218..6c88737dbd 100644 --- a/src/item.c +++ b/src/item.c @@ -873,7 +873,11 @@ static u16 SanitizeItemId(u16 itemId) const u8 *ItemId_GetName(u16 itemId) { + #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_ITEM_NAMES) + return ROM_MIRROR_PTR(gItems[SanitizeItemId(itemId)].name); + #else return gItems[SanitizeItemId(itemId)].name; + #endif } // Unused diff --git a/src/main.c b/src/main.c index 5f12a2996e..13882d092d 100644 --- a/src/main.c +++ b/src/main.c @@ -95,7 +95,12 @@ void AgbMain() #endif //MODERN *(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup InitGpuRegManager(); + // Setup waitstates for all ROM mirrors + #if (DECAP_ENABLED) && (DECAP_MIRRORING) + REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | 0x5B4; // WAITCNT_WSX_S_1 | WAITCNT_WSX_N_3 + #else REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; + #endif InitKeys(); InitIntrHandlers(); m4aSoundInit(); diff --git a/src/main_menu.c b/src/main_menu.c index 6a6ee99557..9f7963ad25 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -162,6 +162,11 @@ * - Destroys itself when done. */ +#if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_MAIN_MENU) +#define AddTextPrinterParameterized3(a, b, c, d, e, f, str) AddTextPrinterParameterized3(a, b, c, d, e, f, MirrorPtr(str)) +#endif + + #define OPTION_MENU_FLAG (1 << 15) // Static type declarations diff --git a/src/naming_screen.c b/src/naming_screen.c index d8ae7ccd4d..c2253aa26d 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -1856,7 +1856,18 @@ static void SaveInputText(void) { if (sNamingScreen->textBuffer[i] != CHAR_SPACE && sNamingScreen->textBuffer[i] != EOS) { - StringCopyN(sNamingScreen->destBuffer, sNamingScreen->textBuffer, sNamingScreen->template->maxChars + 1); + // If there is space, prepend fixed-case character + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if ((sNamingScreen->templateNum == NAMING_SCREEN_PLAYER + || sNamingScreen->templateNum == NAMING_SCREEN_NICKNAME + || 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 + StringCopyN(sNamingScreen->destBuffer, sNamingScreen->textBuffer, sNamingScreen->template->maxChars + 1); break; } } @@ -2584,5 +2595,3 @@ static const struct SpritePalette sSpritePalettes[] = {gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON}, {} }; - - diff --git a/src/option_menu.c b/src/option_menu.c index 67169b1f9b..af10b75a20 100644 --- a/src/option_menu.c +++ b/src/option_menu.c @@ -24,6 +24,11 @@ #define tButtonMode data[5] #define tWindowFrameType data[6] +#if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_OPTION_MENU) +#define AddTextPrinterParameterized3(a, b, c, d, e, f, str) AddTextPrinterParameterized3(a, b, c, d, e, f, MirrorPtr(str)) +#define AddTextPrinterParameterized4(a, b, c, d, e, f, g, h, str) AddTextPrinterParameterized4(a, b, c, d, e, f, g, h, MirrorPtr(str)) +#endif + enum { MENUITEM_TEXTSPEED, diff --git a/src/party_menu.c b/src/party_menu.c index adf833fe89..972d1ec82d 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -73,6 +73,12 @@ #include "constants/rgb.h" #include "constants/songs.h" +#if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_PARTY_MENU) +#define gStringVar4 (MirrorPtr(gStringVar4)) +#define AddTextPrinterParameterized4(a, b, c, d, e, f, g, h, str) (AddTextPrinterParameterized4(a, b, c, d, e, f, g, h, MirrorPtr(str))) +#define AddTextPrinterParameterized3(a, b, c, d, e, f, str) AddTextPrinterParameterized3(a, b, c, d, e, f, MirrorPtr(str)) +#endif + enum { MENU_SUMMARY, MENU_SWITCH, @@ -2567,7 +2573,11 @@ static void PrintMessage(const u8 *text) { DrawStdFrameWithCustomTileAndPalette(WIN_MSG, FALSE, 0x4F, 13); gTextFlags.canABSpeedUpPrint = TRUE; + #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_PARTY_MENU) + AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, MirrorPtr(text), GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); + #else AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, text, GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); + #endif } static void PartyMenuDisplayYesNoMenu(void) diff --git a/src/pokemon.c b/src/pokemon.c index 6ee052fda5..38f907c1b0 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3773,6 +3773,10 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } else { + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if (IsStringAddrSafe(data, POKEMON_NAME_LENGTH)) + *data++ = CHAR_FIXED_CASE; + #endif for (retVal = 0; retVal < POKEMON_NAME_LENGTH; data[retVal] = boxMon->nickname[retVal], retVal++){} @@ -3797,8 +3801,12 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) { retVal = 0; - while (retVal < PLAYER_NAME_LENGTH) - { + #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) + if (IsStringAddrSafe(data, PLAYER_NAME_LENGTH)) + *data++ = CHAR_FIXED_CASE; + #endif + + while (retVal < PLAYER_NAME_LENGTH) { data[retVal] = boxMon->otName[retVal]; retVal++; } diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index c8c87961c8..8262c936c2 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -5497,7 +5497,11 @@ static void InitBoxTitle(u8 boxId) sStorage->wallpaperPalBits |= (1 << 16) << tagIndex; StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH); + #if (DECAP_ENABLED) && (DECAP_MIRRORING) + DrawTextWindowAndBufferTiles(MirrorPtr(sStorage->boxTitleText), sStorage->boxTitleTiles, 0, 0, 2); + #else DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->boxTitleTiles, 0, 0, 2); + #endif LoadSpriteSheet(&spriteSheet); x = GetBoxTitleBaseX(GetBoxNamePtr(boxId)); @@ -9519,7 +9523,11 @@ struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition) u8 *GetBoxNamePtr(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) + #if (DECAP_ENABLED) && (DECAP_MIRRORING) + return MirrorPtr(gPokemonStoragePtr->boxNames[boxId]); + #else return gPokemonStoragePtr->boxNames[boxId]; + #endif else return NULL; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 642b176d6b..2e61a1aa9f 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2704,7 +2704,11 @@ static void ResetWindows(void) static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) { + #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_SUMMARY) + AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, MirrorPtr(string)); + #else AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); + #endif } static void PrintMonInfo(void) diff --git a/src/region_map.c b/src/region_map.c index 21c6314d30..e9ee482a2d 100644 --- a/src/region_map.c +++ b/src/region_map.c @@ -1570,6 +1570,10 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength) u8 *str; u16 i; + #if (DECAP_ENABLED) && !(DECAP_MAP_NAMES) + *dest++ = CHAR_FIXED_CASE; + #endif + if (regionMapId == MAPSEC_SECRET_BASE) { str = GetSecretBaseMapName(dest); diff --git a/src/start_menu.c b/src/start_menu.c index 63914cbb72..72313bbf33 100644 --- a/src/start_menu.c +++ b/src/start_menu.c @@ -47,6 +47,10 @@ #include "constants/rgb.h" #include "constants/songs.h" +#if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_START_MENU) +#define AddTextPrinterParameterized (AddTextPrinterFixedCaseParameterized) +#endif + // Menu actions enum { diff --git a/src/strings.c b/src/strings.c index d2a82ada48..4321ceb80e 100644 --- a/src/strings.c +++ b/src/strings.c @@ -2,6 +2,7 @@ #include "strings.h" #include "battle_pyramid_bag.h" #include "item_menu.h" +#include "text.h" ALIGNED(4) const u8 gText_ExpandedPlaceholder_Empty[] = _(""); @@ -173,7 +174,7 @@ const u8 gText_Number[] = _("No. "); const u8 gText_Level[] = _("Lv. "); const u8 gText_IdNumberSlash[] = _("IDNo. /"); // Unused const u8 gText_Name[] = _("NAME"); -const u8 gText_IDNumber[] = _("IDNo."); +const u8 gText_IDNumber[] = _C("IDNo."); const u8 gText_BirchInTrouble[] = _("PROF. BIRCH is in trouble!\nRelease a POKéMON and rescue him!"); const u8 gText_ConfirmStarterChoice[] = _("Do you choose this POKéMON?"); const u8 gText_Pokemon4[] = _("POKéMON"); // Unused @@ -490,7 +491,7 @@ const u8 gText_SpDef4[] = _("SP. DEF"); const u8 gText_Speed2[] = _("SPEED"); const u8 gText_HP4[] = _("HP"); const u8 gText_EmptyString8[] = _(""); // Unused -const u8 gText_OTSlash[] = _("OT/"); +const u8 gText_OTSlash[] = _C("OT/"); const u8 gText_RentalPkmn[] = _("RENTAL POKéMON"); const u8 gText_TypeSlash[] = _("TYPE/"); const u8 gText_Power[] = _("POWER"); @@ -507,7 +508,7 @@ const u8 gText_Switch[] = _("SWITCH"); const u8 gText_PkmnInfo[] = _("POKéMON INFO"); const u8 gText_PkmnSkills[] = _("POKéMON SKILLS"); const u8 gText_BattleMoves[] = _("BATTLE MOVES"); -const u8 gText_ContestMoves[] = _("C0NTEST MOVES"); +const u8 gText_ContestMoves[] = _("CONTEST MOVES"); const u8 gText_Info[] = _("INFO"); const u8 gText_EggWillTakeALongTime[] = _("It looks like this EGG will\ntake a long time to hatch."); const u8 gText_EggWillTakeSomeTime[] = _("What will hatch from this?\nIt will take some time."); @@ -1093,9 +1094,15 @@ const u8 gText_GamePlayCannotBeContinued[] = _("{COLOR RED}“Game play cannot b const u8 gText_CheckCompleted[] = _("Check completed.\nAttempting to save again.\nPlease wait."); const u8 gText_SaveCompleteGameCannotContinue[] = _("Save completed.\n{COLOR RED}“Game play cannot be continued.\nReturning to the title screen.”"); const u8 gText_SaveCompletePressA[] = _("Save completed.\n{COLOR RED}“Please press the A Button.”"); +#if (DECAP_ENABLED) && !(DECAP_MAP_NAMES) +const u8 gText_Ferry[] = _C("FERRY"); +const u8 gText_SecretBase[] = _C("SECRET BASE"); +const u8 gText_Hideout[] = _C("HIDEOUT"); +#else const u8 gText_Ferry[] = _("FERRY"); const u8 gText_SecretBase[] = _("SECRET BASE"); const u8 gText_Hideout[] = _("HIDEOUT"); +#endif const u8 gText_ResetRTCConfirmCancel[] = _("Reset RTC?\nA: Confirm, B: Cancel"); const u8 gText_PresentTime[] = _("Present time in game"); const u8 gText_PreviousTime[] = _("Previous time in game"); @@ -1189,7 +1196,7 @@ const u8 gText_FirePunch48BP[] = _("FIRE PUNCH{CLEAR_TO 0x4E}48BP"); const u8 gText_PkmnFainted_FldPsn[] = _("{STR_VAR_1} fainted…\p\n"); const u8 gText_Marco[] = _("MARCO"); const u8 gText_TrainerCardName[] = _("NAME: "); -const u8 gText_TrainerCardIDNo[] = _("IDNo."); +const u8 gText_TrainerCardIDNo[] = _C("IDNo."); const u8 gText_TrainerCardMoney[] = _("MONEY"); const u8 gText_PokeDollar[] = _("¥"); // Unused const u8 gText_TrainerCardPokedex[] = _("POKéDEX"); diff --git a/tools/preproc/asm_file.cpp b/tools/preproc/asm_file.cpp index 04a7410e00..95655f6ac9 100644 --- a/tools/preproc/asm_file.cpp +++ b/tools/preproc/asm_file.cpp @@ -170,6 +170,8 @@ Directive AsmFile::GetDirective() if (CheckForDirective(".include")) return Directive::Include; + else if (CheckForDirective(".fixstr")) + return Directive::FixedString; else if (CheckForDirective(".string")) return Directive::String; else if (CheckForDirective(".braille")) diff --git a/tools/preproc/asm_file.h b/tools/preproc/asm_file.h index 29435f76a4..9659c7264f 100644 --- a/tools/preproc/asm_file.h +++ b/tools/preproc/asm_file.h @@ -31,6 +31,7 @@ enum class Directive Include, String, Braille, + FixedString, Unknown }; diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index 508c628731..d400a75d04 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -195,6 +195,7 @@ void CFile::TryConvertString() long oldPos = m_pos; long oldLineNum = m_lineNum; bool noTerminator = false; + bool fixedCase = false; if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1]))) return; @@ -207,6 +208,13 @@ void CFile::TryConvertString() m_pos++; } + // Fixed-case string + if (m_buffer[m_pos] == 'C') + { + fixedCase = true; + m_pos++; + } + SkipWhitespace(); if (m_buffer[m_pos] != '(') @@ -234,7 +242,13 @@ void CFile::TryConvertString() try { - m_pos += stringParser.ParseString(m_pos, s, length); + if (fixedCase) { + s[0] = '\x7d'; // FIXED_CASE + m_pos += stringParser.ParseString(m_pos, s+1, length); + length++; + } else { + m_pos += stringParser.ParseString(m_pos, s, length); + } } catch (std::runtime_error& e) { diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp index eb2d4c8a23..554482dfb1 100644 --- a/tools/preproc/preproc.cpp +++ b/tools/preproc/preproc.cpp @@ -76,6 +76,16 @@ void PreprocAsmFile(std::string filename) PrintAsmBytes(s, length); break; } + // Like String, but prepends FIXED_CASE char + case Directive::FixedString: + { + unsigned char s[kMaxStringLength]; + int length = 0; + s[length++] = '\x7d'; // FIXED_CASE + length += stack.top().ReadString(s+length); + PrintAsmBytes(s, length); + break; + } case Directive::Braille: { unsigned char s[kMaxStringLength]; From 4b73b991cc7c8b8aa563887e6916b2b175d5f0db Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Sun, 7 Jan 2024 20:17:16 -0500 Subject: [PATCH 2/6] Updated README. --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index d9148b32ec..b83ce5bf20 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,43 @@ It builds the following ROM: * [**pokeemerald.gba**](https://datomatic.no-intro.org/index.php?page=show_record&s=23&n=1961) `sha1: f3ae088181bf583e55daf962a92bb46f4f1d07b7` +**guillotine** branch: +* ~~Decapitates~~ Decapitalizes **all** text at runtime, with some exceptions (see the [FAQ](#guillotine-q-how-can-i-keep-my-strings-from-being-decapped)) +* Future-proof, does not require mass-replacing strings + To set up the repository, see [INSTALL.md](INSTALL.md). +## FAQ +### `(guillotine)` Q: How can I keep my string(s) from being decapped? +A: There are a number of ways to make a string "fixed case" so that it will not be decapitalized when displayed: + +C strings: Replace the `_` with `_C`: +```c +// _C = fixed (C)ase string! +const u8 gText_IDNumber[] = _C("IDNo."); +``` +ASM strings: Replace `.string` with `.fixstr`: +```arm +gText_SavingDontTurnOff:: + @ Lasts until the string terminator '$' + .fixstr "SAVING…\n" + .string "DON'T TURN OFF THE POWER.$" +``` +You can fix-case/unfix parts of a string like so: +```arm + .string "{FIXED_CASE}WARNING!{UNFIX_CASE}\p" +``` +For a placeholder (only the placeholder will be fixed-case): +```arm + .string "{STR_VAR_2_FIXED} was transferred to\n" + .string "BOX “{STR_VAR_1}.”$" +``` +See also the configuration in [text.h](gflib/text.h). + +There are also a number of bigram exceptions that will not be decapped: `TV, TM, HP, HM, PC, PP, PM` + + + +## See also + For contacts and other pret projects, see [pret.github.io](https://pret.github.io/). From 90be465a9ab017ba3a1e8d290139e6fda98ea9f8 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:04:25 -0500 Subject: [PATCH 3/6] Ignored case-fixing characters in tests' `TryMessage` --- test/test_runner_battle.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index caccdb2042..8177e02b0a 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -12,6 +12,7 @@ #include "random.h" #include "test/battle.h" #include "window.h" +#include "text.h" #include "constants/trainers.h" #if defined(__INTELLISENSE__) @@ -1185,6 +1186,13 @@ 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; + } + #endif if (string[j] != event->pattern[k]) { break; From 499b4a6d42b0ab2d4eb566d88c39e150c4883057 Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:34:29 -0500 Subject: [PATCH 4/6] Replaced if directives with conditions, where possible. --- gflib/string_util.c | 94 +++++++++++++++++------------------- gflib/text.c | 53 ++++++++------------ gflib/text.h | 6 --- src/battle_message.c | 44 ++++++++--------- src/easy_chat.c | 10 ++-- src/field_message_box.c | 12 ++--- src/item.c | 9 ++-- src/main.c | 9 ++-- src/party_menu.c | 9 ++-- src/pokemon.c | 16 +++--- src/pokemon_storage_system.c | 18 +++---- src/pokemon_summary_screen.c | 9 ++-- src/region_map.c | 5 +- test/test_runner_battle.c | 12 ++--- 14 files changed, 138 insertions(+), 168 deletions(-) diff --git a/gflib/string_util.c b/gflib/string_util.c index e2efbc9325..b7fd0c49a9 100644 --- a/gflib/string_util.c +++ b/gflib/string_util.c @@ -26,7 +26,6 @@ static const s32 sPowersOfTen[] = 1000000000, }; -#if DECAP_ENABLED // 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) @@ -35,19 +34,18 @@ bool32 IsStringAddrSafe(u8 *str, u32 length) { return (str >= gStackBase && (length & 3) && (length & 3) <= 2); return (str >= gStringVar1 && str < sUnknownStringVar); } -#endif u8 *StringCopy_Nickname(u8 *dest, const u8 *src) { u32 i; u32 limit = POKEMON_NAME_LENGTH; - #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) - if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) - *dest++ = CHAR_FIXED_CASE; - else if (*src == CHAR_FIXED_CASE) - *dest++ = *src++; - #endif + if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + else if (*src == CHAR_FIXED_CASE) + *dest++ = *src++; + } for (i = 0; i < limit; i++) { @@ -66,10 +64,10 @@ u8 *StringGet_Nickname(u8 *str) u8 i; u32 limit = POKEMON_NAME_LENGTH; - #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) - if (*str == CHAR_FIXED_CASE) - str++; - #endif + if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (*str == CHAR_FIXED_CASE) + str++; + } for (i = 0; i < limit; i++) if (str[i] == EOS) @@ -84,10 +82,10 @@ 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; - #endif + if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + } for (i = 0; i < limit; i++) { @@ -103,11 +101,11 @@ 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; - #endif + if (DECAP_ENABLED && DECAP_MIRRORING) { + // If `src` is mirrored, prepend fixed-case char + if (IsMirrorPtr(src) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + } while (*src != EOS) { *dest = *src; @@ -158,12 +156,12 @@ u16 StringLength(const u8 *str) s32 StringCompare(const u8 *str1, const u8 *str2) { // Ignore leading fixed-case char - #if DECAP_ENABLED - if (*str1 == CHAR_FIXED_CASE) - str1++; - if (*str2 == CHAR_FIXED_CASE) - str2++; - #endif + if (DECAP_ENABLED) { + if (*str1 == CHAR_FIXED_CASE) + str1++; + if (*str2 == CHAR_FIXED_CASE) + str2++; + } while (*str1 == *str2) { if (*str1 == EOS) @@ -178,12 +176,12 @@ 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 (*str1 == CHAR_FIXED_CASE) - str1++; - if (*str2 == CHAR_FIXED_CASE) - str2++; - #endif + if (DECAP_ENABLED) { + if (*str1 == CHAR_FIXED_CASE) + str1++; + if (*str2 == CHAR_FIXED_CASE) + str2++; + } while (*str1 == *str2) { if (*str1 == EOS) @@ -382,9 +380,7 @@ u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { - #if DECAP_ENABLED bool32 fixedCase = FALSE; - #endif for (;;) { u8 c = *src++; @@ -395,17 +391,17 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { case PLACEHOLDER_BEGIN: placeholderId = *src++; - #if DECAP_ENABLED - // Handle fixed-case versions of placeholders - if (!fixedCase && (placeholderId & PLACEHOLDER_FIXED_MASK || placeholderId == PLACEHOLDER_ID_PLAYER)) { - *dest++ = CHAR_FIXED_CASE; - expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); - dest = StringExpandPlaceholders(dest, expandedString); - *dest++ = CHAR_UNFIX_CASE; - *dest = EOS; - break; + if (DECAP_ENABLED) { + // Handle fixed-case versions of placeholders + if (!fixedCase && (placeholderId & PLACEHOLDER_FIXED_MASK || placeholderId == PLACEHOLDER_ID_PLAYER)) { + *dest++ = CHAR_FIXED_CASE; + expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); + dest = StringExpandPlaceholders(dest, expandedString); + *dest++ = CHAR_UNFIX_CASE; + *dest = EOS; + break; + } } - #endif expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); dest = StringExpandPlaceholders(dest, expandedString); break; @@ -774,10 +770,10 @@ static const u8 *SkipExtCtrlCode(const u8 *s) s++; s += GetExtCtrlCodeLength(*s); } - #if DECAP_ENABLED - while (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE) - s++; - #endif + if (DECAP_ENABLED) { + while (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE) + s++; + } return s; diff --git a/gflib/text.c b/gflib/text.c index 2ea298442e..a543c51774 100644 --- a/gflib/text.c +++ b/gflib/text.c @@ -241,7 +241,6 @@ static void SetFontsPointer(const struct FontInfo *fonts) gFonts = fonts; } -#if (DECAP_ENABLED) && (DECAP_MIRRORING) void * MirrorPtr(const void *ptr) { if (((u32)ptr) >> 27) // ROM return ROM_MIRROR_PTR(ptr); @@ -263,7 +262,6 @@ bool32 IsMirrorPtr(const void *ptr) { else return ((u32)ptr) & RAM_MIRROR_MASK; } -#endif void DeactivateAllTextPrinters(void) { @@ -292,11 +290,9 @@ u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 return AddTextPrinter(&printerTemplate, speed, callback); } -#if (DECAP_ENABLED) && (DECAP_MIRRORING) 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); } -#endif bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { @@ -311,19 +307,18 @@ 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); - } - else - #endif + 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); + } + } sTempTextPrinter.lastChar = 0; - #endif + } for (i = 0; i < (int)ARRAY_COUNT(sTempTextPrinter.subStructFields); i++) sTempTextPrinter.subStructFields[i] = 0; @@ -974,7 +969,6 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c } } -#if DECAP_ENABLED // if table[char] & 0xFF == 0, character is not uppercase const u16 gLowercaseDiffTable[] = { // English @@ -993,15 +987,12 @@ const u16 gLowercaseDiffTable[] = { [CHAR_A_DIAERESIS ... CHAR_U_DIAERESIS] = CHAR_a_DIAERESIS - CHAR_A_DIAERESIS, [EOS] = 0, }; -#endif static u16 RenderText(struct TextPrinter *textPrinter) { struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); u32 currChar; - #if DECAP_ENABLED u32 lastChar; - #endif s32 width; s32 widthHelper; @@ -1029,11 +1020,11 @@ static u16 RenderText(struct TextPrinter *textPrinter) currChar = *textPrinter->printerTemplate.currentChar; textPrinter->printerTemplate.currentChar++; - #if DECAP_ENABLED - lastChar = textPrinter->lastChar; - if (lastChar != CHAR_FIXED_CASE) - textPrinter->lastChar = currChar; - #endif + if (DECAP_ENABLED) { + lastChar = textPrinter->lastChar; + if (lastChar != CHAR_FIXED_CASE) + textPrinter->lastChar = currChar; + } switch (currChar) { @@ -1189,10 +1180,11 @@ static u16 RenderText(struct TextPrinter *textPrinter) textPrinter->printerTemplate.currentX += gCurGlyph.width + textPrinter->printerTemplate.letterSpacing; return RENDER_PRINT; case EOS: - #if DECAP_ENABLED - // Clear fixed case - textPrinter->lastChar = currChar; + if (DECAP_ENABLED) + // Clear fixed case + textPrinter->lastChar = currChar; return RENDER_FINISH; + #if DECAP_ENABLED // Disable/enable decapitalization // In vanilla these are 1-2 pixel spaces case CHAR_FIXED_CASE: @@ -1227,11 +1219,8 @@ static u16 RenderText(struct TextPrinter *textPrinter) // Two consecutive uppercase chars; lowercase this one if (IS_UPPER(currChar) && IS_UPPER(lastChar)) currChar = TO_LOWER(currChar); - } - #else - return RENDER_FINISH; - } #endif + } switch (subStruct->fontId) { diff --git a/gflib/text.h b/gflib/text.h index 0915fff20b..09f72a4634 100644 --- a/gflib/text.h +++ b/gflib/text.h @@ -127,9 +127,7 @@ struct TextPrinter u8 scrollDistance; u8 minLetterSpacing; // 0x20 u8 japanese; - #if DECAP_ENABLED u8 lastChar; // used to determine whether to decap strings - #endif }; struct FontInfo @@ -173,18 +171,14 @@ extern TextFlags gTextFlags; extern u8 gDisableTextPrinters; extern struct TextGlyph gCurGlyph; -#if DECAP_ENABLED extern const u16 gLowercaseDiffTable[]; #define IS_UPPER(x) (gLowercaseDiffTable[(x) & 0xFF]) #define TO_LOWER(x) (((x) + gLowercaseDiffTable[(x)]) & 0xFF) -#if DECAP_MIRRORING void * UnmirrorPtr(const void * ptr); void * MirrorPtr(const void * ptr); bool32 IsMirrorPtr(const void *ptr); u16 AddTextPrinterFixedCaseParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); -#endif -#endif void DeactivateAllTextPrinters(void); u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); diff --git a/src/battle_message.c b/src/battle_message.c index c60ca1b9dd..88cf2f7ed8 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3198,13 +3198,13 @@ 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) { + if (toCpy != text && *toCpy != CHAR_FIXED_CASE) { + *text = CHAR_FIXED_CASE; + StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); + toCpy = text; + } } - #endif return toCpy; } @@ -3637,25 +3637,25 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) if (toCpy != NULL) { - #if DECAP_ENABLED - bool32 fixedCase = *src & PLACEHOLDER_FIXED_MASK; + if (DECAP_ENABLED) { + bool32 fixedCase = *src & PLACEHOLDER_FIXED_MASK; - if (fixedCase) - dst[dstID++] = CHAR_FIXED_CASE; + if (fixedCase) + dst[dstID++] = CHAR_FIXED_CASE; - while (*toCpy != EOS) { - if (*toCpy == CHAR_FIXED_CASE) - fixedCase = TRUE; - else if (*toCpy == CHAR_UNFIX_CASE) - fixedCase = FALSE; - dst[dstID++] = *toCpy++; + while (*toCpy != EOS) { + if (*toCpy == CHAR_FIXED_CASE) + fixedCase = TRUE; + else if (*toCpy == CHAR_UNFIX_CASE) + fixedCase = FALSE; + dst[dstID++] = *toCpy++; + } + if (fixedCase) + dst[dstID++] = CHAR_UNFIX_CASE; + } else { + while (*toCpy != EOS) + dst[dstID++] = *toCpy++; } - if (fixedCase) - dst[dstID++] = CHAR_UNFIX_CASE; - #else - while (*toCpy != EOS) { - dst[dstID++] = *toCpy++; - #endif } if (*src == B_TXT_TRAINER1_LOSE_TEXT || *src == B_TXT_TRAINER2_LOSE_TEXT diff --git a/src/easy_chat.c b/src/easy_chat.c index 84bed75b91..7968efb17e 100644 --- a/src/easy_chat.c +++ b/src/easy_chat.c @@ -3954,9 +3954,8 @@ static void PrintTitle(void) static void PrintEasyChatText(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { - #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_EASY_CHAT) - str = MirrorPtr(str); - #endif + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_EASY_CHAT) + str = MirrorPtr(str); AddTextPrinterParameterized(windowId, fontId, str, x, y, speed, callback); } @@ -5243,9 +5242,8 @@ u8 *ConvertEasyChatWordsToString(u8 *dest, const u16 *src, u16 columns, u16 rows u16 i, j; u16 numColumns = columns - 1; - #if (DECAP_ENABLED) && !(DECAP_EASY_CHAT) - *dest++ = CHAR_FIXED_CASE; - #endif + if (DECAP_ENABLED && !DECAP_EASY_CHAT) + *dest++ = CHAR_FIXED_CASE; for (i = 0; i < rows; i++) { diff --git a/src/field_message_box.c b/src/field_message_box.c index c5f286eb79..55b019a345 100755 --- a/src/field_message_box.c +++ b/src/field_message_box.c @@ -117,12 +117,12 @@ bool8 ShowFieldMessageFromBuffer(void) static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress) { - #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_FIELD_MSG) - gStringVar4[0] = CHAR_FIXED_CASE; - StringExpandPlaceholders(gStringVar4+1, str); - #else - StringExpandPlaceholders(gStringVar4, str); - #endif + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_FIELD_MSG) { + gStringVar4[0] = CHAR_FIXED_CASE; + StringExpandPlaceholders(gStringVar4+1, str); + } else { + StringExpandPlaceholders(gStringVar4, str); + } AddTextPrinterForMessage(allowSkippingDelayWithButtonPress); CreateTask_DrawFieldMessage(); } diff --git a/src/item.c b/src/item.c index 919235735e..762ace99d0 100644 --- a/src/item.c +++ b/src/item.c @@ -875,11 +875,10 @@ static u16 SanitizeItemId(u16 itemId) const u8 *ItemId_GetName(u16 itemId) { - #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_ITEM_NAMES) - return ROM_MIRROR_PTR(gItems[SanitizeItemId(itemId)].name); - #else - return gItems[SanitizeItemId(itemId)].name; - #endif + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_ITEM_NAMES) + return ROM_MIRROR_PTR(gItems[SanitizeItemId(itemId)].name); + else + return gItems[SanitizeItemId(itemId)].name; } u32 ItemId_GetPrice(u16 itemId) diff --git a/src/main.c b/src/main.c index 293dc78ce2..b4eb2bb29f 100644 --- a/src/main.c +++ b/src/main.c @@ -96,11 +96,10 @@ void AgbMain() *(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup InitGpuRegManager(); // Setup waitstates for all ROM mirrors - #if (DECAP_ENABLED) && (DECAP_MIRRORING) - REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | 0x5B4; // WAITCNT_WSX_S_1 | WAITCNT_WSX_N_3 - #else - REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; - #endif + if (DECAP_ENABLED && DECAP_MIRRORING) + REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | 0x5B4; // WAITCNT_WSX_S_1 | WAITCNT_WSX_N_3 + else + REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; InitKeys(); InitIntrHandlers(); m4aSoundInit(); diff --git a/src/party_menu.c b/src/party_menu.c index 71cbd02532..804e62b77a 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -2745,11 +2745,10 @@ static void PrintMessage(const u8 *text) { DrawStdFrameWithCustomTileAndPalette(WIN_MSG, FALSE, 0x4F, 13); gTextFlags.canABSpeedUpPrint = TRUE; - #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_PARTY_MENU) - AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, MirrorPtr(text), GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); - #else - AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, text, GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); - #endif + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_PARTY_MENU) + AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, MirrorPtr(text), GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); + else + AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, text, GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); } static void PartyMenuDisplayYesNoMenu(void) diff --git a/src/pokemon.c b/src/pokemon.c index 1e9389410e..d42d191279 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2154,10 +2154,10 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } else { - #if (DECAP_ENABLED) && !(DECAP_NICKNAMES) - if (IsStringAddrSafe(data, POKEMON_NAME_LENGTH)) - *data++ = CHAR_FIXED_CASE; - #endif + if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (IsStringAddrSafe(data, POKEMON_NAME_LENGTH)) + *data++ = CHAR_FIXED_CASE; + } retVal = 0; while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH)) { @@ -2520,10 +2520,10 @@ 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; - #endif + if (DECAP_ENABLED && !DECAP_NICKNAMES) { + if (IsStringAddrSafe(data, PLAYER_NAME_LENGTH)) + *data++ = CHAR_FIXED_CASE; + } retVal = 0; while (retVal < PLAYER_NAME_LENGTH) diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 4bb439a17c..1f26035513 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -5548,11 +5548,10 @@ static void InitBoxTitle(u8 boxId) sStorage->wallpaperPalBits |= (1 << 16) << tagIndex; StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH); - #if (DECAP_ENABLED) && (DECAP_MIRRORING) - DrawTextWindowAndBufferTiles(MirrorPtr(sStorage->boxTitleText), sStorage->boxTitleTiles, 0, 0, 2); - #else - DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->boxTitleTiles, 0, 0, 2); - #endif + if (DECAP_ENABLED && DECAP_MIRRORING) + DrawTextWindowAndBufferTiles(MirrorPtr(sStorage->boxTitleText), sStorage->boxTitleTiles, 0, 0, 2); + else + DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->boxTitleTiles, 0, 0, 2); LoadSpriteSheet(&spriteSheet); x = GetBoxTitleBaseX(GetBoxNamePtr(boxId)); @@ -9610,11 +9609,10 @@ struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition) u8 *GetBoxNamePtr(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) - #if (DECAP_ENABLED) && (DECAP_MIRRORING) - return MirrorPtr(gPokemonStoragePtr->boxNames[boxId]); - #else - return gPokemonStoragePtr->boxNames[boxId]; - #endif + if (DECAP_ENABLED && DECAP_MIRRORING) + return MirrorPtr(gPokemonStoragePtr->boxNames[boxId]); + else + return gPokemonStoragePtr->boxNames[boxId]; else return NULL; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index a2b4bd173b..64122472bb 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2819,11 +2819,10 @@ static void ResetWindows(void) static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) { - #if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_SUMMARY) - AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, MirrorPtr(string)); - #else - AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); - #endif + if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_SUMMARY) + AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, MirrorPtr(string)); + else + AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); } static void PrintMonInfo(void) diff --git a/src/region_map.c b/src/region_map.c index dfef070002..36c6ae8fc6 100644 --- a/src/region_map.c +++ b/src/region_map.c @@ -1570,9 +1570,8 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength) u8 *str; u16 i; - #if (DECAP_ENABLED) && !(DECAP_MAP_NAMES) - *dest++ = CHAR_FIXED_CASE; - #endif + if (DECAP_ENABLED && !DECAP_MAP_NAMES) + *dest++ = CHAR_FIXED_CASE; if (regionMapId == MAPSEC_SECRET_BASE) { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 8177e02b0a..27f34fe86a 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1186,13 +1186,13 @@ 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) { + // ignore case-fixing characters in string + if (string[j] == CHAR_FIXED_CASE || string[j] == CHAR_UNFIX_CASE) { + k--; // will be incremented in 'continue' + continue; + } } - #endif if (string[j] != event->pattern[k]) { break; From 8a52316dd29acb127bafcbb570636c7a91b8658b Mon Sep 17 00:00:00 2001 From: Ariel A <24759293+aarant@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:12:04 -0500 Subject: [PATCH 5/6] Reformatted for code review. Moved config to `include/config/decap.h`. --- gflib/string_util.c | 52 +++++++++++++++++------------------- gflib/text.c | 54 ++++++++++++++++++++++---------------- gflib/text.h | 37 +++++--------------------- include/config/decap.h | 35 ++++++++++++++++++++++++ include/constants/global.h | 1 + src/battle_message.c | 21 ++++++++------- src/field_message_box.c | 7 +++-- src/naming_screen.c | 12 ++++----- src/pokemon.c | 10 +++---- test/test_runner_battle.c | 12 ++++----- 10 files changed, 130 insertions(+), 111 deletions(-) create mode 100644 include/config/decap.h 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]) { From 84142acb257cdf77c6e5bf24ce5dd67ef875522b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 26 Jan 2024 10:10:12 -0300 Subject: [PATCH 6/6] Using defines instead of magic number --- src/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index b4eb2bb29f..2d69e62043 100644 --- a/src/main.c +++ b/src/main.c @@ -97,7 +97,10 @@ void AgbMain() InitGpuRegManager(); // Setup waitstates for all ROM mirrors if (DECAP_ENABLED && DECAP_MIRRORING) - REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | 0x5B4; // WAITCNT_WSX_S_1 | WAITCNT_WSX_N_3 + REG_WAITCNT = WAITCNT_PREFETCH_ENABLE + | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3 + | WAITCNT_WS1_S_1 | WAITCNT_WS1_N_3 + | WAITCNT_WS2_S_1 | WAITCNT_WS2_N_3; else REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; InitKeys();