This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Disclaimer: This code does not give you functionality for 80x80 sprites in battle. This is only a means of creating 80x80 sprites on the screen; nothing more. Credits:
- froggestspirit for the
CreateBigSpritecode, which I have modified - hedara for the
ConvertToTiles4BppBigcode
sprite.c
u8 CreateBigSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
u8 ic;
struct Sprite *spriteA = &gSprites[index];
struct Sprite *spriteB = &gSprites[index + 1];
struct Sprite *spriteC = &gSprites[index + 2];
struct Sprite *spriteD = &gSprites[index + 3];
struct Sprite *spriteE = &gSprites[index + 4];
struct Sprite *spriteF = &gSprites[index + 5];
ResetSprite(spriteA);
ResetSprite(spriteB);
ResetSprite(spriteC);
ResetSprite(spriteD);
ResetSprite(spriteE);
ResetSprite(spriteF);
spriteA->inUse = TRUE;
spriteA->animBeginning = TRUE;
spriteA->affineAnimBeginning = TRUE;
spriteA->usingSheet = TRUE;
spriteA->subpriority = subpriority;
spriteA->oam = *template->oam;
spriteA->anims = template->anims;
spriteA->affineAnims = template->affineAnims;
spriteA->template = template;
spriteA->callback = template->callback;
spriteB->subpriority = subpriority;
spriteB->oam = *template->oam;
spriteB->anims = template->anims;
spriteB->affineAnims = template->affineAnims;
spriteB->template = template;
spriteB->callback = SpriteCallbackDummy;
spriteC->subpriority = subpriority;
spriteC->oam = *template->oam;
spriteC->anims = template->anims;
spriteC->affineAnims = template->affineAnims;
spriteC->template = template;
spriteC->callback = SpriteCallbackDummy;
spriteD->subpriority = subpriority;
spriteD->oam = *template->oam;
spriteD->anims = template->anims;
spriteD->affineAnims = template->affineAnims;
spriteD->template = template;
spriteD->callback = SpriteCallbackDummy;
spriteE->subpriority = subpriority;
spriteE->oam = *template->oam;
spriteE->anims = template->anims;
spriteE->affineAnims = template->affineAnims;
spriteE->template = template;
spriteE->callback = SpriteCallbackDummy;
spriteF->subpriority = subpriority;
spriteF->oam = *template->oam;
spriteF->anims = template->anims;
spriteF->affineAnims = template->affineAnims;
spriteF->template = template;
spriteF->callback = SpriteCallbackDummy;
spriteA->x = x;
spriteA->y = y;
gSprites[0].x = x;
gSprites[0].y = y;
gSprites[1].x = x;
gSprites[1].y = y + 64;
gSprites[2].x = x + 32;
gSprites[2].y = y + 64;
gSprites[3].x = x + 64;
gSprites[3].y = y;
gSprites[4].x = x + 64;
gSprites[4].y = y + 32;
gSprites[5].x = x + 64;
gSprites[5].y = y + 64;
spriteA->oam.shape = 0; //square
spriteA->oam.size = SPRITE_SIZE(64x64); //64x64
spriteB->oam.shape = 1; //horizontal
spriteB->oam.size = SPRITE_SIZE(32x16); //32x16
spriteC->oam.shape = 1; //horizontal
spriteC->oam.size = SPRITE_SIZE(32x16); //32x16
spriteD->oam.shape = 2; //vertical
spriteD->oam.size = SPRITE_SIZE(16x32); //16x32
spriteE->oam.shape = 2; //vertical
spriteE->oam.size = SPRITE_SIZE(16x32); //16x32
spriteF->oam.shape = 0; //square
spriteF->oam.size = SPRITE_SIZE(16x16); //16x16
if (template->tileTag == 0xFFFF)
{
s16 tileNum;
spriteA->images = template->images;
tileNum = AllocSpriteTiles((u8)(CARD_PIC_SIZE / TILE_SIZE_8BPP)); //allocate for a 80x80 sprite
if (tileNum == -1)
{
ResetSprite(spriteA);
ResetSprite(spriteB);
ResetSprite(spriteC);
ResetSprite(spriteD);
ResetSprite(spriteE);
ResetSprite(spriteF);
return MAX_SPRITES;
}
spriteA->oam.tileNum = tileNum;
spriteA->usingSheet = FALSE;
spriteA->sheetTileStart = 0;
}
else
{
spriteA->sheetTileStart = GetSpriteTileStartByTag(template->tileTag);
SetSpriteSheetFrameTileNum(spriteA);
}
spriteB->usingSheet = spriteA->usingSheet;
spriteB->sheetTileStart = spriteA->sheetTileStart + 0x80;
spriteB->oam.tileNum = spriteA->oam.tileNum + 0x80;
spriteB->images = spriteA->images + 0x600;
spriteC->usingSheet = spriteA->usingSheet;
spriteC->sheetTileStart = spriteA->sheetTileStart + 0x90;
spriteC->oam.tileNum = spriteA->oam.tileNum + 0x90;
spriteC->images = spriteA->images + 0xA00;
spriteD->usingSheet = spriteA->usingSheet;
spriteD->sheetTileStart = spriteA->sheetTileStart + 0xA0;
spriteD->oam.tileNum = spriteA->oam.tileNum + 0xA0;
spriteD->images = spriteA->images + 0xA80;
spriteE->usingSheet = spriteA->usingSheet;
spriteE->sheetTileStart = spriteA->sheetTileStart + 0xB0;
spriteE->oam.tileNum = spriteA->oam.tileNum + 0xB0;
spriteE->images = spriteA->images + 0xB00;
spriteF->usingSheet = spriteA->usingSheet;
spriteF->sheetTileStart = spriteA->sheetTileStart + 0xC0;
spriteF->oam.tileNum = spriteA->oam.tileNum + 0xC0;
spriteF->images = spriteA->images + 0xB80;
if (spriteA->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
InitSpriteAffineAnim(spriteA);
if (template->paletteTag != 0xFFFF){
spriteA->oam.paletteNum = IndexOfSpritePaletteTag(template->paletteTag);
}
spriteB->inUse = TRUE;
spriteC->inUse = TRUE;
spriteD->inUse = TRUE;
spriteE->inUse = TRUE;
spriteF->inUse = TRUE;
return index;
}
u8 CreateBigSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
{
u8 i;
u8 ret = 0;
for (i = 0; i < MAX_SPRITES; i++){
if (!gSprites[i].inUse){
if(++ret == 5){
return CreateBigSpriteAt(i - 4, template, x, y, subpriority);
}
}else{
ret = 0;
}
}
return MAX_SPRITES;
}
tools/gbagfx/gfx.c
static void ConvertToTiles4BppBig(unsigned char *src, unsigned char *dest, int images)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = 10 * 4;
for (int im = 0; im < images; im++) {
metatileX = 0;
metatileY = im * 10;
for (int i = 0; i < 80; i++) { //64x80 part
for (int j = 0; j < 8; j++) {
int srcY = metatileY * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = metatileX * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, 8, 1, 1);
}
metatileX = 0;
metatileY = im * 10;
for (int i = 0; i < 20; i++) { //16x80 part
for (int j = 0; j < 8; j++) {
int srcY = metatileY * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = (metatileX + 8) * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
if(metatileX >= 2) srcPixelPair = 0;
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, 2, 1, 1);
}
}
}
tools/gbagfx/main.c
#define TILE_SIZE 64
#define TILES_PER_ROW 10
int tile_index(int x, int y) {
return y * TILES_PER_ROW + x;
}
void copy_tile(unsigned char *dst, unsigned char *src, int index) {
memcpy(dst, src + index * TILE_SIZE, TILE_SIZE);
}
void HandleBigSpriteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int size;
unsigned char *fileContents = ReadWholeFile(inputPath, &size);
unsigned char *outputContents = malloc(80 * 80); // 6400 bytes
int index = 0;
// 64x64 sprite (tiles 0-7 in rows 0-7)
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 32x16 A (row 8, columns 0–3)
for (int y = 8; y < 10; y++) {
for (int x = 0; x < 4; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 32x16 B (row 8, columns 4–7)
for (int y = 8; y < 10; y++) {
for (int x = 4; x < 8; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x32 A (tile column 8, rows 0–3)
for (int y = 0; y < 4; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x32 B (tile column 8, rows 4–7)
for (int y = 4; y < 8; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
// 16x16 (bottom-right)
for (int y = 8; y < 10; y++) {
for (int x = 8; x < 10; x++) {
copy_tile(outputContents + index, fileContents, tile_index(x, y));
index += TILE_SIZE;
}
}
WriteWholeFile(outputPath, outputContents, size);
free(fileContents);
free(outputContents);
}
How to Use:
In struct CommandHandler handlers[] in int main in tools/gbagfx/main.c, add { "8bpp", "8bpp", HandleBigSpriteCommand }, so that you can do gbagfx pic.8bpp pic.8bpp in command line to convert from a normal pic to the format used for a sprite stitch in the code.
In WriteTileImage in tools/gbagfx/gfx.c, replace case 4 with:
case 4:
if (image->width == 80 && ((image->height % 80) == 0))
{
ConvertToTiles4BppBig(image->pixels, buffer, image->height / 80);
}
else
ConvertToTiles4Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
This will allow you to perform gbagfx pic.png pic.4bpp (the 4bpp sprite reformatting function).
Now, all you have to do is use it like CreateSprite on a SpriteTemplate (SPRITE_SHAPE(64x64) and SPRITE_SIZE(64x64), still) and, if you've performed the gbagfx conversion, it should turn out properly.