mirror of
https://github.com/pret/pokediamond.git
synced 2026-03-21 17:54:29 -05:00
update nitrogfx to the latest version
This commit is contained in:
parent
095fc39474
commit
f4cf15f313
|
|
@ -4,3 +4,4 @@
|
|||
arm9/lib/
|
||||
arm7/lib/
|
||||
include/nitro/
|
||||
tools/nitrogfx/
|
||||
|
|
|
|||
1
tools/nitrogfx/.gitignore
vendored
1
tools/nitrogfx/.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
nitrogfx
|
||||
.vscode/
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user