nds_nflib/source/nf_text.c
lifehackerhansol ef23e0418a
Do not force libfilesystem or libfat dependency
This removes dependency from the (frankly awful) libfilesystem, as well
as libfat.

fatInitDefault() or nitroFSInit() should be initialized outside of
NFlib, if one requires it. Within NFLib, we will only check whether
NitroFS is accessible, and fail otherwise.

This allows a user to still use libfat, libfilesystem or any other
NitroFS / FAT implementation, should one wish to use it, as long as the
implementation of NitroFS mounts to `nitro:/`, or the storage medium is
mounted to `sd:/` or `fat:/`.

This is a breaking change: users will need to update their code to
handle NitroFS and/or FAT inits on their own.

This also removes unnecessary filesystem.h and fat.h includes from all
other files.
2023-04-13 11:30:37 -07:00

502 lines
13 KiB
C

// SPDX-License-Identifier: MIT
//
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
//
// NightFox LIB - Funciones de Textos
// http://www.nightfoxandco.com/
// Version 20140413
// Includes devKitPro
#include <nds.h>
// Includes C
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// Includes propios
#include "nf_basic.h"
#include "nf_2d.h"
#include "nf_tiledbg.h"
#include "nf_text.h"
// Define los buffers para almacenar las capas de texto
NF_TYPE_TEXT_INFO NF_TEXT[2][4];
// Funcion NF_InitTextSys();
void NF_InitTextSys(u8 screen) {
u8 n = 0;
// Reinicia las variables
for (n = 0; n < 4; n ++) {
NF_TEXT[screen][n].width = 0; // Ancho del mapa de texto
NF_TEXT[screen][n].height = 0; // Alto del mapa de texto
NF_TEXT[screen][n].rotation = 0; // Rotacion a 0 (ninguna)
NF_TEXT[screen][n].slot = 255; // Slot donde esta el tileset
NF_TEXT[screen][n].pal = 0; // nº de paleta extendida (0 por defecto)
NF_TEXT[screen][n].exist = false; // Marcalo como no existente
NF_TEXT[screen][n].update = false; // No es necesario actualizarlo
}
}
// Funcion NF_LoadTextFont();
void NF_LoadTextFont(const char* file, const char* name, u16 width, u16 height, u8 rotation) {
// Busca un slot libre
u16 n = 0;
u8 slot = 255;
for (n = 0; n < NF_SLOTS_TBG; n ++) { // Busca en todos los slots
if (NF_TILEDBG[n].available) { // Si esta libre
NF_TILEDBG[n].available = false; // Marcalo como en uso
slot = n; // Guarda el slot a usar
n = NF_SLOTS_TBG; // Deja de buscar
}
}
// Si no hay ningun slot libre, error
if (slot == 255) {
NF_Error(103, "Tiled Bg", NF_SLOTS_TBG);
}
// Verifica que el fondo sea multiplo de 256px (32 tiles)
if (((width % 256) != 0) || ((height % 256) != 0)) {
NF_Error(115, file, 0);
}
// Vacia los buffers que se usaran
free(NF_BUFFER_BGMAP[slot]); // Buffer para los mapas
NF_BUFFER_BGMAP[slot] = NULL;
free(NF_BUFFER_BGTILES[slot]); // Buffer para los tiles
NF_BUFFER_BGTILES[slot] = NULL;
free(NF_BUFFER_BGPAL[slot]); // Buffer para los paletas
NF_BUFFER_BGPAL[slot] = NULL;
// Declara los punteros a los ficheros
FILE* file_id;
// Variable para almacenar el path al archivo
char filename[256];
// Carga el archivo .FNT
sprintf(filename, "%s/%s.fnt", NF_ROOTFOLDER, file);
file_id = fopen(filename, "rb");
if (file_id) { // Si el archivo existe...
// Obten el tamaño del archivo
NF_TILEDBG[slot].tilesize = (NF_TEXT_FONT_CHARS << 6); // 100 caracteres x 64 bytes
// Reserva el espacio en RAM
NF_BUFFER_BGTILES[slot] = (char*) calloc (NF_TILEDBG[slot].tilesize, sizeof(char));
if (NF_BUFFER_BGTILES[slot] == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, NF_TILEDBG[slot].tilesize);
}
fread(NF_BUFFER_BGTILES[slot], 1, NF_TILEDBG[slot].tilesize, file_id); // Lee el resto de caracteres de la fuente
} else { // Si el archivo no existe...
NF_Error(101, filename, 0);
}
fclose(file_id); // Cierra el archivo
// Rota los Gfx de los tiles si es necesario
if (rotation > 0) {
for (n = 0; n < NF_TEXT_FONT_CHARS; n ++) {
NF_RotateTileGfx(slot, n, rotation);
}
}
// Crea un archivo .MAP vacio en RAM
// ((ancho / 8) * (alto / 8)) * 2
NF_TILEDBG[slot].mapsize = (((width >> 3) * (height >> 3)) << 1);
// Reserva el espacio en RAM
NF_BUFFER_BGMAP[slot] = (char*) calloc (NF_TILEDBG[slot].mapsize, sizeof(char));
if (NF_BUFFER_BGMAP[slot] == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, NF_TILEDBG[slot].mapsize);
}
// Y ponlo a 0
memset(NF_BUFFER_BGMAP[slot], 0, NF_TILEDBG[slot].mapsize);
// 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);
NF_TILEDBG[slot].palsize = ftell(file_id);
rewind(file_id);
// Reserva el espacio en RAM
NF_BUFFER_BGPAL[slot] = (char*) calloc (NF_TILEDBG[slot].palsize, sizeof(char));
if (NF_BUFFER_BGPAL[slot] == NULL) { // Si no hay suficiente RAM libre
NF_Error(102, NULL, NF_TILEDBG[slot].palsize);
}
// Lee el archivo y ponlo en la RAM
fread(NF_BUFFER_BGPAL[slot], 1, NF_TILEDBG[slot].palsize, file_id);
} else { // Si el archivo no existe...
NF_Error(101, filename, 0);
}
fclose(file_id); // Cierra el archivo
// Guarda el nombre del Fondo
sprintf(NF_TILEDBG[slot].name, "%s", name);
// Y las medidas
NF_TILEDBG[slot].width = width;
NF_TILEDBG[slot].height = height;
}
// Funcion NF_UnloadTestFont();
void NF_UnloadTextFont(const char* name) {
NF_UnloadTiledBg(name);
}
// Funcion NF_CreateTextLayer();
void NF_CreateTextLayer(u8 screen, u8 layer, u8 rotation, const char* name) {
u8 n = 0; // Bucle
u8 slot = 255; // Slot seleccionado
char bg[32]; // Nombre
// Crea un fondo para usarlo como capa de texto
NF_CreateTiledBg(screen, layer, name);
// Busca el numero de slot donde esta cargada la fuente
sprintf(bg, "%s", name); // Obten el nombre del fondo a buscar
for (n = 0; n < NF_SLOTS_TBG; n ++) { // Busca en todos los slots
if (strcmp(bg, NF_TILEDBG[n].name) == 0) { // Si lo encuentras
slot = n; // Guarda el slot a usar
n = NF_SLOTS_TBG; // Deja de buscar
}
}
// Guarda si el texto debe ser rotado
NF_TEXT[screen][layer].rotation = rotation;
// Guarda las medidas del fondo en tiles (ultimo numero de tile)
NF_TEXT[screen][layer].width = ((NF_TILEDBG[slot].width >> 3) - 1);
NF_TEXT[screen][layer].height = ((NF_TILEDBG[slot].height >> 3) - 1);
// Almacena el slot donde esta cargada la fuente
NF_TEXT[screen][layer].slot = slot;
// Y marcalo como creado
NF_TEXT[screen][layer].exist = true;
}
// Funcion NF_DeleteTextLayer();
void NF_DeleteTextLayer(u8 screen, u8 layer) {
// Verifica si la capa de texto de destino existe
if (!NF_TEXT[screen][layer].exist) {
NF_Error(114, NULL, screen);
}
// Borra el fondo usado como capa de texto
NF_DeleteTiledBg(screen, layer);
// Guarda si el texto debe ser rotado
NF_TEXT[screen][layer].rotation = 0;
// Guarda las medidas del fondo en tiles
NF_TEXT[screen][layer].width = 0;
NF_TEXT[screen][layer].height = 0;
// Y marcalo como creado
NF_TEXT[screen][layer].exist = false;
}
// Funcion NF_WriteText();
void NF_WriteText(u8 screen, u8 layer, u16 x, u16 y, const char* text) {
// Verifica si la capa de texto de destino existe
if (!NF_TEXT[screen][layer].exist) {
NF_Error(114, NULL, screen);
}
u16 n = 0; // Variable de uso general
s16 value = 0; // Valor
u16 tsize = 0; // Almacena el numero de letras
tsize = strlen(text); // Calcula el numero de letras del texto
u8* string; // Buffer temporal
string = NULL;
string = (u8*) calloc (tsize, sizeof(u8));
// Almacena en el buffer temporal el valor de los caracteres
for (n = 0; n < tsize; n ++) {
value = ((int)(text[n])) - 32; // Resta 32 al valor entrado
if (value < 0) value = 0;
string[n] = value;
// Si es un caracter especial...
if (string[n] > 95) {
// Resetea el caracter...
string[n] = 0;
// Verifica caracteres especiales
switch (((int)(text[n]))) {
// Salto de linea
case 10: // \n
string[n] = 200;
break;
// Caracteres especiales
case 199: // Ç
string[n] = 96;
break;
case 231: // ç
string[n] = 97;
break;
case 209: // Ñ
string[n] = 98;
break;
case 241: // ñ
string[n] = 99;
break;
// Acentos Mayusculas
case 193: // Á
string[n] = 100;
break;
case 201: // É
string[n] = 101;
break;
case 205: // Í
string[n] = 102;
break;
case 211: // Ó
string[n] = 103;
break;
case 218: // Ú
string[n] = 104;
break;
// Acentos Minusculas
case 225: // á
string[n] = 105;
break;
case 233: // é
string[n] = 106;
break;
case 237: // í
string[n] = 107;
break;
case 243: // ó
string[n] = 108;
break;
case 250: // ú
string[n] = 109;
break;
// Dieresis
case 239: // ï
string[n] = 110;
break;
case 252: // ü
string[n] = 111;
break;
// Admiracion e interrogante (Español)
case 161: // ¡
string[n] = 112;
break;
case 191: // ¿
string[n] = 113;
break;
// Caracter invalido
default:
string[n] = 0;
break;
}
}
}
// Variable para calcular la posicion del texto
s16 tx = 0; // X
s16 ty = 0; // Y
// Escribe los datos en el buffer de texto, segun la rotacion
switch (NF_TEXT[screen][layer].rotation) {
case 0: // Sin rotacion
// Traspasa las coordenadas virtuales a las reales
tx = x;
ty = y;
// Copia el texto al buffer letra a letra
for (n = 0; n < tsize; n ++) {
// Si es un caracter valido
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR) {
// Escribe la letra correspondiente
NF_SetTileOfMap(screen,layer, tx, ty, ((NF_TEXT[screen][layer].pal << 12) + string[n]));
// Siguiente letra
tx ++;
}
if ((tx > NF_TEXT[screen][layer].width) || (string[n] == 200)) { // Si llegas al final de linea,
tx = 0; // salto de linea
ty ++;
if (ty > NF_TEXT[screen][layer].height) { // Si estas en la ultima linea,
ty = 0; // vuelve a la primera
}
}
}
break;
case 1: // Rotacion 90º a la derecha
// Traspasa las coordenadas virtuales a las reales
tx = (NF_TEXT[screen][layer].width - y);
ty = x;
// Copia el texto al buffer letra a letra
for (n = 0; n < tsize; n ++) {
// Si es un caracter valido
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR) {
// Escribe la letra correspondiente
NF_SetTileOfMap(screen,layer, tx, ty, ((NF_TEXT[screen][layer].pal << 12) + string[n]));
// Siguiente letra
ty ++;
}
if ((ty > NF_TEXT[screen][layer].height) || (string[n] == 200)) { // Si llegas al final de linea,
ty = 0; // salto de linea
tx --;
if (tx < 0) { // Si estas en la ultima linea,
tx = NF_TEXT[screen][layer].width; // vuelve a la primera
}
}
}
break;
case 2: // Rotacion 90º a la izquierda
// Traspasa las coordenadas virtuales a las reales
tx = y;
ty = (NF_TEXT[screen][layer].height - x);
// Copia el texto al buffer letra a letra
for (n = 0; n < tsize; n ++) {
// Si es un caracter valido
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR) {
// Escribe la letra correspondiente
NF_SetTileOfMap(screen,layer, tx, ty, ((NF_TEXT[screen][layer].pal << 12) + string[n]));
// Siguiente letra
ty --;
}
if ((ty < 0) || (string[n] == 200)) { // Si llegas al final de linea,
ty = NF_TEXT[screen][layer].height; // Salto de linea
tx ++;
if (tx > NF_TEXT[screen][layer].width) { // Si llegas a la ultima linea
tx = 0; // vuelve a la primera
}
}
}
break;
}
// Marca esta capa de texto para actualizar
NF_TEXT[screen][layer].update = true;
// Libera el buffer
free(string);
}
// Funcion NF_UpdateTextLayers();
void NF_UpdateTextLayers(void) {
// Variables
u8 screen = 0;
u8 layer = 0;
// Verifica si se tiene que actualizar la capa de texto
for (screen = 0; screen < 2; screen ++) { // Bucle de pantalla
for (layer = 0; layer < 4; layer ++) { // Bucle de capas
if (NF_TEXT[screen][layer].update) { // Si estas marcado para actualizar, hazlo
NF_UpdateVramMap(screen, layer);
// Y marcala como actualizada
NF_TEXT[screen][layer].update = false;
}
}
}
}
// Funcion NF_ClearTextLayer();
void NF_ClearTextLayer(u8 screen, u8 layer) {
// Verifica si la capa de texto de destino existe
if (!NF_TEXT[screen][layer].exist) {
NF_Error(114, NULL, screen);
}
// Calcula el tamaño del buffer
u32 size = (((NF_TEXT[screen][layer].width + 1) * (NF_TEXT[screen][layer].height + 1)) << 1);
// Pon a 0 todos los bytes del mapa de la capa de texto
memset(NF_BUFFER_BGMAP[NF_TEXT[screen][layer].slot], 0, size);
// Marca esta capa de texto para actualizar
NF_TEXT[screen][layer].update = true;
}
// Funcion NF_DefineTextColor();
void NF_DefineTextColor(u8 screen, u8 layer, u8 color, u8 r, u8 g, u8 b) {
// Verifica si la capa de texto de destino existe
if (!NF_TEXT[screen][layer].exist) {
NF_Error(114, NULL, screen);
}
// Calcula el valor RGB
u16 rgb = ((r)|((g) << 5)|((b) << 10));
// Direccion en VRAM
u32 address = 0;
// Modifica la paleta
if (screen == 0) {
vramSetBankE(VRAM_E_LCD);
address = (0x06880000) + (layer << 13) + (color << 9); // Primer color de la paleta
*((u16*)address) = (u16)0xFF00FF;
address = (0x06880000) + (layer << 13) + (color << 9) + 2; // Segundo color de la paleta
*((u16*)address) = rgb;
vramSetBankE(VRAM_E_BG_EXT_PALETTE);
} else { // Paletas de la pantalla 1 (VRAM_H)
vramSetBankH(VRAM_H_LCD);
address = (0x06898000) + (layer << 13) + (color << 9); // Primer color de la paleta
*((u16*)address) = (u16)0xFF00FF;
address = (0x06898000) + (layer << 13) + (color << 9) + 2; // Segundo color de la paleta
*((u16*)address) = rgb;
vramSetBankH(VRAM_H_SUB_BG_EXT_PALETTE);
}
}
// Function NF_SetTextColor();
void NF_SetTextColor(u8 screen, u8 layer, u8 color) {
NF_TEXT[screen][layer].pal = color;
}