Merge pull request #10 from lhearachel/nitro-fonts

Add support for font files used by Gen4 Pokemon
This commit is contained in:
red031000 2024-10-27 04:57:17 +00:00 committed by GitHub
commit f4e3666719
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 736 additions and 361 deletions

547
font.c
View File

@ -4,323 +4,486 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "global.h" #include "global.h"
#include "font.h" #include "font.h"
#include "gfx.h" #include "gfx.h"
#include "options.h"
#include "util.h" #include "util.h"
unsigned char gFontPalette[][3] = { unsigned char gFontPalette[][3] = {
{0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color) {0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color)
{0x38, 0x38, 0x38}, // fg (dark grey) {0x38, 0x38, 0x38}, // fg (dark grey)
{0xD8, 0xD8, 0xD8}, // shadow (light grey) {0xD8, 0xD8, 0xD8}, // shadow (light grey)
{0xFF, 0xFF, 0xFF} // box (white) {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) static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
unsigned int srcPixelsOffset = 0; unsigned int srcPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2; srcPixelsOffset += 2;
} }
} }
} }
} }
} }
static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows) static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
unsigned int destPixelsOffset = 0; unsigned int destPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2; destPixelsOffset += 2;
} }
} }
} }
} }
} }
static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column; unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8; unsigned int pixelsX = column * 8;
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4); unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2; srcPixelsOffset += 2;
} }
} }
} }
} }
} }
static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column; unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8; unsigned int pixelsX = column * 8;
unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4); unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2; destPixelsOffset += 2;
} }
} }
} }
} }
} }
static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column; unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2; srcPixelsOffset += 2;
} }
} }
} }
} }
} }
static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{ {
for (unsigned int row = 0; row < numRows; row++) { for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) { for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column; unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1]; dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset]; dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2; destPixelsOffset += 2;
} }
} }
} }
} }
}
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) static void SetFontPalette(struct Image *image)
{ {
image->hasPalette = true; image->hasPalette = true;
image->palette.numColors = 4; image->palette.numColors = 4;
for (int i = 0; i < image->palette.numColors; i++) { for (int i = 0; i < image->palette.numColors; i++) {
image->palette.colors[i].red = gFontPalette[i][0]; image->palette.colors[i].red = gFontPalette[i][0];
image->palette.colors[i].green = gFontPalette[i][1]; image->palette.colors[i].green = gFontPalette[i][1];
image->palette.colors[i].blue = gFontPalette[i][2]; image->palette.colors[i].blue = gFontPalette[i][2];
} }
image->hasTransparency = false; 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) void ReadLatinFont(char *path, struct Image *image)
{ {
int fileSize; int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize); unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64; int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0) if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16; int numRows = numGlyphs / 16;
image->width = 256; image->width = 256;
image->height = numRows * 16; image->height = numRows * 16;
image->bitDepth = 2; image->bitDepth = 2;
image->pixels = malloc(fileSize); image->pixels = malloc(fileSize);
if (image->pixels == NULL) if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromLatinFont(buffer, image->pixels, numRows); ConvertFromLatinFont(buffer, image->pixels, numRows);
free(buffer); free(buffer);
SetFontPalette(image); SetFontPalette(image);
} }
void WriteLatinFont(char *path, struct Image *image) void WriteLatinFont(char *path, struct Image *image)
{ {
if (image->width != 256) if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0) if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16; int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64; int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize); unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL) if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToLatinFont(image->pixels, buffer, numRows); ConvertToLatinFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize); WriteWholeFile(path, buffer, bufferSize);
free(buffer); free(buffer);
} }
void ReadHalfwidthJapaneseFont(char *path, struct Image *image) void ReadHalfwidthJapaneseFont(char *path, struct Image *image)
{ {
int fileSize; int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize); unsigned char *buffer = ReadWholeFile(path, &fileSize);
int glyphSize = 32; int glyphSize = 32;
if (fileSize % glyphSize != 0) if (fileSize % glyphSize != 0)
FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize); FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize);
int numGlyphs = fileSize / glyphSize; int numGlyphs = fileSize / glyphSize;
if (numGlyphs % 16 != 0) if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16; int numRows = numGlyphs / 16;
image->width = 128; image->width = 128;
image->height = numRows * 16; image->height = numRows * 16;
image->bitDepth = 2; image->bitDepth = 2;
image->pixels = malloc(fileSize); image->pixels = malloc(fileSize);
if (image->pixels == NULL) if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows); ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer); free(buffer);
SetFontPalette(image); SetFontPalette(image);
} }
void WriteHalfwidthJapaneseFont(char *path, struct Image *image) void WriteHalfwidthJapaneseFont(char *path, struct Image *image)
{ {
if (image->width != 128) if (image->width != 128)
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width); FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
if (image->height % 16 != 0) if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16; int numRows = image->height / 16;
int bufferSize = numRows * 16 * 32; int bufferSize = numRows * 16 * 32;
unsigned char *buffer = malloc(bufferSize); unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL) if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows); ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize); WriteWholeFile(path, buffer, bufferSize);
free(buffer); free(buffer);
} }
void ReadFullwidthJapaneseFont(char *path, struct Image *image) void ReadFullwidthJapaneseFont(char *path, struct Image *image)
{ {
int fileSize; int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize); unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64; int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0) if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16; int numRows = numGlyphs / 16;
image->width = 256; image->width = 256;
image->height = numRows * 16; image->height = numRows * 16;
image->bitDepth = 2; image->bitDepth = 2;
image->pixels = malloc(fileSize); image->pixels = malloc(fileSize);
if (image->pixels == NULL) if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows); ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer); free(buffer);
SetFontPalette(image); SetFontPalette(image);
} }
void WriteFullwidthJapaneseFont(char *path, struct Image *image) void WriteFullwidthJapaneseFont(char *path, struct Image *image)
{ {
if (image->width != 256) if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0) if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16; int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64; int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize); unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL) if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n"); FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows); ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize); WriteWholeFile(path, buffer, bufferSize);
free(buffer); 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];
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
View File

@ -5,6 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "gfx.h" #include "gfx.h"
#include "options.h"
void ReadLatinFont(char *path, struct Image *image); void ReadLatinFont(char *path, struct Image *image);
void WriteLatinFont(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 WriteHalfwidthJapaneseFont(char *path, struct Image *image);
void ReadFullwidthJapaneseFont(char *path, struct Image *image); void ReadFullwidthJapaneseFont(char *path, struct Image *image);
void WriteFullwidthJapaneseFont(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 #endif // FONT_H

76
json.c
View File

@ -638,3 +638,79 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options)
free(options->animationResults); free(options->animationResults);
free(options); 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
View File

@ -13,5 +13,7 @@ char *GetNANRJson(struct JsonToAnimationOptions *options);
void FreeNCERCell(struct JsonToCellOptions *options); void FreeNCERCell(struct JsonToCellOptions *options);
void FreeNSCRScreen(struct JsonToScreenOptions *options); void FreeNSCRScreen(struct JsonToScreenOptions *options);
void FreeNANRAnimation(struct JsonToAnimationOptions *options); void FreeNANRAnimation(struct JsonToAnimationOptions *options);
char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata);
struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path);
#endif //JSON_H #endif //JSON_H

241
lz.c
View File

@ -1,153 +1,188 @@
// Copyright (c) 2015 YamaArashi // Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "global.h" #include "global.h"
#include "lz.h" #include "lz.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize) unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{ {
if (srcSize < 4) if (srcSize < 4)
goto fail; goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize); unsigned char *dest = malloc(destSize);
if (dest == NULL) if (dest == NULL)
goto fail; goto fail;
int srcPos = 4; int srcPos = 4;
int destPos = 0; int destPos = 0;
for (;;) { for (;;) {
if (srcPos >= srcSize) if (srcPos >= srcSize)
goto fail; goto fail;
unsigned char flags = src[srcPos++]; unsigned char flags = src[srcPos++];
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if (flags & 0x80) { if (flags & 0x80) {
if (srcPos + 1 >= srcSize) if (srcPos + 1 >= srcSize)
goto fail; goto fail;
int blockSize = (src[srcPos] >> 4) + 3; int blockSize = (src[srcPos] >> 4) + 3;
int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1; int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1;
srcPos += 2; srcPos += 2;
int blockPos = destPos - blockDistance; int blockPos = destPos - blockDistance;
// Some Ruby/Sapphire tilesets overflow. // Some Ruby/Sapphire tilesets overflow.
if (destPos + blockSize > destSize) { if (destPos + blockSize > destSize) {
blockSize = destSize - destPos; blockSize = destSize - destPos;
fprintf(stderr, "Destination buffer overflow.\n"); fprintf(stderr, "Destination buffer overflow.\n");
} }
if (blockPos < 0) if (blockPos < 0)
goto fail; goto fail;
for (int j = 0; j < blockSize; j++) for (int j = 0; j < blockSize; j++)
dest[destPos++] = dest[blockPos + j]; dest[destPos++] = dest[blockPos + j];
} else { } else {
if (srcPos >= srcSize || destPos >= destSize) if (srcPos >= srcSize || destPos >= destSize)
goto fail; goto fail;
dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++];
} }
if (destPos == destSize) { if (destPos == destSize) {
*uncompressedSize = destSize; *uncompressedSize = destSize;
return dest; return dest;
} }
flags <<= 1; flags <<= 1;
} }
} }
fail: fail:
FATAL_ERROR("Fatal error while decompressing LZ file.\n"); FATAL_ERROR("Fatal error while decompressing LZ file.\n");
} }
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance) static void FindBestBlockForwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize)
{ {
if (srcSize <= 0) int blockStart = srcPos < 0x1000 ? 0 : srcPos - 0x1000;
goto fail; while (blockStart != srcPos) {
int blockSize = 0;
int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8); while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
// Round up to the next multiple of four. if (blockSize > *outBestBlockSize
worstCaseDestSize = (worstCaseDestSize + 3) & ~3; && srcPos - blockStart >= minDistance) {
*outBestBlockDistance = srcPos - blockStart;
*outBestBlockSize = blockSize;
unsigned char *dest = malloc(worstCaseDestSize); if (blockSize == 18)
break;
}
if (dest == NULL) blockStart++;
goto fail; }
}
// header static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize)
dest[0] = 0x10; // LZ compression type {
dest[1] = (unsigned char)srcSize; int blockDistance = minDistance;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0; while (blockDistance <= srcPos && blockDistance <= 0x1000) {
int destPos = 4; int blockStart = srcPos - blockDistance;
int blockSize = 0;
for (;;) { while (blockSize < 18
unsigned char *flags = &dest[destPos++]; && srcPos + blockSize < srcSize
*flags = 0; && src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
for (int i = 0; i < 8; i++) { if (blockSize > *outBestBlockSize) {
int bestBlockDistance = 0; *outBestBlockDistance = blockDistance;
int bestBlockSize = 0; *outBestBlockSize = blockSize;
int blockDistance = minDistance;
while (blockDistance <= srcPos && blockDistance <= 0x1000) { if (blockSize == 18)
int blockStart = srcPos - blockDistance; break;
int blockSize = 0; }
while (blockSize < 18 blockDistance++;
&& srcPos + blockSize < srcSize }
&& src[blockStart + blockSize] == src[srcPos + blockSize]) }
blockSize++;
if (blockSize > bestBlockSize) { typedef void (*FindBestBlockFunc)(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize);
bestBlockDistance = blockDistance;
bestBlockSize = blockSize;
if (blockSize == 18) unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration)
break; {
} if (srcSize <= 0)
goto fail;
blockDistance++; int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8);
}
if (bestBlockSize >= 3) { // Round up to the next multiple of four.
*flags |= (0x80 >> i); worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
srcPos += bestBlockSize;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
dest[destPos++] = src[srcPos++];
}
if (srcPos == srcSize) { unsigned char *dest = malloc(worstCaseDestSize);
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) { if (dest == NULL)
for (int i = 0; i < 4 - remainder; i++) goto fail;
dest[destPos++] = 0;
}
*compressedSize = destPos; // header
return dest; dest[0] = 0x10; // LZ compression type
} dest[1] = (unsigned char)srcSize;
} dest[2] = (unsigned char)(srcSize >> 8);
} dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
FindBestBlockFunc FindBestBlock = forwardIteration ? FindBestBlockForwards : FindBestBlockBackwards;
for (;;) {
unsigned char *flags = &dest[destPos++];
*flags = 0;
for (int i = 0; i < 8; i++) {
int bestBlockDistance = 0;
int bestBlockSize = 0;
FindBestBlock(src, srcPos, srcSize, minDistance, &bestBlockDistance, &bestBlockSize);
if (bestBlockSize >= 3) {
*flags |= (0x80 >> i);
srcPos += bestBlockSize;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
dest[destPos++] = src[srcPos++];
}
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
}
fail: fail:
FATAL_ERROR("Fatal error while compressing LZ file.\n"); FATAL_ERROR("Fatal error while compressing LZ file.\n");
} }

4
lz.h
View File

@ -3,7 +3,9 @@
#ifndef LZ_H #ifndef LZ_H
#define LZ_H #define LZ_H
#include "stdbool.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize); unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance); unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration);
#endif // LZ_H #endif // LZ_H

78
main.c
View File

@ -955,6 +955,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
{ {
int overflowSize = 0; int overflowSize = 0;
int minDistance = 2; // default, for compatibility with LZ77UnCompVram() int minDistance = 2; // default, for compatibility with LZ77UnCompVram()
bool forwardIteration = true;
for (int i = 3; i < argc; i++) for (int i = 3; i < argc; i++)
{ {
@ -986,6 +987,10 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
if (minDistance < 1) if (minDistance < 1)
FATAL_ERROR("LZ min search distance must be positive.\n"); FATAL_ERROR("LZ min search distance must be positive.\n");
} }
else if (strcmp(option, "-reverse") == 0)
{
forwardIteration = false;
}
else else
{ {
FATAL_ERROR("Unrecognized option \"%s\".\n", option); FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -1002,7 +1007,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize); unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
int compressedSize; int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance); unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration);
compressedData[1] = (unsigned char)fileSize; compressedData[1] = (unsigned char)fileSize;
compressedData[2] = (unsigned char)(fileSize >> 8); compressedData[2] = (unsigned char)(fileSize >> 8);
@ -1115,6 +1120,75 @@ void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNU
free(uncompressedData); 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) int main(int argc, char **argv)
{ {
if (argc < 3) if (argc < 3)
@ -1159,6 +1233,8 @@ int main(int argc, char **argv)
{ "lz", NULL, HandleLZDecompressCommand }, { "lz", NULL, HandleLZDecompressCommand },
{ NULL, "rl", HandleRLCompressCommand }, { NULL, "rl", HandleRLCompressCommand },
{ "rl", NULL, HandleRLDecompressCommand }, { "rl", NULL, HandleRLDecompressCommand },
{ "NFGR", "png", HandleNtrFontToPngCommand },
{ "png", "NFGR", HandlePngToNtrFontCommand },
{ NULL, NULL, NULL } { NULL, NULL, NULL }
}; };

View File

@ -166,4 +166,21 @@ struct JsonToAnimationOptions {
short resultCount; 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 #endif // OPTIONS_H

128
util.c
View File

@ -12,129 +12,129 @@
bool ParseNumber(char *s, char **end, int radix, int *intValue) bool ParseNumber(char *s, char **end, int radix, int *intValue)
{ {
char *localEnd; char *localEnd;
if (end == NULL) if (end == NULL)
end = &localEnd; end = &localEnd;
errno = 0; errno = 0;
const long longValue = strtol(s, end, radix); const long longValue = strtol(s, end, radix);
if (*end == s) if (*end == s)
return false; // not a number return false; // not a number
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
return false; return false;
if (longValue > INT_MAX) if (longValue > INT_MAX)
return false; return false;
if (longValue < INT_MIN) if (longValue < INT_MIN)
return false; return false;
*intValue = (int)longValue; *intValue = (int)longValue;
return true; return true;
} }
char *GetFileExtension(char *path) char *GetFileExtension(char *path)
{ {
char *extension = path; char *extension = path;
while (*extension != 0) while (*extension != 0)
extension++; extension++;
while (extension > path && *extension != '.') while (extension > path && *extension != '.')
extension--; extension--;
if (extension == path) if (extension == path)
return NULL; return NULL;
extension++; extension++;
if (*extension == 0) if (*extension == 0)
return NULL; return NULL;
return extension; return extension;
} }
unsigned char *ReadWholeFile(char *path, int *size) unsigned char *ReadWholeFile(char *path, int *size)
{ {
FILE *fp = fopen(path, "rb"); FILE *fp = fopen(path, "rb");
if (fp == NULL) if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
*size = ftell(fp); *size = ftell(fp);
unsigned char *buffer = malloc(*size); unsigned char *buffer = malloc(*size);
if (buffer == NULL) if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp); rewind(fp);
if (fread(buffer, *size, 1, fp) != 1) if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path); FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp); fclose(fp);
return buffer; return buffer;
} }
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
{ {
FILE *fp = fopen(path, "rb"); FILE *fp = fopen(path, "rb");
if (fp == NULL) if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
*size = ftell(fp); *size = ftell(fp);
unsigned char *buffer = calloc(*size + padAmount, 1); unsigned char *buffer = calloc(*size + padAmount, 1);
if (buffer == NULL) if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp); rewind(fp);
if (fread(buffer, *size, 1, fp) != 1) if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path); FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp); fclose(fp);
return buffer; return buffer;
} }
void WriteWholeStringToFile(char *path, char *string) void WriteWholeStringToFile(char *path, char *string)
{ {
FILE *fp = fopen(path, "wb"); FILE *fp = fopen(path, "wb");
if (fp == NULL) if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fputs(string, fp) == EOF) if (fputs(string, fp) == EOF)
FATAL_ERROR("Failed to write to \"%s\".\n", path); FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp); fclose(fp);
} }
void WriteWholeFile(char *path, void *buffer, int bufferSize) void WriteWholeFile(char *path, void *buffer, int bufferSize)
{ {
FILE *fp = fopen(path, "wb"); FILE *fp = fopen(path, "wb");
if (fp == NULL) if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fwrite(buffer, bufferSize, 1, fp) != 1) if (fwrite(buffer, bufferSize, 1, fp) != 1)
FATAL_ERROR("Failed to write to \"%s\".\n", path); FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp); fclose(fp);
} }
void WriteGenericNtrHeader(FILE* fp, const char* magicNumber, uint32_t size, bool byteorder, bool version101, uint16_t sectionCount) void WriteGenericNtrHeader(FILE* fp, const char* magicNumber, uint32_t size, bool byteorder, bool version101, uint16_t sectionCount)