mirror of
https://github.com/knightfox75/nds_nflib.git
synced 2025-06-18 16:55:32 -04:00

Also, slightly rework the code of NF_WriteText() to match the code sytle of the rest of the library.
394 lines
12 KiB
C
394 lines
12 KiB
C
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
|
|
// Copyright (c) 2023 Antonio Niño Díaz "AntonioND"
|
|
//
|
|
// NightFox LIB - 8x16 font text support functions
|
|
// http://www.nightfoxandco.com/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <nds.h>
|
|
|
|
#include "nf_2d.h"
|
|
#include "nf_basic.h"
|
|
#include "nf_text16.h"
|
|
#include "nf_text.h"
|
|
#include "nf_tiledbg.h"
|
|
|
|
void NF_LoadTextFont16(const char *file, const char *name, u32 width, u32 height,
|
|
u32 rotation)
|
|
{
|
|
// Look for a free slot and mark it as in use
|
|
int slot = 255;
|
|
for (int n = 0; n < NF_SLOTS_TBG; n++)
|
|
{
|
|
if (NF_TILEDBG[n].available)
|
|
{
|
|
NF_TILEDBG[n].available = false;
|
|
slot = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there are no free slots, fail
|
|
if (slot == 255)
|
|
NF_Error(103, "Tiled Bg", NF_SLOTS_TBG);
|
|
|
|
// Verify that the size of the background is a multiple of 256 px (32 tiles)
|
|
if (((width % 256) != 0) || ((height % 256) != 0))
|
|
NF_Error(115, file, 0);
|
|
|
|
// Free buffers if they were in use
|
|
free(NF_BUFFER_BGMAP[slot]);
|
|
NF_BUFFER_BGMAP[slot] = NULL;
|
|
free(NF_BUFFER_BGTILES[slot]);
|
|
NF_BUFFER_BGTILES[slot] = NULL;
|
|
free(NF_BUFFER_BGPAL[slot]);
|
|
NF_BUFFER_BGPAL[slot] = NULL;
|
|
|
|
// File path
|
|
char filename[256];
|
|
|
|
// Load .FNT/.IMG file
|
|
snprintf(filename, sizeof(filename), "%s/%s.fnt", NF_ROOTFOLDER, file);
|
|
FILE *file_id = fopen(filename, "rb");
|
|
if (file_id == NULL) // If the file can't be opened
|
|
{
|
|
snprintf(filename, sizeof(filename), "%s/%s.img", NF_ROOTFOLDER, file);
|
|
file_id = fopen(filename, "rb");
|
|
if (file_id == NULL) // If the file can't be opened
|
|
NF_Error(101, filename, 0);
|
|
}
|
|
|
|
// Get file size. 256 colors, so 1 byte per pixel (8x16 pixels)
|
|
NF_TILEDBG[slot].tilesize = NF_TEXT_FONT_CHARS_16 * 8 * 16;
|
|
|
|
// Allocate space in RAM
|
|
NF_BUFFER_BGTILES[slot] = malloc(NF_TILEDBG[slot].tilesize);
|
|
if (NF_BUFFER_BGTILES[slot] == NULL)
|
|
NF_Error(102, NULL, NF_TILEDBG[slot].tilesize);
|
|
|
|
// Load file to RAM
|
|
fread(NF_BUFFER_BGTILES[slot], 1, NF_TILEDBG[slot].tilesize, file_id);
|
|
fclose(file_id);
|
|
|
|
// Rotate graphics if required
|
|
if (rotation > 0)
|
|
{
|
|
for (int n = 0; n < (NF_TEXT_FONT_CHARS_16 * 2); n++)
|
|
NF_RotateTileGfx(slot, n, rotation);
|
|
}
|
|
|
|
// Create an empty map in RAM. Each element in the map is 2 bytes.
|
|
NF_TILEDBG[slot].mapsize = ((width / 8) * (height / 8)) * 2;
|
|
|
|
// Allocate space in RAM and zero it in case the file needs padding
|
|
NF_BUFFER_BGMAP[slot] = calloc(NF_TILEDBG[slot].mapsize, sizeof(char));
|
|
if (NF_BUFFER_BGMAP[slot] == NULL)
|
|
NF_Error(102, NULL, NF_TILEDBG[slot].mapsize);
|
|
|
|
// Load .PAL file
|
|
snprintf(filename, sizeof(filename), "%s/%s.pal", NF_ROOTFOLDER, file);
|
|
NF_FileLoad(filename, &NF_BUFFER_BGPAL[slot], &NF_TILEDBG[slot].palsize, 256 * 2);
|
|
|
|
// Save the name of the background
|
|
snprintf(NF_TILEDBG[slot].name, sizeof(NF_TILEDBG[slot].name), "%s", name);
|
|
|
|
// And its size
|
|
NF_TILEDBG[slot].width = width;
|
|
NF_TILEDBG[slot].height = height;
|
|
}
|
|
|
|
void NF_CreateTextLayer16(int screen, u32 layer, u32 rotation, const char* name)
|
|
{
|
|
// Create a background to use it as a text layer
|
|
NF_CreateTiledBg(screen, layer, name);
|
|
|
|
u32 slot = 255;
|
|
|
|
// Look for the tiled background with the specified name
|
|
for (int n = 0; n < NF_SLOTS_TBG; n++)
|
|
{
|
|
if (strcmp(name, NF_TILEDBG[n].name) == 0)
|
|
{
|
|
slot = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If it hasn't been found, fail
|
|
if (slot == 255)
|
|
NF_Error(103, "Tiled BG", NF_SLOTS_TBG);
|
|
|
|
NF_TEXT[screen][layer].rotation = rotation;
|
|
|
|
// Save the background size in tiles (save the index of the last row/column)
|
|
switch (rotation)
|
|
{
|
|
case 0: // No rotation
|
|
NF_TEXT[screen][layer].width = (NF_TILEDBG[slot].width / 8) - 1;
|
|
NF_TEXT[screen][layer].height = (NF_TILEDBG[slot].height / (8 * 2)) - 1;
|
|
break;
|
|
case 1: // 90 degrees clockwise
|
|
NF_TEXT[screen][layer].width = (NF_TILEDBG[slot].width / (8 * 2)) - 1;
|
|
NF_TEXT[screen][layer].height = (NF_TILEDBG[slot].height / 8) - 1;
|
|
break;
|
|
case 2: // 90 degrees counter-clockwise
|
|
NF_TEXT[screen][layer].width = (NF_TILEDBG[slot].width / (8 * 2)) - 1;
|
|
NF_TEXT[screen][layer].height = (NF_TILEDBG[slot].height / 8) - 1;
|
|
break;
|
|
}
|
|
|
|
// Save slot where the font is stored
|
|
NF_TEXT[screen][layer].slot = slot;
|
|
|
|
// Mark layer as used
|
|
NF_TEXT[screen][layer].exist = true;
|
|
}
|
|
|
|
void NF_WriteText16(int screen, u32 layer, u32 x, u32 y, const char *text)
|
|
{
|
|
// Verify that the text layer exists
|
|
if (!NF_TEXT[screen][layer].exist)
|
|
NF_Error(114, NULL, screen);
|
|
|
|
u32 tsize = strlen(text); // Size of the temporary string buffer
|
|
u8 *string = malloc(tsize); // Temporary string buffer
|
|
if (string == NULL)
|
|
NF_Error(102, NULL, tsize);
|
|
|
|
// Store the text string in the temporary buffer
|
|
for (u32 n = 0; n < tsize; n++)
|
|
{
|
|
int value = 0;
|
|
|
|
// If newline found, put the character to the newline. If not, skip the
|
|
// first 32 characters.
|
|
if (text[n] == '\n')
|
|
value = '\n';
|
|
else
|
|
value = text[n] - 32;
|
|
|
|
if (value < 0)
|
|
value = 0;
|
|
|
|
string[n] = value;
|
|
|
|
// Handle special characters (newline characters are special too)
|
|
if ((string[n] > 95) || (string[n] == 10))
|
|
{
|
|
switch (text[n])
|
|
{
|
|
case '\n':
|
|
string[n] = 200;
|
|
break;
|
|
|
|
case 199: // Ç
|
|
string[n] = 96;
|
|
break;
|
|
case 231: // ç
|
|
string[n] = 97;
|
|
break;
|
|
case 209: // Ñ
|
|
string[n] = 98;
|
|
break;
|
|
case 241: // ñ
|
|
string[n] = 99;
|
|
break;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
case 239: // ï
|
|
string[n] = 110;
|
|
break;
|
|
case 252: // ü
|
|
string[n] = 111;
|
|
break;
|
|
|
|
case 161: // ¡
|
|
string[n] = 112;
|
|
break;
|
|
case 191: // ¿
|
|
string[n] = 113;
|
|
break;
|
|
|
|
default:
|
|
string[n] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Variables to calculate the position of the text
|
|
s32 tx, ty;
|
|
|
|
// Write text in the background map, according to the specified rotation
|
|
switch (NF_TEXT[screen][layer].rotation)
|
|
{
|
|
case 0: // No rotacion
|
|
|
|
tx = x;
|
|
ty = y;
|
|
|
|
for (u32 n = 0; n < tsize; n++)
|
|
{
|
|
// If it's a valid character, put character
|
|
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR_16)
|
|
{
|
|
// Calculate the position in the tilemap
|
|
s32 pos_x = tx;
|
|
s32 pos_y = ty * 2;
|
|
|
|
// Print character
|
|
u32 value = (string[n] & ~31) + string[n];
|
|
NF_SetTileOfMap(screen,layer, pos_x, pos_y,
|
|
(NF_TEXT[screen][layer].pal << 12) | value);
|
|
NF_SetTileOfMap(screen,layer, pos_x, pos_y + 1,
|
|
(NF_TEXT[screen][layer].pal << 12) | (value + 32));
|
|
tx++;
|
|
}
|
|
|
|
// If the end of the line is reached or a newline character is found
|
|
if ((tx > NF_TEXT[screen][layer].width) || (string[n] == 200))
|
|
{
|
|
tx = 0;
|
|
ty++;
|
|
// If the last row is reached, return to the first one
|
|
if (ty > NF_TEXT[screen][layer].height)
|
|
ty = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: // 90 degrees of clockwise rotation
|
|
|
|
tx = NF_TEXT[screen][layer].width - y;
|
|
ty = x;
|
|
|
|
for (u32 n = 0; n < tsize; n++)
|
|
{
|
|
// If it's a valid character, put character
|
|
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR_16)
|
|
{
|
|
// Calculate the position in the tilemap
|
|
s32 pos_x = tx * 2;
|
|
s32 pos_y = ty;
|
|
|
|
// Print character
|
|
u32 value = (string[n] & ~31) + string[n];
|
|
NF_SetTileOfMap(screen,layer, pos_x, pos_y,
|
|
(NF_TEXT[screen][layer].pal << 12) | value);
|
|
NF_SetTileOfMap(screen,layer, pos_x - 1, pos_y,
|
|
(NF_TEXT[screen][layer].pal << 12) | (value + 32));
|
|
ty++;
|
|
}
|
|
|
|
// If the end of the line is reached or a newline character is found
|
|
if ((ty > NF_TEXT[screen][layer].height) || (string[n] == 200))
|
|
{
|
|
ty = 0;
|
|
tx--;
|
|
|
|
// If the last row is reached, return to the first one
|
|
if (tx < 0)
|
|
tx = NF_TEXT[screen][layer].width;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: // 90 degrees of counter-clockwise rotation
|
|
|
|
tx = y;
|
|
ty = NF_TEXT[screen][layer].height - x;
|
|
|
|
for (u32 n = 0; n < tsize; n++)
|
|
{
|
|
// If it's a valid character, put character
|
|
if (string[n] <= NF_TEXT_FONT_LAST_VALID_CHAR_16)
|
|
{
|
|
s32 pos_x = tx * 2;
|
|
s32 pos_y = ty;
|
|
|
|
// Print the character
|
|
u32 value = (string[n] & ~31) + string[n];
|
|
NF_SetTileOfMap(screen,layer, pos_x, pos_y,
|
|
(NF_TEXT[screen][layer].pal << 12) | value);
|
|
NF_SetTileOfMap(screen,layer, pos_x + 1, pos_y,
|
|
(NF_TEXT[screen][layer].pal << 12) | (value + 32));
|
|
ty--;
|
|
}
|
|
|
|
// If the end of the line is reached or a newline character is found
|
|
if ((ty < 0) || (string[n] == 200))
|
|
{
|
|
ty = NF_TEXT[screen][layer].height;
|
|
tx++;
|
|
|
|
// If the last row is reached, return to the first one
|
|
if (tx > NF_TEXT[screen][layer].width)
|
|
tx = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Mark this layers as requiring an update
|
|
NF_TEXT[screen][layer].update = true;
|
|
|
|
// Free temporary buffer
|
|
free(string);
|
|
}
|
|
|
|
void NF_ClearTextLayer16(int screen, u32 layer)
|
|
{
|
|
// Verify that the text layer exists
|
|
if (!NF_TEXT[screen][layer].exist)
|
|
NF_Error(114, NULL, screen);
|
|
|
|
// Calculate size of the buffer
|
|
u32 w = NF_TEXT[screen][layer].width + 1;
|
|
u32 h = NF_TEXT[screen][layer].height + 1;
|
|
u32 size = w * h * 4; // 2 tiles per character, 2 bytes per tile
|
|
|
|
// Clear the text layer
|
|
memset(NF_BUFFER_BGMAP[NF_TEXT[screen][layer].slot], 0, size);
|
|
|
|
// Mark this layers as requiring an update
|
|
NF_TEXT[screen][layer].update = true;
|
|
}
|