update nitrogfx to the latest version

This commit is contained in:
red031000 2024-11-13 17:17:33 +02:00
parent 095fc39474
commit f4cf15f313
No known key found for this signature in database
GPG Key ID: D27E50C050AE0CE1
16 changed files with 1186 additions and 471 deletions

View File

@ -4,3 +4,4 @@
arm9/lib/
arm7/lib/
include/nitro/
tools/nitrogfx/

View File

@ -1 +1,2 @@
nitrogfx
.vscode/

View File

@ -6,7 +6,7 @@ ifeq ($(HAVE_LIBPNG),1)
$(error No package 'libpng' found)
endif
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK $(shell pkg-config --cflags libpng zlib)
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=gnu17 -DPNG_SKIP_SETJMP_CHECK $(shell pkg-config --cflags libpng zlib)
LIBS = $(shell pkg-config --libs libpng zlib)
@ -22,7 +22,7 @@ nitrogfx-debug: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h
$(CC) $(CFLAGS) -g -DDEBUG $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
nitrogfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h json.h cJSON.h
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
$(CC) $(CFLAGS) -O2 $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
clean:
$(RM) -r nitrogfx nitrogfx.exe $(OBJS)

View File

@ -96,9 +96,9 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
return (const char*) (global_error.json + global_error.position);
}
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
{
if (!cJSON_IsString(item))
if (!cJSON_IsString(item))
{
return NULL;
}
@ -106,9 +106,9 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
return item->valuestring;
}
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
{
if (!cJSON_IsNumber(item))
if (!cJSON_IsNumber(item))
{
return (double) NAN;
}
@ -117,7 +117,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
}
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15)
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
@ -263,10 +263,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
{
global_hooks.deallocate(item->valuestring);
item->valuestring = NULL;
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
global_hooks.deallocate(item->string);
item->string = NULL;
}
global_hooks.deallocate(item);
item = next;
@ -397,16 +399,33 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
return object->valuedouble = number;
}
/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
{
char *copy = NULL;
size_t v1_len;
size_t v2_len;
/* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
{
return NULL;
}
if (strlen(valuestring) <= strlen(object->valuestring))
/* return NULL if the object is corrupted or valuestring is NULL */
if (object->valuestring == NULL || valuestring == NULL)
{
return NULL;
}
v1_len = strlen(valuestring);
v2_len = strlen(object->valuestring);
if (v1_len <= v2_len)
{
/* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */
if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring ))
{
return NULL;
}
strcpy(object->valuestring, valuestring);
return object->valuestring;
}
@ -511,7 +530,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
return NULL;
}
memcpy(newbuffer, p->buffer, p->offset + 1);
p->hooks.deallocate(p->buffer);
}
@ -562,6 +581,10 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
{
length = sprintf((char*)number_buffer, "null");
}
else if(d == (double)item->valueint)
{
length = sprintf((char*)number_buffer, "%d", item->valueint);
}
else
{
/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
@ -884,6 +907,7 @@ fail:
if (output != NULL)
{
input_buffer->hooks.deallocate(output);
output = NULL;
}
if (input_pointer != NULL)
@ -1103,7 +1127,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer
}
buffer.content = (const unsigned char*)value;
buffer.length = buffer_length;
buffer.length = buffer_length;
buffer.offset = 0;
buffer.hooks = global_hooks;
@ -1226,6 +1250,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i
/* free the buffer */
hooks->deallocate(buffer->buffer);
buffer->buffer = NULL;
}
return printed;
@ -1234,11 +1259,13 @@ fail:
if (buffer->buffer != NULL)
{
hooks->deallocate(buffer->buffer);
buffer->buffer = NULL;
}
if (printed != NULL)
{
hooks->deallocate(printed);
printed = NULL;
}
return NULL;
@ -1279,6 +1306,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
if (!print_value(item, &p))
{
global_hooks.deallocate(p.buffer);
p.buffer = NULL;
return NULL;
}
@ -1650,6 +1678,11 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu
current_item = new_item;
}
if (cannot_access_at_index(input_buffer, 1))
{
goto fail; /* nothing comes after the comma */
}
/* parse the name of the child */
input_buffer->offset++;
buffer_skip_whitespace(input_buffer);
@ -2182,7 +2215,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * c
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
{
if ((parent == NULL) || (item == NULL))
if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL))
{
return NULL;
}
@ -2260,7 +2293,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON
{
cJSON *after_inserted = NULL;
if (which < 0)
if (which < 0 || newitem == NULL)
{
return false;
}
@ -2271,6 +2304,11 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON
return add_item_to_array(array, newitem);
}
if (after_inserted != array->child && after_inserted->prev == NULL) {
/* return false if after_inserted is a corrupted array item */
return false;
}
newitem->next = after_inserted;
newitem->prev = after_inserted->prev;
after_inserted->prev = newitem;
@ -2287,7 +2325,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
{
if ((parent == NULL) || (replacement == NULL) || (item == NULL))
if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
{
return false;
}
@ -2357,6 +2395,11 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO
cJSON_free(replacement->string);
}
replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
if (replacement->string == NULL)
{
return false;
}
replacement->type &= ~cJSON_StringIsConst;
return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
@ -2689,12 +2732,19 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co
if (a && a->child) {
a->child->prev = n;
}
return a;
}
/* Duplication */
cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
return cJSON_Duplicate_rec(item, 0, recurse );
}
cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
{
cJSON *newitem = NULL;
cJSON *child = NULL;
@ -2741,7 +2791,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
child = item->child;
while (child != NULL)
{
newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
if(depth >= CJSON_CIRCULAR_LIMIT) {
goto fail;
}
newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild)
{
goto fail;
@ -3107,4 +3160,5 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
CJSON_PUBLIC(void) cJSON_free(void *object)
{
global_hooks.deallocate(object);
object = NULL;
}

View File

@ -81,7 +81,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 15
#define CJSON_VERSION_PATCH 18
#include <stddef.h>
@ -137,6 +137,12 @@ typedef int cJSON_bool;
#define CJSON_NESTING_LIMIT 1000
#endif
/* Limits the length of circular references can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
@ -279,6 +285,13 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
#define cJSON_SetBoolValue(object, boolValue) ( \
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
cJSON_Invalid\
)
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)

View File

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

View File

@ -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

View File

@ -9,6 +9,26 @@
#include "gfx.h"
#include "util.h"
static unsigned int FindNitroDataBlock(const unsigned char *data, const char *ident, unsigned int fileSize, unsigned int *blockSize_out)
{
unsigned int offset = 0x10;
while (offset < fileSize)
{
unsigned int blockSize = data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24);
if (offset + blockSize > fileSize)
{
FATAL_ERROR("corrupted NTR file");
}
if (memcmp(data + offset, ident, 4) == 0)
{
*blockSize_out = blockSize;
return offset;
}
offset += blockSize;
}
return -1u;
}
#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F)
#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F)
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
@ -694,7 +714,7 @@ void ReadGbaPalette(char *path, struct Palette *palette)
free(data);
}
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex)
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
@ -719,6 +739,7 @@ void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIn
bitdepth = bitdepth ? bitdepth : palette->bitDepth;
size_t paletteSize = (paletteHeader[0x10]) | (paletteHeader[0x11] << 8) | (paletteHeader[0x12] << 16) | (paletteHeader[0x13] << 24);
if (inverted) paletteSize = 0x200 - paletteSize;
if (palIndex == 0) {
palette->numColors = paletteSize / 2;
} else {
@ -769,7 +790,7 @@ void WriteGbaPalette(char *path, struct Palette *palette)
fclose(fp);
}
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp)
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, bool inverted)
{
FILE *fp = fopen(path, "wb");
@ -823,10 +844,11 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in
}
//size
palHeader[16] = size & 0xFF;
palHeader[17] = (size >> 8) & 0xFF;
palHeader[18] = (size >> 16) & 0xFF;
palHeader[19] = (size >> 24) & 0xFF;
int colorSize = inverted ? 0x200 - size : size;
palHeader[16] = colorSize & 0xFF;
palHeader[17] = (colorSize >> 8) & 0xFF;
palHeader[18] = (colorSize >> 16) & 0xFF;
palHeader[19] = (colorSize >> 24) & 0xFF;
fwrite(palHeader, 1, 0x18, fp);
@ -888,38 +910,30 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in
fclose(fp);
}
void ReadNtrCell(char *path, struct JsonToCellOptions *options)
void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
options->cellCount = data[blockOffset + 0x8] | (data[blockOffset + 0x9] << 8);
options->extended = data[blockOffset + 0xA] == 1;
if (memcmp(data, "RECN", 4) != 0) //NCER
{
FATAL_ERROR("Not a valid NCER cell file.\n");
}
options->labelEnabled = data[0xE] != 1;
if (memcmp(data + 0x10, "KBEC", 4) != 0 ) //KBEC
{
FATAL_ERROR("Not a valid KBEC cell file.\n");
}
options->cellCount = data[0x18] | (data[0x19] << 8);
options->extended = data[0x1A] == 1;
int vramTransferOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8);
options->vramTransferEnabled = vramTransferOffset > 0;
/*if (!options->extended)
{
//in theory not extended should be implemented, however not 100% sure
FATAL_ERROR("Don't know how to deal with not extended yet, bug red031000.\n");
}*/
options->mappingType = data[0x20];
options->mappingType = data[blockOffset + 0x10];
options->cells = malloc(sizeof(struct Cell *) * options->cellCount);
int celSize = options->extended ? 0x10 : 0x8;
for (int i = 0; i < options->cellCount; i++)
{
int offset = 0x30 + (i * (options->extended ? 0x10 : 0x8));
int offset = blockOffset + 0x20 + (i * celSize);
if (offset + celSize > blockOffset + blockSize) {
FATAL_ERROR("corrupted CEBK block\n");
}
options->cells[i] = malloc(sizeof(struct Cell));
options->cells[i]->oamCount = data[offset] | (data[offset + 1] << 8);
short cellAttrs = data[offset + 2] | (data[offset + 3] << 8);
@ -938,7 +952,7 @@ void ReadNtrCell(char *path, struct JsonToCellOptions *options)
}
}
int offset = 0x30 + (options->cellCount * (options->extended ? 0x10 : 0x8));
int offset = blockOffset + 0x20 + (options->cellCount * celSize);
for (int i = 0; i < options->cellCount; i++)
{
options->cells[i]->oam = malloc(sizeof(struct OAM) * options->cells[i]->oamCount);
@ -993,35 +1007,84 @@ void ReadNtrCell(char *path, struct JsonToCellOptions *options)
}
}
if (options->labelEnabled)
if (options->vramTransferEnabled)
{
int count = 0;
int offset = 0x30 + (options->cellCount * 0x16) + 0x8;
bool flag = false;
//this entire thing is a huge assumption, it will not work with labels that are less than 2 characters long
while (!flag)
offset = blockOffset + 0x08 + vramTransferOffset;
// first 2 dwords are max size and offset, offset *should* always be 0x08 since the transfer data list immediately follows this
options->vramTransferMaxSize = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24);
offset += 0x08;
// read 1 VRAM transfer data block for each cell (this is an assumption based on the NCERs I looked at)
options->transferData = malloc(sizeof(struct CellVramTransferData *) * options->cellCount);
for (int idx = 0; idx < options->cellCount; idx++)
{
if (strlen((char *) data + offset) < 2)
{
//probably a pointer, maybe?
count++;
offset += 4;
}
else
{
//huzzah a string
flag = true;
}
options->transferData[idx] = malloc(sizeof(struct CellVramTransferData));
options->transferData[idx]->sourceDataOffset = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24);
options->transferData[idx]->size = data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24);
offset += 8;
}
options->labelCount = count;
options->labels = malloc(sizeof(char *) * count);
for (int i = 0; i < count; i++)
}
}
void ReadNtrCell_LABL(unsigned char * restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options)
{
int count = 0;
unsigned int textStart = blockOffset + 8;
while (textStart < blockOffset + blockSize)
{
unsigned int labelOffset = data[textStart] | (data[textStart + 1] << 8) | (data[textStart + 2] << 16) | (data[textStart + 3] << 24);
if (labelOffset > blockSize)
{
options->labels[i] = malloc(strlen((char *) data + offset) + 1);
strcpy(options->labels[i], (char *) data + offset);
offset += strlen(options->labels[i]) + 1;
break;
}
//after this should be txeu, if everything was done right
else {
++count;
textStart += 4;
}
}
options->labelCount = count;
options->labels = malloc(sizeof(char *) * count);
for (int i = 0; i < count; ++i)
{
int offset = textStart + (data[blockOffset + 4 * i + 8] | (data[blockOffset + 4 * i + 9] << 8) | (data[blockOffset + 4 * i + 10] << 16) | (data[blockOffset + 4 * i + 11] << 24));
if (offset > blockOffset + blockSize)
{
FATAL_ERROR("corrupted LABL block\n");
}
unsigned long slen = strnlen((char *)data + offset, blockSize - offset);
options->labels[i] = malloc(slen + 1);
strncpy(options->labels[i], (char *)data + offset, slen + 1);
}
}
void ReadNtrCell(char *path, struct JsonToCellOptions *options)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
unsigned int offset = 0x10;
if (memcmp(data, "RECN", 4) != 0) //NCER
{
FATAL_ERROR("Not a valid NCER cell file.\n");
}
options->labelEnabled = false;
unsigned int blockSize;
offset = FindNitroDataBlock(data, "KBEC", fileSize, &blockSize);
if (offset != -1u)
{
ReadNtrCell_CEBK(data, offset, blockSize, options);
}
else {
FATAL_ERROR("missing CEBK block");
}
offset = FindNitroDataBlock(data, "LBAL", fileSize, &blockSize);
if (offset != -1u)
{
options->labelEnabled = true;
ReadNtrCell_LABL(data, offset, blockSize, options);
}
free(data);
@ -1034,7 +1097,25 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + options->cellCount * (options->extended ? 0x16 : 0xe);
int iterNum = (options->extended ? 0x10 : 0x8);
// KBEC base size: 0x08 per bank, or 0x10 per extended bank
unsigned int kbecSize = options->cellCount * (options->extended ? 0x10 : 0x08);
// if VRAM transfer is enabled, add 0x08 for the header and 0x08 for each cell
if (options->vramTransferEnabled)
{
kbecSize += 0x08 + (0x08 * options->cellCount);
}
// add 0x06 for number of OAMs - can be more than 1
for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum)
{
kbecSize += options->cells[idx / iterNum]->oamCount * 0x06;
}
// KBEC size is padded to be 4-byte aligned
kbecSize += kbecSize % 4;
unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + kbecSize;
if (options->labelEnabled)
{
@ -1059,18 +1140,27 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
KBECHeader[10] = 1; //extended
}
unsigned int size = options->cellCount * (options->extended ? 0x16 : 0xe);
KBECHeader[4] = (size + 0x20) & 0xFF; //size
KBECHeader[5] = (size + 0x20) >> 8; //unlikely to be more than 16 bits, but there are 32 allocated, change if necessary
KBECHeader[4] = (kbecSize + 0x20) & 0xFF; //size
KBECHeader[5] = (kbecSize + 0x20) >> 8; //unlikely to be more than 16 bits, but there are 32 allocated, change if necessary
KBECHeader[16] = (options->mappingType & 0xFF); //not possible to be more than 8 bits, though 32 are allocated
// offset to VRAM transfer data within KBEC section (offset from KBEC start + 0x08)
if (options->vramTransferEnabled)
{
unsigned int vramTransferLength = 0x08 + (0x08 * options->cellCount);
unsigned int vramTransferOffset = (kbecSize + 0x20) - vramTransferLength - 0x08;
KBECHeader[20] = vramTransferOffset & 0xFF;
KBECHeader[21] = (vramTransferOffset >> 8) & 0xFF;
KBECHeader[22] = (vramTransferOffset >> 16) & 0xFF;
KBECHeader[23] = (vramTransferOffset >> 24) & 0xFF;
}
fwrite(KBECHeader, 1, 0x20, fp);
unsigned char *KBECContents = malloc(size);
unsigned char *KBECContents = malloc(kbecSize);
memset(KBECContents, 0, size);
memset(KBECContents, 0, kbecSize);
/*if (!options->extended)
{
@ -1079,7 +1169,6 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
}*/
int i;
int iterNum = (options->extended ? 0x10 : 0x8);
int totalOam = 0;
for (i = 0; i < options->cellCount * iterNum; i += iterNum)
{
@ -1163,7 +1252,38 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
}
}
fwrite(KBECContents, 1, size, fp);
// VRAM transfer data
if (options->vramTransferEnabled)
{
// max transfer size + fixed offset 0x08
KBECContents[offset] = options->vramTransferMaxSize & 0xFF;
KBECContents[offset + 1] = (options->vramTransferMaxSize >> 8) & 0xFF;
KBECContents[offset + 2] = (options->vramTransferMaxSize >> 16) & 0xFF;
KBECContents[offset + 3] = (options->vramTransferMaxSize >> 24) & 0xFF;
KBECContents[offset + 4] = 0x08;
offset += 8;
// write a VRAM transfer block for each cell
for (int idx = 0; idx < options->cellCount; idx++)
{
// offset
KBECContents[offset] = options->transferData[idx]->sourceDataOffset & 0xFF;
KBECContents[offset + 1] = (options->transferData[idx]->sourceDataOffset >> 8) & 0xFF;
KBECContents[offset + 2] = (options->transferData[idx]->sourceDataOffset >> 16) & 0xFF;
KBECContents[offset + 3] = (options->transferData[idx]->sourceDataOffset >> 24) & 0xFF;
// size
KBECContents[offset + 4] = options->transferData[idx]->size & 0xFF;
KBECContents[offset + 5] = (options->transferData[idx]->size >> 8) & 0xFF;
KBECContents[offset + 6] = (options->transferData[idx]->size >> 16) & 0xFF;
KBECContents[offset + 7] = (options->transferData[idx]->size >> 24) & 0xFF;
offset += 8;
}
}
fwrite(KBECContents, 1, kbecSize, fp);
free(KBECContents);
@ -1318,6 +1438,7 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options)
{
if (resultOffsets[k] == options->sequenceData[i]->frameData[j]->resultOffset)
{
options->sequenceData[i]->frameData[j]->resultId = k;
present = true;
break;
}
@ -1330,6 +1451,7 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options)
{
if (resultOffsets[k] == -1)
{
options->sequenceData[i]->frameData[j]->resultId = k;
resultOffsets[k] = options->sequenceData[i]->frameData[j]->resultOffset;
break;
}
@ -1360,37 +1482,49 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options)
options->animationResults[i] = malloc(sizeof(struct AnimationResults));
}
// store the animationElement of the corresponding sequence as this result's resultType
for (int i = 0; i < options->sequenceCount; i++)
{
for (int j = 0; j < options->sequenceData[i]->frameCount; j++)
{
options->animationResults[options->sequenceData[i]->frameData[j]->resultId]->resultType = options->sequenceData[i]->animationElement;
}
}
int resultOffset = 0;
int lastSequence = 0;
for (int i = 0; i < options->resultCount; i++)
{
if (data[offset + 2] == 0xCC && data[offset + 3] == 0xCC)
{
options->animationResults[i]->resultType = 0;
}
else if (data[offset + 2] == 0xEF && data[offset + 3] == 0xBE)
{
options->animationResults[i]->resultType = 2;
}
else
{
options->animationResults[i]->resultType = 1;
}
// find the earliest sequence matching this animation result,
// and add padding if the sequence changes + the total offset is not 4-byte aligned.
bool found = false;
for (int j = 0; j < options->sequenceCount; j++)
{
for (int k = 0; k < options->sequenceData[j]->frameCount; k++)
{
if (options->sequenceData[j]->frameData[k]->resultOffset == resultOffset)
if (options->sequenceData[j]->frameData[k]->resultId == i)
{
options->sequenceData[j]->frameData[k]->resultId = i;
if (lastSequence != j)
{
lastSequence = j;
if (resultOffset % 4 != 0)
{
resultOffset += 0x2;
offset += 0x2;
}
}
found = true;
break;
}
}
if (found) break;
}
switch (options->animationResults[i]->resultType)
{
case 0: //index
options->animationResults[i]->index = data[offset] | (data[offset + 1] << 8);
resultOffset += 0x4;
offset += 0x4;
resultOffset += 0x2;
offset += 0x2;
break;
case 1: //SRT
@ -1414,6 +1548,9 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options)
}
}
// add any missed padding from the final frame before processing labels
if (offset % 4 != 0) offset += 2;
if (options->labelEnabled)
{
options->labelCount = options->sequenceCount; //*should* be the same
@ -1439,17 +1576,69 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
unsigned int totalSize = 0x20 + options->sequenceCount * 0x10 + options->frameCount * 0x8;
//todo: check these
for (int i = 0; i < options->resultCount; i++)
{
if (options->animationResults[i]->resultType == 0)
totalSize += 0x4;
totalSize += 0x2;
else if (options->animationResults[i]->resultType == 1)
totalSize += 0x10;
else if (options->animationResults[i]->resultType == 2)
totalSize += 0x8;
}
// foreach sequence, need to check whether padding is applied for its results
// then add 0x02 to totalSize if padding exists.
// padding exists if the animation results for that sequence are not 4-byte aligned.
// also flag the last result for the sequence with `padded` to save having to redo this same step later.
int *usedResults = malloc(sizeof(int) * options->frameCount);
memset(usedResults, -1, sizeof(int) * options->frameCount);
for (int i = 0; i < options->sequenceCount; i++)
{
int sequenceLen = 0;
int resultIndex = 0;
int lastNewResultIndex = -1;
for (int j = 0; j < options->sequenceData[i]->frameCount; j++)
{
// check if the result has already been used
bool isUsed = false;
for (resultIndex = 0; resultIndex < options->resultCount; resultIndex++)
{
if (usedResults[resultIndex] == options->sequenceData[i]->frameData[j]->resultId)
{
isUsed = true;
break;
}
// if not already used, add it to the list
if (usedResults[resultIndex] == -1)
{
usedResults[resultIndex] = options->sequenceData[i]->frameData[j]->resultId;
lastNewResultIndex = options->sequenceData[i]->frameData[j]->resultId;
break;
}
}
// if not already used, add it to the result size for the sequence
if (!isUsed)
{
if (options->animationResults[resultIndex]->resultType == 0)
sequenceLen += 0x2;
else if (options->animationResults[resultIndex]->resultType == 1)
sequenceLen += 0x10;
else if (options->animationResults[resultIndex]->resultType == 2)
sequenceLen += 0x8;
}
}
if (sequenceLen % 4 != 0 && lastNewResultIndex != -1)
{
totalSize += 0x02;
// mark the last new animationResult index for the sequence as padded, this saves needing to check this again later
options->animationResults[lastNewResultIndex]->padded = true;
}
}
free(usedResults);
unsigned int KNBASize = totalSize;
if (options->labelEnabled)
@ -1508,9 +1697,9 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
KBNAContents[i + 2] = options->sequenceData[i / 0x10]->loopStartFrame & 0xff;
KBNAContents[i + 3] = options->sequenceData[i / 0x10]->loopStartFrame >> 8;
KBNAContents[i + 4] = options->sequenceData[i / 0x10]->animationElement & 0xff;
KBNAContents[i + 5] = options->sequenceData[i / 0x10]->animationElement >> 8;
KBNAContents[i + 5] = (options->sequenceData[i / 0x10]->animationElement >> 8) & 0xff;
KBNAContents[i + 6] = options->sequenceData[i / 0x10]->animationType & 0xff;
KBNAContents[i + 7] = options->sequenceData[i / 0x10]->animationType >> 8;
KBNAContents[i + 7] = (options->sequenceData[i / 0x10]->animationType >> 8) & 0xff;
KBNAContents[i + 8] = options->sequenceData[i / 0x10]->playbackMode & 0xff;
KBNAContents[i + 9] = (options->sequenceData[i / 0x10]->playbackMode >> 8) & 0xff;
KBNAContents[i + 10] = (options->sequenceData[i / 0x10]->playbackMode >> 16) & 0xff;
@ -1530,11 +1719,13 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
int resPtr = 0;
for (int l = 0; l < options->sequenceData[m]->frameData[k]->resultId; l++) {
if (options->animationResults[l]->resultType == 0)
resPtr += 0x4;
resPtr += 0x2;
else if (options->animationResults[l]->resultType == 1)
resPtr += 0x10;
else if (options->animationResults[l]->resultType == 2)
resPtr += 0x8;
if (options->animationResults[l]->padded) resPtr += 0x02;
}
KBNAContents[j + (k * 8)] = resPtr & 0xff;
KBNAContents[j + (k * 8) + 1] = (resPtr >> 8) & 0xff;
@ -1548,7 +1739,6 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
j += options->sequenceData[m]->frameCount * 8;
}
//todo: these are extrapolated, need confirming
int resPtrCounter = j;
for (int k = 0; k < options->resultCount; k++)
{
@ -1557,9 +1747,7 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
case 0:
KBNAContents[resPtrCounter] = options->animationResults[k]->index & 0xff;
KBNAContents[resPtrCounter + 1] = options->animationResults[k]->index >> 8;
KBNAContents[resPtrCounter + 2] = 0xCC;
KBNAContents[resPtrCounter + 3] = 0xCC;
resPtrCounter += 0x4;
resPtrCounter += 0x2;
break;
case 1:
@ -1594,6 +1782,14 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
resPtrCounter += 0x8;
break;
}
// use the `padded` flag which was stored earlier to inject padding
if (options->animationResults[k]->padded)
{
KBNAContents[resPtrCounter] = 0xCC;
KBNAContents[resPtrCounter + 1] = 0xCC;
resPtrCounter += 0x2;
}
}
fwrite(KBNAContents, 1, contentsSize, fp);

View File

@ -58,9 +58,9 @@ void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int
uint32_t mappingType, uint32_t key, bool wrongSize);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex);
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted);
void WriteGbaPalette(char *path, struct Palette *palette);
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp);
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, bool inverted);
void ReadNtrCell(char *path, struct JsonToCellOptions *options);
void WriteNtrCell(char *path, struct JsonToCellOptions *options);
void WriteNtrScreen(char *path, struct JsonToScreenOptions *options);

View File

@ -47,11 +47,13 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
}
cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled");
cJSON *vramTransferBool = cJSON_GetObjectItemCaseSensitive(json, "vramTransferEnabled");
cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended");
cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount");
cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType");
options->labelEnabled = GetBool(labelBool);
options->vramTransferEnabled = GetBool(vramTransferBool);
options->extended = GetBool(extended);
options->cellCount = GetInt(cellCount);
options->mappingType = GetInt(mappingType);
@ -77,6 +79,30 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
}
}
if (options->vramTransferEnabled)
{
cJSON *vramTransferMaxSize = cJSON_GetObjectItemCaseSensitive(json, "vramTransferMaxSize");
options->vramTransferMaxSize = GetInt(vramTransferMaxSize);
options->transferData = malloc(sizeof(struct CellVramTransferData *) * options->cellCount);
cJSON *transfers = cJSON_GetObjectItemCaseSensitive(json, "transferData");
cJSON *transfer = NULL;
int j = 0;
cJSON_ArrayForEach(transfer, transfers)
{
cJSON *vramTransferOffset = cJSON_GetObjectItemCaseSensitive(transfer, "offset");
cJSON *vramTransferSize = cJSON_GetObjectItemCaseSensitive(transfer, "size");
options->transferData[j] = malloc(sizeof(struct CellVramTransferData));
options->transferData[j]->sourceDataOffset = GetInt(vramTransferOffset);
options->transferData[j]->size = GetInt(vramTransferSize);
j++;
}
}
for (int i = 0; i < options->cellCount; i++)
{
options->cells[i] = malloc(sizeof(struct Cell));
@ -195,6 +221,7 @@ char *GetNCERJson(struct JsonToCellOptions *options)
cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled);
cJSON_AddBoolToObject(ncer, "extended", options->extended);
cJSON_AddBoolToObject(ncer, "vramTransferEnabled", options->vramTransferEnabled);
cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount);
cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType);
@ -263,6 +290,20 @@ char *GetNCERJson(struct JsonToCellOptions *options)
cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount);
}
if (options->vramTransferEnabled)
{
cJSON_AddNumberToObject(ncer, "vramTransferMaxSize", options->vramTransferMaxSize);
cJSON *transfers = cJSON_AddArrayToObject(ncer, "transferData");
for (int idx = 0; idx < options->cellCount; idx++)
{
cJSON *transfer = cJSON_CreateObject();
cJSON_AddNumberToObject(transfer, "offset", options->transferData[idx]->sourceDataOffset);
cJSON_AddNumberToObject(transfer, "size", options->transferData[idx]->size);
cJSON_AddItemToArray(transfers, transfer);
}
}
char *jsonString = cJSON_Print(ncer);
cJSON_Delete(ncer);
return jsonString;
@ -600,6 +641,14 @@ void FreeNCERCell(struct JsonToCellOptions *options)
}
free(options->labels);
}
if (options->vramTransferEnabled)
{
for (int j = 0; j < options->cellCount; j++)
{
free(options->transferData[j]);
}
free(options->transferData);
}
free(options->cells);
free(options);
}
@ -638,3 +687,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;
}

View File

@ -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

View File

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

View File

@ -3,7 +3,9 @@
#ifndef LZ_H
#define LZ_H
#include "stdbool.h"
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

View File

@ -74,7 +74,7 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *
if (options->paletteFilePath != NULL)
{
ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex);
ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false);
image.hasPalette = true;
}
else
@ -141,7 +141,7 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
fclose(fp);
struct Image image;
image.bitDepth = options->bitDepth;
image.bitDepth = options->bitDepth == 0 ? 4 : options->bitDepth;
ReadPng(inputPath, &image);
@ -160,7 +160,9 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
free(string);
}
WriteNtrImage(outputPath, options->numTiles, image.bitDepth, options->colsPerChunk, options->rowsPerChunk,
options->bitDepth = options->bitDepth == 0 ? image.bitDepth : options->bitDepth;
WriteNtrImage(outputPath, options->numTiles, options->bitDepth, options->colsPerChunk, options->rowsPerChunk,
&image, !image.hasPalette, options->clobberSize, options->byteOrder, options->version101,
options->sopc, options->vramTransfer, options->scanMode, options->mappingType, key, options->wrongSize);
@ -420,7 +422,7 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
{
struct PngToNtrOptions options;
options.numTiles = 0;
options.bitDepth = 4;
options.bitDepth = 0;
options.colsPerChunk = 1;
options.rowsPerChunk = 1;
options.wrongSize = false;
@ -565,6 +567,7 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
int bitdepth = 0;
int compNum = 0;
bool pcmp = false;
bool inverted = false;
for (int i = 3; i < argc; i++)
{
@ -612,6 +615,10 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
{
pcmp = true;
}
else if (strcmp(option, "-invertsize") == 0)
{
inverted = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -619,7 +626,7 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
}
ReadPngPalette(inputPath, &palette);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted);
}
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
@ -634,6 +641,7 @@ void HandleNtrToJascPaletteCommand(char *inputPath, char *outputPath, int argc,
{
struct Palette palette;
int bitdepth = 0;
bool inverted = false;
for (int i = 3; i < argc; i++)
{
@ -652,13 +660,17 @@ void HandleNtrToJascPaletteCommand(char *inputPath, char *outputPath, int argc,
if (bitdepth != 4 && bitdepth != 8)
FATAL_ERROR("Bitdepth must be 4 or 8.\n");
}
else if (strcmp(option, "-invertsize") == 0)
{
inverted = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
ReadNtrPalette(inputPath, &palette, bitdepth, 0);
ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted);
WriteJascPalette(outputPath, &palette);
}
@ -708,6 +720,7 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
int bitdepth = 0;
int compNum = 0;
bool pcmp = false;
bool inverted = false;
for (int i = 3; i < argc; i++)
{
@ -768,6 +781,10 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
{
pcmp = true;
}
else if (strcmp(option, "-invertsize") == 0)
{
inverted = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -781,7 +798,7 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
if (numColors != 0)
palette.numColors = numColors;
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted);
}
void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
@ -955,6 +972,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
{
int overflowSize = 0;
int minDistance = 2; // default, for compatibility with LZ77UnCompVram()
bool forwardIteration = true;
for (int i = 3; i < argc; i++)
{
@ -986,6 +1004,10 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
if (minDistance < 1)
FATAL_ERROR("LZ min search distance must be positive.\n");
}
else if (strcmp(option, "-reverse") == 0)
{
forwardIteration = false;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -1002,7 +1024,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
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[2] = (unsigned char)(fileSize >> 8);
@ -1115,6 +1137,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 +1250,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 }
};

View File

@ -51,6 +51,11 @@ struct NtrToPngOptions {
bool handleEmpty;
};
struct CellVramTransferData {
int sourceDataOffset;
int size;
};
struct Attr0 {
int YCoordinate;
bool Rotation;
@ -100,9 +105,12 @@ struct Cell {
struct JsonToCellOptions {
bool labelEnabled;
bool extended;
bool vramTransferEnabled;
int mappingType;
int cellCount;
struct Cell **cells;
int vramTransferMaxSize;
struct CellVramTransferData **transferData;
char **labels;
int labelCount;
};
@ -147,6 +155,7 @@ struct AnimationDataT {
struct AnimationResults {
short resultType;
bool padded;
union {
short index;
struct AnimationDataSRT dataSrt;
@ -166,4 +175,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

View File

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