Guillotine: Decapitalize everything (#3949)

This commit is contained in:
Eduardo Quezada D'Ottone 2024-01-26 10:30:50 -03:00 committed by GitHub
commit e2d70d440e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 439 additions and 35 deletions

View File

@ -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

View File

@ -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::

View File

@ -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.$"

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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));

35
include/config/decap.h Normal file
View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -56,6 +56,7 @@ SECTIONS {
/* COMMON starts at 0x30022A8 */
INCLUDE "sym_common.ld"
*libc.a:sbrkr.o(COMMON);
gStackBase = .;
} > IWRAM
/* BEGIN ROM DATA */

View File

@ -52,6 +52,7 @@ SECTIONS {
gflib/*.o(COMMON);
*libc.a:*.o(COMMON);
*libnosys.a:*.o(COMMON);
gStackBase = .;
} > IWRAM
/* BEGIN ROM DATA */

View File

@ -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

View File

@ -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++;
}
}

View File

@ -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++)

View File

@ -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();
}

View File

@ -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,

View File

@ -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)

View File

@ -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();

View File

@ -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

View File

@ -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},
{}
};

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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
{

View File

@ -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");

View File

@ -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;

View File

@ -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"))

View File

@ -31,6 +31,7 @@ enum class Directive
Include,
String,
Braille,
FixedString,
Unknown
};

View File

@ -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)
{

View File

@ -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];