diff --git a/charmap.txt b/charmap.txt index c26c07e78b..7271c621ca 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 @@ -347,6 +354,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 f4a20d118a..9aef8d543a 100644 --- a/gflib/string_util.c +++ b/gflib/string_util.c @@ -26,11 +26,29 @@ static const s32 sPowersOfTen[] = 1000000000, }; +// 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); +} + 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++; + } + for (i = 0; i < limit; i++) { dest[i] = src[i]; @@ -48,6 +66,9 @@ u8 *StringGet_Nickname(u8 *str) u8 i; u32 limit = POKEMON_NAME_LENGTH; + if (DECAP_ENABLED && !DECAP_NICKNAMES && *str == CHAR_FIXED_CASE) + str++; + for (i = 0; i < limit; i++) if (str[i] == EOS) return &str[i]; @@ -61,6 +82,9 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src) s32 i; s32 limit = PLAYER_NAME_LENGTH; + if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(dest, limit) && *src != CHAR_FIXED_CASE) + *dest++ = CHAR_FIXED_CASE; + for (i = 0; i < limit; i++) { dest[i] = src[i]; @@ -75,6 +99,10 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src) u8 *StringCopy(u8 *dest, const u8 *src) { + // 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; @@ -124,6 +152,14 @@ 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++; + } while (*str1 == *str2) { if (*str1 == EOS) @@ -137,6 +173,14 @@ 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++; + } while (*str1 == *str2) { if (*str1 == EOS) @@ -335,6 +379,7 @@ u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) { + bool32 fixedCase = FALSE; for (;;) { u8 c = *src++; @@ -345,7 +390,20 @@ 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; + } + } + expandedString = GetExpandedPlaceholder(placeholderId & ~PLACEHOLDER_FIXED_MASK); dest = StringExpandPlaceholders(dest, expandedString); break; case EXT_CTRL_CODE_BEGIN: @@ -372,8 +430,19 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) } break; case EOS: + if (DECAP_ENABLED && fixedCase) + *dest++ = CHAR_UNFIX_CASE; *dest = EOS; return dest; + #if DECAP_ENABLED + case CHAR_UNFIX_CASE: + fixedCase = FALSE; + *dest++ = c; + break; + case CHAR_FIXED_CASE: + fixedCase = TRUE; + // fallthrough + #endif case CHAR_PROMPT_SCROLL: case CHAR_PROMPT_CLEAR: case CHAR_NEWLINE: @@ -700,6 +769,9 @@ static const u8 *SkipExtCtrlCode(const u8 *s) s += GetExtCtrlCodeLength(*s); } + while (DECAP_ENABLED && (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE)) + s++; + return s; } diff --git a/gflib/string_util.h b/gflib/string_util.h index 9e5dfffd57..75d2ada0a3 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..139fd5ee1a 100644 --- a/gflib/text.c +++ b/gflib/text.c @@ -241,6 +241,35 @@ static void SetFontsPointer(const struct FontInfo *fonts) gFonts = fonts; } +// 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) +{ + u32 value = (u32) ptr; + 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 (IS_ROM_PTR(ptr)) + return ((u32)ptr) & ROM_MIRROR_MASK; + else + return ((u32)ptr) & RAM_MIRROR_MASK; +} + void DeactivateAllTextPrinters(void) { int printer; @@ -268,6 +297,11 @@ 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)) +{ + return AddTextPrinterParameterized(windowId, fontId, MirrorPtr(str), x, y, speed, callback); +} + bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { int i; @@ -281,6 +315,16 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, voi sTempTextPrinter.textSpeed = speed; sTempTextPrinter.delayCounter = 0; sTempTextPrinter.scrollDistance = 0; + 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; + } for (i = 0; i < (int)ARRAY_COUNT(sTempTextPrinter.subStructFields); i++) sTempTextPrinter.subStructFields[i] = 0; @@ -931,10 +975,30 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c } } +// 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] = 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, + [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, +}; + static u16 RenderText(struct TextPrinter *textPrinter) { struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); - u16 currChar; + u32 currChar; + u32 lastChar; s32 width; s32 widthHelper; @@ -962,6 +1026,12 @@ 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; + } switch (currChar) { @@ -1117,7 +1187,47 @@ 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; + #if DECAP_ENABLED + // 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; + #endif + } + + // If not Japanese or fixed case, try to decap + 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); } switch (subStruct->fontId) diff --git a/gflib/text.h b/gflib/text.h index ff13efa9a8..18af356f3d 100644 --- a/gflib/text.h +++ b/gflib/text.h @@ -7,6 +7,14 @@ // loaded at once but not copied to vram yet. #define TEXT_SKIP_DRAW 0xFF +// See include/config/decap.h for decap configuration +#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 + enum { FONT_SMALL, FONT_NORMAL, @@ -92,6 +100,7 @@ struct TextPrinter u8 scrollDistance; u8 minLetterSpacing; // 0x20 u8 japanese; + u8 lastChar; // used to determine whether to decap strings }; struct FontInfo @@ -135,6 +144,19 @@ extern TextFlags gTextFlags; extern u8 gDisableTextPrinters; extern struct TextGlyph gCurGlyph; +extern const u16 gLowercaseDiffTable[]; +// 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); +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)); + 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/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 6a2713c3d6..d2a72d235d 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/include/global.h b/include/global.h index bb0786c86b..e9756df212 100644 --- a/include/global.h +++ b/include/global.h @@ -30,6 +30,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 @@ -1073,4 +1076,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 e9cbae8957..f54272499c 100644 --- a/ld_script.ld +++ b/ld_script.ld @@ -56,6 +56,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 fd35a1ca31..d5724ebf84 100644 --- a/ld_script_modern.ld +++ b/ld_script_modern.ld @@ -52,6 +52,7 @@ SECTIONS { gflib/*.o(COMMON); *libc.a:*.o(COMMON); *libnosys.a:*.o(COMMON); + gStackBase = .; } > IWRAM /* BEGIN ROM DATA */ diff --git a/ld_script_test.ld b/ld_script_test.ld index ec99609a7e..e05971dfde 100644 --- a/ld_script_test.ld +++ b/ld_script_test.ld @@ -55,6 +55,7 @@ SECTIONS { data/*.o(COMMON); test/*.o(COMMON); *libc.a:sbrkr.o(COMMON); + gStackBase = .; /* .persistent starts at 0x3007F00 */ /* WARNING: This is the end of the IRQ stack, if there's too diff --git a/src/battle_message.c b/src/battle_message.c index f2826c2b96..227b300f18 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3204,6 +3204,13 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battler) break; } + 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; } @@ -3260,7 +3267,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) @@ -3635,11 +3642,28 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) if (toCpy != NULL) { - while (*toCpy != EOS) + if (DECAP_ENABLED) { - dst[dstID] = *toCpy; - dstID++; - toCpy++; + 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 + { + while (*toCpy != EOS) + dst[dstID++] = *toCpy++; } } diff --git a/src/easy_chat.c b/src/easy_chat.c index db5bbba8a8..7968efb17e 100644 --- a/src/easy_chat.c +++ b/src/easy_chat.c @@ -3954,6 +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); AddTextPrinterParameterized(windowId, fontId, str, x, y, speed, callback); } @@ -5240,6 +5242,9 @@ 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; + 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..46f0f2898d 100755 --- a/src/field_message_box.c +++ b/src/field_message_box.c @@ -117,7 +117,15 @@ bool8 ShowFieldMessageFromBuffer(void) static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress) { - StringExpandPlaceholders(gStringVar4, str); + 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/fonts.c b/src/fonts.c index 381fd795e9..72de054b73 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 e102af7266..4e4e78345a 100644 --- a/src/item.c +++ b/src/item.c @@ -866,7 +866,10 @@ static u16 SanitizeItemId(u16 itemId) const u8 *ItemId_GetName(u16 itemId) { - return gItems[SanitizeItemId(itemId)].name; + 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 19b8f19976..c56c796eac 100644 --- a/src/main.c +++ b/src/main.c @@ -95,7 +95,14 @@ void AgbMain() { *(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup InitGpuRegManager(); - REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3; + // Setup waitstates for all ROM mirrors + if (DECAP_ENABLED && DECAP_MIRRORING) + 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(); InitIntrHandlers(); m4aSoundInit(); diff --git a/src/main_menu.c b/src/main_menu.c index c1f092b2ca..6a225bf97d 100644 --- a/src/main_menu.c +++ b/src/main_menu.c @@ -163,6 +163,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 0f2e2dbb7f..848e5b1a48 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 + && (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 + 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 07e4b405ff..57a4234183 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -75,6 +75,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, @@ -2740,7 +2746,10 @@ static void PrintMessage(const u8 *text) { DrawStdFrameWithCustomTileAndPalette(WIN_MSG, FALSE, 0x4F, 13); gTextFlags.canABSpeedUpPrint = TRUE; - AddTextPrinterParameterized2(WIN_MSG, FONT_NORMAL, text, GetPlayerTextSpeedDelay(), 0, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); + 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 7a77ea9c17..0c445ab307 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2153,6 +2153,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } else { + 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)) { @@ -2515,6 +2517,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_OT_NAME: { + if (DECAP_ENABLED && !DECAP_NICKNAMES && 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 fcf66ccea8..2fa88b4db5 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -5548,7 +5548,10 @@ static void InitBoxTitle(u8 boxId) sStorage->wallpaperPalBits |= (1 << 16) << tagIndex; StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH); - DrawTextWindowAndBufferTiles(sStorage->boxTitleText, sStorage->boxTitleTiles, 0, 0, 2); + 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)); @@ -9606,7 +9609,10 @@ struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition) u8 *GetBoxNamePtr(u8 boxId) { if (boxId < TOTAL_BOXES_COUNT) - return gPokemonStoragePtr->boxNames[boxId]; + 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 41c4f9f76d..64122472bb 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2819,7 +2819,10 @@ static void ResetWindows(void) static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) { - AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); + 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 8936e668ac..36c6ae8fc6 100644 --- a/src/region_map.c +++ b/src/region_map.c @@ -1570,6 +1570,9 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength) u8 *str; u16 i; + if (DECAP_ENABLED && !DECAP_MAP_NAMES) + *dest++ = CHAR_FIXED_CASE; + if (regionMapId == MAPSEC_SECRET_BASE) { str = GetSecretBaseMapName(dest); diff --git a/src/start_menu.c b/src/start_menu.c index 0525ccf70d..857eed168d 100644 --- a/src/start_menu.c +++ b/src/start_menu.c @@ -48,6 +48,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 565f0a8f62..35f610017a 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 @@ -500,7 +501,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"); @@ -517,7 +518,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."); @@ -1103,9 +1104,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"); @@ -1203,7 +1210,7 @@ const u8 gText_PkmnFainted_FldPsn[] = _("{STR_VAR_1} survived the poisoning.\nTh #endif 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/test/test_runner_battle.c b/test/test_runner_battle.c index caccdb2042..043e93b427 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 && (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]) { break; 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];