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 DOWN_ARROW = 7A
LEFT_ARROW = 7B LEFT_ARROW = 7B
RIGHT_ARROW = 7C 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 SUPER_E = 84
'<' = 85 '<' = 85
'>' = 86 '>' = 86
@ -347,6 +354,10 @@ ARCHIE = FD 0A
MAXIE = FD 0B MAXIE = FD 0B
KYOGRE = FD 0C KYOGRE = FD 0C
GROUDON = FD 0D 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 @ battle string placeholders

View File

@ -1,11 +1,11 @@
gText_PkmnTransferredSomeonesPC:: 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 "SOMEONE'S PC.\p"
.string "It was placed in \n" .string "It was placed in \n"
.string "BOX “{STR_VAR_1}.”$" .string "BOX “{STR_VAR_1}.”$"
gText_PkmnTransferredLanettesPC:: 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 "LANETTE'S PC.\p"
.string "It was placed in \n" .string "It was placed in \n"
.string "BOX “{STR_VAR_1}.”$" .string "BOX “{STR_VAR_1}.”$"
@ -13,13 +13,13 @@ gText_PkmnTransferredLanettesPC::
gText_PkmnTransferredSomeonesPCBoxFull:: gText_PkmnTransferredSomeonesPCBoxFull::
.string "BOX “{STR_VAR_3}” on\n" .string "BOX “{STR_VAR_3}” on\n"
.string "SOMEONE'S PC was full.\p" .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}.”$" .string "BOX “{STR_VAR_1}.”$"
gText_PkmnTransferredLanettesPCBoxFull:: gText_PkmnTransferredLanettesPCBoxFull::
.string "BOX “{STR_VAR_3}” on\n" .string "BOX “{STR_VAR_3}” on\n"
.string "LANETTE'S PC was full.\p" .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}.”$" .string "BOX “{STR_VAR_1}.”$"
gText_NoMoreRoomForPokemon:: gText_NoMoreRoomForPokemon::

View File

@ -7,14 +7,14 @@ gText_AlreadySavedFile::
.string "Is it okay to overwrite it?$" .string "Is it okay to overwrite it?$"
gText_SavingDontTurnOff:: gText_SavingDontTurnOff::
.string "SAVING…\n" .fixstr "SAVING…\n"
.string "DON'T TURN OFF THE POWER.$" .string "DON'T TURN OFF THE POWER.$"
gText_PlayerSavedGame:: gText_PlayerSavedGame::
.string "{PLAYER} saved the game.$" .string "{PLAYER} saved the game.$"
gText_DifferentSaveFile:: gText_DifferentSaveFile::
.string "WARNING!\p" .string "{FIXED_CASE}WARNING!{UNFIX_CASE}\p"
.string "There is a different game file that\n" .string "There is a different game file that\n"
.string "is already saved.\p" .string "is already saved.\p"
.string "If you save now, the other file's\n" .string "If you save now, the other file's\n"
@ -29,5 +29,5 @@ gText_SaveError::
.string "backup memory.$" .string "backup memory.$"
gText_SavingDontTurnOffPower:: gText_SavingDontTurnOffPower::
.string "SAVING…\n" .fixstr "SAVING…\n"
.string "DON'T TURN OFF THE POWER.$" .string "DON'T TURN OFF THE POWER.$"

View File

@ -77,6 +77,9 @@
#define CHAR_DOWN_ARROW 0x7A #define CHAR_DOWN_ARROW 0x7A
#define CHAR_LEFT_ARROW 0x7B #define CHAR_LEFT_ARROW 0x7B
#define CHAR_RIGHT_ARROW 0x7C #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_SUPER_E 0x84
#define CHAR_LESS_THAN 0x85 #define CHAR_LESS_THAN 0x85
@ -262,6 +265,10 @@
#define PLACEHOLDER_ID_MAXIE 0xB #define PLACEHOLDER_ID_MAXIE 0xB
#define PLACEHOLDER_ID_KYOGRE 0xC #define PLACEHOLDER_ID_KYOGRE 0xC
#define PLACEHOLDER_ID_GROUDON 0xD #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 // battle placeholders are located in battle_message.h

View File

@ -26,11 +26,29 @@ static const s32 sPowersOfTen[] =
1000000000, 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 *StringCopy_Nickname(u8 *dest, const u8 *src)
{ {
u8 i; u32 i;
u32 limit = POKEMON_NAME_LENGTH; 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++) for (i = 0; i < limit; i++)
{ {
dest[i] = src[i]; dest[i] = src[i];
@ -48,6 +66,9 @@ u8 *StringGet_Nickname(u8 *str)
u8 i; u8 i;
u32 limit = POKEMON_NAME_LENGTH; u32 limit = POKEMON_NAME_LENGTH;
if (DECAP_ENABLED && !DECAP_NICKNAMES && *str == CHAR_FIXED_CASE)
str++;
for (i = 0; i < limit; i++) for (i = 0; i < limit; i++)
if (str[i] == EOS) if (str[i] == EOS)
return &str[i]; return &str[i];
@ -61,6 +82,9 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src)
s32 i; s32 i;
s32 limit = PLAYER_NAME_LENGTH; 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++) for (i = 0; i < limit; i++)
{ {
dest[i] = src[i]; dest[i] = src[i];
@ -75,6 +99,10 @@ u8 *StringCopy_PlayerName(u8 *dest, const u8 *src)
u8 *StringCopy(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) while (*src != EOS)
{ {
*dest = *src; *dest = *src;
@ -124,6 +152,14 @@ u16 StringLength(const u8 *str)
s32 StringCompare(const u8 *str1, const u8 *str2) 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) while (*str1 == *str2)
{ {
if (*str1 == EOS) 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) 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) while (*str1 == *str2)
{ {
if (*str1 == EOS) if (*str1 == EOS)
@ -335,6 +379,7 @@ u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8
u8 *StringExpandPlaceholders(u8 *dest, const u8 *src) u8 *StringExpandPlaceholders(u8 *dest, const u8 *src)
{ {
bool32 fixedCase = FALSE;
for (;;) for (;;)
{ {
u8 c = *src++; u8 c = *src++;
@ -345,7 +390,20 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src)
{ {
case PLACEHOLDER_BEGIN: case PLACEHOLDER_BEGIN:
placeholderId = *src++; 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); dest = StringExpandPlaceholders(dest, expandedString);
break; break;
case EXT_CTRL_CODE_BEGIN: case EXT_CTRL_CODE_BEGIN:
@ -372,8 +430,19 @@ u8 *StringExpandPlaceholders(u8 *dest, const u8 *src)
} }
break; break;
case EOS: case EOS:
if (DECAP_ENABLED && fixedCase)
*dest++ = CHAR_UNFIX_CASE;
*dest = EOS; *dest = EOS;
return dest; 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_SCROLL:
case CHAR_PROMPT_CLEAR: case CHAR_PROMPT_CLEAR:
case CHAR_NEWLINE: case CHAR_NEWLINE:
@ -700,6 +769,9 @@ static const u8 *SkipExtCtrlCode(const u8 *s)
s += GetExtCtrlCodeLength(*s); s += GetExtCtrlCodeLength(*s);
} }
while (DECAP_ENABLED && (*s == CHAR_FIXED_CASE || *s == CHAR_UNFIX_CASE))
s++;
return s; return s;
} }

View File

@ -13,6 +13,7 @@ enum StringConvertMode
STR_CONV_MODE_LEADING_ZEROS STR_CONV_MODE_LEADING_ZEROS
}; };
bool32 IsStringAddrSafe(u8 *ptr, u32 length);
u8 *StringCopy_Nickname(u8 *dest, const u8 *src); u8 *StringCopy_Nickname(u8 *dest, const u8 *src);
u8 *StringGet_Nickname(u8 *str); u8 *StringGet_Nickname(u8 *str);
u8 *StringCopy_PlayerName(u8 *dest, const u8 *src); u8 *StringCopy_PlayerName(u8 *dest, const u8 *src);

View File

@ -241,6 +241,35 @@ static void SetFontsPointer(const struct FontInfo *fonts)
gFonts = 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) void DeactivateAllTextPrinters(void)
{ {
int printer; int printer;
@ -268,6 +297,11 @@ u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8
return AddTextPrinter(&printerTemplate, speed, callback); 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)) bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
{ {
int i; int i;
@ -281,6 +315,16 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, voi
sTempTextPrinter.textSpeed = speed; sTempTextPrinter.textSpeed = speed;
sTempTextPrinter.delayCounter = 0; sTempTextPrinter.delayCounter = 0;
sTempTextPrinter.scrollDistance = 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++) for (i = 0; i < (int)ARRAY_COUNT(sTempTextPrinter.subStructFields); i++)
sTempTextPrinter.subStructFields[i] = 0; 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) static u16 RenderText(struct TextPrinter *textPrinter)
{ {
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
u16 currChar; u32 currChar;
u32 lastChar;
s32 width; s32 width;
s32 widthHelper; s32 widthHelper;
@ -962,6 +1026,12 @@ static u16 RenderText(struct TextPrinter *textPrinter)
currChar = *textPrinter->printerTemplate.currentChar; currChar = *textPrinter->printerTemplate.currentChar;
textPrinter->printerTemplate.currentChar++; textPrinter->printerTemplate.currentChar++;
if (DECAP_ENABLED)
{
lastChar = textPrinter->lastChar;
if (lastChar != CHAR_FIXED_CASE)
textPrinter->lastChar = currChar;
}
switch (currChar) switch (currChar)
{ {
@ -1117,7 +1187,47 @@ static u16 RenderText(struct TextPrinter *textPrinter)
textPrinter->printerTemplate.currentX += gCurGlyph.width + textPrinter->printerTemplate.letterSpacing; textPrinter->printerTemplate.currentX += gCurGlyph.width + textPrinter->printerTemplate.letterSpacing;
return RENDER_PRINT; return RENDER_PRINT;
case EOS: case EOS:
if (DECAP_ENABLED)
// Clear fixed case
textPrinter->lastChar = currChar;
return RENDER_FINISH; 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) switch (subStruct->fontId)

View File

@ -7,6 +7,14 @@
// loaded at once but not copied to vram yet. // loaded at once but not copied to vram yet.
#define TEXT_SKIP_DRAW 0xFF #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 { enum {
FONT_SMALL, FONT_SMALL,
FONT_NORMAL, FONT_NORMAL,
@ -92,6 +100,7 @@ struct TextPrinter
u8 scrollDistance; u8 scrollDistance;
u8 minLetterSpacing; // 0x20 u8 minLetterSpacing; // 0x20
u8 japanese; u8 japanese;
u8 lastChar; // used to determine whether to decap strings
}; };
struct FontInfo struct FontInfo
@ -135,6 +144,19 @@ extern TextFlags gTextFlags;
extern u8 gDisableTextPrinters; extern u8 gDisableTextPrinters;
extern struct TextGlyph gCurGlyph; 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); void DeactivateAllTextPrinters(void);
u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)); 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)); 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/item.h"
#include "config/pokemon.h" #include "config/pokemon.h"
#include "config/overworld.h" #include "config/overworld.h"
#include "config/decap.h"
// Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. // 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. // 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 // We define these when using certain IDEs to fool preproc
#define _(x) {x} #define _(x) {x}
#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(...) {0}
#define INCBIN_U8 INCBIN #define INCBIN_U8 INCBIN
#define INCBIN_U16 INCBIN #define INCBIN_U16 INCBIN
@ -1073,4 +1076,6 @@ struct MapPosition
s8 elevation; s8 elevation;
}; };
extern u8 gStackBase[]; // Start of stack-allocated IWRAM
#endif // GUARD_GLOBAL_H #endif // GUARD_GLOBAL_H

View File

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

View File

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

View File

@ -55,6 +55,7 @@ SECTIONS {
data/*.o(COMMON); data/*.o(COMMON);
test/*.o(COMMON); test/*.o(COMMON);
*libc.a:sbrkr.o(COMMON); *libc.a:sbrkr.o(COMMON);
gStackBase = .;
/* .persistent starts at 0x3007F00 */ /* .persistent starts at 0x3007F00 */
/* WARNING: This is the end of the IRQ stack, if there's too /* 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; 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; return toCpy;
} }
@ -3260,7 +3267,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
if (*src == PLACEHOLDER_BEGIN) if (*src == PLACEHOLDER_BEGIN)
{ {
src++; src++;
switch (*src) switch (*src & ~PLACEHOLDER_FIXED_MASK)
{ {
case B_TXT_BUFF1: case B_TXT_BUFF1:
if (gBattleTextBuff1[0] == B_BUFF_PLACEHOLDER_BEGIN) if (gBattleTextBuff1[0] == B_BUFF_PLACEHOLDER_BEGIN)
@ -3635,11 +3642,28 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst)
if (toCpy != NULL) if (toCpy != NULL)
{ {
while (*toCpy != EOS) if (DECAP_ENABLED)
{ {
dst[dstID] = *toCpy; bool32 fixedCase = *src & PLACEHOLDER_FIXED_MASK;
dstID++;
toCpy++; 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)) 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); 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 i, j;
u16 numColumns = columns - 1; u16 numColumns = columns - 1;
if (DECAP_ENABLED && !DECAP_EASY_CHAT)
*dest++ = CHAR_FIXED_CASE;
for (i = 0; i < rows; i++) for (i = 0; i < rows; i++)
{ {
for (j = 0; j < numColumns; j++) for (j = 0; j < numColumns; j++)

View File

@ -117,7 +117,15 @@ bool8 ShowFieldMessageFromBuffer(void)
static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress) 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); AddTextPrinterForMessage(allowSkippingDelayWithButtonPress);
CreateTask_DrawFieldMessage(); 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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) 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) 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 *(vu16 *)BG_PLTT = RGB_WHITE; // Set the backdrop to white on startup
InitGpuRegManager(); 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(); InitKeys();
InitIntrHandlers(); InitIntrHandlers();
m4aSoundInit(); m4aSoundInit();

View File

@ -163,6 +163,11 @@
* - Destroys itself when done. * - 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) #define OPTION_MENU_FLAG (1 << 15)
// Static type declarations // Static type declarations

View File

@ -1856,7 +1856,18 @@ static void SaveInputText(void)
{ {
if (sNamingScreen->textBuffer[i] != CHAR_SPACE && sNamingScreen->textBuffer[i] != EOS) 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; break;
} }
} }
@ -2584,5 +2595,3 @@ static const struct SpritePalette sSpritePalettes[] =
{gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON}, {gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON},
{} {}
}; };

View File

@ -24,6 +24,11 @@
#define tButtonMode data[5] #define tButtonMode data[5]
#define tWindowFrameType data[6] #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 enum
{ {
MENUITEM_TEXTSPEED, MENUITEM_TEXTSPEED,

View File

@ -75,6 +75,12 @@
#include "constants/rgb.h" #include "constants/rgb.h"
#include "constants/songs.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 { enum {
MENU_SUMMARY, MENU_SUMMARY,
MENU_SWITCH, MENU_SWITCH,
@ -2740,7 +2746,10 @@ static void PrintMessage(const u8 *text)
{ {
DrawStdFrameWithCustomTileAndPalette(WIN_MSG, FALSE, 0x4F, 13); DrawStdFrameWithCustomTileAndPalette(WIN_MSG, FALSE, 0x4F, 13);
gTextFlags.canABSpeedUpPrint = TRUE; 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) static void PartyMenuDisplayYesNoMenu(void)

View File

@ -2153,6 +2153,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
} }
else else
{ {
if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, POKEMON_NAME_LENGTH))
*data++ = CHAR_FIXED_CASE;
retVal = 0; retVal = 0;
while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH)) while (retVal < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH))
{ {
@ -2515,6 +2517,8 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
break; break;
case MON_DATA_OT_NAME: case MON_DATA_OT_NAME:
{ {
if (DECAP_ENABLED && !DECAP_NICKNAMES && IsStringAddrSafe(data, PLAYER_NAME_LENGTH))
*data++ = CHAR_FIXED_CASE;
retVal = 0; retVal = 0;
while (retVal < PLAYER_NAME_LENGTH) while (retVal < PLAYER_NAME_LENGTH)

View File

@ -5548,7 +5548,10 @@ static void InitBoxTitle(u8 boxId)
sStorage->wallpaperPalBits |= (1 << 16) << tagIndex; sStorage->wallpaperPalBits |= (1 << 16) << tagIndex;
StringCopyPadded(sStorage->boxTitleText, GetBoxNamePtr(boxId), 0, BOX_NAME_LENGTH); 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); LoadSpriteSheet(&spriteSheet);
x = GetBoxTitleBaseX(GetBoxNamePtr(boxId)); x = GetBoxTitleBaseX(GetBoxNamePtr(boxId));
@ -9606,7 +9609,10 @@ struct BoxPokemon *GetBoxedMonPtr(u8 boxId, u8 boxPosition)
u8 *GetBoxNamePtr(u8 boxId) u8 *GetBoxNamePtr(u8 boxId)
{ {
if (boxId < TOTAL_BOXES_COUNT) 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 else
return NULL; 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) 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) static void PrintMonInfo(void)

View File

@ -1570,6 +1570,9 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength)
u8 *str; u8 *str;
u16 i; u16 i;
if (DECAP_ENABLED && !DECAP_MAP_NAMES)
*dest++ = CHAR_FIXED_CASE;
if (regionMapId == MAPSEC_SECRET_BASE) if (regionMapId == MAPSEC_SECRET_BASE)
{ {
str = GetSecretBaseMapName(dest); str = GetSecretBaseMapName(dest);

View File

@ -48,6 +48,10 @@
#include "constants/rgb.h" #include "constants/rgb.h"
#include "constants/songs.h" #include "constants/songs.h"
#if (DECAP_ENABLED) && (DECAP_MIRRORING) && !(DECAP_START_MENU)
#define AddTextPrinterParameterized (AddTextPrinterFixedCaseParameterized)
#endif
// Menu actions // Menu actions
enum enum
{ {

View File

@ -2,6 +2,7 @@
#include "strings.h" #include "strings.h"
#include "battle_pyramid_bag.h" #include "battle_pyramid_bag.h"
#include "item_menu.h" #include "item_menu.h"
#include "text.h"
ALIGNED(4) ALIGNED(4)
const u8 gText_ExpandedPlaceholder_Empty[] = _(""); const u8 gText_ExpandedPlaceholder_Empty[] = _("");
@ -173,7 +174,7 @@ const u8 gText_Number[] = _("No. ");
const u8 gText_Level[] = _("Lv. "); const u8 gText_Level[] = _("Lv. ");
const u8 gText_IdNumberSlash[] = _("IDNo. /"); // Unused const u8 gText_IdNumberSlash[] = _("IDNo. /"); // Unused
const u8 gText_Name[] = _("NAME"); 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_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_ConfirmStarterChoice[] = _("Do you choose this POKéMON?");
const u8 gText_Pokemon4[] = _("POKéMON"); // Unused 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_Speed2[] = _("SPEED");
const u8 gText_HP4[] = _("HP"); const u8 gText_HP4[] = _("HP");
const u8 gText_EmptyString8[] = _(""); // Unused 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_RentalPkmn[] = _("RENTAL POKéMON");
const u8 gText_TypeSlash[] = _("TYPE/"); const u8 gText_TypeSlash[] = _("TYPE/");
const u8 gText_Power[] = _("POWER"); const u8 gText_Power[] = _("POWER");
@ -517,7 +518,7 @@ const u8 gText_Switch[] = _("SWITCH");
const u8 gText_PkmnInfo[] = _("POKéMON INFO"); const u8 gText_PkmnInfo[] = _("POKéMON INFO");
const u8 gText_PkmnSkills[] = _("POKéMON SKILLS"); const u8 gText_PkmnSkills[] = _("POKéMON SKILLS");
const u8 gText_BattleMoves[] = _("BATTLE MOVES"); 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_Info[] = _("INFO");
const u8 gText_EggWillTakeALongTime[] = _("It looks like this EGG will\ntake a long time to hatch."); 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."); 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_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_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.”"); 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_Ferry[] = _("FERRY");
const u8 gText_SecretBase[] = _("SECRET BASE"); const u8 gText_SecretBase[] = _("SECRET BASE");
const u8 gText_Hideout[] = _("HIDEOUT"); const u8 gText_Hideout[] = _("HIDEOUT");
#endif
const u8 gText_ResetRTCConfirmCancel[] = _("Reset RTC?\nA: Confirm, B: Cancel"); const u8 gText_ResetRTCConfirmCancel[] = _("Reset RTC?\nA: Confirm, B: Cancel");
const u8 gText_PresentTime[] = _("Present time in game"); const u8 gText_PresentTime[] = _("Present time in game");
const u8 gText_PreviousTime[] = _("Previous 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 #endif
const u8 gText_Marco[] = _("MARCO"); const u8 gText_Marco[] = _("MARCO");
const u8 gText_TrainerCardName[] = _("NAME: "); const u8 gText_TrainerCardName[] = _("NAME: ");
const u8 gText_TrainerCardIDNo[] = _("IDNo."); const u8 gText_TrainerCardIDNo[] = _C("IDNo.");
const u8 gText_TrainerCardMoney[] = _("MONEY"); const u8 gText_TrainerCardMoney[] = _("MONEY");
const u8 gText_PokeDollar[] = _("¥"); // Unused const u8 gText_PokeDollar[] = _("¥"); // Unused
const u8 gText_TrainerCardPokedex[] = _("POKéDEX"); const u8 gText_TrainerCardPokedex[] = _("POKéDEX");

View File

@ -12,6 +12,7 @@
#include "random.h" #include "random.h"
#include "test/battle.h" #include "test/battle.h"
#include "window.h" #include "window.h"
#include "text.h"
#include "constants/trainers.h" #include "constants/trainers.h"
#if defined(__INTELLISENSE__) #if defined(__INTELLISENSE__)
@ -1185,6 +1186,13 @@ static s32 TryMessage(s32 i, s32 n, const u8 *string)
if (string[j] == CHAR_PROMPT_CLEAR) if (string[j] == CHAR_PROMPT_CLEAR)
j++; 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]) if (string[j] != event->pattern[k])
{ {
break; break;

View File

@ -170,6 +170,8 @@ Directive AsmFile::GetDirective()
if (CheckForDirective(".include")) if (CheckForDirective(".include"))
return Directive::Include; return Directive::Include;
else if (CheckForDirective(".fixstr"))
return Directive::FixedString;
else if (CheckForDirective(".string")) else if (CheckForDirective(".string"))
return Directive::String; return Directive::String;
else if (CheckForDirective(".braille")) else if (CheckForDirective(".braille"))

View File

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

View File

@ -195,6 +195,7 @@ void CFile::TryConvertString()
long oldPos = m_pos; long oldPos = m_pos;
long oldLineNum = m_lineNum; long oldLineNum = m_lineNum;
bool noTerminator = false; bool noTerminator = false;
bool fixedCase = false;
if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1]))) if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1])))
return; return;
@ -207,6 +208,13 @@ void CFile::TryConvertString()
m_pos++; m_pos++;
} }
// Fixed-case string
if (m_buffer[m_pos] == 'C')
{
fixedCase = true;
m_pos++;
}
SkipWhitespace(); SkipWhitespace();
if (m_buffer[m_pos] != '(') if (m_buffer[m_pos] != '(')
@ -234,7 +242,13 @@ void CFile::TryConvertString()
try 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) catch (std::runtime_error& e)
{ {

View File

@ -76,6 +76,16 @@ void PreprocAsmFile(std::string filename)
PrintAsmBytes(s, length); PrintAsmBytes(s, length);
break; 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: case Directive::Braille:
{ {
unsigned char s[kMaxStringLength]; unsigned char s[kMaxStringLength];