mirror of
https://github.com/red031000/nitrogfx.git
synced 2025-06-18 13:15:35 -04:00
Add support for font files used by Gen4 Pokemon
This commit is contained in:
parent
616bfba6f5
commit
0a893d4d3e
171
font.c
171
font.c
@ -4,9 +4,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "global.h"
|
||||
#include "font.h"
|
||||
#include "gfx.h"
|
||||
#include "options.h"
|
||||
#include "util.h"
|
||||
|
||||
unsigned char gFontPalette[][3] = {
|
||||
@ -16,6 +18,14 @@ unsigned char gFontPalette[][3] = {
|
||||
{0xFF, 0xFF, 0xFF} // box (white)
|
||||
};
|
||||
|
||||
// special palette for DS subscreen font
|
||||
unsigned char gFontPalette_Subscreen[][3] = {
|
||||
{0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color)
|
||||
{0xFF, 0xFF, 0xFF}, // fg (white)
|
||||
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
|
||||
{0x38, 0x38, 0x38}, // outline (dark grey)
|
||||
};
|
||||
|
||||
static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
||||
{
|
||||
unsigned int srcPixelsOffset = 0;
|
||||
@ -158,6 +168,54 @@ static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *de
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertFromNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata)
|
||||
{
|
||||
unsigned int srcPixelsOffset = 0;
|
||||
unsigned int curGlyph = 0;
|
||||
|
||||
for (unsigned int row = 0; row < numRows; row++) {
|
||||
for (unsigned int column = 0; column < 16 && curGlyph < metadata->numGlyphs; column++, curGlyph++) {
|
||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
||||
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
||||
|
||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
||||
|
||||
srcPixelsOffset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertToNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata)
|
||||
{
|
||||
unsigned int destPixelsOffset = 0;
|
||||
unsigned int curGlyph = 0;
|
||||
|
||||
for (unsigned int row = 0; row < numRows; row++) {
|
||||
for (unsigned int column = 0; column < 16 && curGlyph < metadata->numGlyphs; column++, curGlyph++) {
|
||||
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
|
||||
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
|
||||
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
|
||||
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
|
||||
|
||||
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
|
||||
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
|
||||
|
||||
destPixelsOffset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SetFontPalette(struct Image *image)
|
||||
{
|
||||
image->hasPalette = true;
|
||||
@ -173,6 +231,21 @@ static void SetFontPalette(struct Image *image)
|
||||
image->hasTransparency = false;
|
||||
}
|
||||
|
||||
static void SetSubscreenFontPalette(struct Image *image)
|
||||
{
|
||||
image->hasPalette = true;
|
||||
|
||||
image->palette.numColors = 4;
|
||||
|
||||
for (int i = 0; i < image->palette.numColors; i++) {
|
||||
image->palette.colors[i].red = gFontPalette_Subscreen[i][0];
|
||||
image->palette.colors[i].green = gFontPalette_Subscreen[i][1];
|
||||
image->palette.colors[i].blue = gFontPalette_Subscreen[i][2];
|
||||
}
|
||||
|
||||
image->hasTransparency = false;
|
||||
}
|
||||
|
||||
void ReadLatinFont(char *path, struct Image *image)
|
||||
{
|
||||
int fileSize;
|
||||
@ -324,3 +397,101 @@ void WriteFullwidthJapaneseFont(char *path, struct Image *image)
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static inline uint32_t ReadLittleEndianWord(unsigned char *buffer, size_t start)
|
||||
{
|
||||
return (buffer[start + 3] << 24)
|
||||
| (buffer[start + 2] << 16)
|
||||
| (buffer[start + 1] << 8)
|
||||
| (buffer[start]);
|
||||
}
|
||||
|
||||
void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata, bool useSubscreenPalette)
|
||||
{
|
||||
int filesize;
|
||||
unsigned char *buffer = ReadWholeFile(path, &filesize);
|
||||
|
||||
metadata->size = ReadLittleEndianWord(buffer, 0x00);
|
||||
metadata->widthTableOffset = ReadLittleEndianWord(buffer, 0x04);
|
||||
metadata->numGlyphs = ReadLittleEndianWord(buffer, 0x08);
|
||||
metadata->maxWidth = buffer[0x0C];
|
||||
metadata->maxHeight = buffer[0x0D];
|
||||
metadata->glyphWidth = buffer[0x0E];
|
||||
metadata->glyphHeight = buffer[0x0F];
|
||||
|
||||
printf("header size: %lu\n", metadata->size);
|
||||
printf("width table offset: %lu\n", metadata->widthTableOffset);
|
||||
printf("num glyphs: %lu\n", metadata->numGlyphs);
|
||||
printf("max width: %hhu\n", metadata->maxWidth);
|
||||
printf("max height: %hhu\n", metadata->maxHeight);
|
||||
printf("glyph width: %hhu\n", metadata->glyphWidth);
|
||||
printf("glyph height: %hhu\n", metadata->glyphHeight);
|
||||
|
||||
int numRows = (metadata->numGlyphs + 15) / 16; // Round up to next multiple of 16.
|
||||
|
||||
metadata->glyphWidthTable = malloc(metadata->numGlyphs);
|
||||
memcpy(metadata->glyphWidthTable, buffer + metadata->widthTableOffset, metadata->numGlyphs);
|
||||
|
||||
image->width = 256;
|
||||
image->height = numRows * 16;
|
||||
image->bitDepth = 2;
|
||||
image->pixels = malloc(filesize);
|
||||
|
||||
if (image->pixels == NULL)
|
||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
||||
|
||||
ConvertFromNitroFont(buffer + metadata->size, image->pixels, numRows, metadata);
|
||||
|
||||
free(buffer);
|
||||
|
||||
if (useSubscreenPalette)
|
||||
SetSubscreenFontPalette(image);
|
||||
else
|
||||
SetFontPalette(image);
|
||||
}
|
||||
|
||||
void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata)
|
||||
{
|
||||
if (image->width != 256)
|
||||
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
|
||||
|
||||
if (image->height % 16 != 0)
|
||||
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
|
||||
|
||||
int numRows = image->height / 16;
|
||||
int bufferSize = metadata->widthTableOffset + metadata->numGlyphs;
|
||||
unsigned char *buffer = malloc(bufferSize);
|
||||
|
||||
if (buffer == NULL)
|
||||
FATAL_ERROR("Failed to allocate memory for font.\n");
|
||||
|
||||
buffer[0x00] = (metadata->size & 0x000000FF);
|
||||
buffer[0x01] = (metadata->size & 0x0000FF00) >> 8;
|
||||
buffer[0x02] = (metadata->size & 0x00FF0000) >> 16;
|
||||
buffer[0x03] = (metadata->size & 0xFF000000) >> 24;
|
||||
buffer[0x04] = (metadata->widthTableOffset & 0x000000FF);
|
||||
buffer[0x05] = (metadata->widthTableOffset & 0x0000FF00) >> 8;
|
||||
buffer[0x06] = (metadata->widthTableOffset & 0x00FF0000) >> 16;
|
||||
buffer[0x07] = (metadata->widthTableOffset & 0xFF000000) >> 24;
|
||||
buffer[0x08] = (metadata->numGlyphs & 0x000000FF);
|
||||
buffer[0x09] = (metadata->numGlyphs & 0x0000FF00) >> 8;
|
||||
buffer[0x0A] = (metadata->numGlyphs & 0x00FF0000) >> 16;
|
||||
buffer[0x0B] = (metadata->numGlyphs & 0xFF000000) >> 24;
|
||||
buffer[0x0C] = metadata->maxWidth;
|
||||
buffer[0x0D] = metadata->maxHeight;
|
||||
buffer[0x0E] = metadata->glyphWidth;
|
||||
buffer[0x0F] = metadata->glyphHeight;
|
||||
|
||||
ConvertToNitroFont(image->pixels, buffer + metadata->size, numRows, metadata);
|
||||
memcpy(buffer + metadata->widthTableOffset, metadata->glyphWidthTable, metadata->numGlyphs);
|
||||
|
||||
WriteWholeFile(path, buffer, bufferSize);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void FreeNtrFontMetadata(struct NtrFontMetadata *metadata)
|
||||
{
|
||||
free(metadata->glyphWidthTable);
|
||||
free(metadata);
|
||||
}
|
||||
|
4
font.h
4
font.h
@ -5,6 +5,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "gfx.h"
|
||||
#include "options.h"
|
||||
|
||||
void ReadLatinFont(char *path, struct Image *image);
|
||||
void WriteLatinFont(char *path, struct Image *image);
|
||||
@ -12,5 +13,8 @@ void ReadHalfwidthJapaneseFont(char *path, struct Image *image);
|
||||
void WriteHalfwidthJapaneseFont(char *path, struct Image *image);
|
||||
void ReadFullwidthJapaneseFont(char *path, struct Image *image);
|
||||
void WriteFullwidthJapaneseFont(char *path, struct Image *image);
|
||||
void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata, bool useSubscreenPalette);
|
||||
void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata);
|
||||
void FreeNtrFontMetadata(struct NtrFontMetadata *metadata);
|
||||
|
||||
#endif // FONT_H
|
||||
|
76
json.c
76
json.c
@ -638,3 +638,79 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options)
|
||||
free(options->animationResults);
|
||||
free(options);
|
||||
}
|
||||
|
||||
char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata)
|
||||
{
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddNumberToObject(json, "maxGlyphWidth", metadata->maxWidth);
|
||||
cJSON_AddNumberToObject(json, "maxGlyphHeight", metadata->maxHeight);
|
||||
|
||||
cJSON *glyphWidths = cJSON_AddArrayToObject(json, "glyphWidths");
|
||||
for (int i = 0; i < metadata->numGlyphs; i++)
|
||||
{
|
||||
cJSON *width = cJSON_CreateNumber(metadata->glyphWidthTable[i]);
|
||||
cJSON_AddItemToArray(glyphWidths, width);
|
||||
}
|
||||
|
||||
char *jsonString = cJSON_Print(json);
|
||||
cJSON_Delete(json);
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
#define TILE_DIMENSION_PIXELS 8
|
||||
#define PIXELS_FOR_DIMENSION(dim) ((dim) * TILE_DIMENSION_PIXELS)
|
||||
#define TILES_FOR_PIXELS(num) (((num) + TILE_DIMENSION_PIXELS - 1) / TILE_DIMENSION_PIXELS)
|
||||
#define PIXELS_PER_BYTE_2BPP 4
|
||||
#define NTR_FONT_HEADER_SIZE 16
|
||||
|
||||
struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path)
|
||||
{
|
||||
int fileLength;
|
||||
unsigned char *jsonString = ReadWholeFile(path, &fileLength);
|
||||
|
||||
cJSON *json = cJSON_Parse((const char *)jsonString);
|
||||
if (json == NULL)
|
||||
{
|
||||
const char *errorPtr = cJSON_GetErrorPtr();
|
||||
FATAL_ERROR("Error in line \"%s\"\n", errorPtr);
|
||||
}
|
||||
|
||||
cJSON *labelMaxGlyphWidth = cJSON_GetObjectItemCaseSensitive(json, "maxGlyphWidth");
|
||||
cJSON *labelMaxGlyphHeight = cJSON_GetObjectItemCaseSensitive(json, "maxGlyphHeight");
|
||||
cJSON *labelGlyphWidths = cJSON_GetObjectItemCaseSensitive(json, "glyphWidths");
|
||||
int numGlyphs = cJSON_GetArraySize(labelGlyphWidths);
|
||||
|
||||
struct NtrFontMetadata *metadata = malloc(sizeof(struct NtrFontMetadata));
|
||||
|
||||
metadata->size = NTR_FONT_HEADER_SIZE;
|
||||
metadata->numGlyphs = numGlyphs;
|
||||
metadata->maxWidth = GetInt(labelMaxGlyphWidth);
|
||||
metadata->maxHeight = GetInt(labelMaxGlyphHeight);
|
||||
|
||||
metadata->glyphWidth = TILES_FOR_PIXELS(metadata->maxWidth);
|
||||
metadata->glyphHeight = TILES_FOR_PIXELS(metadata->maxHeight);
|
||||
|
||||
int glyphBitmapSize = (PIXELS_FOR_DIMENSION(metadata->glyphWidth) * PIXELS_FOR_DIMENSION(metadata->glyphHeight)) / PIXELS_PER_BYTE_2BPP;
|
||||
metadata->widthTableOffset = metadata->size + (metadata->numGlyphs * glyphBitmapSize);
|
||||
|
||||
metadata->glyphWidthTable = malloc(metadata->numGlyphs);
|
||||
|
||||
uint8_t *glyphWidthCursor = metadata->glyphWidthTable;
|
||||
cJSON *glyphWidthIter = NULL;
|
||||
cJSON_ArrayForEach(glyphWidthIter, labelGlyphWidths)
|
||||
{
|
||||
if (!cJSON_IsNumber(glyphWidthIter))
|
||||
{
|
||||
const char *errorPtr = cJSON_GetErrorPtr();
|
||||
FATAL_ERROR("Error in line \"%s\"\n", errorPtr);
|
||||
}
|
||||
|
||||
*glyphWidthCursor = glyphWidthIter->valueint;
|
||||
glyphWidthCursor++;
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
free(jsonString);
|
||||
return metadata;
|
||||
}
|
||||
|
2
json.h
2
json.h
@ -13,5 +13,7 @@ char *GetNANRJson(struct JsonToAnimationOptions *options);
|
||||
void FreeNCERCell(struct JsonToCellOptions *options);
|
||||
void FreeNSCRScreen(struct JsonToScreenOptions *options);
|
||||
void FreeNANRAnimation(struct JsonToAnimationOptions *options);
|
||||
char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata);
|
||||
struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path);
|
||||
|
||||
#endif //JSON_H
|
||||
|
71
main.c
71
main.c
@ -1115,6 +1115,75 @@ void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNU
|
||||
free(uncompressedData);
|
||||
}
|
||||
|
||||
void HandleNtrFontToPngCommand(char *inputPath, char *outputPath, int argc, char **argv)
|
||||
{
|
||||
struct NtrFontOptions options;
|
||||
options.metadataFilePath = NULL;
|
||||
options.useSubscreenPalette = false;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
char *option = argv[i];
|
||||
|
||||
if (strcmp(option, "-metadata") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No file path following \"-metadata\".\n");
|
||||
|
||||
options.metadataFilePath = argv[++i];
|
||||
}
|
||||
else if (strcmp(option, "-subscreen") == 0)
|
||||
{
|
||||
options.useSubscreenPalette = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.metadataFilePath == NULL)
|
||||
FATAL_ERROR("No file path given for \"-metadata\".\n");
|
||||
|
||||
struct Image image;
|
||||
struct NtrFontMetadata metadata;
|
||||
ReadNtrFont(inputPath, &image, &metadata, options.useSubscreenPalette);
|
||||
WritePng(outputPath, &image);
|
||||
|
||||
char *metadataJson = GetNtrFontMetadataJson(&metadata);
|
||||
WriteWholeStringToFile(options.metadataFilePath, metadataJson);
|
||||
|
||||
free(metadata.glyphWidthTable);
|
||||
FreeImage(&image);
|
||||
}
|
||||
|
||||
void HandlePngToNtrFontCommand(char *inputPath, char *outputPath, int argc, char **argv)
|
||||
{
|
||||
struct NtrFontOptions options;
|
||||
options.metadataFilePath = NULL;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
char *option = argv[i];
|
||||
|
||||
if (strcmp(option, "-metadata") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No file path following \"-metadata\".\n");
|
||||
|
||||
options.metadataFilePath = argv[++i];
|
||||
}
|
||||
}
|
||||
|
||||
if (options.metadataFilePath == NULL)
|
||||
FATAL_ERROR("No file path given for \"-metadata\".\n");
|
||||
|
||||
struct NtrFontMetadata *metadata = ParseNtrFontMetadataJson(options.metadataFilePath);
|
||||
struct Image image = { .bitDepth = 2 };
|
||||
|
||||
ReadPng(inputPath, &image);
|
||||
WriteNtrFont(outputPath, &image, metadata);
|
||||
|
||||
FreeNtrFontMetadata(metadata);
|
||||
FreeImage(&image);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
@ -1159,6 +1228,8 @@ int main(int argc, char **argv)
|
||||
{ "lz", NULL, HandleLZDecompressCommand },
|
||||
{ NULL, "rl", HandleRLCompressCommand },
|
||||
{ "rl", NULL, HandleRLDecompressCommand },
|
||||
{ "NFGR", "png", HandleNtrFontToPngCommand },
|
||||
{ "png", "NFGR", HandlePngToNtrFontCommand },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
17
options.h
17
options.h
@ -166,4 +166,21 @@ struct JsonToAnimationOptions {
|
||||
short resultCount;
|
||||
};
|
||||
|
||||
struct NtrFontOptions {
|
||||
char *metadataFilePath;
|
||||
bool useSubscreenPalette;
|
||||
};
|
||||
|
||||
struct NtrFontMetadata {
|
||||
uint32_t size;
|
||||
uint32_t widthTableOffset;
|
||||
uint32_t numGlyphs;
|
||||
uint8_t maxWidth;
|
||||
uint8_t maxHeight;
|
||||
uint8_t glyphWidth;
|
||||
uint8_t glyphHeight;
|
||||
|
||||
uint8_t *glyphWidthTable;
|
||||
};
|
||||
|
||||
#endif // OPTIONS_H
|
||||
|
Loading…
Reference in New Issue
Block a user