nds_nflib/source/nf_sprite256.c
2025-02-09 12:48:26 +01:00

799 lines
26 KiB
C

// SPDX-License-Identifier: MIT
//
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
// Copyright (c) 2023 Antonio Niño Díaz "AntonioND"
//
// NightFox LIB - 256 color sprites functions
// http://www.nightfoxandco.com/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <nds.h>
#include "nf_2d.h"
#include "nf_basic.h"
#include "nf_sprite256.h"
// Buffers to store the sprite graphics and palettes
char *NF_BUFFER_SPR256GFX[NF_SLOTS_SPR256GFX];
char *NF_BUFFER_SPR256PAL[NF_SLOTS_SPR256PAL];
// Information structures for sprite graphics and palettes
NF_TYPE_SPR256GFX_INFO NF_SPR256GFX[NF_SLOTS_SPR256GFX];
NF_TYPE_SPR256PAL_INFO NF_SPR256PAL[NF_SLOTS_SPR256PAL];
// Structures for sprite graphics and palettes stored in VRAM
NF_TYPE_SPR256VRAM_INFO NF_SPR256VRAM[2][128];
NF_TYPE_SPRPALSLOT_INFO NF_SPRPALSLOT[2][16];
// Structure with OAM information of sprites
NF_TYPE_SPRITEOAM_INFO NF_SPRITEOAM[2][128];
// Main VRAM control structure for sprites
NF_TYPE_SPRVRAM_INFO NF_SPRVRAM[2];
void NF_InitSpriteBuffers(void)
{
// Initialize graphics buffers
for (int n = 0; n < NF_SLOTS_SPR256GFX; n++)
{
NF_BUFFER_SPR256GFX[n] = NULL;
NF_SPR256GFX[n].size = 0;
NF_SPR256GFX[n].width = 0;
NF_SPR256GFX[n].height = 0;
NF_SPR256GFX[n].available = true; // Mark as available
}
// Initialize palette buffers
for (int n = 0; n < NF_SLOTS_SPR256PAL; n++)
{
NF_BUFFER_SPR256PAL[n] = NULL;
NF_SPR256PAL[n].size = 0;
NF_SPR256PAL[n].available = true; // Mark as available
}
}
void NF_ResetSpriteBuffers(void)
{
// Free graphics and palette buffers
for (int n = 0; n < NF_SLOTS_SPR256GFX; n++)
free(NF_BUFFER_SPR256GFX[n]);
for (int n = 0; n < NF_SLOTS_SPR256PAL; n++)
free(NF_BUFFER_SPR256PAL[n]);
// Reset sprite system
NF_InitSpriteBuffers();
}
void NF_InitSpriteSys(int screen, ...)
{
va_list options;
va_start(options, screen);
u8 mode = va_arg(options, int);
va_end(options);
// Initialize sprite allocator and OAM information
for (int n = 0; n < 128; n++)
{
// Graphics in VRAM
NF_SPR256VRAM[screen][n].size = 0;
NF_SPR256VRAM[screen][n].width = 0;
NF_SPR256VRAM[screen][n].height = 0;
NF_SPR256VRAM[screen][n].address = 0;
NF_SPR256VRAM[screen][n].ramid = 0;
NF_SPR256VRAM[screen][n].framesize = 0;
NF_SPR256VRAM[screen][n].lastframe = 0;
NF_SPR256VRAM[screen][n].keepframes = false;
NF_SPR256VRAM[screen][n].inuse = false; // Mark as not in use
// OAM
NF_SPRITEOAM[screen][n].index = n;
NF_SPRITEOAM[screen][n].x = 0;
NF_SPRITEOAM[screen][n].y = 0;
NF_SPRITEOAM[screen][n].layer = 0;
NF_SPRITEOAM[screen][n].pal = 0;
NF_SPRITEOAM[screen][n].size = SpriteSize_8x8;
NF_SPRITEOAM[screen][n].color = SpriteColorFormat_256Color;
NF_SPRITEOAM[screen][n].gfx = NULL;
NF_SPRITEOAM[screen][n].rot = -1;
NF_SPRITEOAM[screen][n].doublesize = false;
NF_SPRITEOAM[screen][n].hide = true;
NF_SPRITEOAM[screen][n].hflip = false;
NF_SPRITEOAM[screen][n].vflip = false;
NF_SPRITEOAM[screen][n].mosaic = false;
NF_SPRITEOAM[screen][n].gfxid = 0;
NF_SPRITEOAM[screen][n].frame = 0;
NF_SPRITEOAM[screen][n].framesize = 0;
NF_SPRITEOAM[screen][n].lastframe = 0;
NF_SPRITEOAM[screen][n].created = false; // Mark sprite as not created
}
// Initialize VRAM allocator
if (mode == 128)
NF_SPRVRAM[screen].max = 131072;
else
NF_SPRVRAM[screen].max = 65536;
NF_SPRVRAM[screen].free = NF_SPRVRAM[screen].max; // All memory is available
NF_SPRVRAM[screen].last = 0;
NF_SPRVRAM[screen].deleted = 0;
NF_SPRVRAM[screen].fragmented = 0;
NF_SPRVRAM[screen].inarow = NF_SPRVRAM[screen].max; // All memory is contiguous
for (int n = 0; n < 128; n++)
{
NF_SPRVRAM[screen].pos[n] = 0;
NF_SPRVRAM[screen].size[n] = 0;
}
// Initialize palette information
for (int n = 0; n < 16; n++)
{
NF_SPRPALSLOT[screen][n].inuse = false;
NF_SPRPALSLOT[screen][n].ramslot = 0;
}
// Setup 2D engine and VRAM banks of the specified screen
if (screen == 0)
{
REG_DISPCNT |= DISPLAY_SPR_ACTIVE; // Enable sprites in the top screen
vramSetBankB(VRAM_B_LCD); // Enable CPU writes to VRAM_B
memset((void *)0x06400000, 0, 131072); // Clear VRAM_B
vramSetBankB(VRAM_B_MAIN_SPRITE_0x06400000); // VRAM_B is used for sprites (128 KB)
NF_SPRVRAM[screen].next = 0x06400000;
vramSetBankF(VRAM_F_LCD); // VRAM_F used for sprite ext. palettes (8 / 16 KB)
memset((void *)0x06890000, 0, 8192); // Clear VRAM_F
// Enable OAM with 128 or 64 mapping and extended palettes enabled
if (mode == 128)
oamInit(&oamMain, SpriteMapping_1D_128, true);
else
oamInit(&oamMain, SpriteMapping_1D_64, true);
}
else
{
REG_DISPCNT_SUB |= DISPLAY_SPR_ACTIVE; // Enable sprites in the bottom screen
vramSetBankD(VRAM_D_LCD); // Enable CPU writes to VRAM_D
memset((void *)0x06600000, 0, 131072); // Clear VRAM_D
vramSetBankD(VRAM_D_SUB_SPRITE);
NF_SPRVRAM[screen].next = 0x06600000;
vramSetBankI(VRAM_I_LCD); // VRAM_I used for sprite ext. palettes (8 / 16 KB)
memset((void *)0x068A0000, 0, 8192); // Clear VRAM_I
// Enable OAM with 128 or 64 mapping and extended palettes enabled
if (mode == 128)
oamInit(&oamSub, SpriteMapping_1D_128, true);
else
oamInit(&oamSub, SpriteMapping_1D_64, true);
}
}
void NF_LoadSpriteGfx(const char *file, u32 id, u32 width, u32 height)
{
if (id >= NF_SLOTS_SPR256GFX)
NF_Error(106, "Sprite graphics", NF_SLOTS_SPR256GFX);
if (!NF_SPR256GFX[id].available)
NF_Error(109, "Sprite graphics", id);
// Free the buffer if it was in use
free(NF_BUFFER_SPR256GFX[id]);
NF_BUFFER_SPR256GFX[id] = NULL;
// File path
char filename[256];
// Load .IMG file
snprintf(filename, sizeof(filename), "%s/%s.img", NF_ROOTFOLDER, file);
NF_FileLoad(filename, &NF_BUFFER_SPR256GFX[id], &NF_SPR256GFX[id].size, 0);
// Save information about the graphics
NF_SPR256GFX[id].width = width;
NF_SPR256GFX[id].height = height;
NF_SPR256GFX[id].available = false; // Mark ID as being in use
}
void NF_UnloadSpriteGfx(u32 id)
{
if (id >= NF_SLOTS_SPR256GFX)
NF_Error(106, "Sprite graphics", NF_SLOTS_SPR256GFX);
if (NF_SPR256GFX[id].available)
NF_Error(110, "Sprite graphics", id);
// Free buffer
free(NF_BUFFER_SPR256GFX[id]);
// Reset variables
NF_BUFFER_SPR256GFX[id] = NULL;
NF_SPR256GFX[id].size = 0;
NF_SPR256GFX[id].width = 0;
NF_SPR256GFX[id].height = 0;
NF_SPR256GFX[id].available = true; // Mark as available
}
void NF_LoadSpritePal(const char *file, u32 id)
{
if (id >= NF_SLOTS_SPR256PAL)
NF_Error(106, "Sprite palette", NF_SLOTS_SPR256PAL);
if (!NF_SPR256PAL[id].available)
NF_Error(109, "Sprite palette", id);
// Free buffer if it was used already
free(NF_BUFFER_SPR256PAL[id]);
NF_BUFFER_SPR256PAL[id] = NULL;
// FIle path
char filename[256];
// Load .PAL file, and allocate space for at least 256 colors
snprintf(filename, sizeof(filename), "%s/%s.pal", NF_ROOTFOLDER, file);
NF_FileLoad(filename, &NF_BUFFER_SPR256PAL[id], &NF_SPR256PAL[id].size, 256 * 2);
// Mark it as being in use
NF_SPR256PAL[id].available = false;
}
void NF_UnloadSpritePal(u32 id)
{
if (id >= NF_SLOTS_SPR256PAL)
NF_Error(106, "Sprite palette", NF_SLOTS_SPR256PAL);
if (NF_SPR256PAL[id].available)
NF_Error(110, "Sprite palette", id);
// Free buffer
free(NF_BUFFER_SPR256PAL[id]);
// Reset variables
NF_BUFFER_SPR256PAL[id] = NULL;
NF_SPR256PAL[id].size = 0;
NF_SPR256PAL[id].available = true; // Mark slot as free
}
void NF_VramSpriteGfx(int screen, u32 ram, u32 vram, bool keepframes)
{
if (ram >= NF_SLOTS_SPR256GFX)
NF_Error(106, "Sprite graphics", (NF_SLOTS_SPR256GFX - 1));
if (NF_SPR256GFX[ram].available)
NF_Error(110, "Sprite graphics", ram);
if (vram > 127)
NF_Error(106, "VRAM graphics", 127);
if (NF_SPR256VRAM[screen][vram].inuse)
NF_Error(109, "VRAM", vram);
s16 id = 255; // This will hold the ID of a free slot
// Calculate the size of one frame. One time is 64 bytes (8 * 8)
u32 width = NF_SPR256GFX[ram].width / 8;
u32 height = NF_SPR256GFX[ram].height / 8;
NF_SPR256VRAM[screen][vram].framesize = (width * height) * 64;
// Calculate the last frame of the animation
NF_SPR256VRAM[screen][vram].lastframe =
(NF_SPR256GFX[ram].size / NF_SPR256VRAM[screen][vram].framesize) - 1;
NF_SPR256VRAM[screen][vram].inuse = true; // Mark this slot as being in use
// Calculate the size of the graphics to copy depending on whether all
// frames need to be copied or not.
u32 gfxsize;
if (keepframes) // Copy the first frame only
gfxsize = NF_SPR256VRAM[screen][vram].framesize;
else // Copy all frames
gfxsize = NF_SPR256GFX[ram].size;
// Update the available VRAM
NF_SPRVRAM[screen].free -= gfxsize;
if (NF_SPRVRAM[screen].free < 0) // If there isn't enough VRAM, fail
NF_Error(113, "Sprites", gfxsize);
// Try to reuse any deleted block
if (NF_SPRVRAM[screen].deleted > 0)
{
// First, try to find any block with the same size
for (int n = 0; n < NF_SPRVRAM[screen].deleted; n++)
{
if (NF_SPRVRAM[screen].size[n] == gfxsize)
{
id = n;
break;
}
}
// If not, try to find any block that is big enough, fragmenting it
if (id == 255)
{
for (int n = 0; n < NF_SPRVRAM[screen].deleted; n++)
{
if (NF_SPRVRAM[screen].size[n] > gfxsize)
{
id = n;
break;
}
}
}
}
if (id != 255)
{
// We have managed to reuse a deleted block, determine if we need to
// reorganize the VRAM allocation structures.
bool organize = true; // Do we have to reorganize the free blocks array?
// Copy graphics to VRAM
NF_DmaMemCopy((void *)NF_SPRVRAM[screen].pos[id], NF_BUFFER_SPR256GFX[ram], gfxsize);
// Save the destination address
NF_SPR256VRAM[screen][vram].address = NF_SPRVRAM[screen].pos[id];
// If not all the size has been used, split this block into used and free
if (gfxsize < NF_SPRVRAM[screen].size[id])
{
// Calculate the new free space
u32 size = NF_SPRVRAM[screen].size[id] - gfxsize;
// Update data
NF_SPRVRAM[screen].pos[id] += gfxsize; // New free space address
NF_SPRVRAM[screen].size[id] = size; // New free space size
NF_SPRVRAM[screen].fragmented -= gfxsize; // Reduce the number or fragmented bytes
organize = false; // We don't need to reorganize anything after this
}
else
{
// If all the size has been used, simply reduce the counter of
// fragmented bytes.
NF_SPRVRAM[screen].fragmented -= NF_SPRVRAM[screen].size[id];
}
// Do we have to reorganize the array of free blocks?
if (organize)
{
int last_reuse = NF_SPRVRAM[screen].deleted - 1;
// If there are more than one deleted blocks and this isn't the last
// position.
if ((last_reuse > 0) && (id != last_reuse))
{
// Copy the values of the last position in this one
NF_SPRVRAM[screen].pos[id] = NF_SPRVRAM[screen].pos[last_reuse];
NF_SPRVRAM[screen].size[id] = NF_SPRVRAM[screen].size[last_reuse];
}
NF_SPRVRAM[screen].deleted--; // Update counter of free blocks
}
}
else
{
// We couldn't reuse any deleted block, add the new graphics to the end
// of the used VRAM.
// Update the contiguous available VRAM (the last block at the end)
NF_SPRVRAM[screen].inarow -= gfxsize;
// If there isn't enough VRAM, fail
if (NF_SPRVRAM[screen].inarow < 0)
NF_Error(113, "Sprites", gfxsize);
// Copy graphics to VRAM
NF_DmaMemCopy((void *)NF_SPRVRAM[screen].next, NF_BUFFER_SPR256GFX[ram], gfxsize);
// Copy location and size in VRAM
NF_SPR256VRAM[screen][vram].address = NF_SPRVRAM[screen].next;
NF_SPRVRAM[screen].last = NF_SPRVRAM[screen].next;
// Calculate the next available address
NF_SPRVRAM[screen].next += gfxsize;
}
// Save graphics information
NF_SPR256VRAM[screen][vram].size = gfxsize;
NF_SPR256VRAM[screen][vram].width = NF_SPR256GFX[ram].width;
NF_SPR256VRAM[screen][vram].height = NF_SPR256GFX[ram].height;
NF_SPR256VRAM[screen][vram].ramid = ram; // Source slot in RAM
NF_SPR256VRAM[screen][vram].keepframes = keepframes;
}
void NF_FreeSpriteGfx(int screen, u32 id)
{
if (!NF_SPR256VRAM[screen][id].inuse)
NF_Error(110, "Sprite graphics", id);
// Clear graphics from VRAM
memset((void *)NF_SPR256VRAM[screen][id].address, 0, NF_SPR256VRAM[screen][id].size);
// Update free VRAM
NF_SPRVRAM[screen].free += NF_SPR256VRAM[screen][id].size;
// Save address and size of the deleted block to be reused
NF_SPRVRAM[screen].pos[NF_SPRVRAM[screen].deleted] = NF_SPR256VRAM[screen][id].address;
NF_SPRVRAM[screen].size[NF_SPRVRAM[screen].deleted] = NF_SPR256VRAM[screen][id].size;
// Increment counter of deleted blocks
NF_SPRVRAM[screen].deleted++;
// Increment counter of fragmented memory
NF_SPRVRAM[screen].fragmented += NF_SPR256VRAM[screen][id].size;
// Reset graphics state
NF_SPR256VRAM[screen][id].size = 0;
NF_SPR256VRAM[screen][id].width = 0;
NF_SPR256VRAM[screen][id].height = 0;
NF_SPR256VRAM[screen][id].address = 0;
NF_SPR256VRAM[screen][id].framesize = 0;
NF_SPR256VRAM[screen][id].lastframe = 0;
NF_SPR256VRAM[screen][id].inuse = false;
// If the memory is too fragmented, defragment it
if (NF_SPRVRAM[screen].fragmented >= (NF_SPRVRAM[screen].inarow / 2))
NF_VramSpriteGfxDefrag(screen);
}
void NF_VramSpriteGfxDefrag(int screen)
{
// Allocate temporary buffer with all the currently used VRAM
u32 used_vram = (NF_SPRVRAM[screen].max - NF_SPRVRAM[screen].free) + 1;
char *buffer = malloc(used_vram);
if (buffer == NULL) // Not enough RAM
NF_Error(102, NULL, used_vram);
char *address[128]; // RAM address of each sprite
u32 size[128]; // Size of buffer
u32 ram = 0; // Pointer to the current RAM address
u32 frame_address = 0; // Address of the current frame
// Copy data from VRAM to the temporary buffer in RAM
for (int n = 0; n < 128; n++)
{
// Copy all used graphics to the temporary buffer
if (NF_SPR256VRAM[screen][n].inuse)
{
address[n] = buffer + ram;
size[n] = NF_SPR256VRAM[screen][n].size;
NF_DmaMemCopy(address[n], (void *)NF_SPR256VRAM[screen][n].address, size[n]);
ram += size[n];
}
}
// Reset VRAM allocation information
NF_SPRVRAM[screen].free = NF_SPRVRAM[screen].max;
NF_SPRVRAM[screen].last = 0;
NF_SPRVRAM[screen].deleted = 0;
NF_SPRVRAM[screen].fragmented = 0;
NF_SPRVRAM[screen].inarow = NF_SPRVRAM[screen].max;
for (int n = 0; n < 128; n++)
{
NF_SPRVRAM[screen].pos[n] = 0;
NF_SPRVRAM[screen].size[n] = 0;
}
if (screen == 0)
NF_SPRVRAM[screen].next = 0x06400000;
else
NF_SPRVRAM[screen].next = 0x06600000;
// Now copy all data from the temporary buffer to VRAM
for (int n = 0; n < 128; n++)
{
if (NF_SPR256VRAM[screen][n].inuse)
{
NF_DmaMemCopy((void*)NF_SPRVRAM[screen].next, address[n], size[n]);
NF_SPR256VRAM[screen][n].address = NF_SPRVRAM[screen].next;
NF_SPRVRAM[screen].free -= size[n];
NF_SPRVRAM[screen].inarow -= size[n];
NF_SPRVRAM[screen].last = NF_SPRVRAM[screen].next;
NF_SPRVRAM[screen].next += size[n];
}
}
// Link the new graphics addresses to the sprites
for (int n = 0; n < 128; n++)
{
if (NF_SPRITEOAM[screen][n].created)
{
if (NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][n].gfxid].keepframes)
{
// If keepframes is enabled, only assign the base address of the
// graphics
NF_SPRITEOAM[screen][n].gfx =
(u32 *)NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][n].gfxid].address;
}
else
{
// If keepframes is disabled calculate the offset of the
// graphics
frame_address = NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][n].gfxid].address
+ (NF_SPRITEOAM[screen][n].framesize * NF_SPRITEOAM[screen][n].frame);
NF_SPRITEOAM[screen][n].gfx = (u32 *)frame_address;
}
}
}
// Free temporary buffer
free(buffer);
}
void NF_VramSpritePal(int screen, u32 id, u32 slot)
{
if (id >= NF_SLOTS_SPR256PAL)
NF_Error(106, "Sprite palette", NF_SLOTS_SPR256PAL);
if (NF_SPR256PAL[id].available)
NF_Error(110, "Sprite palette", id);
if (slot > 15)
NF_Error(106, "Sprite palette slot", 15);
// Copy palette to VRAM
if (screen == 0)
{
u32 address = 0x06890000 + (slot * 256 * 2);
vramSetBankF(VRAM_F_LCD); // Enable CPU writes to VRAM_F
NF_DmaMemCopy((void *)address, NF_BUFFER_SPR256PAL[id], NF_SPR256PAL[id].size);
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE);
}
else
{
u32 address = 0x068A0000 + (slot * 256 * 2);
vramSetBankI(VRAM_I_LCD); // Enable CPU writes to VRAM_I
NF_DmaMemCopy((void *)address, NF_BUFFER_SPR256PAL[id], NF_SPR256PAL[id].size);
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE);
}
NF_SPRPALSLOT[screen][slot].inuse = true; // Mark slot as in use
NF_SPRPALSLOT[screen][slot].ramslot = id; // Save slot where the palette is stored
}
void NF_CreateSprite(int screen, u32 id, u32 gfx, u32 pal, s32 x, s32 y)
{
if (id > 127)
NF_Error(106, "Sprite ID", 127);
if (gfx > 127)
NF_Error(106, "Sprite graphics", 127);
if (!NF_SPR256VRAM[screen][gfx].inuse)
NF_Error(111, "Sprite graphics", gfx);
if (pal > 15)
NF_Error(106, "Sprite palette slot", 15);
if (!NF_SPRPALSLOT[screen][pal].inuse)
NF_Error(111, "Sprite palette", pal);
NF_SPRITEOAM[screen][id].index = id;
NF_SPRITEOAM[screen][id].gfx = (u32 *)NF_SPR256VRAM[screen][gfx].address; // VRAM address
NF_SPRITEOAM[screen][id].pal = pal;
NF_SPRITEOAM[screen][id].x = x;
NF_SPRITEOAM[screen][id].y = y;
NF_SPRITEOAM[screen][id].color = SpriteColorFormat_256Color;
NF_SPRITEOAM[screen][id].hide = false; // Show sprite
NF_SPRITEOAM[screen][id].gfxid = gfx; // Graphics index
NF_SPRITEOAM[screen][id].created = true; // Mark sprite as created
if ((NF_SPR256VRAM[screen][gfx].width == 8) && (NF_SPR256VRAM[screen][gfx].height == 8))
{
if (NF_SPRVRAM[screen].max != 131072) // In mode 1D_128 this size is invalid
NF_SPRITEOAM[screen][id].size = SpriteSize_8x8;
else
NF_Error(120, NULL, id);
}
else
{
struct
{
int x, y;
SpriteSize size;
}
size_array[] =
{
{ 16, 16, SpriteSize_16x16 },
{ 32, 32, SpriteSize_32x32 },
{ 64, 64, SpriteSize_64x64 },
{ 16, 8, SpriteSize_16x8 },
{ 32, 8, SpriteSize_32x8 },
{ 32, 16, SpriteSize_32x16 },
{ 64, 32, SpriteSize_64x32 },
{ 8, 16, SpriteSize_8x16 },
{ 8, 32, SpriteSize_8x32 },
{ 16, 32, SpriteSize_16x32 },
{ 32, 64, SpriteSize_32x64 },
{ 0, 0, 0 },
};
int i = 0;
while (1)
{
if (size_array[i].x == 0)
NF_Error(106, "Sprite size", 0);
if ((NF_SPR256VRAM[screen][gfx].width == size_array[i].x) &&
(NF_SPR256VRAM[screen][gfx].height == size_array[i].y))
{
NF_SPRITEOAM[screen][id].size = size_array[i].size;
break;
}
i++;
}
}
NF_SPRITEOAM[screen][id].lastframe = NF_SPR256VRAM[screen][gfx].lastframe;
NF_SPRITEOAM[screen][id].framesize = NF_SPR256VRAM[screen][gfx].framesize;
NF_SPRITEOAM[screen][id].frame = 0; // Start at frame 0
}
void NF_DeleteSprite(int screen, u32 id)
{
if (id > 127)
NF_Error(106, "Sprite ID", 127);
// Verify that the sprite is created
if (!NF_SPRITEOAM[screen][id].created)
{
char text[4];
snprintf(text, sizeof(text), "%d", screen);
NF_Error(112, text, id);
}
// Reset sprite state
NF_SPRITEOAM[screen][id].index = id;
NF_SPRITEOAM[screen][id].x = 0;
NF_SPRITEOAM[screen][id].y = 0;
NF_SPRITEOAM[screen][id].layer = 0;
NF_SPRITEOAM[screen][id].pal = 0;
NF_SPRITEOAM[screen][id].size = SpriteSize_8x8;
NF_SPRITEOAM[screen][id].color = SpriteColorFormat_256Color;
NF_SPRITEOAM[screen][id].gfx = NULL;
NF_SPRITEOAM[screen][id].rot = -1;
NF_SPRITEOAM[screen][id].doublesize = false;
NF_SPRITEOAM[screen][id].hide = true;
NF_SPRITEOAM[screen][id].hflip = false;
NF_SPRITEOAM[screen][id].vflip = false;
NF_SPRITEOAM[screen][id].mosaic = false;
NF_SPRITEOAM[screen][id].gfxid = 0;
NF_SPRITEOAM[screen][id].frame = 0;
NF_SPRITEOAM[screen][id].framesize = 0;
NF_SPRITEOAM[screen][id].lastframe = 0;
NF_SPRITEOAM[screen][id].created = false; // Mark sprite as not created
}
void NF_SpriteOamSet(int screen)
{
if (screen == 0)
{
for (int n = 0; n < 128; n++)
{
oamSet(&oamMain,
NF_SPRITEOAM[screen][n].index,
NF_SPRITEOAM[screen][n].x,
NF_SPRITEOAM[screen][n].y,
NF_SPRITEOAM[screen][n].layer,
NF_SPRITEOAM[screen][n].pal,
NF_SPRITEOAM[screen][n].size,
NF_SPRITEOAM[screen][n].color,
NF_SPRITEOAM[screen][n].gfx,
NF_SPRITEOAM[screen][n].rot,
NF_SPRITEOAM[screen][n].doublesize,
NF_SPRITEOAM[screen][n].hide,
NF_SPRITEOAM[screen][n].hflip,
NF_SPRITEOAM[screen][n].vflip,
NF_SPRITEOAM[screen][n].mosaic);
}
}
else
{
for (int n = 0; n < 128; n++)
{
oamSet(&oamSub,
NF_SPRITEOAM[screen][n].index,
NF_SPRITEOAM[screen][n].x,
NF_SPRITEOAM[screen][n].y,
NF_SPRITEOAM[screen][n].layer,
NF_SPRITEOAM[screen][n].pal,
NF_SPRITEOAM[screen][n].size,
NF_SPRITEOAM[screen][n].color,
NF_SPRITEOAM[screen][n].gfx,
NF_SPRITEOAM[screen][n].rot,
NF_SPRITEOAM[screen][n].doublesize,
NF_SPRITEOAM[screen][n].hide,
NF_SPRITEOAM[screen][n].hflip,
NF_SPRITEOAM[screen][n].vflip,
NF_SPRITEOAM[screen][n].mosaic);
}
}
}
void NF_SpriteSetPalColor(int screen, u32 pal, u32 number, u32 r, u32 g, u32 b)
{
if (!NF_SPRPALSLOT[screen][pal].inuse)
NF_Error(111, "Sprite palette", pal);
// Pack RGB value
u32 rgb = r | (g << 5) | (b << 10);
// Modify palette
if (screen == 0)
{
u32 address = 0x06890000 + (pal * 256 * 2) + (number * 2);
vramSetBankF(VRAM_F_LCD); // Enable CPU writes to VRAM_F
*((u16 *)address) = rgb;
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE);
}
else
{
u32 address = 0x068A0000 + (pal * 256 * 2) + (number * 2);
vramSetBankI(VRAM_I_LCD); // Enable CPU writes to VRAM_I
*((u16 *)address) = rgb;
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE);
}
}
void NF_SpriteEditPalColor(int screen, u32 pal, u32 number, u32 r, u32 g, u32 b)
{
if (!NF_SPRPALSLOT[screen][pal].inuse)
NF_Error(111, "Sprite palette", pal);
// Pack RGB value
u32 rgb = r | (g << 5) | (b << 10);
u32 hibyte = (rgb >> 8) & 0xff;
u32 lobyte = rgb & 0xff;
*(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + (number * 2)) = lobyte;
*(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + ((number * 2) + 1)) = hibyte;
}
void NF_SpriteUpdatePalette(int screen, u32 pal)
{
if (!NF_SPRPALSLOT[screen][pal].inuse)
NF_Error(111, "Sprite palette", pal);
// Get slot where the palette is stored in RAM
u32 slot = NF_SPRPALSLOT[screen][pal].ramslot;
// Update palette in VRAM
if (screen == 0)
{
u32 address = 0x06890000 + (pal * 256 * 2);
vramSetBankF(VRAM_F_LCD); // Enable CPU writes to VRAM_F
NF_DmaMemCopy((void *)address, NF_BUFFER_SPR256PAL[slot], NF_SPR256PAL[slot].size);
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE);
}
else
{
u32 address = 0x068A0000 + (pal * 256 * 2);
vramSetBankI(VRAM_I_LCD); // Enable CPU writes to VRAM_I
NF_DmaMemCopy((void *)address, NF_BUFFER_SPR256PAL[slot], NF_SPR256PAL[slot].size);
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE);
}
}
void NF_SpriteGetPalColor(int screen, u32 pal, u32 number, u8 *r, u8 *g, u8 *b)
{
if (!NF_SPRPALSLOT[screen][pal].inuse)
NF_Error(111, "Sprite palette", pal);
u32 ramslot = NF_SPRPALSLOT[screen][pal].ramslot;
u32 lobyte = *(NF_BUFFER_SPR256PAL[ramslot] + (number * 2));
u32 hibyte = *(NF_BUFFER_SPR256PAL[ramslot] + ((number * 2) + 1));
// Get RGB value
u32 rgb = (hibyte << 8) | lobyte;
// Split components
*r = rgb & 0x1F;
*g = (rgb >> 5) & 0x1F;
*b = (rgb >> 10) & 0x1F;
}