nds_nflib/source/nf_sprite256.c
Antonio Niño Díaz 151db13cc0 license: Fix licenses
- Creative Commons (except for CC0) shouldn't be used for code:

   https://creativecommons.org/faq/#can-i-apply-a-creative-commons-license-to-software

   MIT has the same spirit as the CC-BY license.

- CC-BY has been retained for the assets included in the repository.

- Also, the years were wrong, this library was started in 2009.

- Make all examples use the CC0 license.
2023-04-01 19:56:45 +01:00

991 lines
34 KiB
C

// SPDX-License-Identifier: MIT
//
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
//
// NightFox LIB - Funciones de Sprites a 256 colores
// http://www.nightfoxandco.com/
// Version 20140413
// Includes devKitPro
#include <nds.h>
#include <filesystem.h>
#include <fat.h>
// Includes C
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
// Includes propios
#include "nf_basic.h"
#include "nf_2d.h"
#include "nf_sprite256.h"
// Define los Buffers para almacenar los Sprites
char* NF_BUFFER_SPR256GFX[NF_SLOTS_SPR256GFX];
char* NF_BUFFER_SPR256PAL[NF_SLOTS_SPR256PAL];
// Define la estructura de datos de los Graficos de los Sprites
NF_TYPE_SPR256GFX_INFO NF_SPR256GFX[NF_SLOTS_SPR256GFX];
// Define la estructura de datos de las Paletas de los Sprites
NF_TYPE_SPR256PAL_INFO NF_SPR256PAL[NF_SLOTS_SPR256PAL];
// Define la estructura de Gfx en VRAM
NF_TYPE_SPR256VRAM_INFO NF_SPR256VRAM[2][128];
// Datos de paletas de Sprites en VRAM (en uso, slot en ram, etc)
NF_TYPE_SPRPALSLOT_INFO NF_SPRPALSLOT[2][16];
// Define la estructura de datos del OAM (Sprites)
NF_TYPE_SPRITEOAM_INFO NF_SPRITEOAM[2][128]; // 2 pantallas, 128 sprites
// Define la esturctura de control de la VRAM para Sprites
NF_TYPE_SPRVRAM_INFO NF_SPRVRAM[2]; // Informacion VRAM de Sprites en ambas pantallas
// Funcion NF_InitSpriteBuffers()
void NF_InitSpriteBuffers(void) {
u16 n = 0; // Variable comun
// Inicializa Buffers de GFX
for (n = 0; n < NF_SLOTS_SPR256GFX; n ++) {
NF_BUFFER_SPR256GFX[n] = NULL; // Inicializa puntero
NF_SPR256GFX[n].size = 0; // Tamaño (en bytes) del grafico (GFX)
NF_SPR256GFX[n].width = 0; // Ancho del Gfx
NF_SPR256GFX[n].height = 0; // Altura del Gfx
NF_SPR256GFX[n].available = true; // Disponibilidat del Slot
}
// Inicializa Buffers de PAL
for (n = 0; n < NF_SLOTS_SPR256PAL; n ++) {
NF_BUFFER_SPR256PAL[n] = NULL; // Inicializa puntero
NF_SPR256PAL[n].size = 0; // Tamaño (en bytes) de la paleta (PAL)
NF_SPR256PAL[n].available = true; // Disponibilidat del Slot
}
}
// Funcion NF_ResetSpriteBuffers()
void NF_ResetSpriteBuffers(void) {
u16 n = 0; // Variable comun
// Borra los Buffers de GFX
for (n = 0; n < NF_SLOTS_SPR256GFX; n ++) {
free(NF_BUFFER_SPR256GFX[n]);
}
// Borra los Buffers de PAL
for (n = 0; n < NF_SLOTS_SPR256PAL; n ++) {
free(NF_BUFFER_SPR256PAL[n]);
}
// Reinicia el sistema de Sprites
NF_InitSpriteBuffers();
}
// Funcion NF_InitSpriteSys();
void NF_InitSpriteSys(int screen, ...) {
// Analiza los parametros variables de la funcion
va_list options;
va_start(options, screen);
u8 mode = va_arg(options, int);
va_end(options);
// Variables
u8 n = 0; // Uso comun
// Inicializa la estructura de Gfx en VRAM
// y la estructura de datos del OAM (Sprites)
for (n = 0; n < 128; n ++) { // 128 sprites
// Gfx en la VRAM (128 Gfx x pantalla)
NF_SPR256VRAM[screen][n].size = 0; // Tamaño (en bytes) del Gfx
NF_SPR256VRAM[screen][n].width = 0; // Ancho del Gfx
NF_SPR256VRAM[screen][n].height = 0; // Altura del Gfx
NF_SPR256VRAM[screen][n].address = 0; // Posicion en la VRAM
NF_SPR256VRAM[screen][n].ramid = 0; // Numero de Slot en RAM del que provienes
NF_SPR256VRAM[screen][n].framesize = 0; // Tamaño del frame (en bytes)
NF_SPR256VRAM[screen][n].lastframe = 0; // Ultimo frame
NF_SPR256VRAM[screen][n].keepframes = false; // Si es un Sprite animado, debes de mantener los frames en RAM ?
NF_SPR256VRAM[screen][n].inuse = false; // Esta en uso ?
// OAM (128 Sprites x pantalla)
NF_SPRITEOAM[screen][n].index = n; // Numero de Sprite (Index = N)
NF_SPRITEOAM[screen][n].x = 0; // Coordenada X del Sprite (0 por defecto)
NF_SPRITEOAM[screen][n].y = 0; // Coordenada Y del Sprite (0 por defecto)
NF_SPRITEOAM[screen][n].layer = 0; // Prioridad en las capas (0 por defecto)
NF_SPRITEOAM[screen][n].pal = 0; // Paleta que usaras (0 por defecto)
NF_SPRITEOAM[screen][n].size = SpriteSize_8x8; // Tamaño del Sprite (macro) (8x8 por defecto)
NF_SPRITEOAM[screen][n].color = SpriteColorFormat_256Color; // Modo de color (macro) (256 colores)
NF_SPRITEOAM[screen][n].gfx = NULL; // Puntero al grafico usado
NF_SPRITEOAM[screen][n].rot = -1; // Id de rotacion (-1 por defecto) (0 - 31 Id de rotacion)
NF_SPRITEOAM[screen][n].doublesize = false; // Usar el "double size" al rotar ? ("NO" por defecto)
NF_SPRITEOAM[screen][n].hide = true; // Ocultar el Sprite ("SI" por defecto)
NF_SPRITEOAM[screen][n].hflip = false; // Volteado Horizontal ("NO" por defecto)
NF_SPRITEOAM[screen][n].vflip = false; // Volteado Vertical ("NO" por defecto)
NF_SPRITEOAM[screen][n].mosaic = false; // Mosaico ("NO" por defecto)
NF_SPRITEOAM[screen][n].gfxid = 0; // Numero de Gfx usado
NF_SPRITEOAM[screen][n].frame = 0; // Frame actual
NF_SPRITEOAM[screen][n].framesize = 0; // Tamaño del frame (en bytes)
NF_SPRITEOAM[screen][n].lastframe = 0; // Ultimo frame
NF_SPRITEOAM[screen][n].created = false; // Esta creado este sprite ?
}
// Inicializa la estructura de datos de la VRAM de Sprites
if (mode == 128) {
NF_SPRVRAM[screen].max = 131072;
} else {
NF_SPRVRAM[screen].max = 65536;
}
NF_SPRVRAM[screen].free = NF_SPRVRAM[screen].max; // Memoria VRAM libre (64kb/128kb)
NF_SPRVRAM[screen].last = 0; // Ultima posicion usada
NF_SPRVRAM[screen].deleted = 0; // Ningun Gfx borrado
NF_SPRVRAM[screen].fragmented = 0; // Memoria VRAM fragmentada
NF_SPRVRAM[screen].inarow = NF_SPRVRAM[screen].max; // Memoria VRAM contigua
for (n = 0; n < 128; n ++) {
NF_SPRVRAM[screen].pos[n] = 0; // Posicion en VRAM para reusar despues de un borrado
NF_SPRVRAM[screen].size[n] = 0; // Tamaño del bloque libre para reusar
}
// Inicializa los datos de las paletas
for (n = 0; n < 16; n ++) {
NF_SPRPALSLOT[screen][n].inuse = false;
NF_SPRPALSLOT[screen][n].ramslot = 0;
}
// Configura el Motor 2D y VRAM segun la pantalla de destino
if (screen == 0) {
// Configura la pantalla 0
REG_DISPCNT |= (DISPLAY_SPR_ACTIVE); // Activa los Sprites en la pantalla superior
vramSetBankB(VRAM_B_MAIN_SPRITE_0x06400000); // Banco B de la VRAM para Sprites (128kb)
memset((void*)0x06400000, 0, 131072); // Borra el contenido del banco B
NF_SPRVRAM[screen].next = (0x06400000); // Guarda la primera posicion de VRAM para Gfx
vramSetBankF(VRAM_F_LCD); // Banco F de la VRAM para paletas extendidas (Sprites) (8kb de 16kb)
memset((void*)0x06890000, 0, 8192); // Borra el contenido del banco F
if (mode == 128) {
oamInit(&oamMain, SpriteMapping_1D_128, true); // Inicializa el OAM (Mapeado de 128 bytes, Paletas extendidas)
} else {
oamInit(&oamMain, SpriteMapping_1D_64, true); // Inicializa el OAM (Mapeado de 64 bytes, Paletas extendidas)
}
} else {
// Configura la pantalla 1
REG_DISPCNT_SUB |= (DISPLAY_SPR_ACTIVE); // Activa los Sprites en la pantalla inferior
vramSetBankD(VRAM_D_SUB_SPRITE); // Banco D de la VRAM para Sprites (128kb)
memset((void*)0x06600000, 0, 131072); // Borra el contenido del banco D
NF_SPRVRAM[screen].next = (0x06600000); // Guarda la primera posicion de VRAM para Gfx
vramSetBankI(VRAM_I_LCD); // Banco I de la VRAM para paletas extendidas (Sprites) (8kb de 16kb)
memset((void*)0x068A0000, 0, 8192); // Borra el contenido del banco I
if (mode == 128) {
oamInit(&oamSub, SpriteMapping_1D_128, true); // Inicializa el OAM (Mapeado de 128 bytes, Paletas extendidas)
} else {
oamInit(&oamSub, SpriteMapping_1D_64, true); // Inicializa el OAM (Mapeado de 64 bytes, Paletas extendidas)
}
}
}
// Funcion NF_LoadSpriteGfx();
void NF_LoadSpriteGfx(const char* file, u16 id, u16 width, u16 height) {
// Verifica el rango de Id's
if ((id < 0) || (id >= NF_SLOTS_SPR256GFX)) {
NF_Error(106, "Sprite GFX", NF_SLOTS_SPR256GFX);
}
// Verifica si la Id esta libre
if (!NF_SPR256GFX[id].available) {
NF_Error(109, "Sprite GFX", id);
}
// Vacia los buffers que se usaran
free(NF_BUFFER_SPR256GFX[id]);
NF_BUFFER_SPR256GFX[id] = NULL;
// Declara los punteros a los ficheros
FILE* file_id;
// Variable para almacenar el path al archivo
char filename[256];
// Carga el archivo .IMG
sprintf(filename, "%s/%s.img", NF_ROOTFOLDER, file);
file_id = fopen(filename, "rb");
if (file_id) { // Si el archivo existe...
// Obten el tamaño del archivo
fseek(file_id, 0, SEEK_END);
NF_SPR256GFX[id].size = ftell(file_id);
rewind(file_id);
// Reserva el espacio en RAM
NF_BUFFER_SPR256GFX[id] = (char*) calloc (NF_SPR256GFX[id].size, sizeof(char));
if (NF_BUFFER_SPR256GFX[id] == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, NF_SPR256GFX[id].size);
}
// Lee el archivo y ponlo en la RAM
fread(NF_BUFFER_SPR256GFX[id], 1, NF_SPR256GFX[id].size, file_id);
} else { // Si el archivo no existe...
NF_Error(101, filename, 0);
}
fclose(file_id); // Cierra el archivo
// Guarda las medidas del grafico
NF_SPR256GFX[id].width = width; // Ancho del Gfx
NF_SPR256GFX[id].height = height; // Altura del Gfx
// Y marca esta ID como usada
NF_SPR256GFX[id].available = false;
}
// Funcion NF_UnloadSpriteGfx();
void NF_UnloadSpriteGfx(u16 id) {
// Verifica el rango de Id's
if ((id < 0) || (id >= NF_SLOTS_SPR256GFX)) {
NF_Error(106, "Sprite GFX", NF_SLOTS_SPR256GFX);
}
// Verifica si la Id esta libre
if (NF_SPR256GFX[id].available) {
NF_Error(110, "Sprite GFX", id);
}
// Vacia el buffer
free(NF_BUFFER_SPR256GFX[id]);
// Y reinicia las variables
NF_BUFFER_SPR256GFX[id] = NULL; // Inicializa puntero
NF_SPR256GFX[id].size = 0; // Tamaño (en bytes) del grafico (GFX)
NF_SPR256GFX[id].width = 0; // Ancho del Gfx
NF_SPR256GFX[id].height = 0; // Altura del Gfx
NF_SPR256GFX[id].available = true; // Disponibilidat del Slot
}
// Funcion NF_LoadSpritePal();
void NF_LoadSpritePal(const char* file, u8 id) {
// Variable temporal del tamaño de la paleta
u32 pal_size = 0;
// Verifica el rango de Id's
if ((id < 0) || (id >= NF_SLOTS_SPR256PAL)) {
NF_Error(106, "Sprite PAL", NF_SLOTS_SPR256PAL);
}
// Verifica si la Id esta libre
if (!NF_SPR256PAL[id].available) {
NF_Error(109, "Sprite PAL", id);
}
// Vacia los buffers que se usaran
free(NF_BUFFER_SPR256PAL[id]);
NF_BUFFER_SPR256PAL[id] = NULL;
// Declara los punteros a los ficheros
FILE* file_id;
// Variable para almacenar el path al archivo
char filename[256];
// Carga el archivo .PAL
sprintf(filename, "%s/%s.pal", NF_ROOTFOLDER, file);
file_id = fopen(filename, "rb");
if (file_id) { // Si el archivo existe...
// Obten el tamaño del archivo
fseek(file_id, 0, SEEK_END);
pal_size = ftell(file_id);
NF_SPR256PAL[id].size = pal_size;
rewind(file_id);
// Si el tamaño es inferior a 512 bytes, ajustalo
if (NF_SPR256PAL[id].size < 512) NF_SPR256PAL[id].size = 512;
// Reserva el espacio en RAM
NF_BUFFER_SPR256PAL[id] = (char*) calloc (NF_SPR256PAL[id].size, sizeof(char));
if (NF_BUFFER_SPR256PAL[id] == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, NF_SPR256PAL[id].size);
}
// Lee el archivo y ponlo en la RAM
fread(NF_BUFFER_SPR256PAL[id], 1, pal_size, file_id);
} else { // Si el archivo no existe...
NF_Error(101, filename, 0);
}
fclose(file_id); // Cierra el archivo
// Y marca esta ID como usada
NF_SPR256PAL[id].available = false;
}
// Funcion NF_UnloadSpritePal();
void NF_UnloadSpritePal(u8 id) {
// Verifica el rango de Id's
if ((id < 0) || (id >= NF_SLOTS_SPR256PAL)) {
NF_Error(106, "Sprite PAL", NF_SLOTS_SPR256PAL);
}
// Verifica si la Id esta libre
if (NF_SPR256PAL[id].available) {
NF_Error(110, "Sprite PAL", id);
}
// Vacia el buffer
free(NF_BUFFER_SPR256PAL[id]);
// Y reinicia las variables
NF_BUFFER_SPR256PAL[id] = NULL; // Inicializa puntero
NF_SPR256PAL[id].size = 0; // Tamaño (en bytes) de la paleta (PAL)
NF_SPR256PAL[id].available = true; // Disponibilidat del Slot
}
// Funcion NF_VramSpriteGfx();
void NF_VramSpriteGfx(u8 screen, u16 ram, u16 vram, bool keepframes) {
// Verifica el rango de Id's de RAM
if ((ram < 0) || (ram >= NF_SLOTS_SPR256GFX)) {
NF_Error(106, "Sprite GFX", (NF_SLOTS_SPR256GFX - 1));
}
// Verifica si slot de RAM esta vacio
if (NF_SPR256GFX[ram].available) {
NF_Error(110, "Sprite GFX", ram);
}
// Verifica el rango de Id's de VRAM
if ((vram < 0) || (vram > 127)) {
NF_Error(106, "VRAM GFX", 127);
}
// Verifica si el slot de VRAM esta libre
if (NF_SPR256VRAM[screen][vram].inuse) {
NF_Error(109, "VRAM", vram);
}
// Variables de uso general
s16 n = 0; // General
s16 id = 255; // Id del posible bloque libre
s16 last_reuse = 0; // Nº del ultimo bloque reusable
u32 gfxsize = 0; // Tamaño de los datos que se copiaran
u32 size = 0; // Diferencia de tamaños entre bloque libre y datos
u8 width = 0; // Calculo de las medidas
u8 height = 0;
bool organize = true; // Se debe de reorganizar el array de bloques libres ?
// Auto calcula el tamaño de 1 frame
width = (NF_SPR256GFX[ram].width >> 3); // (width / 8)
height = (NF_SPR256GFX[ram].height >> 3); // (height / 8)
NF_SPR256VRAM[screen][vram].framesize = ((width * height) << 6); // ((width * height) * 64)
// Auto calcula el ultimo frame de la animacion
NF_SPR256VRAM[screen][vram].lastframe = ((int)(NF_SPR256GFX[ram].size / NF_SPR256VRAM[screen][vram].framesize)) - 1;
NF_SPR256VRAM[screen][vram].inuse = true; // Slot ocupado
// Calcula el tamaño del grafico a copiar segun si debes o no copiar todos los frames
if (keepframes) { // Si debes de mantener los frames en RAM, solo copia el primero
gfxsize = NF_SPR256VRAM[screen][vram].framesize;
} else { // Si no, copialos todos
gfxsize = NF_SPR256GFX[ram].size;
}
// Actualiza la VRAM disponible
NF_SPRVRAM[screen].free -= gfxsize;
// Si no hay suficiente VRAM, error
if (NF_SPRVRAM[screen].free < 0) {
NF_Error(113, "Sprites", gfxsize);
}
// Si hay que aprovechar algun bloque borrado... (tamaño identico, preferente)
if (NF_SPRVRAM[screen].deleted > 0) {
// Busca un bloque vacio del tamaño identico
for (n = 0; n < NF_SPRVRAM[screen].deleted; n ++) {
if (NF_SPRVRAM[screen].size[n] == gfxsize) { // Si el bloque tiene el tamaño suficiente
id = n; // Guarda la Id
n = NF_SPRVRAM[screen].deleted; // y sal
}
}
// Si no habia ningun bloque de tamaño identico, busca el mas parecido (produce fragmentacion)
if (id == 255) {
// Busca un bloque vacio del tamaño suficiente
for (n = 0; n < NF_SPRVRAM[screen].deleted; n ++) {
if (NF_SPRVRAM[screen].size[n] > gfxsize) { // Si el bloque tiene el tamaño suficiente
id = n; // Guarda la Id
n = NF_SPRVRAM[screen].deleted; // y sal
}
}
}
}
// Si hay algun bloque borrado libre del tamaño suficiente...
if (id != 255) {
// Transfiere el grafico a la VRAM
NF_DmaMemCopy((void*)NF_SPRVRAM[screen].pos[id], NF_BUFFER_SPR256GFX[ram], gfxsize);
// Guarda el puntero donde lo has almacenado
NF_SPR256VRAM[screen][vram].address = NF_SPRVRAM[screen].pos[id];
// Si no has usado todo el tamaño, deja constancia
if (gfxsize < NF_SPRVRAM[screen].size[id]) {
// Calcula el tamaño del nuevo bloque libre
size = (NF_SPRVRAM[screen].size[id] - gfxsize);
// Actualiza los datos
NF_SPRVRAM[screen].pos[id] += gfxsize; // Nueva direccion
NF_SPRVRAM[screen].size[id] = size; // Nuevo tamaño
NF_SPRVRAM[screen].fragmented -= gfxsize; // Actualiza el contador de VRAM fragmentada
organize = false; // No se debe de reorganizar el array de bloques
} else { // Si has usado todo el tamaño, deja constancia
NF_SPRVRAM[screen].fragmented -= NF_SPRVRAM[screen].size[id]; // Actualiza el contador de VRAM fragmentada
}
// Se tiene que reorganizar el array de bloques libres ?
if (organize) {
last_reuse = (NF_SPRVRAM[screen].deleted - 1);
if (
(last_reuse > 0) // Si hay mas de un bloque borrado
&&
(id != last_reuse) // Y no es la ultima posicion
) {
// Coloca los valores de la ultima posicion en esta
NF_SPRVRAM[screen].pos[id] = NF_SPRVRAM[screen].pos[last_reuse]; // Nueva direccion
NF_SPRVRAM[screen].size[id] = NF_SPRVRAM[screen].size[last_reuse]; // Nuevo tamaño
}
NF_SPRVRAM[screen].deleted --; // Actualiza el contador de bloques libres, borrando el ultimo registro
}
} else { // Si no habia ningun bloque borrado o con el tamaño suficiente, colacalo al final de la VRAM ocupada
// Actualiza la VRAM contigua disponible (mayor bloque libre al final)
NF_SPRVRAM[screen].inarow -= gfxsize;
// Si no hay suficiente VRAM (contigua), error
if (NF_SPRVRAM[screen].inarow < 0) {
NF_Error(113, "Sprites", gfxsize);
}
// Transfiere el grafico a la VRAM
NF_DmaMemCopy((void*)NF_SPRVRAM[screen].next, NF_BUFFER_SPR256GFX[ram], gfxsize);
// Guarda el puntero donde lo has almacenado
NF_SPR256VRAM[screen][vram].address = NF_SPRVRAM[screen].next;
// Guarda la direccion actual como la ultima usada
NF_SPRVRAM[screen].last = NF_SPRVRAM[screen].next;
// Calcula la siguiente posicion libre
NF_SPRVRAM[screen].next += gfxsize;
}
// Guarda los datos del Gfx que se copiara a la VRAM.
NF_SPR256VRAM[screen][vram].size = gfxsize; // Tamaño en bytes de los datos copiados
NF_SPR256VRAM[screen][vram].width = NF_SPR256GFX[ram].width; // Alto (px)
NF_SPR256VRAM[screen][vram].height = NF_SPR256GFX[ram].height; // Ancho (px)
NF_SPR256VRAM[screen][vram].ramid = ram; // Slot RAM de origen
NF_SPR256VRAM[screen][vram].keepframes = keepframes; // Debes guardar los frames en RAM o copiarlos a la VRAM?
}
// Funcion NF_FreeSpriteGfx();
void NF_FreeSpriteGfx(u8 screen, u16 id) {
// Verifica si hay un grafico cargado en esa Id.
if (!NF_SPR256VRAM[screen][id].inuse) {
NF_Error(110, "Sprite Gfx", id);
}
// Borra el Gfx de la VRAM (pon a 0 todos los Bytes)
memset((void*)NF_SPR256VRAM[screen][id].address, 0, NF_SPR256VRAM[screen][id].size);
// Actualiza la cantidad de VRAM disponible
NF_SPRVRAM[screen].free += NF_SPR256VRAM[screen][id].size;
// Guarda la posicion y tamaño del bloque borrado para su reutilizacion
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;
// Incrementa en contador de bloques borrados
NF_SPRVRAM[screen].deleted ++;
// Incrementa el contador de memoria fragmentada
NF_SPRVRAM[screen].fragmented += NF_SPR256VRAM[screen][id].size;
// Reinicia los datos de esta Id. de gfx
NF_SPR256VRAM[screen][id].size = 0; // Tamaño en bytes
NF_SPR256VRAM[screen][id].width = 0; // Alto (px)
NF_SPR256VRAM[screen][id].height = 0; // Ancho (px)
NF_SPR256VRAM[screen][id].address = 0; // Puntero en VRAM
NF_SPR256VRAM[screen][id].framesize = 0; // Tamaño del frame (en bytes)
NF_SPR256VRAM[screen][id].lastframe = 0; // Ultimo frame
NF_SPR256VRAM[screen][id].inuse = false;
// Debes desfragmentar la VRAM
if (NF_SPRVRAM[screen].fragmented >= (NF_SPRVRAM[screen].inarow >> 1)) {
NF_VramSpriteGfxDefrag(screen);
}
}
// Funcion NF_VramSpriteGfxDefrag();
void NF_VramSpriteGfxDefrag(u8 screen) {
// Calcula la VRAM en uso y crea un buffer para guardarla
u32 used_vram = ((NF_SPRVRAM[screen].max - NF_SPRVRAM[screen].free) + 1);
char* buffer;
buffer = (char*) calloc (used_vram, sizeof(char));
if (buffer == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, used_vram);
}
char* address[128]; // Guarda la direccion en RAM
u32 size[128]; // Guarda el tamaño
u32 ram = 0; // Puntero inicial de RAM
u8 n = 0; // Variable General
u32 frame_address = 0; // Guarda la direccion de VRAM del frame
// Copia los datos de la VRAM a la RAM
for (n = 0; n < 128; n ++) {
// Si esta en uso
if (NF_SPR256VRAM[screen][n].inuse) {
// Copia el Gfx a la RAM
address[n] = (buffer + ram); // Calcula el puntero
size[n] = NF_SPR256VRAM[screen][n].size; // Almacena el tamaño
NF_DmaMemCopy(address[n], (void*)NF_SPR256VRAM[screen][n].address, size[n]); // Copialo a la VRAM
ram += size[n]; // Siguiente posicion en RAM (relativa)
}
}
// Inicializa la estructura de datos de la VRAM de Sprites
NF_SPRVRAM[screen].free = NF_SPRVRAM[screen].max; // Memoria VRAM libre (128kb)
NF_SPRVRAM[screen].last = 0; // Ultima posicion usada
NF_SPRVRAM[screen].deleted = 0; // Ningun Gfx borrado
NF_SPRVRAM[screen].fragmented = 0; // Memoria VRAM fragmentada
NF_SPRVRAM[screen].inarow = NF_SPRVRAM[screen].max; // Memoria VRAM contigua
for (n = 0; n < 128; n ++) {
NF_SPRVRAM[screen].pos[n] = 0; // Posicion en VRAM para reusar despues de un borrado
NF_SPRVRAM[screen].size[n] = 0; // Tamaño del bloque libre para reusar
}
// Aplica la direccion de inicio de la VRAM
if (screen == 0) {
NF_SPRVRAM[screen].next = (0x06400000);
} else {
NF_SPRVRAM[screen].next = (0x06600000);
}
// Ahora, copia de nuevo los datos a la VRAM, pero alineados
for (n = 0; n < 128; n ++) {
// Si esta en uso
if (NF_SPR256VRAM[screen][n].inuse) {
NF_DmaMemCopy((void*)NF_SPRVRAM[screen].next, address[n], size[n]); // Vuelve a colocar la el Gfx en VRAM
NF_SPR256VRAM[screen][n].address = NF_SPRVRAM[screen].next; // Guarda la nueva posicion en VRAM
NF_SPRVRAM[screen].free -= size[n]; // Ram libre
NF_SPRVRAM[screen].inarow -= size[n]; // Ram libre en bloque
NF_SPRVRAM[screen].last = NF_SPRVRAM[screen].next; // Guarda la posicion como ultima usada
NF_SPRVRAM[screen].next += size[n]; // Y calcula la siguiente posicion a escribir
}
}
// Reasigna a los sprites las nuevas posiciones de los graficos que usan
for (n = 0; n < 128; n ++) {
if (NF_SPRITEOAM[screen][n].created) {
if (NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][n].gfxid].keepframes) {
// Si la opcion de animacion KEEP FRAMES esta activada,
// simplemente asigna la nueva direccion en VRAM del grafico.
NF_SPRITEOAM[screen][n].gfx = (u32*)NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][n].gfxid].address;
} else {
// Si la opcion KEEP FRAMES esta desactivada,
// calcula el desplazamiento dentro de la nueva direccion asignada.
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;
}
}
}
// Vacia el buffer
free(buffer);
buffer = NULL;
}
// Funcion NF_VramSpritePal();
void NF_VramSpritePal(u8 screen, u8 id, u8 slot) {
// Verifica el rango de Id's
if ((id < 0) || (id >= NF_SLOTS_SPR256PAL)) {
NF_Error(106, "Sprite PAL", NF_SLOTS_SPR256PAL);
}
// Verifica si la Id esta libre
if (NF_SPR256PAL[id].available) {
NF_Error(110, "Sprite PAL", id);
}
// Verifica si te has salido de rango (Paleta)
if ((slot < 0) || (slot > 15)) {
NF_Error(106, "Sprite Palette Slot", 15);
}
// Copia la paleta a la VRAM, segun la pantalla y el Slot
u32 address = 0;
if (screen == 0) {
address = (0x06890000) + (slot << 9); // Calcula donde guardaras la paleta
vramSetBankF(VRAM_F_LCD); // Bloquea el banco F para escribir las paletas
NF_DmaMemCopy((void*)address, NF_BUFFER_SPR256PAL[id], NF_SPR256PAL[id].size); // Copia la paleta al banco F
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE); // Pon el banco F en modo paleta extendida
} else {
address = (0x068A0000) + (slot << 9); // Calcula donde guardaras la paleta
vramSetBankI(VRAM_I_LCD); // Bloquea el banco I para escribir las paletas
NF_DmaMemCopy((void*)address, NF_BUFFER_SPR256PAL[id], NF_SPR256PAL[id].size); // Copia la paleta al banco I
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE); // Pon el banco I en modo paleta extendida
}
NF_SPRPALSLOT[screen][slot].inuse = true; // Marca el SLOT de paleta como en uso
NF_SPRPALSLOT[screen][slot].ramslot = id; // Guarda el slot de RAM donde esta la paleta original
}
// Funcion NF_CreateSprite();
void NF_CreateSprite(u8 screen, u8 id, u16 gfx, u8 pal, s16 x, s16 y) {
// Verifica el rango de Id's de Sprites
if ((id < 0) || (id > 127)) {
NF_Error(106, "Sprite", 127);
}
// Verifica el rango de Id's de Gfx
if ((gfx < 0) || (gfx > 127)) {
NF_Error(106, "Sprite GFX", 127);
}
// Verifica si esta el Gfx en VRAM
if (!NF_SPR256VRAM[screen][gfx].inuse) {
NF_Error(111, "Sprite GFX", gfx);
}
// Verifica el rango de slots de paletas
if ((pal < 0) || (pal > 15)) {
NF_Error(106, "Sprite Palette Slot", 15);
}
// Verifica si esta la paleta en VRAM
if (!NF_SPRPALSLOT[screen][pal].inuse) {
NF_Error(111, "Sprite PAL", pal);
}
// // Informa al array de OAM del Id
NF_SPRITEOAM[screen][id].index = id;
// Informa al array de OAM del Gfx a usar
NF_SPRITEOAM[screen][id].gfx = (u32*)NF_SPR256VRAM[screen][gfx].address;
// Informa al array de OAM de la Paleta a usar
NF_SPRITEOAM[screen][id].pal = pal;
// Informa al array de OAM de la coordenada X
NF_SPRITEOAM[screen][id].x = x;
// Informa al array de OAM de la coordenada X
NF_SPRITEOAM[screen][id].y = y;
// Informa al array de OAM del numero de colores
NF_SPRITEOAM[screen][id].color = SpriteColorFormat_256Color;
// Informa al array de OAM de que debe mostrar el sprite
NF_SPRITEOAM[screen][id].hide = false;
// Informa al array de OAM del Id de Gfx usado
NF_SPRITEOAM[screen][id].gfxid = gfx;
// Informa al array de OAM de que el sprite se ha creado
NF_SPRITEOAM[screen][id].created = true;
// Informa al array de OAM del tamaño
if ((NF_SPR256VRAM[screen][gfx].width == 8) && (NF_SPR256VRAM[screen][gfx].height == 8)) { // 8x8
if (NF_SPRVRAM[screen].max != 131072) { // En modo 1D_128, este tamaño es ilegal
NF_SPRITEOAM[screen][id].size = SpriteSize_8x8;
} else {
NF_Error(120, NULL, id);
}
}
if ((NF_SPR256VRAM[screen][gfx].width == 16) && (NF_SPR256VRAM[screen][gfx].height == 16)) { // 16x16
NF_SPRITEOAM[screen][id].size = SpriteSize_16x16;
}
if ((NF_SPR256VRAM[screen][gfx].width == 32) && (NF_SPR256VRAM[screen][gfx].height == 32)) { // 32x32
NF_SPRITEOAM[screen][id].size = SpriteSize_32x32;
}
if ((NF_SPR256VRAM[screen][gfx].width == 64) && (NF_SPR256VRAM[screen][gfx].height == 64)) { // 64x64
NF_SPRITEOAM[screen][id].size = SpriteSize_64x64;
}
if ((NF_SPR256VRAM[screen][gfx].width == 16) && (NF_SPR256VRAM[screen][gfx].height == 8)) { // 16x8
NF_SPRITEOAM[screen][id].size = SpriteSize_16x8;
}
if ((NF_SPR256VRAM[screen][gfx].width == 32) && (NF_SPR256VRAM[screen][gfx].height == 8)) { // 32x8
NF_SPRITEOAM[screen][id].size = SpriteSize_32x8;
}
if ((NF_SPR256VRAM[screen][gfx].width == 32) && (NF_SPR256VRAM[screen][gfx].height == 16)) { // 32x16
NF_SPRITEOAM[screen][id].size = SpriteSize_32x16;
}
if ((NF_SPR256VRAM[screen][gfx].width == 64) && (NF_SPR256VRAM[screen][gfx].height == 32)) { // 64x32
NF_SPRITEOAM[screen][id].size = SpriteSize_64x32;
}
if ((NF_SPR256VRAM[screen][gfx].width == 8) && (NF_SPR256VRAM[screen][gfx].height == 16)) { // 8x16
NF_SPRITEOAM[screen][id].size = SpriteSize_8x16;
}
if ((NF_SPR256VRAM[screen][gfx].width == 8) && (NF_SPR256VRAM[screen][gfx].height == 32)) { // 8x32
NF_SPRITEOAM[screen][id].size = SpriteSize_8x32;
}
if ((NF_SPR256VRAM[screen][gfx].width == 16) && (NF_SPR256VRAM[screen][gfx].height == 32)) { // 16x32
NF_SPRITEOAM[screen][id].size = SpriteSize_16x32;
}
if ((NF_SPR256VRAM[screen][gfx].width == 32) && (NF_SPR256VRAM[screen][gfx].height == 64)) { // 32x64
NF_SPRITEOAM[screen][id].size = SpriteSize_32x64;
}
// Informa al array de OAM del ultimo frame del Sprite
NF_SPRITEOAM[screen][id].lastframe = NF_SPR256VRAM[screen][gfx].lastframe;
// Informa al array de OAM del tamaño del frame del Sprite (en bytes)
NF_SPRITEOAM[screen][id].framesize = NF_SPR256VRAM[screen][gfx].framesize;
// Por defecto, el primer frame (0)
NF_SPRITEOAM[screen][id].frame = 0;
}
// Funcion NF_DeleteSprite();
void NF_DeleteSprite(u8 screen, u8 id) {
// Verifica el rango de Id's de Sprites
if ((id < 0) || (id > 127)) {
NF_Error(106, "Sprite", 127);
}
// Verifica si el Sprite esta creado
if (!NF_SPRITEOAM[screen][id].created) {
char text[4];
sprintf(text, "%d", screen);
NF_Error(112, text, id);
}
// Reinicia todas las variables de ese Sprite
NF_SPRITEOAM[screen][id].index = id; // Numero de Sprite
NF_SPRITEOAM[screen][id].x = 0; // Coordenada X del Sprite (0 por defecto)
NF_SPRITEOAM[screen][id].y = 0; // Coordenada Y del Sprite (0 por defecto)
NF_SPRITEOAM[screen][id].layer = 0; // Prioridad en las capas (0 por defecto)
NF_SPRITEOAM[screen][id].pal = 0; // Paleta que usaras (0 por defecto)
NF_SPRITEOAM[screen][id].size = SpriteSize_8x8; // Tamaño del Sprite (macro) (8x8 por defecto)
NF_SPRITEOAM[screen][id].color = SpriteColorFormat_256Color; // Modo de color (macro) (256 colores)
NF_SPRITEOAM[screen][id].gfx = NULL; // Puntero al grafico usado
NF_SPRITEOAM[screen][id].rot = -1; // Id de rotacion (-1 ninguno) (0 - 31 Id de rotacion)
NF_SPRITEOAM[screen][id].doublesize = false; // Usar el "double size" al rotar ? ("NO" por defecto)
NF_SPRITEOAM[screen][id].hide = true; // Ocultar el Sprite ("SI" por defecto)
NF_SPRITEOAM[screen][id].hflip = false; // Volteado Horizontal ("NO" por defecto)
NF_SPRITEOAM[screen][id].vflip = false; // Volteado Vertical ("NO" por defecto)
NF_SPRITEOAM[screen][id].mosaic = false; // Mosaico ("NO" por defecto)
NF_SPRITEOAM[screen][id].gfxid = 0; // Numero de Gfx usado
NF_SPRITEOAM[screen][id].frame = 0; // Frame actual
NF_SPRITEOAM[screen][id].framesize = 0; // Tamaño del frame (en bytes)
NF_SPRITEOAM[screen][id].lastframe = 0; // Ultimo frame
NF_SPRITEOAM[screen][id].created = false; // Esta creado este sprite ?
}
// Funcion NF_SpriteOamSet();
void NF_SpriteOamSet(u8 screen) {
u8 n = 0; // Variable de uso general
if (screen == 0) {
for (n = 0; n < 128; n ++) {
oamSet(&oamMain, // OAM pantalla superior (Main, 0)
NF_SPRITEOAM[screen][n].index, // Numero de Sprite
NF_SPRITEOAM[screen][n].x, // Coordenada X del Sprite
NF_SPRITEOAM[screen][n].y, // Coordenada Y del Sprite
NF_SPRITEOAM[screen][n].layer, // Prioridad en las capas
NF_SPRITEOAM[screen][n].pal, // Paleta que usaras
NF_SPRITEOAM[screen][n].size, // Tamaño del Sprite (macro)
NF_SPRITEOAM[screen][n].color, // Modo de color (macro)
NF_SPRITEOAM[screen][n].gfx, // Puntero al grafico usado
NF_SPRITEOAM[screen][n].rot, // Valor de la rotacion
NF_SPRITEOAM[screen][n].doublesize, // Usar el "double size" al rotar ?
NF_SPRITEOAM[screen][n].hide, // Ocultar el Sprite
NF_SPRITEOAM[screen][n].hflip, // Volteado Horizontal
NF_SPRITEOAM[screen][n].vflip, // Volteado Vertical
NF_SPRITEOAM[screen][n].mosaic); // Mosaico
}
} else {
for (n = 0; n < 128; n ++) {
oamSet(&oamSub, // OAM pantalla superior (Main, 0)
NF_SPRITEOAM[screen][n].index, // Numero de Sprite
NF_SPRITEOAM[screen][n].x, // Coordenada X del Sprite
NF_SPRITEOAM[screen][n].y, // Coordenada Y del Sprite
NF_SPRITEOAM[screen][n].layer, // Prioridad en las capas
NF_SPRITEOAM[screen][n].pal, // Paleta que usaras
NF_SPRITEOAM[screen][n].size, // Tamaño del Sprite (macro)
NF_SPRITEOAM[screen][n].color, // Modo de color (macro)
NF_SPRITEOAM[screen][n].gfx, // Puntero al grafico usado
NF_SPRITEOAM[screen][n].rot, // Valor de la rotacion
NF_SPRITEOAM[screen][n].doublesize, // Usar el "double size" al rotar ?
NF_SPRITEOAM[screen][n].hide, // Ocultar el Sprite
NF_SPRITEOAM[screen][n].hflip, // Volteado Horizontal
NF_SPRITEOAM[screen][n].vflip, // Volteado Vertical
NF_SPRITEOAM[screen][n].mosaic); // Mosaico
}
}
}
// Funcion NF_SpriteSetPalColor();
void NF_SpriteSetPalColor(u8 screen, u8 pal, u8 number, u8 r, u8 g, u8 b) {
// Verifica si esta la paleta en VRAM
if (!NF_SPRPALSLOT[screen][pal].inuse) {
NF_Error(111, "Sprite PAL", pal);
}
// Calcula el valor RGB
u16 rgb = ((r)|((g) << 5)|((b) << 10));
// Direccion en VRAM
u32 address = 0;
// Modifica la paleta
if (screen == 0) {
address = (0x06890000) + (pal << 9) + (number << 1); // Calcula donde guardaras el color de la paleta
vramSetBankF(VRAM_F_LCD); // Bloquea el banco F para escribir las paletas
*((u16*)address) = rgb; // Cambia el color
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE); // Pon el banco F en modo paleta extendida
} else {
address = (0x068A0000) + (pal << 9) + (number << 1); // Calcula donde guardaras el color de la paleta
vramSetBankI(VRAM_I_LCD); // Bloquea el banco I para escribir las paletas
*((u16*)address) = rgb; // Cambia el color
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE); // Pon el banco I en modo paleta extendida
}
}
// Funcion NF_SpriteEditPalColor();
void NF_SpriteEditPalColor(u8 screen, u8 pal, u8 number, u8 r, u8 g, u8 b) {
// Verifica si esta la paleta en VRAM
if (!NF_SPRPALSLOT[screen][pal].inuse) {
NF_Error(111, "Sprite PAL", pal);
}
// Calcula el valor RGB
u16 rgb = ((r)|((g) << 5)|((b) << 10));
// Calcula los valores para el HI-Byte y el LO-Byte
u8 hibyte = ((rgb >> 8) & 0xff);
u8 lobyte = (rgb & 0xff);
// Graba los bytes
*(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + (number << 1)) = lobyte;
*(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + ((number << 1) + 1)) = hibyte;
}
// Funcion NF_SpriteUpdatePalette();
void NF_SpriteUpdatePalette(u8 screen, u8 pal) {
// Verifica si esta la paleta en VRAM
if (!NF_SPRPALSLOT[screen][pal].inuse) {
NF_Error(111, "Sprite PAL", pal);
}
// Direccion en VRAM
u32 address = 0;
// Obten el slot donde esta la paleta en RAM
u8 slot = NF_SPRPALSLOT[screen][pal].ramslot;
// Actualiza la paleta en VRAM
if (screen == 0) {
address = (0x06890000) + (pal << 9); // Calcula donde guardaras la paleta
vramSetBankF(VRAM_F_LCD); // Bloquea el banco F para escribir las paletas
NF_DmaMemCopy((void*)address, NF_BUFFER_SPR256PAL[slot], NF_SPR256PAL[slot].size); // Copia la paleta al banco F
vramSetBankF(VRAM_F_SPRITE_EXT_PALETTE); // Pon el banco F en modo paleta extendida
} else {
address = (0x068A0000) + (pal << 9); // Calcula donde guardaras la paleta
vramSetBankI(VRAM_I_LCD); // Bloquea el banco I para escribir las paletas
NF_DmaMemCopy((void*)address, NF_BUFFER_SPR256PAL[slot], NF_SPR256PAL[slot].size); // Copia la paleta al banco I
vramSetBankI(VRAM_I_SUB_SPRITE_EXT_PALETTE); // Pon el banco I en modo paleta extendida
}
}
// Funcion NF_SpriteGetPalColor();
void NF_SpriteGetPalColor(u8 screen, u8 pal, u8 number, u8* r, u8* g, u8* b) {
// Verifica si esta la paleta en VRAM
if (!NF_SPRPALSLOT[screen][pal].inuse) {
NF_Error(111, "Sprite PAL", pal);
}
// Obten los bytes
u8 lobyte = *(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + (number << 1));
u8 hibyte = *(NF_BUFFER_SPR256PAL[NF_SPRPALSLOT[screen][pal].ramslot] + ((number << 1) + 1));
// Calcula el RGB (compuesto)
u16 rgb = ((hibyte << 8) | lobyte);
// Calcula los RGB
*r = (rgb & 0x1F);
*g = ((rgb >> 5) & 0x1F);
*b = ((rgb >> 10) & 0x1F);
}