summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--berry_fix/charmap.txt2
-rw-r--r--berry_fix/payload/charmap.txt2
-rw-r--r--charmap.txt2
-rwxr-xr-xinclude/pokemon_summary_screen.h4
-rw-r--r--src/battle_anim_mons.c2
-rw-r--r--src/battle_message.c10
-rw-r--r--src/easy_chat.c10
-rw-r--r--src/graphics.c4
-rw-r--r--src/pokemon_summary_screen.c42
-rw-r--r--src/title_screen.c5
-rw-r--r--tools/gbagfx/convert_png.c2
-rw-r--r--tools/gbagfx/gfx.c163
-rw-r--r--tools/gbagfx/gfx.h17
-rw-r--r--tools/gbagfx/main.c35
-rw-r--r--tools/gbagfx/options.h4
15 files changed, 263 insertions, 41 deletions
diff --git a/berry_fix/charmap.txt b/berry_fix/charmap.txt
index a736b40f2..94eabe8bc 100644
--- a/berry_fix/charmap.txt
+++ b/berry_fix/charmap.txt
@@ -414,7 +414,7 @@ SIZE = FC 06 @ note that anything other than "SMALL" is invalid
UNKNOWN_7 = FC 07
PAUSE = FC 08 @ manually print the wait byte after this, havent mapped them
PAUSE_UNTIL_PRESS = FC 09
-UNKNOWN_A = FC 0A
+WAIT_SE = FC 0A
PLAY_BGM = FC 0B
ESCAPE = FC 0C
SHIFT_TEXT = FC 0D
diff --git a/berry_fix/payload/charmap.txt b/berry_fix/payload/charmap.txt
index a736b40f2..94eabe8bc 100644
--- a/berry_fix/payload/charmap.txt
+++ b/berry_fix/payload/charmap.txt
@@ -414,7 +414,7 @@ SIZE = FC 06 @ note that anything other than "SMALL" is invalid
UNKNOWN_7 = FC 07
PAUSE = FC 08 @ manually print the wait byte after this, havent mapped them
PAUSE_UNTIL_PRESS = FC 09
-UNKNOWN_A = FC 0A
+WAIT_SE = FC 0A
PLAY_BGM = FC 0B
ESCAPE = FC 0C
SHIFT_TEXT = FC 0D
diff --git a/charmap.txt b/charmap.txt
index c449d12b0..3a34bada5 100644
--- a/charmap.txt
+++ b/charmap.txt
@@ -418,7 +418,7 @@ SIZE = FC 06 @ note that anything other than "SMALL" is invalid
UNKNOWN_7 = FC 07
PAUSE = FC 08 @ manually print the wait byte after this, havent mapped them
PAUSE_UNTIL_PRESS = FC 09
-UNKNOWN_A = FC 0A
+WAIT_SE = FC 0A
PLAY_BGM = FC 0B
ESCAPE = FC 0C
SHIFT_TEXT = FC 0D
diff --git a/include/pokemon_summary_screen.h b/include/pokemon_summary_screen.h
index 1c7510db6..6413dcdec 100755
--- a/include/pokemon_summary_screen.h
+++ b/include/pokemon_summary_screen.h
@@ -9,10 +9,10 @@ extern const u8 *const gMoveDescriptionPointers[];
extern const u8 *const gNatureNamePointers[];
void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void));
-void ShowSelectMovePokemonSummaryScreen(struct Pokemon *, u8, u8, MainCallback, u16);
+void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void), u16 newMove);
void ShowPokemonSummaryScreenSet40EF(u8 mode, struct BoxPokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void));
u8 GetMoveSlotToReplace(void);
-void SummaryScreen_SetUnknownTaskId(u8 a0);
+void SummaryScreen_SetUnknownTaskId(u8 taskId);
void SummaryScreen_DestroyUnknownTask(void);
// The Pokemon Summary Screen can operate in different modes. Certain features,
diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c
index 861c5f6ca..e5a866999 100644
--- a/src/battle_anim_mons.c
+++ b/src/battle_anim_mons.c
@@ -1743,7 +1743,7 @@ void PrepareAffineAnimInTaskData(struct Task *task, u8 spriteId, const union Aff
bool8 RunAffineAnimFromTaskData(struct Task *task)
{
- gAnimTaskAffineAnim = LoadPointerFromVars(task->data[13], task->data[14]) + (task->data[7] << 3);
+ gAnimTaskAffineAnim = &((union AffineAnimCmd *)LoadPointerFromVars(task->data[13], task->data[14]))[task->data[7]];
switch (gAnimTaskAffineAnim->type)
{
default:
diff --git a/src/battle_message.c b/src/battle_message.c
index f60527d7a..efa4500c8 100644
--- a/src/battle_message.c
+++ b/src/battle_message.c
@@ -58,8 +58,8 @@ static const u8 sText_Trainer1LoseText[] = _("{B_TRAINER1_LOSE_TEXT}");
static const u8 sText_PkmnGainedEXP[] = _("{B_BUFF1} gained{B_BUFF2}\n{B_BUFF3} EXP. Points!\p");
static const u8 sText_EmptyString4[] = _("");
static const u8 sText_ABoosted[] = _(" a boosted");
-static const u8 sText_PkmnGrewToLv[] = _("{B_BUFF1} grew to\nLV. {B_BUFF2}!{UNKNOWN_A}\p");
-static const u8 sText_PkmnLearnedMove[] = _("{B_BUFF1} learned\n{B_BUFF2}!{UNKNOWN_A}\p");
+static const u8 sText_PkmnGrewToLv[] = _("{B_BUFF1} grew to\nLV. {B_BUFF2}!{WAIT_SE}\p");
+static const u8 sText_PkmnLearnedMove[] = _("{B_BUFF1} learned\n{B_BUFF2}!{WAIT_SE}\p");
static const u8 sText_TryToLearnMove1[] = _("{B_BUFF1} is trying to\nlearn {B_BUFF2}.\p");
static const u8 sText_TryToLearnMove2[] = _("But, {B_BUFF1} can't learn\nmore than four moves.\p");
static const u8 sText_TryToLearnMove3[] = _("Delete a move to make\nroom for {B_BUFF2}?");
@@ -464,8 +464,8 @@ static const u8 sText_PkmnBrokeFree[] = _("Oh, no!\nThe POKéMON broke free!");
static const u8 sText_ItAppearedCaught[] = _("Aww!\nIt appeared to be caught!");
static const u8 sText_AarghAlmostHadIt[] = _("Aargh!\nAlmost had it!");
static const u8 sText_ShootSoClose[] = _("Shoot!\nIt was so close, too!");
-static const u8 sText_GotchaPkmnCaught[] = _("Gotcha!\n{B_OPPONENT_MON1_NAME} was caught!{UNKNOWN_A}{PLAY_BGM MUS_KACHI22}\p");
-static const u8 sText_GotchaPkmnCaught2[] = _("Gotcha!\n{B_OPPONENT_MON1_NAME} was caught!{UNKNOWN_A}{PLAY_BGM MUS_KACHI22}{PAUSE 127}");
+static const u8 sText_GotchaPkmnCaught[] = _("Gotcha!\n{B_OPPONENT_MON1_NAME} was caught!{WAIT_SE}{PLAY_BGM MUS_KACHI22}\p");
+static const u8 sText_GotchaPkmnCaught2[] = _("Gotcha!\n{B_OPPONENT_MON1_NAME} was caught!{WAIT_SE}{PLAY_BGM MUS_KACHI22}{PAUSE 127}");
static const u8 sText_GiveNicknameCaptured[] = _("Give a nickname to the\ncaptured {B_OPPONENT_MON1_NAME}?");
static const u8 sText_PkmnSentToPC[] = _("{B_OPPONENT_MON1_NAME} was sent to\n{B_PC_CREATOR_NAME} PC.");
static const u8 sText_Someones[] = _("someone's");
@@ -1150,7 +1150,7 @@ const u16 gTrappingMoves[] =
};
const u8 gText_PkmnIsEvolving[] = _("What?\n{STR_VAR_1} is evolving!");
-const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{UNKNOWN_A}\p");
+const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{WAIT_SE}\p");
const u8 gText_PkmnStoppedEvolving[] = _("Huh? {STR_VAR_1}\nstopped evolving!\p");
const u8 gText_EllipsisQuestionMark[] = _("……?\p");
const u8 gText_WhatWillPkmnDo[] = _("What will\n{B_ACTIVE_NAME_WITH_PREFIX} do?");
diff --git a/src/easy_chat.c b/src/easy_chat.c
index 05e366420..1bdb3fdc0 100644
--- a/src/easy_chat.c
+++ b/src/easy_chat.c
@@ -2794,7 +2794,7 @@ static bool8 sub_811BFA4(void)
DeactivateAllTextPrinters();
sub_811CF64();
sub_811CF04();
- CpuFastFill(0, (void *)VRAM + 0x1000000, 0x400);
+ CpuFastFill(0, (void *)OAM, OAM_SIZE);
break;
case 1:
DecompressAndLoadBgGfxUsingHeap(3, gEasyChatWindow_Gfx, 0, 0, 0);
@@ -2828,8 +2828,12 @@ static bool8 sub_811BFA4(void)
else
{
sub_811DE5C(0, 0, 0, 0);
- SetGpuReg(REG_OFFSET_WININ, WIN_RANGE(0, 63));
- SetGpuReg(REG_OFFSET_WINOUT, WIN_RANGE(0, 59));
+ SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR);
+ SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG0
+ | WINOUT_WIN01_BG1
+ | WINOUT_WIN01_BG3
+ | WINOUT_WIN01_OBJ
+ | WINOUT_WIN01_CLR);
ShowBg(3);
ShowBg(1);
ShowBg(2);
diff --git a/src/graphics.c b/src/graphics.c
index d8222cfae..7a3a225c2 100644
--- a/src/graphics.c
+++ b/src/graphics.c
@@ -1541,8 +1541,8 @@ const u16 gMonIconPalettes[][16] =
INCBIN_U16("graphics/pokemon/icon_palettes/icon_palette_2.gbapal"),
};
-const u16 gTitleScreenBgPalettes[] = INCBIN_U16("graphics/title_screen/pokemon_logo.gbapal");
-const u16 gTitleScreenBgPalettes2[] = INCBIN_U16("graphics/title_screen/rayquaza_and_clouds.gbapal");
+const u16 gTitleScreenBgPalettes[] = INCBIN_U16("graphics/title_screen/pokemon_logo.gbapal",
+ "graphics/title_screen/rayquaza_and_clouds.gbapal");
const u16 gTitleScreenEmeraldVersionPal[] = INCBIN_U16("graphics/title_screen/emerald_version.gbapal");
diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c
index 1cdaf3422..fc0e432bd 100644
--- a/src/pokemon_summary_screen.c
+++ b/src/pokemon_summary_screen.c
@@ -296,7 +296,7 @@ static void CreateMoveSelectorSprites(u8 idArrayStart);
static void SpriteCb_MoveSelector(struct Sprite *sprite);
static void DestroyMoveSelectorSprites(u8 firstArrayId);
static void SetMainMoveSelectorColor(u8 whichColor);
-static void MakeMoveSelectorVisible(u8 a);
+static void KeepMoveSelectorVisible(u8 firstSpriteId);
// const rom data
#include "data/text/move_descriptions.h"
@@ -1095,7 +1095,7 @@ void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex,
}
sMonSummaryScreen->currPageIndex = sMonSummaryScreen->minPageIndex;
- SummaryScreen_SetUnknownTaskId(-1);
+ SummaryScreen_SetUnknownTaskId(0xFF);
if (gMonSpritesGfxPtr == NULL)
sub_806F2AC(0, 0);
@@ -1975,16 +1975,16 @@ static void ChangeSelectedMove(s16 *taskData, s8 direction, u8 *moveIndexPtr)
}
*moveIndexPtr = newMoveIndex;
- // Not sure what the purpose of this function is, seems to have no effect whatsoever.
+ // Get rid of the 'flicker' effect(while idle) when scrolling.
if (moveIndexPtr == &sMonSummaryScreen->firstMoveIndex)
- MakeMoveSelectorVisible(SPRITE_ARR_ID_MOVE_SELECTOR1);
+ KeepMoveSelectorVisible(SPRITE_ARR_ID_MOVE_SELECTOR1);
else
- MakeMoveSelectorVisible(SPRITE_ARR_ID_MOVE_SELECTOR2);
+ KeepMoveSelectorVisible(SPRITE_ARR_ID_MOVE_SELECTOR2);
}
static void CloseMoveSelectMode(u8 taskId)
{
- DestroyMoveSelectorSprites(8);
+ DestroyMoveSelectorSprites(SPRITE_ARR_ID_MOVE_SELECTOR1);
ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_SWITCH);
PutWindowTilemap(PSS_LABEL_WINDOW_PROMPT_INFO);
PrintMoveDetails(0);
@@ -2678,14 +2678,10 @@ static void ResetWindows(void)
InitWindows(sSummaryTemplate);
DeactivateAllTextPrinters();
- for (i = 0; i < 20; i++)
- {
+ for (i = 0; i < PSS_LABEL_WINDOW_END; i++)
FillWindowPixelBuffer(i, PIXEL_FILL(0));
- }
for (i = 0; i < ARRAY_COUNT(sMonSummaryScreen->windowIds); i++)
- {
sMonSummaryScreen->windowIds[i] = 0xFF;
- }
}
static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId)
@@ -3299,7 +3295,7 @@ static void Task_PrintSkillsPage(u8 taskId)
static void PrintHeldItemName(void)
{
const u8 *text;
- int offset;
+ int x;
if (sMonSummaryScreen->summary.item == ITEM_ENIGMA_BERRY
&& IsMultiBattle() == TRUE
@@ -3317,14 +3313,14 @@ static void PrintHeldItemName(void)
text = gStringVar1;
}
- offset = GetStringCenterAlignXOffset(1, text, 72) + 6;
- PrintTextOnWindow(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_HELD_ITEM), text, offset, 1, 0, 0);
+ x = GetStringCenterAlignXOffset(1, text, 72) + 6;
+ PrintTextOnWindow(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_HELD_ITEM), text, x, 1, 0, 0);
}
static void PrintRibbonCount(void)
{
const u8 *text;
- int offset;
+ int x;
if (sMonSummaryScreen->summary.ribbonCount == 0)
{
@@ -3337,8 +3333,8 @@ static void PrintRibbonCount(void)
text = gStringVar4;
}
- offset = GetStringCenterAlignXOffset(1, text, 70) + 6;
- PrintTextOnWindow(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_RIBBON_COUNT), text, offset, 1, 0, 0);
+ x = GetStringCenterAlignXOffset(1, text, 70) + 6;
+ PrintTextOnWindow(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_RIBBON_COUNT), text, x, 1, 0, 0);
}
static void BufferLeftColumnStats(void)
@@ -3799,7 +3795,7 @@ static void SetMoveTypeIcons(void)
if (summary->moves[i] != MOVE_NONE)
SetTypeSpritePosAndPal(gBattleMoves[summary->moves[i]].type, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE);
else
- SetSpriteInvisibility(i + 3, TRUE);
+ SetSpriteInvisibility(i + SPRITE_ARR_ID_TYPE, TRUE);
}
}
@@ -3812,7 +3808,7 @@ static void SetContestMoveTypeIcons(void)
if (summary->moves[i] != MOVE_NONE)
SetTypeSpritePosAndPal(NUMBER_OF_MON_TYPES + gContestMoves[summary->moves[i]].contestCategory, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE);
else
- SetSpriteInvisibility(i + 3, TRUE);
+ SetSpriteInvisibility(i + SPRITE_ARR_ID_TYPE, TRUE);
}
}
@@ -3938,9 +3934,9 @@ static void SpriteCB_Pokemon(struct Sprite *sprite)
}
}
-void SummaryScreen_SetUnknownTaskId(u8 a0)
+void SummaryScreen_SetUnknownTaskId(u8 taskId)
{
- sUnknownTaskId = a0;
+ sUnknownTaskId = taskId;
}
void SummaryScreen_DestroyUnknownTask(void)
@@ -4041,7 +4037,7 @@ static void CreateMoveSelectorSprites(u8 idArrayStart)
if (idArrayStart == SPRITE_ARR_ID_MOVE_SELECTOR1)
subpriority = 1;
- for (i = 0; i < 10; i++)
+ for (i = 0; i < MOVE_SELECTOR_SPRITES_COUNT; i++)
{
spriteIds[i] = CreateSprite(&sMoveSelectorSpriteTemplate, i * 16 + 89, 40, subpriority);
if (i == 0)
@@ -4104,7 +4100,7 @@ static void SetMainMoveSelectorColor(u8 which)
}
}
-static void MakeMoveSelectorVisible(u8 firstSpriteId)
+static void KeepMoveSelectorVisible(u8 firstSpriteId)
{
u8 i;
u8 *spriteIds = &sMonSummaryScreen->spriteIds[firstSpriteId];
diff --git a/src/title_screen.c b/src/title_screen.c
index 600dd18c8..aa91351df 100644
--- a/src/title_screen.c
+++ b/src/title_screen.c
@@ -546,11 +546,14 @@ void CB2_InitTitleScreen(void)
gMain.state = 1;
break;
case 1:
- LZ77UnCompVram(gTitleScreenPokemonLogoGfx, (void *)VRAM);
+ // bg2
+ LZ77UnCompVram(gTitleScreenPokemonLogoGfx, (void *)(BG_CHAR_ADDR(0)));
LZ77UnCompVram(gUnknown_08DE0644, (void *)(BG_SCREEN_ADDR(9)));
LoadPalette(gTitleScreenBgPalettes, 0, 0x1E0);
+ // bg3
LZ77UnCompVram(sTitleScreenRayquazaGfx, (void *)(BG_CHAR_ADDR(2)));
LZ77UnCompVram(sTitleScreenRayquazaTilemap, (void *)(BG_SCREEN_ADDR(26)));
+ // bg1
LZ77UnCompVram(sTitleScreenCloudsGfx, (void *)(BG_CHAR_ADDR(3)));
LZ77UnCompVram(gUnknown_08DDE458, (void *)(BG_SCREEN_ADDR(27)));
ScanlineEffect_Stop();
diff --git a/tools/gbagfx/convert_png.c b/tools/gbagfx/convert_png.c
index cdfa39a7a..4f1b39e6d 100644
--- a/tools/gbagfx/convert_png.c
+++ b/tools/gbagfx/convert_png.c
@@ -125,7 +125,7 @@ void ReadPng(char *path, struct Image *image)
free(row_pointers);
fclose(fp);
- if (bit_depth != image->bitDepth)
+ if (bit_depth != image->bitDepth && image->tilemap.data.affine == NULL)
{
unsigned char *src = image->pixels;
diff --git a/tools/gbagfx/gfx.c b/tools/gbagfx/gfx.c
index f927deed9..8d959465f 100644
--- a/tools/gbagfx/gfx.c
+++ b/tools/gbagfx/gfx.c
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
+#include <string.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
@@ -203,6 +204,147 @@ static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numT
}
}
+static void DecodeAffineTilemap(unsigned char *input, unsigned char *output, unsigned char *tilemap, int tileSize, int numTiles)
+{
+ for (int i = 0; i < numTiles; i++)
+ {
+ memcpy(&output[i * tileSize], &input[tilemap[i] * tileSize], tileSize);
+ }
+}
+
+#define REVERSE_BIT_ORDER(x) ({ \
+ ((((x) >> 7) & 1) << 0) \
+ | ((((x) >> 6) & 1) << 1) \
+ | ((((x) >> 5) & 1) << 2) \
+ | ((((x) >> 4) & 1) << 3) \
+ | ((((x) >> 3) & 1) << 4) \
+ | ((((x) >> 2) & 1) << 5) \
+ | ((((x) >> 1) & 1) << 6) \
+ | ((((x) >> 0) & 1) << 7); \
+})
+
+#define SWAP_BYTES(a, b) ({ \
+ unsigned char tmp = *(a); \
+ *(a) = *(b); \
+ *(b) = tmp; \
+})
+
+#define NSWAP(x) ({ (((x) >> 4) & 0xF) | (((x) << 4) & 0xF0); })
+
+#define SWAP_NYBBLES(a, b) ({ \
+ unsigned char tmp = NSWAP(*(a)); \
+ *(a) = NSWAP(*(b)); \
+ *(b) = tmp; \
+})
+
+static void VflipTile(unsigned char * tile, int bitDepth)
+{
+ int i;
+ switch (bitDepth)
+ {
+ case 1:
+ SWAP_BYTES(&tile[0], &tile[7]);
+ SWAP_BYTES(&tile[1], &tile[6]);
+ SWAP_BYTES(&tile[2], &tile[5]);
+ SWAP_BYTES(&tile[3], &tile[4]);
+ break;
+ case 4:
+ for (i = 0; i < 4; i++)
+ {
+ SWAP_BYTES(&tile[i + 0], &tile[i + 28]);
+ SWAP_BYTES(&tile[i + 4], &tile[i + 24]);
+ SWAP_BYTES(&tile[i + 8], &tile[i + 20]);
+ SWAP_BYTES(&tile[i + 12], &tile[i + 16]);
+ }
+ break;
+ case 8:
+ for (i = 0; i < 8; i++)
+ {
+ SWAP_BYTES(&tile[i + 0], &tile[i + 56]);
+ SWAP_BYTES(&tile[i + 8], &tile[i + 48]);
+ SWAP_BYTES(&tile[i + 16], &tile[i + 40]);
+ SWAP_BYTES(&tile[i + 24], &tile[i + 32]);
+ }
+ break;
+ }
+}
+
+static void HflipTile(unsigned char * tile, int bitDepth)
+{
+ int i;
+ switch (bitDepth)
+ {
+ case 1:
+ for (i = 0; i < 8; i++)
+ tile[i] = REVERSE_BIT_ORDER(tile[i]);
+ break;
+ case 4:
+ for (i = 0; i < 8; i++)
+ {
+ SWAP_NYBBLES(&tile[4 * i + 0], &tile[4 * i + 3]);
+ SWAP_NYBBLES(&tile[4 * i + 1], &tile[4 * i + 2]);;
+ }
+ break;
+ case 8:
+ for (i = 0; i < 8; i++)
+ {
+ SWAP_BYTES(&tile[8 * i + 0], &tile[8 * i + 7]);
+ SWAP_BYTES(&tile[8 * i + 1], &tile[8 * i + 6]);
+ SWAP_BYTES(&tile[8 * i + 2], &tile[8 * i + 5]);
+ SWAP_BYTES(&tile[8 * i + 3], &tile[8 * i + 4]);
+ }
+ break;
+ }
+}
+
+static void DecodeNonAffineTilemap(unsigned char *input, unsigned char *output, struct NonAffineTile *tilemap, int tileSize, int outTileSize, int bitDepth, int numTiles)
+{
+ unsigned char * in_tile;
+ unsigned char * out_tile = output;
+ int effectiveBitDepth = tileSize == outTileSize ? bitDepth : 8;
+ for (int i = 0; i < numTiles; i++)
+ {
+ in_tile = &input[tilemap[i].index * tileSize];
+ if (tileSize == outTileSize)
+ memcpy(out_tile, in_tile, tileSize);
+ else
+ {
+ for (int j = 0; j < 64; j++)
+ {
+ int shift = (j & 1) * 4;
+ out_tile[j] = (in_tile[j / 2] & (0xF << shift)) >> shift;
+ }
+ }
+ if (tilemap[i].hflip)
+ HflipTile(out_tile, effectiveBitDepth);
+ if (tilemap[i].vflip)
+ VflipTile(out_tile, effectiveBitDepth);
+ if (bitDepth == 4 && effectiveBitDepth == 8)
+ {
+ for (int j = 0; j < 64; j++)
+ {
+ out_tile[j] &= 0xF;
+ out_tile[j] |= (15 - tilemap[i].palno) << 4;
+ }
+ }
+ out_tile += outTileSize;
+ }
+}
+
+static unsigned char *DecodeTilemap(unsigned char *tiles, struct Tilemap *tilemap, int *numTiles_p, bool isAffine, int tileSize, int outTileSize, int bitDepth)
+{
+ int mapTileSize = isAffine ? 1 : 2;
+ int numTiles = tilemap->size / mapTileSize;
+ unsigned char *decoded = calloc(numTiles, outTileSize);
+ if (isAffine)
+ DecodeAffineTilemap(tiles, decoded, tilemap->data.affine, tileSize, numTiles);
+ else
+ DecodeNonAffineTilemap(tiles, decoded, tilemap->data.non_affine, tileSize, outTileSize, bitDepth, numTiles);
+ free(tiles);
+ *numTiles_p = numTiles;
+ return decoded;
+}
+
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
@@ -211,6 +353,16 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numTiles = fileSize / tileSize;
+ if (image->tilemap.data.affine != NULL)
+ {
+ int outTileSize = (bitDepth == 4 && image->palette.numColors > 16) ? 64 : tileSize;
+ buffer = DecodeTilemap(buffer, &image->tilemap, &numTiles, image->isAffine, tileSize, outTileSize, bitDepth);
+ if (outTileSize == 64)
+ {
+ tileSize = 64;
+ image->bitDepth = bitDepth = 8;
+ }
+ }
int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth;
@@ -298,6 +450,11 @@ void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int m
void FreeImage(struct Image *image)
{
+ if (image->tilemap.data.affine != NULL)
+ {
+ free(image->tilemap.data.affine);
+ image->tilemap.data.affine = NULL;
+ }
free(image->pixels);
image->pixels = NULL;
}
@@ -318,6 +475,12 @@ void ReadGbaPalette(char *path, struct Palette *palette)
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
}
+ // png can only accept 16 or 256 colors, so fill the remainder with black
+ if (palette->numColors > 16)
+ {
+ memset(&palette->colors[palette->numColors], 0, (256 - palette->numColors) * sizeof(struct Color));
+ palette->numColors = 256;
+ }
free(data);
}
diff --git a/tools/gbagfx/gfx.h b/tools/gbagfx/gfx.h
index 5355ced85..edb9e62c4 100644
--- a/tools/gbagfx/gfx.h
+++ b/tools/gbagfx/gfx.h
@@ -17,6 +17,21 @@ struct Palette {
int numColors;
};
+struct NonAffineTile {
+ unsigned short index:10;
+ unsigned short hflip:1;
+ unsigned short vflip:1;
+ unsigned short palno:4;
+} __attribute__((packed));
+
+struct Tilemap {
+ union {
+ struct NonAffineTile *non_affine;
+ unsigned char *affine;
+ } data;
+ int size;
+};
+
struct Image {
int width;
int height;
@@ -25,6 +40,8 @@ struct Image {
bool hasPalette;
struct Palette palette;
bool hasTransparency;
+ struct Tilemap tilemap;
+ bool isAffine;
};
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c
index 957918505..61e93ea45 100644
--- a/tools/gbagfx/main.c
+++ b/tools/gbagfx/main.c
@@ -45,6 +45,20 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
image.hasPalette = false;
}
+ if (options->tilemapFilePath != NULL)
+ {
+ int fileSize;
+ image.tilemap.data.affine = ReadWholeFile(options->tilemapFilePath, &fileSize);
+ if (options->isAffineMap && options->bitDepth != 8)
+ FATAL_ERROR("affine maps are necessarily 8bpp\n");
+ image.isAffine = options->isAffineMap;
+ image.tilemap.size = fileSize;
+ }
+ else
+ {
+ image.tilemap.data.affine = NULL;
+ }
+
ReadImage(inputPath, options->width, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
image.hasTransparency = options->hasTransparency;
@@ -59,6 +73,7 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
struct Image image;
image.bitDepth = options->bitDepth;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
ReadPng(inputPath, &image);
@@ -77,6 +92,7 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
options.width = 1;
options.metatileWidth = 1;
options.metatileHeight = 1;
+ options.isAffineMap = false;
for (int i = 3; i < argc; i++)
{
@@ -134,6 +150,17 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
if (options.metatileHeight < 1)
FATAL_ERROR("metatile height must be positive.\n");
}
+ else if (strcmp(option, "-tilemap") == 0)
+ {
+ if (i + 1 >= argc)
+ FATAL_ERROR("No tilemap value following \"-tilemap\".\n");
+ i++;
+ options.tilemapFilePath = argv[i];
+ }
+ else if (strcmp(option, "-affine") == 0)
+ {
+ options.isAffineMap = true;
+ }
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@@ -155,6 +182,8 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
options.bitDepth = bitDepth;
options.metatileWidth = 1;
options.metatileHeight = 1;
+ options.tilemapFilePath = NULL;
+ options.isAffineMap = false;
for (int i = 3; i < argc; i++)
{
@@ -272,6 +301,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc,
void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
ReadLatinFont(inputPath, &image);
WritePng(outputPath, &image);
@@ -282,6 +312,7 @@ void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNU
void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
image.bitDepth = 2;
@@ -294,6 +325,7 @@ void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNU
void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
ReadHalfwidthJapaneseFont(inputPath, &image);
WritePng(outputPath, &image);
@@ -304,6 +336,7 @@ void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath,
void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
image.bitDepth = 2;
@@ -316,6 +349,7 @@ void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath,
void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
ReadFullwidthJapaneseFont(inputPath, &image);
WritePng(outputPath, &image);
@@ -326,6 +360,7 @@ void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath,
void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
+ image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
image.bitDepth = 2;
diff --git a/tools/gbagfx/options.h b/tools/gbagfx/options.h
index 2ff3967a4..3b038f572 100644
--- a/tools/gbagfx/options.h
+++ b/tools/gbagfx/options.h
@@ -12,6 +12,8 @@ struct GbaToPngOptions {
int width;
int metatileWidth;
int metatileHeight;
+ char *tilemapFilePath;
+ bool isAffineMap;
};
struct PngToGbaOptions {
@@ -19,6 +21,8 @@ struct PngToGbaOptions {
int bitDepth;
int metatileWidth;
int metatileHeight;
+ char *tilemapFilePath;
+ bool isAffineMap;
};
#endif // OPTIONS_H