diff options
-rw-r--r-- | berry_fix/charmap.txt | 2 | ||||
-rw-r--r-- | berry_fix/payload/charmap.txt | 2 | ||||
-rw-r--r-- | charmap.txt | 2 | ||||
-rwxr-xr-x | include/pokemon_summary_screen.h | 4 | ||||
-rw-r--r-- | src/battle_anim_mons.c | 2 | ||||
-rw-r--r-- | src/battle_message.c | 10 | ||||
-rw-r--r-- | src/easy_chat.c | 10 | ||||
-rw-r--r-- | src/graphics.c | 4 | ||||
-rw-r--r-- | src/pokemon_summary_screen.c | 42 | ||||
-rw-r--r-- | src/title_screen.c | 5 | ||||
-rw-r--r-- | tools/gbagfx/convert_png.c | 2 | ||||
-rw-r--r-- | tools/gbagfx/gfx.c | 163 | ||||
-rw-r--r-- | tools/gbagfx/gfx.h | 17 | ||||
-rw-r--r-- | tools/gbagfx/main.c | 35 | ||||
-rw-r--r-- | tools/gbagfx/options.h | 4 |
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 |