From c2fba5a463a56516cc94dbd1459ca3d87ab3f2ef Mon Sep 17 00:00:00 2001 From: PikalaxALT Date: Sat, 29 Nov 2025 09:14:31 -0500 Subject: [PATCH] Nuke changes to nitrogfx The merge is just too complicated --- .clang-format-ignore | 3 +- tools/nitrogfx/LICENSE | 2 +- tools/nitrogfx/cJSON.c | 2105 +++++++++++++++++++------------- tools/nitrogfx/cJSON.h | 7 +- tools/nitrogfx/convert_png.c | 148 +-- tools/nitrogfx/font.c | 117 +- tools/nitrogfx/font.h | 1 - tools/nitrogfx/gfx.c | 2234 +++++++++++++++++++--------------- tools/nitrogfx/gfx.h | 52 +- tools/nitrogfx/global.h | 35 +- tools/nitrogfx/json.c | 142 ++- tools/nitrogfx/json.h | 2 +- tools/nitrogfx/lz.c | 144 +-- tools/nitrogfx/lz.h | 2 +- tools/nitrogfx/main.c | 811 ++++++------ tools/nitrogfx/options.h | 35 +- tools/nitrogfx/util.c | 57 +- 17 files changed, 3329 insertions(+), 2568 deletions(-) diff --git a/.clang-format-ignore b/.clang-format-ignore index a49edb564..0abdb6854 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -2,4 +2,5 @@ **/*.inc **/*.json lib/ -tools/nitrogfx/ +tools/nitrogfx/*.c +tools/nitrogfx/*.h diff --git a/tools/nitrogfx/LICENSE b/tools/nitrogfx/LICENSE index be4a59938..828b9152d 100644 --- a/tools/nitrogfx/LICENSE +++ b/tools/nitrogfx/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015 YamaArashi, 2021-2024 red031000 +Copyright (c) 2015 YamaArashi, 2021-2025 red031000 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tools/nitrogfx/cJSON.c b/tools/nitrogfx/cJSON.c index 1b8c52ed0..e0fccbe50 100644 --- a/tools/nitrogfx/cJSON.c +++ b/tools/nitrogfx/cJSON.c @@ -32,25 +32,25 @@ #pragma GCC visibility push(default) #endif #if defined(_MSC_VER) -#pragma warning(push) +#pragma warning (push) /* disable warning about single line comments in system headers */ -#pragma warning(disable : 4001) +#pragma warning (disable : 4001) #endif +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #ifdef ENABLE_LOCALES #include #endif #if defined(_MSC_VER) -#pragma warning(pop) +#pragma warning (pop) #endif #ifdef __GNUC__ #pragma GCC visibility pop @@ -81,7 +81,7 @@ #ifdef _WIN32 #define NAN sqrt(-1.0) #else -#define NAN 0.0 / 0.0 +#define NAN 0.0/0.0 #endif #endif @@ -91,36 +91,38 @@ typedef struct { } error; static error global_error = { NULL, 0 }; -CJSON_PUBLIC(const char *) -cJSON_GetErrorPtr(void) { - return (const char *)(global_error.json + global_error.position); +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) { - if (!cJSON_IsString(item)) { +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { return NULL; } return item->valuestring; } -CJSON_PUBLIC(double) -cJSON_GetNumberValue(const cJSON *const item) { - if (!cJSON_IsNumber(item)) { - return (double)NAN; +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; } return item->valuedouble; } /* 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 != 18) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 19) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif -CJSON_PUBLIC(const char *) -cJSON_Version(void) { +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ static char version[15]; sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); @@ -128,17 +130,22 @@ cJSON_Version(void) { } /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ -static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { - if ((string1 == NULL) || (string2 == NULL)) { +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { return 1; } - if (string1 == string2) { + if (string1 == string2) + { return 0; } - for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { - if (*string1 == '\0') { + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { return 0; } } @@ -146,26 +153,30 @@ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned return tolower(*string1) - tolower(*string2); } -typedef struct internal_hooks { +typedef struct internal_hooks +{ void *(CJSON_CDECL *allocate)(size_t size); - void(CJSON_CDECL *deallocate)(void *pointer); + void (CJSON_CDECL *deallocate)(void *pointer); void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; #if defined(_MSC_VER) /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ -static void *CJSON_CDECL internal_malloc(size_t size) { +static void * CJSON_CDECL internal_malloc(size_t size) +{ return malloc(size); } -static void CJSON_CDECL internal_free(void *pointer) { +static void CJSON_CDECL internal_free(void *pointer) +{ free(pointer); } -static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ return realloc(pointer, size); } #else -#define internal_malloc malloc -#define internal_free free +#define internal_malloc malloc +#define internal_free free #define internal_realloc realloc #endif @@ -174,17 +185,20 @@ static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; -static unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) { +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ size_t length = 0; unsigned char *copy = NULL; - if (string == NULL) { + if (string == NULL) + { return NULL; } - length = strlen((const char *)string) + sizeof(""); - copy = (unsigned char *)hooks->allocate(length); - if (copy == NULL) { + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { return NULL; } memcpy(copy, string, length); @@ -192,9 +206,10 @@ static unsigned char *cJSON_strdup(const unsigned char *string, const internal_h return copy; } -CJSON_PUBLIC(void) -cJSON_InitHooks(cJSON_Hooks *hooks) { - if (hooks == NULL) { +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { /* Reset hooks */ global_hooks.allocate = malloc; global_hooks.deallocate = free; @@ -203,26 +218,31 @@ cJSON_InitHooks(cJSON_Hooks *hooks) { } global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) { + if (hooks->malloc_fn != NULL) + { global_hooks.allocate = hooks->malloc_fn; } global_hooks.deallocate = free; - if (hooks->free_fn != NULL) { + if (hooks->free_fn != NULL) + { global_hooks.deallocate = hooks->free_fn; } /* use realloc only if both free and malloc are used */ global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { global_hooks.reallocate = realloc; } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { - cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); - if (node) { +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { memset(node, '\0', sizeof(cJSON)); } @@ -230,19 +250,23 @@ static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { } /* Delete a cJSON structure. */ -CJSON_PUBLIC(void) -cJSON_Delete(cJSON *item) { +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ cJSON *next = NULL; - while (item != NULL) { + while (item != NULL) + { next = item->next; - if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { cJSON_Delete(item->child); } - if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { global_hooks.deallocate(item->valuestring); item->valuestring = NULL; } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { global_hooks.deallocate(item->string); item->string = NULL; } @@ -252,10 +276,11 @@ cJSON_Delete(cJSON *item) { } /* get the decimal point character of the current locale */ -static unsigned char get_decimal_point(void) { +static unsigned char get_decimal_point(void) +{ #ifdef ENABLE_LOCALES struct lconv *lconv = localeconv(); - return (unsigned char)lconv->decimal_point[0]; + return (unsigned char) lconv->decimal_point[0]; #else return '.'; #endif @@ -273,86 +298,123 @@ typedef struct /* check if the given size is left to read in a given parse buffer (starting with 1) */ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) /* check if the buffer can be accessed at the given index (starting with 0) */ -#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) /* Parse the input text to generate a number, and populate the result into item. */ -static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ double number = 0; unsigned char *after_end = NULL; - unsigned char number_c_string[64]; + unsigned char *number_c_string; unsigned char decimal_point = get_decimal_point(); size_t i = 0; + size_t number_string_length = 0; + cJSON_bool has_decimal_point = false; - if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { return false; } /* copy the number into a temporary buffer and replace '.' with the decimal point * of the current locale (for strtod) * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { - switch (buffer_at_offset(input_buffer)[i]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; - break; + for (i = 0; can_access_at_index(input_buffer, i); i++) { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_string_length++; + break; - case '.': - number_c_string[i] = decimal_point; - break; + case '.': + number_string_length++; + has_decimal_point = true; + break; - default: - goto loop_end; + default: + goto loop_end; } } loop_end: - number_c_string[i] = '\0'; + /* malloc for temporary buffer, add 1 for '\0' */ + number_c_string = (unsigned char *)input_buffer->hooks.allocate(number_string_length + 1); + if (number_c_string == NULL) { + return false; /* allocation failure */ + } - number = strtod((const char *)number_c_string, (char **)&after_end); - if (number_c_string == after_end) { + memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length); + number_c_string[number_string_length] = '\0'; + + if (has_decimal_point) { + for (i = 0; i < number_string_length; i++) { + if (number_c_string[i] == '.') { + /* replace '.' with the decimal point of the current locale (for strtod) */ + number_c_string[i] = decimal_point; + } + } + } + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return false; /* parse_error */ } item->valuedouble = number; /* use saturation in case of overflow */ - if (number >= INT_MAX) { + if (number >= INT_MAX) + { item->valueint = INT_MAX; - } else if (number <= (double)INT_MIN) { + } + else if (number <= (double)INT_MIN) + { item->valueint = INT_MIN; - } else { + } + else + { item->valueint = (int)number; } item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return true; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) -cJSON_SetNumberHelper(cJSON *object, double number) { - if (number >= INT_MAX) { +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { object->valueint = INT_MAX; - } else if (number <= (double)INT_MIN) { + } + else if (number <= (double)INT_MIN) + { object->valueint = INT_MIN; - } else { + } + else + { object->valueint = (int)number; } @@ -360,36 +422,42 @@ cJSON_SetNumberHelper(cJSON *object, double 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) { +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 == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { return NULL; } /* return NULL if the object is corrupted or valuestring is NULL */ - if (object->valuestring == NULL || valuestring == NULL) { + if (object->valuestring == NULL || valuestring == NULL) + { return NULL; } v1_len = strlen(valuestring); v2_len = strlen(object->valuestring); - if (v1_len <= v2_len) { + 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)) { + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { return NULL; } strcpy(object->valuestring, valuestring); return object->valuestring; } - copy = (char *)cJSON_strdup((const unsigned char *)valuestring, &global_hooks); - if (copy == NULL) { + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { return NULL; } - if (object->valuestring != NULL) { + if (object->valuestring != NULL) + { cJSON_free(object->valuestring); } object->valuestring = copy; @@ -409,26 +477,31 @@ typedef struct } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char *ensure(printbuffer *const p, size_t needed) { +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ unsigned char *newbuffer = NULL; size_t newsize = 0; - if ((p == NULL) || (p->buffer == NULL)) { + if ((p == NULL) || (p->buffer == NULL)) + { return NULL; } - if ((p->length > 0) && (p->offset >= p->length)) { + if ((p->length > 0) && (p->offset >= p->length)) + { /* make sure that offset is valid */ return NULL; } - if (needed > INT_MAX) { + if (needed > INT_MAX) + { /* sizes bigger than INT_MAX are currently not supported */ return NULL; } needed += p->offset + 1; - if (needed <= p->length) { + if (needed <= p->length) + { return p->buffer + p->offset; } @@ -437,31 +510,42 @@ static unsigned char *ensure(printbuffer *const p, size_t needed) { } /* calculate new buffer size */ - if (needed > (INT_MAX / 2)) { + if (needed > (INT_MAX / 2)) + { /* overflow of int, use INT_MAX if possible */ - if (needed <= INT_MAX) { + if (needed <= INT_MAX) + { newsize = INT_MAX; - } else { + } + else + { return NULL; } - } else { + } + else + { newsize = needed * 2; } - if (p->hooks.reallocate != NULL) { + if (p->hooks.reallocate != NULL) + { /* reallocate with realloc if available */ - newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) { + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; return NULL; } - } else { + } + else + { /* otherwise reallocate manually */ - newbuffer = (unsigned char *)p->hooks.allocate(newsize); - if (!newbuffer) { + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { p->hooks.deallocate(p->buffer); p->length = 0; p->buffer = NULL; @@ -479,67 +563,82 @@ static unsigned char *ensure(printbuffer *const p, size_t needed) { } /* calculate the new length of the string in a printbuffer and update the offset */ -static void update_offset(printbuffer *const buffer) { +static void update_offset(printbuffer * const buffer) +{ const unsigned char *buffer_pointer = NULL; - if ((buffer == NULL) || (buffer->buffer == NULL)) { + if ((buffer == NULL) || (buffer->buffer == NULL)) + { return; } buffer_pointer = buffer->buffer + buffer->offset; - buffer->offset += strlen((const char *)buffer_pointer); + buffer->offset += strlen((const char*)buffer_pointer); } /* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) { +static cJSON_bool compare_double(double a, double b) +{ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); - return fabs(a - b) <= maxVal * DBL_EPSILON; + return (fabs(a - b) <= maxVal * DBL_EPSILON); } /* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ unsigned char *output_pointer = NULL; double d = item->valuedouble; int length = 0; size_t i = 0; - unsigned char number_buffer[26] = { 0 }; /* temporary buffer to print the number into */ + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); double test = 0.0; - if (output_buffer == NULL) { + if (output_buffer == NULL) + { return false; } /* This checks for NaN and Infinity */ - if (isnan(d) || isinf(d)) { - length = sprintf((char *)number_buffer, "null"); - } else if (d == (double)item->valueint) { - length = sprintf((char *)number_buffer, "%d", item->valueint); - } else { + if (isnan(d) || isinf(d)) + { + 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 */ - length = sprintf((char *)number_buffer, "%1.15g", d); + length = sprintf((char*)number_buffer, "%1.15g", d); /* Check whether the original double can be recovered */ - if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { /* If not, print with 17 decimal places of precision */ - length = sprintf((char *)number_buffer, "%1.17g", d); + length = sprintf((char*)number_buffer, "%1.17g", d); } } /* sprintf failed or buffer overrun occurred */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { return false; } /* reserve appropriate space in the output */ output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } /* copy the printed number to the output and replace locale * dependent decimal point with '.' */ - for (i = 0; i < ((size_t)length); i++) { - if (number_buffer[i] == decimal_point) { + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { output_pointer[i] = '.'; continue; } @@ -554,24 +653,33 @@ static cJSON_bool print_number(const cJSON *const item, printbuffer *const outpu } /* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char *const input) { +static unsigned parse_hex4(const unsigned char * const input) +{ unsigned int h = 0; size_t i = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) + { /* parse digit */ - if ((input[i] >= '0') && (input[i] <= '9')) { - h += (unsigned int)input[i] - '0'; - } else if ((input[i] >= 'A') && (input[i] <= 'F')) { - h += (unsigned int)10 + input[i] - 'A'; - } else if ((input[i] >= 'a') && (input[i] <= 'f')) { - h += (unsigned int)10 + input[i] - 'a'; - } else /* invalid */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ { return 0; } - if (i < 3) { + if (i < 3) + { /* shift left to make place for the next nibble */ h = h << 4; } @@ -582,7 +690,8 @@ static unsigned parse_hex4(const unsigned char *const input) { /* converts a UTF-16 literal to UTF-8 * A literal can be one or two sequences of the form \uXXXX */ -static unsigned char utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, unsigned char **output_pointer) { +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ long unsigned int codepoint = 0; unsigned int first_code = 0; const unsigned char *first_sequence = input_pointer; @@ -591,7 +700,8 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin unsigned char sequence_length = 0; unsigned char first_byte_mark = 0; - if ((input_end - first_sequence) < 6) { + if ((input_end - first_sequence) < 6) + { /* input ends unexpectedly */ goto fail; } @@ -600,22 +710,26 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin first_code = parse_hex4(first_sequence + 2); /* check that the code is valid */ - if ((first_code >= 0xDC00) && (first_code <= 0xDFFF)) { + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { goto fail; } /* UTF16 surrogate pair */ - if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { const unsigned char *second_sequence = first_sequence + 6; unsigned int second_code = 0; sequence_length = 12; /* \uXXXX\uXXXX */ - if ((input_end - second_sequence) < 6) { + if ((input_end - second_sequence) < 6) + { /* input ends unexpectedly */ goto fail; } - if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { /* missing second half of the surrogate pair */ goto fail; } @@ -623,14 +737,18 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin /* get the second utf16 sequence */ second_code = parse_hex4(second_sequence + 2); /* check that the code is valid */ - if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { /* invalid second half of the surrogate pair */ goto fail; } + /* calculate the unicode codepoint from the surrogate pair */ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); - } else { + } + else + { sequence_length = 6; /* \uXXXX */ codepoint = first_code; } @@ -638,36 +756,49 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin /* encode as UTF-8 * takes at maximum 4 bytes to encode: * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint < 0x80) { + if (codepoint < 0x80) + { /* normal ascii, encoding 0xxxxxxx */ utf8_length = 1; - } else if (codepoint < 0x800) { + } + else if (codepoint < 0x800) + { /* two bytes, encoding 110xxxxx 10xxxxxx */ utf8_length = 2; first_byte_mark = 0xC0; /* 11000000 */ - } else if (codepoint < 0x10000) { + } + else if (codepoint < 0x10000) + { /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ utf8_length = 3; first_byte_mark = 0xE0; /* 11100000 */ - } else if (codepoint <= 0x10FFFF) { + } + else if (codepoint <= 0x10FFFF) + { /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ utf8_length = 4; first_byte_mark = 0xF0; /* 11110000 */ - } else { + } + else + { /* invalid unicode codepoint */ goto fail; } /* encode as utf8 */ - for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { /* 10xxxxxx */ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); codepoint >>= 6; } /* encode first byte */ - if (utf8_length > 1) { + if (utf8_length > 1) + { (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); - } else { + } + else + { (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); } @@ -680,14 +811,16 @@ fail: } /* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; unsigned char *output_pointer = NULL; unsigned char *output = NULL; /* not a string */ - if (buffer_at_offset(input_buffer)[0] != '\"') { + if (buffer_at_offset(input_buffer)[0] != '\"') + { goto fail; } @@ -695,10 +828,13 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff /* calculate approximate size of the output (overestimate) */ size_t allocation_length = 0; size_t skipped_bytes = 0; - while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { /* is escape sequence */ - if (input_end[0] == '\\') { - if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { /* prevent buffer overflow when last input character is a backslash */ goto fail; } @@ -707,64 +843,72 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff } input_end++; } - if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { goto fail; /* string ended unexpectedly */ } /* This is at most how much we need for the output */ - allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof("")); - if (output == NULL) { + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { goto fail; /* allocation failure */ } } output_pointer = output; /* loop through the string literal */ - while (input_pointer < input_end) { - if (*input_pointer != '\\') { + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { *output_pointer++ = *input_pointer++; } /* escape sequence */ - else { + else + { unsigned char sequence_length = 2; - if ((input_end - input_pointer) < 1) { + if ((input_end - input_pointer) < 1) + { goto fail; } - switch (input_pointer[1]) { - case 'b': - *output_pointer++ = '\b'; - break; - case 'f': - *output_pointer++ = '\f'; - break; - case 'n': - *output_pointer++ = '\n'; - break; - case 'r': - *output_pointer++ = '\r'; - break; - case 't': - *output_pointer++ = '\t'; - break; - case '\"': - case '\\': - case '/': - *output_pointer++ = input_pointer[1]; - break; + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; - /* UTF-16 literal */ - case 'u': - sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); - if (sequence_length == 0) { - /* failed to convert UTF16-literal to UTF-8 */ + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: goto fail; - } - break; - - default: - goto fail; } input_pointer += sequence_length; } @@ -774,20 +918,22 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff *output_pointer = '\0'; item->type = cJSON_String; - item->valuestring = (char *)output; + item->valuestring = (char*)output; - input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset = (size_t) (input_end - input_buffer->content); input_buffer->offset++; return true; fail: - if (output != NULL) { + if (output != NULL) + { input_buffer->hooks.deallocate(output); output = NULL; } - if (input_pointer != NULL) { + if (input_pointer != NULL) + { input_buffer->offset = (size_t)(input_pointer - input_buffer->content); } @@ -795,7 +941,8 @@ fail: } /* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) { +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ const unsigned char *input_pointer = NULL; unsigned char *output = NULL; unsigned char *output_pointer = NULL; @@ -803,51 +950,59 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer /* numbers of additional characters needed for escaping */ size_t escape_characters = 0; - if (output_buffer == NULL) { + if (output_buffer == NULL) + { return false; } /* empty string */ - if (input == NULL) { + if (input == NULL) + { output = ensure(output_buffer, sizeof("\"\"")); - if (output == NULL) { + if (output == NULL) + { return false; } - strcpy((char *)output, "\"\""); + strcpy((char*)output, "\"\""); return true; } /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) { - switch (*input_pointer) { - case '\"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - /* one character escape sequence */ - escape_characters++; - break; - default: - if (*input_pointer < 32) { - /* UTF-16 escape sequence uXXXX */ - escape_characters += 5; - } - break; + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; } } output_length = (size_t)(input_pointer - input) + escape_characters; output = ensure(output_buffer, output_length + sizeof("\"\"")); - if (output == NULL) { + if (output == NULL) + { return false; } /* no characters have to be escaped */ - if (escape_characters == 0) { + if (escape_characters == 0) + { output[0] = '\"'; memcpy(output + 1, input, output_length); output[output_length + 1] = '\"'; @@ -859,40 +1014,45 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer output[0] = '\"'; output_pointer = output + 1; /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { - if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { /* normal character, copy */ *output_pointer = *input_pointer; - } else { + } + else + { /* character needs to be escaped */ *output_pointer++ = '\\'; - switch (*input_pointer) { - case '\\': - *output_pointer = '\\'; - break; - case '\"': - *output_pointer = '\"'; - break; - case '\b': - *output_pointer = 'b'; - break; - case '\f': - *output_pointer = 'f'; - break; - case '\n': - *output_pointer = 'n'; - break; - case '\r': - *output_pointer = 'r'; - break; - case '\t': - *output_pointer = 't'; - break; - default: - /* escape and print as unicode codepoint */ - sprintf((char *)output_pointer, "u%04x", *input_pointer); - output_pointer += 4; - break; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; } } } @@ -903,33 +1063,39 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer } /* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON *const item, printbuffer *const p) { - return print_string_ptr((unsigned char *)item->valuestring, p); +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); } /* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer); -static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer); -static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer); -static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ -static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { - if ((buffer == NULL) || (buffer->content == NULL)) { +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { return NULL; } - if (cannot_access_at_index(buffer, 0)) { + if (cannot_access_at_index(buffer, 0)) + { return buffer; } - while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { - buffer->offset++; + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; } - if (buffer->offset == buffer->length) { + if (buffer->offset == buffer->length) + { buffer->offset--; } @@ -937,23 +1103,27 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { } /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ -static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) { - if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { return NULL; } - if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { buffer->offset += 3; } return buffer; } -CJSON_PUBLIC(cJSON *) -cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ size_t buffer_length; - if (NULL == value) { + if (NULL == value) + { return NULL; } @@ -964,22 +1134,21 @@ cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool } /* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) -cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { - parse_buffer buffer = { - 0, 0, 0, 0, { 0, 0, 0 } - }; +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; /* reset error position */ global_error.json = NULL; global_error.position = 0; - if (value == NULL || 0 == buffer_length) { + if (value == NULL || 0 == buffer_length) + { goto fail; } - buffer.content = (const unsigned char *)value; + buffer.content = (const unsigned char*)value; buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -990,42 +1159,52 @@ cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char ** goto fail; } - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { /* parse failure. ep is set. */ goto fail; } /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) { + if (require_null_terminated) + { buffer_skip_whitespace(&buffer); - if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { goto fail; } } - if (return_parse_end) { - *return_parse_end = (const char *)buffer_at_offset(&buffer); + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); } return item; fail: - if (item != NULL) { + if (item != NULL) + { cJSON_Delete(item); } - if (value != NULL) { + if (value != NULL) + { error local_error; - local_error.json = (const unsigned char *)value; + local_error.json = (const unsigned char*)value; local_error.position = 0; - if (buffer.offset < buffer.length) { + if (buffer.offset < buffer.length) + { local_error.position = buffer.offset; - } else if (buffer.length > 0) { + } + else if (buffer.length > 0) + { local_error.position = buffer.length - 1; } - if (return_parse_end != NULL) { - *return_parse_end = (const char *)local_error.json + local_error.position; + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; } global_error = local_error; @@ -1035,19 +1214,20 @@ fail: } /* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) -cJSON_Parse(const char *value) { +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ return cJSON_ParseWithOpts(value, 0, 0); } -CJSON_PUBLIC(cJSON *) -cJSON_ParseWithLength(const char *value, size_t buffer_length) { +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); } #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) -static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks) { +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; @@ -1055,31 +1235,36 @@ static unsigned char *print(const cJSON *const item, cJSON_bool format, const in memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size); + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); buffer->length = default_buffer_size; buffer->format = format; buffer->hooks = *hooks; - if (buffer->buffer == NULL) { + if (buffer->buffer == NULL) + { goto fail; } /* print the value */ - if (!print_value(item, buffer)) { + if (!print_value(item, buffer)) + { goto fail; } update_offset(buffer); /* check if reallocate is available */ - if (hooks->reallocate != NULL) { - printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); if (printed == NULL) { goto fail; } buffer->buffer = NULL; - } else /* otherwise copy the JSON over to a new buffer */ + } + else /* otherwise copy the JSON over to a new buffer */ { - printed = (unsigned char *)hooks->allocate(buffer->offset + 1); - if (printed == NULL) { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { goto fail; } memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); @@ -1093,12 +1278,14 @@ static unsigned char *print(const cJSON *const item, cJSON_bool format, const in return printed; fail: - if (buffer->buffer != NULL) { + if (buffer->buffer != NULL) + { hooks->deallocate(buffer->buffer); buffer->buffer = NULL; } - if (printed != NULL) { + if (printed != NULL) + { hooks->deallocate(printed); printed = NULL; } @@ -1107,28 +1294,28 @@ fail: } /* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) -cJSON_Print(const cJSON *item) { - return (char *)print(item, true, &global_hooks); +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); } -CJSON_PUBLIC(char *) -cJSON_PrintUnformatted(const cJSON *item) { - return (char *)print(item, false, &global_hooks); +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); } -CJSON_PUBLIC(char *) -cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { - printbuffer p = { - 0, 0, 0, 0, 0, 0, { 0, 0, 0 } - }; +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - if (prebuffer < 0) { + if (prebuffer < 0) + { return NULL; } - p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) { + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { return NULL; } @@ -1138,26 +1325,26 @@ cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { p.format = fmt; p.hooks = global_hooks; - if (!print_value(item, &p)) { + if (!print_value(item, &p)) + { global_hooks.deallocate(p.buffer); p.buffer = NULL; return NULL; } - return (char *)p.buffer; + return (char*)p.buffer; } -CJSON_PUBLIC(cJSON_bool) -cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { - printbuffer p = { - 0, 0, 0, 0, 0, 0, { 0, 0, 0 } - }; +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - if ((length < 0) || (buffer == NULL)) { + if ((length < 0) || (buffer == NULL)) + { return false; } - p.buffer = (unsigned char *)buffer; + p.buffer = (unsigned char*)buffer; p.length = (size_t)length; p.offset = 0; p.noalloc = true; @@ -1168,45 +1355,54 @@ cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON } /* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer) { - if ((input_buffer == NULL) || (input_buffer->content == NULL)) { +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { return false; /* no input */ } /* parse the different types of values */ /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) { + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { item->type = cJSON_NULL; input_buffer->offset += 4; return true; } /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) { + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { item->type = cJSON_False; input_buffer->offset += 5; return true; } /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) { + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { item->type = cJSON_True; item->valueint = 1; input_buffer->offset += 4; return true; } /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { return parse_string(item, input_buffer); } /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { return parse_number(item, input_buffer); } /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { return parse_array(item, input_buffer); } /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { return parse_object(item, input_buffer); } @@ -1214,94 +1410,108 @@ static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffe } /* Render a value to text. */ -static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ unsigned char *output = NULL; - if ((item == NULL) || (output_buffer == NULL)) { + if ((item == NULL) || (output_buffer == NULL)) + { return false; } - switch ((item->type) & 0xFF) { - case cJSON_NULL: - output = ensure(output_buffer, 5); - if (output == NULL) { - return false; - } - strcpy((char *)output, "null"); - return true; + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; - case cJSON_False: - output = ensure(output_buffer, 6); - if (output == NULL) { - return false; - } - strcpy((char *)output, "false"); - return true; + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; - case cJSON_True: - output = ensure(output_buffer, 5); - if (output == NULL) { - return false; - } - strcpy((char *)output, "true"); - return true; + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; - case cJSON_Number: - return print_number(item, output_buffer); + case cJSON_Number: + return print_number(item, output_buffer); - case cJSON_Raw: { - size_t raw_length = 0; - if (item->valuestring == NULL) { - return false; + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; } - raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); - if (output == NULL) { + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: return false; - } - memcpy(output, item->valuestring, raw_length); - return true; - } - - case cJSON_String: - return print_string(item, output_buffer); - - case cJSON_Array: - return print_array(item, output_buffer); - - case cJSON_Object: - return print_object(item, output_buffer); - - default: - return false; } } /* Build an array from input text. */ -static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ cJSON *head = NULL; /* head of the linked list */ cJSON *current_item = NULL; - if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { return false; /* to deeply nested */ } input_buffer->depth++; - if (buffer_at_offset(input_buffer)[0] != '[') { + if (buffer_at_offset(input_buffer)[0] != '[') + { /* not an array */ goto fail; } input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { /* empty array */ goto success; } /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) { + if (cannot_access_at_index(input_buffer, 0)) + { input_buffer->offset--; goto fail; } @@ -1309,18 +1519,23 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ - do { + do + { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) { + if (new_item == NULL) + { goto fail; /* allocation failure */ } /* attach next item to list */ - if (head == NULL) { + if (head == NULL) + { /* start the linked list */ current_item = head = new_item; - } else { + } + else + { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; @@ -1330,13 +1545,16 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe /* parse next value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) { + if (!parse_value(current_item, input_buffer)) + { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); - } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { goto fail; /* expected end of array */ } @@ -1355,7 +1573,8 @@ success: return true; fail: - if (head != NULL) { + if (head != NULL) + { cJSON_Delete(head); } @@ -1363,19 +1582,22 @@ fail: } /* Render an array to text */ -static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_element = item->child; - if (output_buffer == NULL) { + if (output_buffer == NULL) + { return false; } /* Compose the output array. */ /* opening square bracket */ output_pointer = ensure(output_buffer, 1); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } @@ -1383,19 +1605,24 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output output_buffer->offset++; output_buffer->depth++; - while (current_element != NULL) { - if (!print_value(current_element, output_buffer)) { + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { return false; } update_offset(output_buffer); - if (current_element->next) { - length = (size_t)(output_buffer->format ? 2 : 1); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } *output_pointer++ = ','; - if (output_buffer->format) { + if(output_buffer->format) + { *output_pointer++ = ' '; } *output_pointer = '\0'; @@ -1405,7 +1632,8 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output } output_pointer = ensure(output_buffer, 2); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } *output_pointer++ = ']'; @@ -1416,27 +1644,32 @@ static cJSON_bool print_array(const cJSON *const item, printbuffer *const output } /* Build an object from the text. */ -static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) { +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ cJSON *head = NULL; /* linked list head */ cJSON *current_item = NULL; - if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { return false; /* to deeply nested */ } input_buffer->depth++; - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { goto fail; /* not an object */ } input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { goto success; /* empty object */ } /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) { + if (cannot_access_at_index(input_buffer, 0)) + { input_buffer->offset--; goto fail; } @@ -1444,32 +1677,39 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff /* step back to character in front of the first element */ input_buffer->offset--; /* loop through the comma separated array elements */ - do { + do + { /* allocate next item */ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) { + if (new_item == NULL) + { goto fail; /* allocation failure */ } /* attach next item to list */ - if (head == NULL) { + if (head == NULL) + { /* start the linked list */ current_item = head = new_item; - } else { + } + else + { /* add to the end and advance */ current_item->next = new_item; new_item->prev = current_item; current_item = new_item; } - if (cannot_access_at_index(input_buffer, 1)) { + 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); - if (!parse_string(current_item, input_buffer)) { + if (!parse_string(current_item, input_buffer)) + { goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); @@ -1478,20 +1718,24 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff current_item->string = current_item->valuestring; current_item->valuestring = NULL; - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { goto fail; /* invalid object */ } /* parse the value */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) { + if (!parse_value(current_item, input_buffer)) + { goto fail; /* failed to parse value */ } buffer_skip_whitespace(input_buffer); - } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { goto fail; /* expected end of object */ } @@ -1509,7 +1753,8 @@ success: return true; fail: - if (head != NULL) { + if (head != NULL) + { cJSON_Delete(head); } @@ -1517,61 +1762,73 @@ fail: } /* Render an object to text. */ -static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer) { +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ unsigned char *output_pointer = NULL; size_t length = 0; cJSON *current_item = item->child; - if (output_buffer == NULL) { + if (output_buffer == NULL) + { return false; } /* Compose the output: */ - length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } *output_pointer++ = '{'; output_buffer->depth++; - if (output_buffer->format) { + if (output_buffer->format) + { *output_pointer++ = '\n'; } output_buffer->offset += length; - while (current_item) { - if (output_buffer->format) { + while (current_item) + { + if (output_buffer->format) + { size_t i; output_pointer = ensure(output_buffer, output_buffer->depth); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } - for (i = 0; i < output_buffer->depth; i++) { + for (i = 0; i < output_buffer->depth; i++) + { *output_pointer++ = '\t'; } output_buffer->offset += output_buffer->depth; } /* print key */ - if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) { + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { return false; } update_offset(output_buffer); - length = (size_t)(output_buffer->format ? 2 : 1); + length = (size_t) (output_buffer->format ? 2 : 1); output_pointer = ensure(output_buffer, length); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } *output_pointer++ = ':'; - if (output_buffer->format) { + if (output_buffer->format) + { *output_pointer++ = '\t'; } output_buffer->offset += length; /* print value */ - if (!print_value(current_item, output_buffer)) { + if (!print_value(current_item, output_buffer)) + { return false; } update_offset(output_buffer); @@ -1579,14 +1836,17 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu /* print comma if not last */ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } - if (current_item->next) { + if (current_item->next) + { *output_pointer++ = ','; } - if (output_buffer->format) { + if (output_buffer->format) + { *output_pointer++ = '\n'; } *output_pointer = '\0'; @@ -1596,12 +1856,15 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu } output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); - if (output_pointer == NULL) { + if (output_pointer == NULL) + { return false; } - if (output_buffer->format) { + if (output_buffer->format) + { size_t i; - for (i = 0; i < (output_buffer->depth - 1); i++) { + for (i = 0; i < (output_buffer->depth - 1); i++) + { *output_pointer++ = '\t'; } } @@ -1613,18 +1876,20 @@ static cJSON_bool print_object(const cJSON *const item, printbuffer *const outpu } /* Get Array size/item / object item. */ -CJSON_PUBLIC(int) -cJSON_GetArraySize(const cJSON *array) { +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ cJSON *child = NULL; size_t size = 0; - if (array == NULL) { + if (array == NULL) + { return 0; } child = array->child; - while (child != NULL) { + while(child != NULL) + { size++; child = child->next; } @@ -1634,15 +1899,18 @@ cJSON_GetArraySize(const cJSON *array) { return (int)size; } -static cJSON *get_array_item(const cJSON *array, size_t index) { +static cJSON* get_array_item(const cJSON *array, size_t index) +{ cJSON *current_child = NULL; - if (array == NULL) { + if (array == NULL) + { return NULL; } current_child = array->child; - while ((current_child != NULL) && (index > 0)) { + while ((current_child != NULL) && (index > 0)) + { index--; current_child = current_child->next; } @@ -1650,29 +1918,37 @@ static cJSON *get_array_item(const cJSON *array, size_t index) { return current_child; } -CJSON_PUBLIC(cJSON *) -cJSON_GetArrayItem(const cJSON *array, int index) { - if (index < 0) { +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { return NULL; } return get_array_item(array, (size_t)index); } -static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive) { +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ cJSON *current_element = NULL; - if ((object == NULL) || (name == NULL)) { + if ((object == NULL) || (name == NULL)) + { return NULL; } current_element = object->child; - if (case_sensitive) { - while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { current_element = current_element->next; } - } else { - while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)(current_element->string)) != 0)) { + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { current_element = current_element->next; } } @@ -1684,36 +1960,40 @@ static cJSON *get_object_item(const cJSON *const object, const char *const name, return current_element; } -CJSON_PUBLIC(cJSON *) -cJSON_GetObjectItem(const cJSON *const object, const char *const string) { +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ return get_object_item(object, string, false); } -CJSON_PUBLIC(cJSON *) -cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ return get_object_item(object, string, true); } -CJSON_PUBLIC(cJSON_bool) -cJSON_HasObjectItem(const cJSON *object, const char *string) { +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ return cJSON_GetObjectItem(object, string) ? 1 : 0; } /* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) { +static void suffix_object(cJSON *prev, cJSON *item) +{ prev->next = item; item->prev = prev; } /* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks) { +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ cJSON *reference = NULL; - if (item == NULL) { + if (item == NULL) + { return NULL; } reference = cJSON_New_Item(hooks); - if (reference == NULL) { + if (reference == NULL) + { return NULL; } @@ -1724,10 +2004,12 @@ static cJSON *create_reference(const cJSON *item, const internal_hooks *const ho return reference; } -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ cJSON *child = NULL; - if ((item == NULL) || (array == NULL) || (array == item)) { + if ((item == NULL) || (array == NULL) || (array == item)) + { return false; } @@ -1735,14 +2017,18 @@ static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { /* * To find the last item in array quickly, we use prev in array */ - if (child == NULL) { + if (child == NULL) + { /* list is empty, start new one */ array->child = item; item->prev = item; item->next = NULL; - } else { + } + else + { /* append to the end */ - if (child->prev) { + if (child->prev) + { suffix_object(child->prev, item); array->child->prev = item; } @@ -1752,46 +2038,55 @@ static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { } /* Add item to array/object. */ -CJSON_PUBLIC(cJSON_bool) -cJSON_AddItemToArray(cJSON *array, cJSON *item) { +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ return add_item_to_array(array, item); } -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic push +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif /* helper function to cast away const */ -static void *cast_away_const(const void *string) { - return (void *)string; +static void* cast_away_const(const void* string) +{ + return (void*)string; } -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic pop +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop #endif -static cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item, const internal_hooks *const hooks, const cJSON_bool constant_key) { + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ char *new_key = NULL; int new_type = cJSON_Invalid; - if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { return false; } - if (constant_key) { - new_key = (char *)cast_away_const(string); + if (constant_key) + { + new_key = (char*)cast_away_const(string); new_type = item->type | cJSON_StringIsConst; - } else { - new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks); - if (new_key == NULL) { + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { return false; } new_type = item->type & ~cJSON_StringIsConst; } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { hooks->deallocate(item->string); } @@ -1801,39 +2096,42 @@ static cJSON_bool add_item_to_object(cJSON *const object, const char *const stri return add_item_to_array(object, item); } -CJSON_PUBLIC(cJSON_bool) -cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ return add_item_to_object(object, string, item, &global_hooks, false); } /* Add an item to an object with constant string as key */ -CJSON_PUBLIC(cJSON_bool) -cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ return add_item_to_object(object, string, item, &global_hooks, true); } -CJSON_PUBLIC(cJSON_bool) -cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { - if (array == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { return false; } return add_item_to_array(array, create_reference(item, &global_hooks)); } -CJSON_PUBLIC(cJSON_bool) -cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { - if ((object == NULL) || (string == NULL)) { +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { return false; } return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } -CJSON_PUBLIC(cJSON *) -cJSON_AddNullToObject(cJSON *const object, const char *const name) { +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) { + if (add_item_to_object(object, name, null, &global_hooks, false)) + { return null; } @@ -1841,10 +2139,11 @@ cJSON_AddNullToObject(cJSON *const object, const char *const name) { return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddTrueToObject(cJSON *const object, const char *const name) { +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) { + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { return true_item; } @@ -1852,10 +2151,11 @@ cJSON_AddTrueToObject(cJSON *const object, const char *const name) { return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddFalseToObject(cJSON *const object, const char *const name) { +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) { + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { return false_item; } @@ -1863,10 +2163,11 @@ cJSON_AddFalseToObject(cJSON *const object, const char *const name) { return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean) { +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { return bool_item; } @@ -1874,10 +2175,11 @@ cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_b return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number) { +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) { + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { return number_item; } @@ -1885,10 +2187,11 @@ cJSON_AddNumberToObject(cJSON *const object, const char *const name, const doubl return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string) { +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) { + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { return string_item; } @@ -1896,10 +2199,11 @@ cJSON_AddStringToObject(cJSON *const object, const char *const name, const char return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw) { +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { return raw_item; } @@ -1907,10 +2211,11 @@ cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *co return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddObjectToObject(cJSON *const object, const char *const name) { +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) { + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { return object_item; } @@ -1918,10 +2223,11 @@ cJSON_AddObjectToObject(cJSON *const object, const char *const name) { return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_AddArrayToObject(cJSON *const object, const char *const name) { +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) { + if (add_item_to_object(object, name, array, &global_hooks, false)) + { return array; } @@ -1929,25 +2235,31 @@ cJSON_AddArrayToObject(cJSON *const object, const char *const name) { return NULL; } -CJSON_PUBLIC(cJSON *) -cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) { - if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) { +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) + { return NULL; } - if (item != parent->child) { + if (item != parent->child) + { /* not the first element */ item->prev->next = item->next; } - if (item->next != NULL) { + if (item->next != NULL) + { /* not the last element */ item->next->prev = item->prev; } - if (item == parent->child) { + if (item == parent->child) + { /* first element */ parent->child = item->next; - } else if (item->next == NULL) { + } + else if (item->next == NULL) + { /* last element */ parent->child->prev = item->prev; } @@ -1959,55 +2271,58 @@ cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) { return item; } -CJSON_PUBLIC(cJSON *) -cJSON_DetachItemFromArray(cJSON *array, int which) { - if (which < 0) { +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { return NULL; } return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); } -CJSON_PUBLIC(void) -cJSON_DeleteItemFromArray(cJSON *array, int which) { +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } -CJSON_PUBLIC(cJSON *) -cJSON_DetachItemFromObject(cJSON *object, const char *string) { +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ cJSON *to_detach = cJSON_GetObjectItem(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } -CJSON_PUBLIC(cJSON *) -cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); return cJSON_DetachItemViaPointer(object, to_detach); } -CJSON_PUBLIC(void) -cJSON_DeleteItemFromObject(cJSON *object, const char *string) { +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } -CJSON_PUBLIC(void) -cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } /* Replace array/object items with new ones. */ -CJSON_PUBLIC(cJSON_bool) -cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ cJSON *after_inserted = NULL; - if (which < 0 || newitem == NULL) { + if (which < 0 || newitem == NULL) + { return false; } after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) { + if (after_inserted == NULL) + { return add_item_to_array(array, newitem); } @@ -2019,43 +2334,55 @@ cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { newitem->next = after_inserted; newitem->prev = after_inserted->prev; after_inserted->prev = newitem; - if (after_inserted == array->child) { + if (after_inserted == array->child) + { array->child = newitem; - } else { + } + else + { newitem->prev->next = newitem; } return true; } -CJSON_PUBLIC(cJSON_bool) -cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement) { - if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) + { return false; } - if (replacement == item) { + if (replacement == item) + { return true; } replacement->next = item->next; replacement->prev = item->prev; - if (replacement->next != NULL) { + if (replacement->next != NULL) + { replacement->next->prev = replacement; } - if (parent->child == item) { - if (parent->child->prev == parent->child) { + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { replacement->prev = replacement; } parent->child = replacement; - } else { /* - * To find the last item in array quickly, we use prev in array. - * We can't modify the last item's next pointer where this item was the parent's child - */ - if (replacement->prev != NULL) { + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { replacement->prev->next = replacement; } - if (replacement->next == NULL) { + if (replacement->next == NULL) + { parent->child->prev = replacement; } } @@ -2067,26 +2394,31 @@ cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *repla return true; } -CJSON_PUBLIC(cJSON_bool) -cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { - if (which < 0) { +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { return false; } return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { - if ((replacement == NULL) || (string == NULL)) { +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { return false; } /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { cJSON_free(replacement->string); } - replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); - if (replacement->string == NULL) { + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { return false; } @@ -2095,70 +2427,80 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } -CJSON_PUBLIC(cJSON_bool) -cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ return replace_item_in_object(object, string, newitem, false); } -CJSON_PUBLIC(cJSON_bool) -cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ -CJSON_PUBLIC(cJSON *) -cJSON_CreateNull(void) { +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_NULL; } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateTrue(void) { +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_True; } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateFalse(void) { +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_False; } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateBool(cJSON_bool boolean) { +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = boolean ? cJSON_True : cJSON_False; } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateNumber(double num) { +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_Number; item->valuedouble = num; /* use saturation in case of overflow */ - if (num >= INT_MAX) { + if (num >= INT_MAX) + { item->valueint = INT_MAX; - } else if (num <= (double)INT_MIN) { + } + else if (num <= (double)INT_MIN) + { item->valueint = INT_MIN; - } else { + } + else + { item->valueint = (int)num; } } @@ -2166,13 +2508,15 @@ cJSON_CreateNumber(double num) { return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateString(const char *string) { +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_String; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); - if (!item->valuestring) { + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { cJSON_Delete(item); return NULL; } @@ -2181,46 +2525,48 @@ cJSON_CreateString(const char *string) { return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateStringReference(const char *string) { +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { + if (item != NULL) + { item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char *)cast_away_const(string); + item->valuestring = (char*)cast_away_const(string); } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateObjectReference(const cJSON *child) { +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON *)cast_away_const(child); + item->child = (cJSON*)cast_away_const(child); } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateArrayReference(const cJSON *child) { +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { cJSON *item = cJSON_New_Item(&global_hooks); if (item != NULL) { item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON *)cast_away_const(child); + item->child = (cJSON*)cast_away_const(child); } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateRaw(const char *raw) { +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if(item) + { item->type = cJSON_Raw; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); - if (!item->valuestring) { + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { cJSON_Delete(item); return NULL; } @@ -2229,20 +2575,22 @@ cJSON_CreateRaw(const char *raw) { return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateArray(void) { +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { - item->type = cJSON_Array; + if(item) + { + item->type=cJSON_Array; } return item; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateObject(void) { +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ cJSON *item = cJSON_New_Item(&global_hooks); - if (item) { + if (item) + { item->type = cJSON_Object; } @@ -2250,28 +2598,34 @@ cJSON_CreateObject(void) { } /* Create Arrays: */ -CJSON_PUBLIC(cJSON *) -cJSON_CreateIntArray(const int *numbers, int count) { +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if ((count < 0) || (numbers == NULL)) { + if ((count < 0) || (numbers == NULL)) + { return NULL; } a = cJSON_CreateArray(); - for (i = 0; a && (i < (size_t)count); i++) { + for(i = 0; a && (i < (size_t)count); i++) + { n = cJSON_CreateNumber(numbers[i]); - if (!n) { + if (!n) + { cJSON_Delete(a); return NULL; } - if (!i) { + if(!i) + { a->child = n; - } else { + } + else + { suffix_object(p, n); } p = n; @@ -2284,28 +2638,34 @@ cJSON_CreateIntArray(const int *numbers, int count) { return a; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateFloatArray(const float *numbers, int count) { +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if ((count < 0) || (numbers == NULL)) { + if ((count < 0) || (numbers == NULL)) + { return NULL; } a = cJSON_CreateArray(); - for (i = 0; a && (i < (size_t)count); i++) { + for(i = 0; a && (i < (size_t)count); i++) + { n = cJSON_CreateNumber((double)numbers[i]); - if (!n) { + if(!n) + { cJSON_Delete(a); return NULL; } - if (!i) { + if(!i) + { a->child = n; - } else { + } + else + { suffix_object(p, n); } p = n; @@ -2318,28 +2678,34 @@ cJSON_CreateFloatArray(const float *numbers, int count) { return a; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateDoubleArray(const double *numbers, int count) { +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if ((count < 0) || (numbers == NULL)) { + if ((count < 0) || (numbers == NULL)) + { return NULL; } a = cJSON_CreateArray(); - for (i = 0; a && (i < (size_t)count); i++) { + for(i = 0; a && (i < (size_t)count); i++) + { n = cJSON_CreateNumber(numbers[i]); - if (!n) { + if(!n) + { cJSON_Delete(a); return NULL; } - if (!i) { + if(!i) + { a->child = n; - } else { + } + else + { suffix_object(p, n); } p = n; @@ -2352,29 +2718,35 @@ cJSON_CreateDoubleArray(const double *numbers, int count) { return a; } -CJSON_PUBLIC(cJSON *) -cJSON_CreateStringArray(const char *const *strings, int count) { +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if ((count < 0) || (strings == NULL)) { + if ((count < 0) || (strings == NULL)) + { return NULL; } a = cJSON_CreateArray(); - for (i = 0; a && (i < (size_t)count); i++) { + for (i = 0; a && (i < (size_t)count); i++) + { n = cJSON_CreateString(strings[i]); - if (!n) { + if(!n) + { cJSON_Delete(a); return NULL; } - if (!i) { + if(!i) + { a->child = n; - } else { - suffix_object(p, n); + } + else + { + suffix_object(p,n); } p = n; } @@ -2387,88 +2759,105 @@ cJSON_CreateStringArray(const char *const *strings, int count) { } /* Duplication */ -cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); +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_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 * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) +{ cJSON *newitem = NULL; cJSON *child = NULL; cJSON *next = NULL; cJSON *newchild = NULL; /* Bail on bad ptr */ - if (!item) { + if (!item) + { goto fail; } /* Create new item */ newitem = cJSON_New_Item(&global_hooks); - if (!newitem) { + if (!newitem) + { goto fail; } /* Copy over all vars */ newitem->type = item->type & (~cJSON_IsReference); newitem->valueint = item->valueint; newitem->valuedouble = item->valuedouble; - if (item->valuestring) { - newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks); - if (!newitem->valuestring) { + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { goto fail; } } - if (item->string) { - newitem->string = (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks); - if (!newitem->string) { + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { goto fail; } } /* If non-recursive, then we're done! */ - if (!recurse) { + if (!recurse) + { return newitem; } /* Walk the ->next chain for the child. */ child = item->child; - while (child != NULL) { - if (depth >= CJSON_CIRCULAR_LIMIT) { + while (child != NULL) + { + 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) { + if (!newchild) + { goto fail; } - if (next != NULL) { + if (next != NULL) + { /* If newitem->child already set, then crosswire ->prev and ->next and move on */ next->next = newchild; newchild->prev = next; next = newchild; - } else { + } + else + { /* Set newitem->child and move to it */ newitem->child = newchild; next = newchild; } child = child->next; } - if (newitem && newitem->child) { + if (newitem && newitem->child) + { newitem->child->prev = newchild; } return newitem; fail: - if (newitem != NULL) { + if (newitem != NULL) + { cJSON_Delete(newitem); } return NULL; } -static void skip_oneline_comment(char **input) { +static void skip_oneline_comment(char **input) +{ *input += static_strlen("//"); - for (; (*input)[0] != '\0'; ++(*input)) { + for (; (*input)[0] != '\0'; ++(*input)) + { if ((*input)[0] == '\n') { *input += static_strlen("\n"); return; @@ -2476,11 +2865,14 @@ static void skip_oneline_comment(char **input) { } } -static void skip_multiline_comment(char **input) { +static void skip_multiline_comment(char **input) +{ *input += static_strlen("/*"); - for (; (*input)[0] != '\0'; ++(*input)) { - if (((*input)[0] == '*') && ((*input)[1] == '/')) { + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { *input += static_strlen("*/"); return; } @@ -2492,6 +2884,7 @@ static void minify_string(char **input, char **output) { *input += static_strlen("\""); *output += static_strlen("\""); + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { (*output)[0] = (*input)[0]; @@ -2508,41 +2901,47 @@ static void minify_string(char **input, char **output) { } } -CJSON_PUBLIC(void) -cJSON_Minify(char *json) { +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ char *into = json; - if (json == NULL) { + if (json == NULL) + { return; } - while (json[0] != '\0') { - switch (json[0]) { - case ' ': - case '\t': - case '\r': - case '\n': - json++; - break; - - case '/': - if (json[1] == '/') { - skip_oneline_comment(&json); - } else if (json[1] == '*') { - skip_multiline_comment(&json); - } else { + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': json++; - } - break; + break; - case '\"': - minify_string(&json, (char **)&into); - break; + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; - default: - into[0] = json[0]; - json++; - into++; + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; } } @@ -2550,210 +2949,238 @@ cJSON_Minify(char *json) { *into = '\0'; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsInvalid(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_Invalid; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsFalse(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_False; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsTrue(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xff) == cJSON_True; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsBool(const cJSON *const item) { - if (item == NULL) { + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & (cJSON_True | cJSON_False)) != 0; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsNull(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_NULL; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsNumber(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_Number; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsString(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_String; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsArray(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_Array; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsObject(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_Object; } -CJSON_PUBLIC(cJSON_bool) -cJSON_IsRaw(const cJSON *const item) { - if (item == NULL) { +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { return false; } return (item->type & 0xFF) == cJSON_Raw; } -CJSON_PUBLIC(cJSON_bool) -cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { return false; } /* check if type is valid */ - switch (a->type & 0xFF) { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - case cJSON_Number: - case cJSON_String: - case cJSON_Raw: - case cJSON_Array: - case cJSON_Object: - break; + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; - default: - return false; + default: + return false; } /* identical objects are equal */ - if (a == b) { + if (a == b) + { return true; } - switch (a->type & 0xFF) { - /* in these cases and equal type is enough */ - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - - case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) { + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: return true; - } - return false; - case cJSON_String: - case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } return false; - } - if (strcmp(a->valuestring, b->valuestring) == 0) { + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + return true; } - return false; + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } - case cJSON_Array: { - cJSON *a_element = a->child; - cJSON *b_element = b->child; - - for (; (a_element != NULL) && (b_element != NULL);) { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { - return false; + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } } - a_element = a_element->next; - b_element = b_element->next; + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; } - /* one of the arrays is longer than the other */ - if (a_element != b_element) { + default: return false; - } - - return true; - } - - case cJSON_Object: { - cJSON *a_element = NULL; - cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { - return false; - } - } - - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) { - return false; - } - } - - return true; - } - - default: - return false; } } -CJSON_PUBLIC(void *) -cJSON_malloc(size_t size) { +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ return global_hooks.allocate(size); } -CJSON_PUBLIC(void) -cJSON_free(void *object) { +CJSON_PUBLIC(void) cJSON_free(void *object) +{ global_hooks.deallocate(object); object = NULL; } diff --git a/tools/nitrogfx/cJSON.h b/tools/nitrogfx/cJSON.h index 1cd166d32..cab5feb42 100644 --- a/tools/nitrogfx/cJSON.h +++ b/tools/nitrogfx/cJSON.h @@ -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 18 +#define CJSON_VERSION_PATCH 19 #include @@ -287,7 +287,10 @@ 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) + (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) diff --git a/tools/nitrogfx/convert_png.c b/tools/nitrogfx/convert_png.c index 60ce294f6..efd6989f0 100644 --- a/tools/nitrogfx/convert_png.c +++ b/tools/nitrogfx/convert_png.c @@ -1,47 +1,39 @@ // Copyright (c) 2015 YamaArashi -#include "convert_png.h" - -#include -#include #include - +#include +#include #include "global.h" - +#include "convert_png.h" #include "gfx.h" -static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo) { +static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo) +{ FILE *fp = fopen(path, "rb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); - } unsigned char sig[8]; - if (fread(sig, 8, 1, fp) != 1) { + if (fread(sig, 8, 1, fp) != 1) FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path); - } - if (png_sig_cmp(sig, 0, 8)) { + if (png_sig_cmp(sig, 0, 8)) FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path); - } png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { + if (!png_ptr) FATAL_ERROR("Failed to create PNG read struct.\n"); - } png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { + if (!info_ptr) FATAL_ERROR("Failed to create PNG info struct.\n"); - } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path); - } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); @@ -53,7 +45,8 @@ static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo) return fp; } -static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int destBitDepth, int numPixels) { +static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int destBitDepth, int numPixels) +{ // Round the number of bits up to the next 8 and divide by 8 to get the number of bytes. int srcSize = ((numPixels * srcBitDepth + 7) & ~7) / 8; int destSize = ((numPixels * destBitDepth + 7) & ~7) / 8; @@ -63,18 +56,20 @@ static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int d int j; int destBit = 8 - destBitDepth; - for (i = 0; i < srcSize; i++) { + for (i = 0; i < srcSize; i++) + { unsigned char srcByte = src[i]; - for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth) { + for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth) + { unsigned char pixel = (srcByte >> j) % (1 << srcBitDepth); - if (pixel >= (1 << destBitDepth)) { + if (pixel >= (1 << destBitDepth)) FATAL_ERROR("Image exceeds the maximum color value for a %ibpp image.\n", destBitDepth); - } *dest |= pixel << destBit; destBit -= destBitDepth; - if (destBit < 0) { + if (destBit < 0) + { dest++; destBit = 8 - destBitDepth; } @@ -84,7 +79,8 @@ static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int d return output; } -void ReadPng(char *path, struct Image *image) { +void ReadPng(char *path, struct Image *image) +{ png_structp png_ptr; png_infop info_ptr; @@ -94,22 +90,12 @@ void ReadPng(char *path, struct Image *image) { int color_type = png_get_color_type(png_ptr, info_ptr); - switch (color_type) { - case PNG_COLOR_TYPE_GRAY: - case PNG_COLOR_TYPE_GA: - case PNG_COLOR_TYPE_PALETTE: - case PNG_COLOR_TYPE_RGB: - case PNG_COLOR_TYPE_RGBA: - break; - default: + if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE) FATAL_ERROR("\"%s\" has an unsupported color type.\n", path); - } // Check if the image has a palette so that we can tell if the colors need to be inverted later. // Don't read the palette because it's not needed for now. - image->hasPalette = (color_type & PNG_COLOR_MASK_COLOR) != 0; - image->hasTransparency = color_type == PNG_COLOR_TYPE_PALETTE || ((color_type & PNG_COLOR_MASK_ALPHA) != 0); - image->pixelsAreRGB = (color_type & PNG_COLOR_MASK_COLOR) != 0 && (color_type & PNG_COLOR_MASK_PALETTE) == 0; + image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE); image->width = png_get_image_width(png_ptr, info_ptr); image->height = png_get_image_height(png_ptr, info_ptr); @@ -118,23 +104,19 @@ void ReadPng(char *path, struct Image *image) { image->pixels = malloc(image->height * rowbytes); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate pixel buffer.\n"); - } png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); - if (row_pointers == NULL) { + if (row_pointers == NULL) FATAL_ERROR("Failed to allocate row pointers.\n"); - } - for (int i = 0; i < image->height; i++) { + for (int i = 0; i < image->height; i++) row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes)); - } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Error reading from \"%s\".\n", path); - } png_read_image(png_ptr, row_pointers); @@ -143,19 +125,20 @@ void ReadPng(char *path, struct Image *image) { free(row_pointers); fclose(fp); - if ((color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth != image->bitDepth) { + if (bit_depth != image->bitDepth) + { unsigned char *src = image->pixels; - if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8) { + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8) FATAL_ERROR("Bit depth of image must be 1, 2, 4, or 8.\n"); - } image->pixels = ConvertBitDepth(image->pixels, bit_depth, image->bitDepth, image->width * image->height); free(src); image->bitDepth = bit_depth; } } -void ReadPngPalette(char *path, struct Palette *palette) { +void ReadPngPalette(char *path, struct Palette *palette) +{ png_structp png_ptr; png_infop info_ptr; png_colorp colors; @@ -163,17 +146,14 @@ void ReadPngPalette(char *path, struct Palette *palette) { FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr); - if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE) { + if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE) FATAL_ERROR("The image \"%s\" does not contain a palette.\n", path); - } - if (png_get_PLTE(png_ptr, info_ptr, &colors, &numColors) != PNG_INFO_PLTE) { + if (png_get_PLTE(png_ptr, info_ptr, &colors, &numColors) != PNG_INFO_PLTE) FATAL_ERROR("Failed to retrieve palette from \"%s\".\n", path); - } - if (numColors > 256) { + if (numColors > 256) FATAL_ERROR("Images with more than 256 colors are not supported.\n"); - } palette->numColors = numColors; for (int i = 0; i < numColors; i++) { @@ -189,12 +169,12 @@ void ReadPngPalette(char *path, struct Palette *palette) { fclose(fp); } -void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette) { +void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette) +{ png_colorp colors = malloc(palette->numColors * sizeof(png_color)); - if (colors == NULL) { + if (colors == NULL) FATAL_ERROR("Failed to allocate PNG palette.\n"); - } for (int i = 0; i < palette->numColors; i++) { colors[i].red = palette->colors[i].red; @@ -207,50 +187,40 @@ void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *pale free(colors); } -void WritePng(char *path, struct Image *image) { +void WritePng(char *path, struct Image *image) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { + if (!png_ptr) FATAL_ERROR("Failed to create PNG write struct.\n"); - } png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { + if (!info_ptr) FATAL_ERROR("Failed to create PNG info struct.\n"); - } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path); - } png_init_io(png_ptr, fp); - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Error writing header for \"%s\".\n", path); - } - int color_type = PNG_COLOR_TYPE_GRAY; + int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY; + + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + image->bitDepth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + if (image->hasPalette) { - color_type |= PNG_COLOR_MASK_COLOR; - if (!image->pixelsAreRGB) { - color_type |= PNG_COLOR_MASK_PALETTE; - } - } - if (image->hasTransparency && !(color_type & PNG_COLOR_MASK_PALETTE)) { - color_type |= PNG_COLOR_MASK_ALPHA; - } - - png_set_IHDR(png_ptr, info_ptr, image->width, image->height, image->bitDepth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - if (color_type == PNG_COLOR_TYPE_PALETTE) { SetPngPalette(png_ptr, info_ptr, &image->palette); + if (image->hasTransparency) { png_byte trans = 0; png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0); @@ -261,25 +231,21 @@ void WritePng(char *path, struct Image *image) { png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); - if (row_pointers == NULL) { + if (row_pointers == NULL) FATAL_ERROR("Failed to allocate row pointers.\n"); - } int rowbytes = png_get_rowbytes(png_ptr, info_ptr); - for (int i = 0; i < image->height; i++) { + for (int i = 0; i < image->height; i++) row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes)); - } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Error writing \"%s\".\n", path); - } png_write_image(png_ptr, row_pointers); - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(png_ptr))) FATAL_ERROR("Error ending write of \"%s\".\n", path); - } png_write_end(png_ptr, NULL); diff --git a/tools/nitrogfx/font.c b/tools/nitrogfx/font.c index 045d1517d..f929e30ce 100644 --- a/tools/nitrogfx/font.c +++ b/tools/nitrogfx/font.c @@ -1,32 +1,29 @@ // Copyright (c) 2015 YamaArashi -#include "font.h" - -#include -#include #include #include +#include +#include #include - #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) + {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) @@ -39,7 +36,7 @@ static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsign 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 pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); dest[destPixelsOffset] = src[srcPixelsOffset + 1]; @@ -88,7 +85,7 @@ static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char * 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]; @@ -171,7 +168,8 @@ static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *de } } -static void ConvertFromNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) { +static void ConvertFromNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) +{ unsigned int srcPixelsOffset = 0; unsigned int curGlyph = 0; @@ -194,7 +192,8 @@ static void ConvertFromNitroFont(unsigned char *src, unsigned char *dest, unsign } } -static void ConvertToNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) { +static void ConvertToNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) +{ unsigned int destPixelsOffset = 0; unsigned int curGlyph = 0; @@ -232,7 +231,8 @@ static void SetFontPalette(struct Image *image) image->hasTransparency = false; } -static void SetSubscreenFontPalette(struct Image *image) { +static void SetSubscreenFontPalette(struct Image *image) +{ image->hasPalette = true; image->palette.numColors = 4; @@ -253,9 +253,8 @@ void ReadLatinFont(char *path, struct Image *image) int numGlyphs = fileSize / 64; - if (numGlyphs % 16 != 0) { + if (numGlyphs % 16 != 0) FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - } int numRows = numGlyphs / 16; @@ -264,9 +263,8 @@ void ReadLatinFont(char *path, struct Image *image) image->bitDepth = 2; image->pixels = malloc(fileSize); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertFromLatinFont(buffer, image->pixels, numRows); @@ -277,21 +275,18 @@ void ReadLatinFont(char *path, struct Image *image) void WriteLatinFont(char *path, struct Image *image) { - if (image->width != 256) { + if (image->width != 256) FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); - } - if (image->height % 16 != 0) { + if (image->height % 16 != 0) FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); - } int numRows = image->height / 16; int bufferSize = numRows * 16 * 64; unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertToLatinFont(image->pixels, buffer, numRows); @@ -307,15 +302,13 @@ void ReadHalfwidthJapaneseFont(char *path, struct Image *image) int glyphSize = 32; - if (fileSize % glyphSize != 0) { + if (fileSize % glyphSize != 0) FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize); - } int numGlyphs = fileSize / glyphSize; - - if (numGlyphs % 16 != 0) { + + if (numGlyphs % 16 != 0) FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - } int numRows = numGlyphs / 16; @@ -324,9 +317,8 @@ void ReadHalfwidthJapaneseFont(char *path, struct Image *image) image->bitDepth = 2; image->pixels = malloc(fileSize); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows); @@ -337,21 +329,18 @@ void ReadHalfwidthJapaneseFont(char *path, struct Image *image) void WriteHalfwidthJapaneseFont(char *path, struct Image *image) { - if (image->width != 128) { + if (image->width != 128) FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width); - } - if (image->height % 16 != 0) { + if (image->height % 16 != 0) FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); - } int numRows = image->height / 16; int bufferSize = numRows * 16 * 32; unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows); @@ -367,9 +356,8 @@ void ReadFullwidthJapaneseFont(char *path, struct Image *image) int numGlyphs = fileSize / 64; - if (numGlyphs % 16 != 0) { + if (numGlyphs % 16 != 0) FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - } int numRows = numGlyphs / 16; @@ -378,9 +366,8 @@ void ReadFullwidthJapaneseFont(char *path, struct Image *image) image->bitDepth = 2; image->pixels = malloc(fileSize); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows); @@ -391,21 +378,18 @@ void ReadFullwidthJapaneseFont(char *path, struct Image *image) void WriteFullwidthJapaneseFont(char *path, struct Image *image) { - if (image->width != 256) { + if (image->width != 256) FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); - } - if (image->height % 16 != 0) { + if (image->height % 16 != 0) FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); - } int numRows = image->height / 16; int bufferSize = numRows * 16 * 64; unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows); @@ -414,14 +398,16 @@ void WriteFullwidthJapaneseFont(char *path, struct Image *image) free(buffer); } -static inline uint32_t ReadLittleEndianWord(unsigned char *buffer, size_t start) { +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) { +void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata, bool useSubscreenPalette) +{ int filesize; unsigned char *buffer = ReadWholeFile(path, &filesize); @@ -443,37 +429,33 @@ void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metada image->bitDepth = 2; image->pixels = malloc(filesize); - if (image->pixels == NULL) { + 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) { + if (useSubscreenPalette) SetSubscreenFontPalette(image); - } else { + else SetFontPalette(image); - } } -void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata) { - if (image->width != 256) { +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) { + 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) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for font.\n"); - } buffer[0x00] = (metadata->size & 0x000000FF); buffer[0x01] = (metadata->size & 0x0000FF00) >> 8; @@ -500,7 +482,8 @@ void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metad free(buffer); } -void FreeNtrFontMetadata(struct NtrFontMetadata *metadata) { +void FreeNtrFontMetadata(struct NtrFontMetadata *metadata) +{ free(metadata->glyphWidthTable); free(metadata); } diff --git a/tools/nitrogfx/font.h b/tools/nitrogfx/font.h index fcaaef6ee..70dca0808 100644 --- a/tools/nitrogfx/font.h +++ b/tools/nitrogfx/font.h @@ -4,7 +4,6 @@ #define FONT_H #include - #include "gfx.h" #include "options.h" diff --git a/tools/nitrogfx/gfx.c b/tools/nitrogfx/gfx.c index 2c215a3bc..dade89b53 100644 --- a/tools/nitrogfx/gfx.c +++ b/tools/nitrogfx/gfx.c @@ -1,26 +1,27 @@ -// Copyright (c) 2015 YamaArashi, 2021-2024 red031000 +// Copyright (c) 2015 YamaArashi, 2021-2025 red031000 -#include "gfx.h" - -#include -#include +#include #include #include -#include - +#include +#include #include "global.h" - +#include "gfx.h" #include "json.h" #include "util.h" -static unsigned int FindNitroDataBlock(const unsigned char *data, const char *ident, unsigned int fileSize, unsigned int *blockSize_out) { +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) { + 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) { + if (offset + blockSize > fileSize) + { FATAL_ERROR("corrupted NTR file"); } - if (memcmp(data + offset, ident, 4) == 0) { + if (memcmp(data + offset, ident, 4) == 0) + { *blockSize_out = blockSize; return offset; } @@ -29,8 +30,8 @@ static unsigned int FindNitroDataBlock(const unsigned char *data, const char *id return -1u; } -#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F) -#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F) +#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) #define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r)) @@ -39,7 +40,8 @@ static unsigned int FindNitroDataBlock(const unsigned char *data, const char *id #define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8) -static void AdvanceTilePosition(int *tilesSoFar, int *rowsSoFar, int *chunkStartX, int *chunkStartY, int chunksWide, int colsPerChunk, int rowsPerChunk) { +static void AdvanceTilePosition(int *tilesSoFar, int *rowsSoFar, int *chunkStartX, int *chunkStartY, int chunksWide, int colsPerChunk, int rowsPerChunk) +{ (*tilesSoFar)++; if (*tilesSoFar == colsPerChunk) { *tilesSoFar = 0; @@ -55,7 +57,8 @@ static void AdvanceTilePosition(int *tilesSoFar, int *rowsSoFar, int *chunkStart } } -static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -80,7 +83,8 @@ static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int nu } } -static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -110,76 +114,95 @@ static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int nu } } -static void ConvertFromTiles4BppCell(unsigned char *src, unsigned char *dest, int oamWidth, int oamHeight, int imageWidth, int startX, int startY, bool hFlip, bool vFlip, bool hvFlip, bool toPNG, int plttNum, UNUSED int mappingType, struct Image *image) { +static void Convert8BppFrom4BppTiles(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors, int palIndex) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; int chunkStartY = 0; - int pitch = image->hasPalette ? imageWidth / 2 : imageWidth; + int pitch = (chunksWide * colsPerChunk) * 8; + + for (int i = 0; i < numTiles; i++) { + for (int j = 0; j < 8; j++) { + int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; + + for (int k = 0; k < 8; k += 2) { + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; + unsigned char srcPixelPair = *src++; + unsigned char leftPixel = srcPixelPair & 0xF; + unsigned char rightPixel = srcPixelPair >> 4; + + if (invertColors) { + leftPixel = 15 - leftPixel; + rightPixel = 15 - rightPixel; + } + + dest[idxComponentY * pitch + idxComponentX] = ((palIndex - 1) << 4) | leftPixel; + dest[idxComponentY * pitch + idxComponentX + 1] = ((palIndex - 1) << 4) | rightPixel; + } + } + + AdvanceTilePosition(&tilesSoFar, &rowsSoFar, &chunkStartX, &chunkStartY, chunksWide, colsPerChunk, rowsPerChunk); + } +} + +static void ConvertFromTiles4BppCell(unsigned char *src, unsigned char *dest, int oamWidth, int oamHeight, int imageWidth, int startX, int startY, bool hFlip, bool vFlip, bool hvFlip, bool toPNG) +{ + int tilesSoFar = 0; + int rowsSoFar = 0; + int chunkStartX = 0; + int chunkStartY = 0; + int pitch = imageWidth / 2; for (int i = 0; i < oamHeight * oamWidth; i++) { for (int j = 0; j < 8; j++) { int idxComponentY = (chunkStartY + rowsSoFar) * 8 + j + startY; - if (vFlip) { + if (vFlip) + { idxComponentY = (rowsSoFar + oamHeight - chunkStartY) * 8 + j + startY; } - if (hvFlip) { + if (hvFlip) + { idxComponentY += 8 - j * 2; } - if (image->hasPalette) { - idxComponentY *= 2; - } for (int k = 0; k < 4; k++) { - int idxComponentX = (chunkStartX + tilesSoFar) * 4 + k + startX / 2; - unsigned char srcPixelPair; - unsigned char leftPixel; - unsigned char rightPixel; + int idxComponentX = (chunkStartX + tilesSoFar) * 4 + k + startX/2; - if (hFlip) { - idxComponentX = (tilesSoFar + oamWidth - chunkStartX) * 4 + -k + startX / 2 - 1; + if (hFlip) + { + idxComponentX = (tilesSoFar + oamWidth - chunkStartX) * 4 + - k + startX/2 - 1; - if (toPNG) { + unsigned char srcPixelPair = *src; + unsigned char leftPixel = srcPixelPair & 0xF; + unsigned char rightPixel = srcPixelPair >> 4; + + if (toPNG) + { srcPixelPair = *src++; leftPixel = srcPixelPair & 0xF; rightPixel = srcPixelPair >> 4; - if (image->hasPalette) { - dest[idxComponentY * pitch + idxComponentX * 2 + 0] = leftPixel | (plttNum << 4); - dest[idxComponentY * pitch + idxComponentX * 2 + 1] = rightPixel | (plttNum << 4); - } else { - dest[idxComponentY * pitch + idxComponentX] = (leftPixel << 4) | rightPixel; - } - } else { - if (image->hasPalette) { - leftPixel = src[idxComponentY * pitch + idxComponentX * 2 + 0] & 0xF; - rightPixel = src[idxComponentY * pitch + idxComponentX * 2 + 1] & 0xF; - } else { - srcPixelPair = src[idxComponentY * pitch + idxComponentX]; - leftPixel = srcPixelPair & 0xF; - rightPixel = srcPixelPair >> 4; - } + dest[idxComponentY * pitch + idxComponentX] = (leftPixel << 4) | rightPixel; + } + else + { + srcPixelPair = src[idxComponentY * pitch + idxComponentX]; + leftPixel = srcPixelPair & 0xF; + rightPixel = srcPixelPair >> 4; + *dest++ = (leftPixel << 4) | rightPixel; } - } else { - if (toPNG) { - if (image->hasPalette) { - srcPixelPair = *src++; - leftPixel = srcPixelPair & 0xF; - rightPixel = srcPixelPair >> 4; - dest[idxComponentY * pitch + idxComponentX * 2 + 0] = rightPixel | (plttNum << 4); - dest[idxComponentY * pitch + idxComponentX * 2 + 1] = leftPixel | (plttNum << 4); - } else { - dest[idxComponentY * pitch + idxComponentX] = *src++; - } - } else { - if (image->hasPalette) { - rightPixel = src[idxComponentY * pitch + idxComponentX * 2 + 0] & 0xF; - leftPixel = src[idxComponentY * pitch + idxComponentX * 2 + 1] & 0xF; - *dest++ = leftPixel | (rightPixel << 4); - } else { - *dest++ = src[idxComponentY * pitch + idxComponentX]; - } + } + else + { + if (toPNG) + { + dest[idxComponentY * pitch + idxComponentX] = *src++; + } + else + { + *dest++ = src[idxComponentY * pitch + idxComponentX]; } } } @@ -189,30 +212,10 @@ static void ConvertFromTiles4BppCell(unsigned char *src, unsigned char *dest, in } } -static uint32_t ConvertFromScanned4Bpp(unsigned char *src, unsigned char *dest, int fileSize, bool invertColours, bool scanFrontToBack) { - uint32_t encValue = 0; - if (scanFrontToBack) { - encValue = (src[1] << 8) | src[0]; - for (int i = 0; i < fileSize; i += 2) { - uint16_t val = src[i] | (src[i + 1] << 8); - val ^= (encValue & 0xFFFF); - src[i] = val; - src[i + 1] = val >> 8; - encValue = encValue * 1103515245; - encValue = encValue + 24691; - } - } else { - encValue = (src[fileSize - 1] << 8) | src[fileSize - 2]; - for (int i = fileSize; i > 0; i -= 2) { - uint16_t val = (src[i - 1] << 8) | src[i - 2]; - val ^= (encValue & 0xFFFF); - src[i - 1] = (val >> 8); - src[i - 2] = val; - encValue = encValue * 1103515245; - encValue = encValue + 24691; - } - } - for (int i = 0; i < fileSize; i++) { +static void ConvertScanned4Bpp(unsigned char *src, unsigned char *dest, int charDataSize, bool invertColours) +{ + for (int i = 0; i < charDataSize; i++) + { unsigned char srcPixelPair = src[i]; unsigned char leftPixel = srcPixelPair & 0xF; unsigned char rightPixel = srcPixelPair >> 4; @@ -224,10 +227,10 @@ static uint32_t ConvertFromScanned4Bpp(unsigned char *src, unsigned char *dest, dest[i] = (leftPixel << 4) | rightPixel; } - return encValue; } -static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -242,9 +245,8 @@ static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int nu int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; unsigned char srcPixel = *src++; - if (invertColors) { + if (invertColors) srcPixel = 255 - srcPixel; - } dest[idxComponentY * pitch + idxComponentX] = srcPixel; } @@ -254,100 +256,40 @@ static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int nu } } -static void ConvertFromTiles8BppCell(unsigned char *src, unsigned char *dest, int oamWidth, int oamHeight, int imageWidth, int startX, int startY, bool hFlip, bool vFlip, bool hvFlip, bool toPNG, int plttNum, int mappingType, struct Image *image) { - static bool setTransparencyColor = false; - +static void ConvertFromTiles8BppCell(unsigned char *src, unsigned char *dest, int oamWidth, int oamHeight, int imageWidth, int startX, int startY, bool hFlip, bool vFlip, bool hvFlip, bool toPNG) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; int chunkStartY = 0; int pitch = imageWidth; - int pitchFactor = mappingType == 2 ? (image->hasTransparency ? 4 : 3) : 1; - pitch *= pitchFactor; for (int i = 0; i < oamHeight * oamWidth; i++) { for (int j = 0; j < 8; j++) { int idxComponentY = (chunkStartY + rowsSoFar) * 8 + j + startY; - if (vFlip) { + if (vFlip) + { idxComponentY = (rowsSoFar + oamHeight - chunkStartY) * 8 + j + startY; } - if (hvFlip) { + if (hvFlip) + { idxComponentY += 8 - j * 2; } - idxComponentY *= pitchFactor; for (int k = 0; k < 8; k++) { int idxComponentX = (chunkStartX + tilesSoFar) * 8 + k + startX; - if (hFlip) { - idxComponentX = (tilesSoFar + oamWidth - chunkStartX) * 4 + -k + startX; + if (hFlip) + { + idxComponentX = (tilesSoFar + oamWidth - chunkStartX) * 4 + - k + startX; } - if (toPNG) { - if (image->hasPalette && mappingType == 2) { - // Color mode 256x16 extpltt is handled specially - // The underlying PNG image is 24-bit RGB or 32-bit RGBA - int colorIdx = plttNum * 256 + (*src++); - struct Color *color = &image->palette.colors[colorIdx]; - dest[idxComponentY * pitch + idxComponentX * pitchFactor + 0] = color->red; - dest[idxComponentY * pitch + idxComponentX * pitchFactor + 1] = color->green; - dest[idxComponentY * pitch + idxComponentX * pitchFactor + 2] = color->blue; - if (image->hasTransparency) { - // Alpha on DS is binary. Alpha in PNG is 8-bit. Scale accordingly. - dest[idxComponentY * pitch + idxComponentX * pitchFactor + 3] = colorIdx == 0 ? 0 : 255; - } - } else { - dest[idxComponentY * pitch + idxComponentX] = *src++; - } - } else { - if (image->hasPalette && mappingType == 2) { - // Color mode 256x16 extpltt is handled specially - // The underlying PNG image is 24-bit RGB or 32-bit RGBA - int colorIdx; - struct Color color = { - .red = src[idxComponentY * pitch + idxComponentX * pitchFactor + 0], - .green = src[idxComponentY * pitch + idxComponentX * pitchFactor + 1], - .blue = src[idxComponentY * pitch + idxComponentX * pitchFactor + 2], - }; - if (image->hasTransparency && src[idxComponentY * pitch + idxComponentX * pitchFactor + 3] == 0) { - // First color is hardcoded to be transparency - colorIdx = 0; - if (!setTransparencyColor) { - memcpy(&image->palette.colors[0], &color, sizeof(struct Color)); - setTransparencyColor = true; - if (image->palette.numColors == 0) { - image->palette.numColors = 1; - } - } else { - // No other color is permitted to be transparency - if (memcmp(&image->palette.colors[0], &color, sizeof(struct Color)) != 0) { - FATAL_ERROR("Transparency color is not uniform\n"); - } - } - } else if (image->palette.numColors == 0) { - // Haven't registered a color yet, and the very first pixel is not transparency - memcpy(&image->palette.colors[1], &color, sizeof(struct Color)); - image->palette.numColors = 2; - colorIdx = 1; - } else { - // Assume that palette is arranged in order of use, excluding transparency - // It is not known whether this holds for retail ROMs - for (colorIdx = 1; colorIdx < 4096 && colorIdx < image->palette.numColors; colorIdx++) { - if (memcmp(&image->palette.colors[colorIdx], &color, sizeof(struct Color)) == 0) { - break; - } - } - if (colorIdx == image->palette.numColors) { - if (colorIdx == 4096) { - FATAL_ERROR("Too many unique colors for DS object extpltt\n"); - } - memcpy(&image->palette.colors[colorIdx], &color, sizeof(struct Color)); - image->palette.numColors++; - } - } - *dest++ = colorIdx & 0xFF; - } else { - *dest++ = src[idxComponentY * pitch + idxComponentX]; - } + if (toPNG) + { + dest[idxComponentY * pitch + idxComponentX] = *src++; + } + else + { + *dest++ = src[idxComponentY * pitch + idxComponentX]; } } } @@ -356,30 +298,10 @@ static void ConvertFromTiles8BppCell(unsigned char *src, unsigned char *dest, in } } -static uint32_t ConvertFromScanned8Bpp(unsigned char *src, unsigned char *dest, int fileSize, bool invertColours, bool scanFrontToBack) { - uint32_t encValue = 0; - if (scanFrontToBack) { - encValue = (src[1] << 8) | src[0]; - for (int i = 0; i < fileSize; i += 2) { - uint16_t val = src[i] | (src[i + 1] << 8); - val ^= (encValue & 0xFFFF); - src[i] = val; - src[i + 1] = val >> 8; - encValue = encValue * 1103515245; - encValue = encValue + 24691; - } - } else { - encValue = (src[fileSize - 1] << 8) | src[fileSize - 2]; - for (int i = fileSize; i > 0; i -= 2) { - uint16_t val = (src[i - 1] << 8) | src[i - 2]; - val ^= (encValue & 0xFFFF); - src[i - 1] = (val >> 8); - src[i - 2] = val; - encValue = encValue * 1103515245; - encValue = encValue + 24691; - } - } - for (int i = 0; i < fileSize; i++) { +static void ConvertScanned8Bpp(unsigned char *src, unsigned char *dest, int charDataSize, bool invertColours) +{ + for (int i = 0; i < charDataSize; i++) + { unsigned char srcPixel = src[i]; if (invertColours) { @@ -388,10 +310,10 @@ static uint32_t ConvertFromScanned8Bpp(unsigned char *src, unsigned char *dest, dest[i] = srcPixel; } - return encValue; } -static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -416,7 +338,8 @@ static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numT } } -static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -446,38 +369,8 @@ static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numT } } -static void ConvertToScanned4Bpp(unsigned char *src, unsigned char *dest, int fileSize, bool invertColours, uint32_t encValue, uint32_t scanMode) { - for (int i = 0; i < fileSize; i++) { - unsigned char srcPixelPair = src[i]; - unsigned char leftPixel = srcPixelPair & 0xF; - unsigned char rightPixel = srcPixelPair >> 4; - if (invertColours) { - leftPixel = 15 - leftPixel; - rightPixel = 15 - rightPixel; - } - dest[i] = (leftPixel << 4) | rightPixel; - } - - if (scanMode == 2) { // front to back - for (int i = fileSize - 1; i > 0; i -= 2) { - uint16_t val = dest[i - 1] | (dest[i] << 8); - encValue = (encValue - 24691) * 4005161829; - val ^= (encValue & 0xFFFF); - dest[i] = (val >> 8); - dest[i - 1] = val; - } - } else if (scanMode == 1) { - for (int i = 1; i < fileSize; i += 2) { - uint16_t val = (dest[i] << 8) | dest[i - 1]; - encValue = (encValue - 24691) * 4005161829; - val ^= (encValue & 0xFFFF); - dest[i] = (val >> 8); - dest[i - 1] = val; - } - } -} - -static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) { +static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ int tilesSoFar = 0; int rowsSoFar = 0; int chunkStartX = 0; @@ -492,9 +385,8 @@ static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numT int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; unsigned char srcPixel = src[idxComponentY * pitch + idxComponentX]; - if (invertColors) { + if (invertColors) srcPixel = 255 - srcPixel; - } *dest++ = srcPixel; } @@ -504,7 +396,91 @@ static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numT } } -void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors) { +static uint32_t Decode(unsigned char *src, int charDataSize, uint32_t encodeMode) +{ + uint32_t encValue = 0; + if (encodeMode == 2) { // front to back + encValue = (src[1] << 8) | src[0]; + for (int i = 0; i < charDataSize; i += 2) + { + uint16_t val = src[i] | (src[i + 1] << 8); + val ^= (encValue & 0xFFFF); + src[i] = val; + src[i + 1] = val >> 8; + encValue = encValue * 1103515245; + encValue = encValue + 24691; + } + } else if (encodeMode == 1) { // back to front + encValue = (src[charDataSize - 1] << 8) | src[charDataSize - 2]; + for (int i = charDataSize; i > 0; i -= 2) + { + uint16_t val = (src[i - 1] << 8) | src[i - 2]; + val ^= (encValue & 0xFFFF); + src[i - 1] = (val >> 8); + src[i - 2] = val; + encValue = encValue * 1103515245; + encValue = encValue + 24691; + } + } + return encValue; +} + +static void Encode(unsigned char *dest, int charDataSize, uint32_t encValue, uint32_t encodeMode) +{ + if (encodeMode == 2) { // front to back + for (int i = charDataSize - 1; i > 0; i -= 2) + { + uint16_t val = dest[i - 1] | (dest[i] << 8); + encValue = (encValue - 24691) * 4005161829; + val ^= (encValue & 0xFFFF); + dest[i] = (val >> 8); + dest[i - 1] = val; + } + } + else if (encodeMode == 1) { // back to front + for (int i = 1; i < charDataSize; i += 2) + { + uint16_t val = (dest[i] << 8) | dest[i - 1]; + encValue = (encValue - 24691) * 4005161829; + val ^= (encValue & 0xFFFF); + dest[i] = (val >> 8); + dest[i - 1] = val; + } + } +} + +static void Convert8BppTo4BppTiles(unsigned char *src, unsigned char *dest, int numTiles, int chunksWide, int colsPerChunk, int rowsPerChunk, bool invertColors) +{ + int tilesSoFar = 0; + int rowsSoFar = 0; + int chunkStartX = 0; + int chunkStartY = 0; + int pitch = (chunksWide * colsPerChunk) * 8; + + for (int i = 0; i < numTiles; i++) { + for (int j = 0; j < 8; j++) { + int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; + + for (int k = 0; k < 8; k += 2) { + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; + unsigned char leftPixel = src[idxComponentY * pitch + idxComponentX] & 0xF; + unsigned char rightPixel = src[idxComponentY * pitch + idxComponentX + 1] & 0xF; + + if (invertColors) { + leftPixel = 15 - leftPixel; + rightPixel = 15 - rightPixel; + } + + *dest++ = (rightPixel << 4) | leftPixel; + } + } + + AdvanceTilePosition(&tilesSoFar, &rowsSoFar, &chunkStartX, &chunkStartY, chunksWide, colsPerChunk, rowsPerChunk); + } +} + +void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors) +{ int tileSize = bitDepth * 8; // number of bytes per tile int fileSize; @@ -514,22 +490,19 @@ void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int ro int tilesTall = (numTiles + tilesWide - 1) / tilesWide; - if (tilesWide % colsPerChunk != 0) { + if (tilesWide % colsPerChunk != 0) FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified tiles per row (%d)", tilesWide, colsPerChunk); - } - if (tilesTall % rowsPerChunk != 0) { + if (tilesTall % rowsPerChunk != 0) FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified rows per chunk (%d)", tilesTall, rowsPerChunk); - } image->width = tilesWide * 8; image->height = tilesTall * 8; image->bitDepth = bitDepth; image->pixels = calloc(tilesWide * tilesTall, tileSize); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate memory for pixels.\n"); - } int chunksWide = tilesWide / colsPerChunk; // how many chunks side-by-side are needed for the full width of the image @@ -548,17 +521,20 @@ void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int ro free(buffer); } -uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool scanFrontToBack) { +uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, uint32_t encodeMode, bool convertTo8Bpp, int palIndex, bool verbose) +{ int fileSize; unsigned char *buffer = ReadWholeFile(path, &fileSize); - if (memcmp(buffer, "RGCN", 4) != 0) { + if (memcmp(buffer, "RGCN", 4) != 0) + { FATAL_ERROR("Not a valid NCGR character file.\n"); } unsigned char *charHeader = buffer + 0x10; - if (memcmp(charHeader, "RAHC", 4) != 0) { + if (memcmp(charHeader, "RAHC", 4) != 0) + { FATAL_ERROR("No valid CHAR file after NCLR header.\n"); } @@ -568,7 +544,49 @@ uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, bool scanned = charHeader[0x14]; + if (verbose) + { + if (!convertTo8Bpp) { + printf("-bitdepth %d ", bitDepth); + } else { + printf("-convertTo4Bpp "); + } + + if (buffer[0x6] == 1) { + printf("-version101 "); + } + + if (charHeader[0x8] == 0xFF && charHeader[0x9] == 0xFF && charHeader[0xA] == 0xFF && charHeader[0xB] == 0xFF) + { + printf("-clobbersize "); + } + + if (buffer[0xE] == 2) { + printf("-sopc "); + } + + if (charHeader[0x12]) { + printf("-mappingtype %d ", 1 << (5 + (charHeader[0x12] >> 4))); + } + + if (scanned) + { + printf("-scanned "); + } + + if (charHeader[0x15] == 1) { + printf("-vram "); + } + } + + if (bitDepth == 4 && (scanned || !convertTo8Bpp)) + { + image->palette.numColors = 16; + } + int tileSize = bitDepth * 8; // number of bytes per tile + if (bitDepth == 4 && convertTo8Bpp && !scanned) + tileSize *= 2; if (tilesWide == 0) { tilesWide = ReadS16(charHeader, 0xA); @@ -580,47 +598,64 @@ uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int numTiles = ReadS32(charHeader, 0x18) / (64 / (8 / bitDepth)); int tilesTall = ReadS16(charHeader, 0x8); - if (tilesTall < 0) { + if (tilesTall < 0) tilesTall = (numTiles + tilesWide - 1) / tilesWide; - } - if (tilesWide % colsPerChunk != 0) { + if (tilesWide % colsPerChunk != 0) FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified tiles per row (%d)", tilesWide, colsPerChunk); - } - if (tilesTall % rowsPerChunk != 0) { + if (tilesTall % rowsPerChunk != 0) FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified rows per chunk (%d)", tilesTall, rowsPerChunk); - } + image->width = tilesWide * 8; image->height = tilesTall * 8; - image->bitDepth = bitDepth; + image->bitDepth = !scanned && convertTo8Bpp ? 8 : bitDepth; image->pixels = calloc(tilesWide * tilesTall, tileSize); - if (image->pixels == NULL) { + if (image->pixels == NULL) FATAL_ERROR("Failed to allocate memory for pixels.\n"); - } int chunksWide = tilesWide / colsPerChunk; // how many chunks side-by-side are needed for the full width of the image uint32_t key = 0; - if (scanned) { - switch (bitDepth) { - case 4: - key = ConvertFromScanned4Bpp(imageData, image->pixels, fileSize - 0x30, invertColors, scanFrontToBack); - break; - case 8: - key = ConvertFromScanned8Bpp(imageData, image->pixels, fileSize - 0x30, invertColors, scanFrontToBack); - break; + uint32_t charDataSize = ReadS32(charHeader, 0x4) - 0x20; // read explicitly to account for possible SOPC chunk + if (encodeMode) + { + key = Decode(imageData, charDataSize, encodeMode); + } + if (scanned) + { + switch (bitDepth) + { + case 4: + ConvertScanned4Bpp(imageData, image->pixels, charDataSize, invertColors); + break; + case 8: + ConvertScanned8Bpp(imageData, image->pixels, charDataSize, invertColors); + break; } - } else { - switch (bitDepth) { - case 4: - ConvertFromTiles4Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, invertColors); - break; - case 8: - ConvertFromTiles8Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, invertColors); - break; + } + else + { + switch (bitDepth) + { + case 4: + if (convertTo8Bpp) + { + Convert8BppFrom4BppTiles(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors, palIndex); + } + else + { + ConvertFromTiles4Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + } + break; + case 8: + ConvertFromTiles8Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + break; } } @@ -628,230 +663,326 @@ uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, return key; } -void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG) { +// accounts for OAMs overlapping by a few pixels +static int SnapToTile(int val) +{ + int displacement = val % 8; + if (displacement < 4) + { + val -= displacement; + } + else + { + val += 8 - displacement; + } + return val; +} + +struct Dimensions { + int width; + int height; +}; + +static struct Dimensions CalculateOAMDimensions(struct OAM *oam) +{ + struct Dimensions oamdim = { + .width = 0, + .height = 0, + }; + + int oamSize = oam->attr1.Size; + if (oamSize > 3) + { + FATAL_ERROR("oamSize greater than expected\n"); + } + switch (oam->attr0.Shape) + { + case 0: + oamdim.height = 1 << oamSize; + oamdim.width = oamdim.height; + break; + case 1: + switch (oamSize) + { + case 0: + oamdim.height = 1; + oamdim.width = 2; + break; + case 1: + oamdim.height = 1; + oamdim.width = 4; + break; + case 2: + oamdim.height = 2; + oamdim.width = 4; + break; + case 3: + oamdim.height = 4; + oamdim.width = 8; + break; + } + break; + case 2: + switch (oamSize) + { + case 0: + oamdim.height = 2; + oamdim.width = 1; + break; + case 1: + oamdim.height = 4; + oamdim.width = 1; + break; + case 2: + oamdim.height = 4; + oamdim.width = 2; + break; + case 3: + oamdim.height = 8; + oamdim.width = 4; + break; + } + break; + } + + return oamdim; +} + +struct CellInfo { + int height; + int minX; + int minY; +}; + +void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG, bool snap) +{ char *cellFileExtension = GetFileExtension(cellFilePath); - if (cellFileExtension == NULL) { + if (cellFileExtension == NULL) + { FATAL_ERROR("NULL cell file path\n"); } struct JsonToCellOptions *options; - if (strcmp(cellFileExtension, "NCER") == 0) { + if (strcmp(cellFileExtension, "NCER") == 0) + { options = malloc(sizeof(struct JsonToCellOptions)); ReadNtrCell(cellFilePath, options); - } else { - if (strcmp(cellFileExtension, "json") == 0) { + } + else + { + if (strcmp(cellFileExtension, "json") == 0) + { options = ParseNCERJson(cellFilePath); - } else { + } + else + { FATAL_ERROR("Incompatible cell file type\n"); } } - int outputHeight = 0; + int outputHeight = -1; int outputWidth = 0; int numTiles = 0; + struct CellInfo *cellInfo = malloc(sizeof(struct CellInfo) * options->cellCount); - for (int i = 0; i < options->cellCount; i++) { - if (options->cells[i]->oamCount == 0) { + for (int i = 0; i < options->cellCount; i++) + { + if (options->cells[i]->oamCount == 0) + { continue; } int cellHeight = 0; int cellWidth = 0; - if (options->cells[i]->attributes.boundingRect) { - cellHeight = options->cells[i]->maxY - options->cells[i]->minY + 1; - cellWidth = options->cells[i]->maxX - options->cells[i]->minX + 1; - } else { - FATAL_ERROR("No bounding rectangle. Incompatible NCER\n"); + if (options->cells[i]->attributes.boundingRect) + { + cellHeight = options->cells[i]->maxY - options->cells[i]->minY; + cellWidth = options->cells[i]->maxX - options->cells[i]->minX; + cellInfo[i].minX = options->cells[i]->minX; + cellInfo[i].minY = options->cells[i]->minY; + } + else + { + int minX = 0; + int minY = 0; + int maxX = 0; + int maxY = 0; + for (int j = 0; j < options->cells[i]->oamCount; j++) + { + struct Dimensions oamdim = CalculateOAMDimensions(&options->cells[i]->oam[j]); + int xCoord = options->cells[i]->oam[j].attr1.XCoordinate; + if (xCoord & (1 << 8)) + { + xCoord |= ~0x1FF; + } + int yCoord = options->cells[i]->oam[j].attr0.YCoordinate; + if (yCoord & (1 << 7)) + { + yCoord |= ~0xFF; + } + if (xCoord < minX || j == 0) + { + minX = xCoord; + } + if (yCoord < minY || j == 0) + { + minY = yCoord; + } + if (xCoord + (oamdim.width * 8) > maxX || j == 0) + { + maxX = xCoord + (oamdim.width * 8); + } + if (yCoord + (oamdim.height * 8) > maxY || j == 0) + { + maxY = yCoord + (oamdim.height * 8); + } + } + cellWidth = maxX - minX; + cellHeight = maxY - minY; + cellInfo[i].minX = minX; + cellInfo[i].minY = minY; + } + if (snap) + { + cellHeight = SnapToTile(cellHeight); + cellWidth = SnapToTile(cellWidth); } - outputHeight += cellHeight; - if (outputWidth < cellWidth) { + outputHeight += cellHeight + 1; + if (outputWidth < cellWidth) + { outputWidth = cellWidth; } - if (i) { - outputHeight++; - } + cellInfo[i].height = cellHeight; } - if (outputHeight == 0 || outputWidth == 0) { + if (outputHeight < 1 || outputWidth == 0) + { FATAL_ERROR("No cells. Incompatible NCER\n"); } + unsigned char *newPixels = malloc(outputHeight * outputWidth); + memset(newPixels, 255, outputHeight * outputWidth); - uint32_t outSizeFactor_numer = 1, outSizeFactor_denom = 1; - switch (options->mappingType) { - case 0: - if (image->bitDepth != 4) { - FATAL_ERROR("cannot parse %dbpp image with mapping type 0\n", image->bitDepth); - } - if (!image->hasPalette) { - outSizeFactor_denom = 2; - } - break; - case 1: - if (image->bitDepth != 8) { - FATAL_ERROR("cannot parse %dbpp image with mapping type 0\n", image->bitDepth); - } - break; - case 2: - if (image->bitDepth != 8) { - FATAL_ERROR("cannot parse %dbpp image with mapping type 0\n", image->bitDepth); - } - if (image->hasPalette) { - outSizeFactor_numer = image->hasTransparency ? 4 : 3; - } - break; - default: - FATAL_ERROR("invalid mappingType %d\n", options->mappingType); - } - unsigned char *newPixels = malloc(outputHeight * outputWidth * outSizeFactor_numer / outSizeFactor_denom); - memset(newPixels, image->hasPalette ? 0 : 255, outputHeight * outputWidth * outSizeFactor_numer / outSizeFactor_denom); - - int scanHeight = 0; - int maxTile = 0; + int scanHeight = -1; int tileMask[outputHeight * outputWidth]; // check for unused (starting) tiles memset(tileMask, 0, outputHeight * outputWidth * sizeof(int)); - for (int i = 0; i < options->cellCount; i++) { - if (options->cells[i]->oamCount == 0) { + for (int i = 0; i < options->cellCount; i++) + { + if (options->cells[i]->oamCount == 0) + { continue; } - if (i) { - scanHeight++; + scanHeight++; + int cellHeight = cellInfo[i].height; + if (snap) + { + cellHeight = SnapToTile(cellHeight); } - int cellHeight = options->cells[i]->maxY - options->cells[i]->minY + 1; int uniqueOAMs = options->cells[i]->oamCount; - for (int j = 0; j < options->cells[i]->oamCount; j++) { - int oamHeight = 0; - int oamWidth = 0; - int oamSize = options->cells[i]->oam[j].attr1.Size; - switch (options->cells[i]->oam[j].attr0.Shape) { - case 0: - oamHeight = 1 << oamSize; - oamWidth = oamHeight; - break; - case 1: - switch (oamSize) { - case 0: - oamHeight = 1; - oamWidth = 2; - break; - case 1: - oamHeight = 1; - oamWidth = 4; - break; - case 2: - oamHeight = 2; - oamWidth = 4; - break; - case 3: - oamHeight = 4; - oamWidth = 8; - break; - } - break; - case 2: - switch (oamSize) { - case 0: - oamHeight = 2; - oamWidth = 1; - break; - case 1: - oamHeight = 4; - oamWidth = 1; - break; - case 2: - oamHeight = 4; - oamWidth = 2; - break; - case 3: - oamHeight = 8; - oamWidth = 4; - break; - } - break; - } + for (int j = 0; j < options->cells[i]->oamCount; j++) + { + struct Dimensions oamdim = CalculateOAMDimensions(&options->cells[i]->oam[j]); - int x = options->cells[i]->oam[j].attr1.XCoordinate; // 8 bits - if ((x & 0x80) != 0) { - x = (x | ~0xFF); + int x = options->cells[i]->oam[j].attr1.XCoordinate; + if (x & (1 << 8)) + { + x |= ~0x1FF; } - int y = options->cells[i]->oam[j].attr0.YCoordinate; // 7 bits - if ((y & 0x40) != 0) { - y = (y | ~0x7F); + int y = options->cells[i]->oam[j].attr0.YCoordinate; + if (y & (1 << 7)) + { + y |= ~0xFF; + } + x -= cellInfo[i].minX; + y -= cellInfo[i].minY; + + if (snap) + { + x = SnapToTile(x); + y = SnapToTile(y); } - x -= options->cells[i]->minX; - y -= options->cells[i]->minY; int pixelOffset = 0; - switch (options->mappingType) { - case 0: - pixelOffset = options->cells[i]->oam[j].attr2.CharName * 32; - maxTile = options->cells[i]->oam[j].attr2.CharName + oamHeight * oamWidth; - if (maxTile > numTiles) { - numTiles = maxTile; - } - if (tileMask[options->cells[i]->oam[j].attr2.CharName]) { - uniqueOAMs--; - continue; - } - tileMask[options->cells[i]->oam[j].attr2.CharName]++; - break; - case 1: - pixelOffset = options->cells[i]->oam[j].attr2.CharName * 64 + (scanHeight - i) * outputWidth / 2; - numTiles += oamHeight * oamWidth; - break; - case 2: - pixelOffset = options->cells[i]->oam[j].attr2.CharName * 128; - maxTile = options->cells[i]->oam[j].attr2.CharName * 4 + oamHeight * oamWidth; - if (maxTile > numTiles) { - numTiles = maxTile; - } - if (tileMask[options->cells[i]->oam[j].attr2.CharName]) { - uniqueOAMs--; - continue; - } - tileMask[options->cells[i]->oam[j].attr2.CharName]++; - break; + switch (options->mappingType) + { + case 0: + pixelOffset = options->cells[i]->oam[j].attr2.CharName * 32; + break; + case 1: + pixelOffset = options->cells[i]->oam[j].attr2.CharName * 64; + break; + case 2: + pixelOffset = options->cells[i]->oam[j].attr2.CharName * 128; + break; + case 3: + pixelOffset = options->cells[i]->oam[j].attr2.CharName * 256; + break; } + + if (options->vramTransferEnabled) + { + pixelOffset += options->transferData[i]->sourceDataOffset; + } + if (tileMask[pixelOffset]) + { + uniqueOAMs--; + continue; + } + tileMask[pixelOffset] = 1; + numTiles += oamdim.height * oamdim.width; + bool rotationScaling = options->cells[i]->oam[j].attr1.RotationScaling; bool hFlip = options->cells[i]->attributes.hFlip && rotationScaling; bool vFlip = options->cells[i]->attributes.vFlip && rotationScaling; bool hvFlip = options->cells[i]->attributes.hvFlip && rotationScaling; - int plttNum = options->cells[i]->oam[j].attr2.Palette; - switch (image->bitDepth) { - case 4: - ConvertFromTiles4BppCell(image->pixels + pixelOffset, newPixels, oamWidth, oamHeight, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, toPNG, plttNum, options->mappingType, image); - break; - case 8: - pixelOffset *= 2; - ConvertFromTiles8BppCell(image->pixels + pixelOffset, newPixels, oamWidth, oamHeight, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, toPNG, plttNum, options->mappingType, image); - break; + switch (image->bitDepth) + { + case 4: + if (toPNG) + { + ConvertFromTiles4BppCell(image->pixels + pixelOffset, newPixels, oamdim.width, oamdim.height, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, true); + } + else + { + ConvertFromTiles4BppCell(image->pixels, newPixels + pixelOffset, oamdim.width, oamdim.height, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, false); + } + break; + case 8: + pixelOffset *= 2; + if (toPNG) + { + ConvertFromTiles8BppCell(image->pixels + pixelOffset, newPixels, oamdim.width, oamdim.height, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, true); + } + else + { + ConvertFromTiles8BppCell(image->pixels, newPixels + pixelOffset, oamdim.width, oamdim.height, outputWidth, x, y + scanHeight, hFlip, vFlip, hvFlip, false); + } + break; } } - if (uniqueOAMs == 0) { - outputHeight -= cellHeight; - if (i) { - scanHeight--; - outputHeight--; - } - } else { + if (uniqueOAMs == 0) + { + outputHeight -= cellHeight + 1; + scanHeight--; + } + else + { scanHeight += cellHeight; } } free(image->pixels); - if (toPNG) { + free(cellInfo); + if (toPNG) + { image->pixels = newPixels; image->height = outputHeight; image->width = outputWidth; - if (image->hasPalette) { - image->bitDepth = 8; - if (options->mappingType == 2) { - image->pixelsAreRGB = true; - } - } - } else { + } + else + { image->pixels = newPixels; image->height = numTiles * 8; image->width = 8; @@ -859,200 +990,37 @@ void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG) { FreeNCERCell(options); } -void ReadNtrScrn(char *path, struct NSCRFile **ppScrnHeader) { - int fileSize; - unsigned char *data = ReadWholeFile(path, &fileSize); - unsigned int offset = 0x10; - - if (memcmp(data, "RCSN", 4) != 0) // NSCR - { - FATAL_ERROR("Not a valid NSCR cell file.\n"); - } - - unsigned int blockSize; - *ppScrnHeader = NULL; - offset = FindNitroDataBlock(data, "NRCS", fileSize, &blockSize); - if (offset != -1u) { - struct NSCRFile *scrnHeader = (struct NSCRFile *)malloc(sizeof(struct NSCRFile) + blockSize - 0x14); - memset(scrnHeader, 0, sizeof(struct NSCRFile) + blockSize - 0x14); - scrnHeader->scrnWidth = ReadU16(data, offset + 0x8); - scrnHeader->scrnHeight = ReadU16(data, offset + 0xA); - scrnHeader->colorMode = ReadU16(data, offset + 0xC); - scrnHeader->scrnMode = ReadU16(data, offset + 0xE); - scrnHeader->scrnSize = ReadU32(data, offset + 0x10); - memcpy(scrnHeader->data, data + offset + 0x14, blockSize - 0x14); - *ppScrnHeader = scrnHeader; - } else { - FATAL_ERROR("missing SCRN block"); - } - - free(data); -} - -static inline uint32_t NSCR_tileToPixelOffset(uint32_t tileIdx, uint32_t width, int bitDepth) { - div_t tile_yx = div(tileIdx, width / 8); - return tile_yx.quot * bitDepth * width + tile_yx.rem * bitDepth; -} - -void ApplyScrnToImage(char *scrnFilePath, struct Image *image) { - char *ext = GetFileExtension(scrnFilePath); - if (strncmp(ext, "NSCR", 4) != 0) { - FATAL_ERROR("incorrect file format for NSCR\n"); - } - - struct NSCRFile *pScrnHeader; - ReadNtrScrn(scrnFilePath, &pScrnHeader); - - // 3 screen modes - // Mode 0: 4bpp, 16x16 pltt - // If has palette: output PNG is 8bpp - // Mode 1: 8bpp, 1x256 pltt - // If has palette: no change - // Mode 2: 8bpp, 16x256 pltt - // If has palette: output PNG is RBGA - unsigned outSizeMul, outSizeDiv; - switch (pScrnHeader->scrnMode) { - case 0: - if (image->bitDepth != 4) { - FATAL_ERROR("cannot convert %dbpp image with text scrn\n", image->bitDepth); - } - outSizeMul = 1; - outSizeDiv = 2 - image->hasPalette; - break; - case 1: - if (image->bitDepth != 8) { - FATAL_ERROR("cannot convert %dbpp image with affine scrn\n", image->bitDepth); - } - outSizeMul = 1; - outSizeDiv = 1; - break; - case 2: - if (image->bitDepth != 8) { - FATAL_ERROR("cannot convert %dbpp image with extpltt scrn\n", image->bitDepth); - } - outSizeMul = image->hasPalette ? 3 + image->hasTransparency : 1; - outSizeDiv = 1; - break; - default: - FATAL_ERROR("unsupported screen format %d\n", pScrnHeader->scrnMode); - } - - unsigned char *newPixels = malloc(pScrnHeader->scrnHeight * pScrnHeader->scrnWidth * outSizeMul / outSizeDiv); - if (newPixels == NULL) { - FATAL_ERROR("unable to allocate new pixel buffer\n"); - } - memset(newPixels, 0, pScrnHeader->scrnHeight * pScrnHeader->scrnWidth * outSizeMul / outSizeDiv); - uint32_t numTiles = pScrnHeader->scrnHeight * pScrnHeader->scrnWidth / 64; - - uint16_t tileIdx; - bool hFlip = false; - bool vFlip = false; - uint8_t plttIndex = 0; - uint32_t dstOffset; - uint32_t srcOffset; - uint32_t dstPixelOffset; - uint32_t srcPixelOffset; - uint32_t colorIdx; - int i, x, y, xOffset, yOffset; - - for (i = 0; i < numTiles; ++i) { - dstOffset = NSCR_tileToPixelOffset(i, pScrnHeader->scrnWidth, 8 * outSizeMul / outSizeDiv); - if (pScrnHeader->scrnMode == 1) { - tileIdx = pScrnHeader->data[i]; - } else { - uint16_t tileData = ReadU16(pScrnHeader->data, i * 2); - tileIdx = tileData & 0x3FF; - hFlip = (tileData >> 10) & 1; - vFlip = (tileData >> 11) & 1; - plttIndex = (tileData >> 12) & 0xF; - } - srcOffset = NSCR_tileToPixelOffset(tileIdx, image->width, image->bitDepth); - for (y = 0; y < 8; ++y) { - yOffset = vFlip ? 7 - y : y; - for (x = 0; x < 8; ++x) { - xOffset = hFlip ? 7 - x : x; - dstPixelOffset = dstOffset + (yOffset * pScrnHeader->scrnWidth + xOffset) * outSizeMul / outSizeDiv; - srcPixelOffset = srcOffset + (y * image->width + x) * image->bitDepth / 8; - switch (pScrnHeader->scrnMode) { - case 0: { - unsigned shift = (4 * (1 - (x & 1))); - unsigned pixelVal = (image->pixels[srcPixelOffset] >> shift) & 0xF; - if (image->hasPalette) { - newPixels[dstPixelOffset] = pixelVal | (plttIndex << 4); - } else { - unsigned dstShift = hFlip ? 4 - shift : shift; - unsigned dstMask = ~(0xF << dstShift); - newPixels[dstPixelOffset] = (newPixels[dstPixelOffset] & dstMask) | (pixelVal << dstShift); - } - } break; - case 1: - newPixels[dstPixelOffset] = image->pixels[srcPixelOffset]; - break; - case 2: - if (image->hasPalette) { - colorIdx = image->pixels[srcPixelOffset] | (plttIndex << 8); - newPixels[dstPixelOffset + 0] = image->palette.colors[colorIdx].red; - newPixels[dstPixelOffset + 1] = image->palette.colors[colorIdx].green; - newPixels[dstPixelOffset + 2] = image->palette.colors[colorIdx].blue; - if (image->hasTransparency) { - newPixels[dstPixelOffset + 3] = colorIdx == 0 ? 0 : 255; - } - } else { - newPixels[dstPixelOffset] = image->pixels[srcPixelOffset]; - } - break; - } - } - } - } - - free(image->pixels); - image->pixels = newPixels; - if (image->hasPalette) { - image->bitDepth = 8; - } - image->width = pScrnHeader->scrnWidth; - image->height = pScrnHeader->scrnHeight; - image->pixelsAreRGB = pScrnHeader->scrnMode == 2 && image->hasPalette; - free(pScrnHeader); -} - -void WriteImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors) { +void WriteImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors) +{ int tileSize = bitDepth * 8; // number of bytes per tile - if (image->width % 8 != 0) { + if (image->width % 8 != 0) FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width); - } - if (image->height % 8 != 0) { + if (image->height % 8 != 0) FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height); - } - int tilesWide = image->width / 8; // how many tiles wide the image is + int tilesWide = image->width / 8; // how many tiles wide the image is int tilesTall = image->height / 8; // how many tiles tall the image is - if (tilesWide % colsPerChunk != 0) { + if (tilesWide % colsPerChunk != 0) FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified tiles per row (%d)", tilesWide, colsPerChunk); - } - if (tilesTall % rowsPerChunk != 0) { + if (tilesTall % rowsPerChunk != 0) FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified rows per chunk (%d)", tilesTall, rowsPerChunk); - } int maxNumTiles = tilesWide * tilesTall; - if (numTiles == 0) { + if (numTiles == 0) numTiles = maxNumTiles; - } else if (numTiles > maxNumTiles) { + else if (numTiles > maxNumTiles) FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles); - } int bufferSize = numTiles * tileSize; unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for pixels.\n"); - } int chunksWide = tilesWide / colsPerChunk; // how many chunks side-by-side are needed for the full width of the image @@ -1073,130 +1041,154 @@ void WriteImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int ro free(buffer); } -void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, uint32_t scanMode, uint32_t mappingType, uint32_t key, bool wrongSize) { +void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, + bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, bool scan, + uint32_t encodeMode, uint32_t mappingType, uint32_t key, bool wrongSize, bool convertTo4Bpp) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } int tileSize = bitDepth * 8; // number of bytes per tile + if (bitDepth == 8 && convertTo4Bpp && !scan) + tileSize /= 2; - if (image->width % 8 != 0) { + if (image->width % 8 != 0) FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width); - } - if (image->height % 8 != 0) { + if (image->height % 8 != 0) FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height); - } - int tilesWide = image->width / 8; // how many tiles wide the image is + int tilesWide = image->width / 8; // how many tiles wide the image is int tilesTall = image->height / 8; // how many tiles tall the image is - if (tilesWide % colsPerChunk != 0) { + if (tilesWide % colsPerChunk != 0) FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified tiles per row (%d)", tilesWide, colsPerChunk); - } - if (tilesTall % rowsPerChunk != 0) { + if (tilesTall % rowsPerChunk != 0) FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified rows per chunk (%d)", tilesTall, rowsPerChunk); - } int maxNumTiles = tilesWide * tilesTall; - if (numTiles == 0) { + if (numTiles == 0) numTiles = maxNumTiles; - } else if (numTiles > maxNumTiles) { + else if (numTiles > maxNumTiles) FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles); - } int bufferSize = numTiles * tileSize; unsigned char *pixelBuffer = malloc(bufferSize); - if (pixelBuffer == NULL) { + if (pixelBuffer == NULL) FATAL_ERROR("Failed to allocate memory for pixels.\n"); - } int chunksWide = tilesWide / colsPerChunk; // how many chunks side-by-side are needed for the full width of the image - if (scanMode) { - switch (bitDepth) { - case 4: - ConvertToScanned4Bpp(image->pixels, pixelBuffer, bufferSize, invertColors, key, scanMode); - break; - case 8: - FATAL_ERROR("8Bpp not supported yet.\n"); - break; + if (scan) + { + switch (bitDepth) + { + case 4: + ConvertScanned4Bpp(image->pixels, pixelBuffer, bufferSize, invertColors); + break; + case 8: + ConvertScanned8Bpp(image->pixels, pixelBuffer, bufferSize, invertColors); + break; } - } else { - switch (bitDepth) { - case 4: - ConvertToTiles4Bpp(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, invertColors); - break; - case 8: - ConvertToTiles8Bpp(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, invertColors); - break; + } + else + { + switch (bitDepth) + { + case 4: + ConvertToTiles4Bpp(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + break; + case 8: + if (convertTo4Bpp) + { + Convert8BppTo4BppTiles(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + } + else + { + ConvertToTiles8Bpp(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + } + break; } } + if (encodeMode) + { + Encode(pixelBuffer, bufferSize, key, encodeMode); + } WriteGenericNtrHeader(fp, "RGCN", bufferSize + (sopc ? 0x30 : 0x20) + (wrongSize ? -8 : 0), byteOrder, version101, sopc ? 2 : 1); - unsigned char charHeader[0x20] = { 0x52, 0x41, 0x48, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00 }; + unsigned char charHeader[0x20] = { 0x52, 0x41, 0x48, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00 }; charHeader[4] = (bufferSize + 0x20 + (wrongSize ? -8 : 0)) & 0xFF; charHeader[5] = ((bufferSize + 0x20 + (wrongSize ? -8 : 0)) >> 8) & 0xFF; charHeader[6] = ((bufferSize + 0x20 + (wrongSize ? -8 : 0)) >> 16) & 0xFF; charHeader[7] = ((bufferSize + 0x20 + (wrongSize ? -8 : 0)) >> 24) & 0xFF; - if (!clobberSize) { + if (!clobberSize) + { charHeader[8] = tilesTall & 0xFF; charHeader[9] = (tilesTall >> 8) & 0xFF; charHeader[10] = tilesWide & 0xFF; charHeader[11] = (tilesWide >> 8) & 0xFF; - } else { + } + else + { charHeader[8] = 0xFF; charHeader[9] = 0xFF; charHeader[10] = 0xFF; charHeader[11] = 0xFF; - charHeader[16] = 0x10; // size clobbering implies mapping type is some variant of 1d - *should* have a mapping type that's not 0 + charHeader[16] = 0x10; //size clobbering implies mapping type is some variant of 1d - *should* have a mapping type that's not 0 - if (mappingType == 0) { + if (mappingType == 0) + { mappingType = 32; // if not specified assume that it is 32k } } - charHeader[12] = bitDepth == 4 ? 3 : 4; + charHeader[12] = bitDepth == 4 || convertTo4Bpp ? 3 : 4; if (mappingType != 0) { uint32_t val = 0; switch (mappingType) { - case 32: - val = 0; - break; - case 64: - val = 0x10; - break; - case 128: - val = 0x20; - break; - case 256: - val = 0x30; - break; - default: - FATAL_ERROR("Invalid mapping type %d\n", mappingType); - break; + case 32: + val = 0; + break; + case 64: + val = 0x10; + break; + case 128: + val = 0x20; + break; + case 256: + val = 0x30; + break; + default: + FATAL_ERROR("Invalid mapping type %d\n", mappingType); + break; } charHeader[18] = val; } - if (scanMode) { - charHeader[20] = 1; // implies BMP + if (scan) + { + charHeader[20] = 1; //implies BMP } - if (vram) { - charHeader[21] = 1; // implies VRAM transfer + if (vram) + { + charHeader[21] = 1; //implies VRAM transfer } charHeader[24] = bufferSize & 0xFF; @@ -1208,7 +1200,8 @@ void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int fwrite(pixelBuffer, 1, bufferSize, fp); - if (sopc) { + if (sopc) + { unsigned char sopcBuffer[0x10] = { 0x53, 0x4F, 0x50, 0x43, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; sopcBuffer[12] = tilesWide & 0xFF; @@ -1224,18 +1217,19 @@ void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int fclose(fp); } -void FreeImage(struct Image *image) { +void FreeImage(struct Image *image) +{ free(image->pixels); image->pixels = NULL; } -void ReadGbaPalette(char *path, struct Palette *palette) { +void ReadGbaPalette(char *path, struct Palette *palette) +{ int fileSize; unsigned char *data = ReadWholeFile(path, &fileSize); - if (fileSize % 2 != 0) { - FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize); - } + if (fileSize % 2 != 0) + FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize); palette->numColors = fileSize / 2; @@ -1249,49 +1243,52 @@ void ReadGbaPalette(char *path, struct Palette *palette) { free(data); } -void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted) { +void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted, bool convertTo8Bpp) +{ int fileSize; unsigned char *data = ReadWholeFile(path, &fileSize); - if (memcmp(data, "RLCN", 4) != 0 && memcmp(data, "RPCN", 4) != 0) // NCLR / NCPR + if (memcmp(data, "RLCN", 4) != 0 && memcmp(data, "RPCN", 4) != 0) //NCLR / NCPR { FATAL_ERROR("Not a valid NCLR or NCPR palette file.\n"); } unsigned char *paletteHeader = data + 0x10; - if (memcmp(paletteHeader, "TTLP", 4) != 0) { + if (memcmp(paletteHeader, "TTLP", 4) != 0) + { FATAL_ERROR("No valid PLTT file after NCLR header.\n"); } - if ((fileSize - 0x28) % 2 != 0) { + if ((fileSize - 0x28) % 2 != 0) FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize); - } palette->bitDepth = paletteHeader[0x8] == 3 ? 4 : 8; 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 (inverted) paletteSize = 0x200 - paletteSize; if (palIndex == 0) { palette->numColors = paletteSize / 2; } else { - palette->numColors = bitdepth == 4 ? 16 : 256; // remove header and divide by 2 + palette->numColors = bitdepth == 4 && !convertTo8Bpp ? 16 : 256; //remove header and divide by 2 --palIndex; } unsigned char *paletteData = paletteHeader + 0x18; - for (int i = 0; i < 16 * 256; i++) { - if (i < palette->numColors) { - uint16_t paletteEntry = (paletteData[(32 * palIndex) + i * 2 + 1] << 8) | paletteData[(32 * palIndex) + i * 2]; + for (int i = 0; i < 256; i++) + { + if (i < palette->numColors) + { + uint16_t paletteEntry = (paletteData[(32 * (convertTo8Bpp ? 0 : palIndex)) + i * 2 + 1] << 8) | paletteData[(32 * (convertTo8Bpp ? 0 : palIndex)) + i * 2]; palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry)); palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry)); palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry)); - } else { + } + else + { palette->colors[i].red = 0; palette->colors[i].green = 0; palette->colors[i].blue = 0; @@ -1301,12 +1298,12 @@ void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIn free(data); } -void WriteGbaPalette(char *path, struct Palette *palette) { +void WriteGbaPalette(char *path, struct Palette *palette) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } for (int i = 0; i < palette->numColors; i++) { unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red); @@ -1322,22 +1319,23 @@ 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, bool inverted) { +void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, int pcmpStartIndex, bool inverted, bool convertTo4Bpp) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } int colourNum = pad ? 256 : palette->numColors; - uint32_t size = colourNum * 2; // todo check if there's a better way to detect :/ + uint32_t size = colourNum * 2; //todo check if there's a better way to detect :/ uint32_t extSize = size + (ncpr ? 0x10 : 0x18); int numSections = 1; int pcmpColorNum = 0; uint32_t pcmpSize = 0; - if (pcmp) { + if (pcmp) + { pcmpColorNum = colourNum / (bitdepth == 4 ? 16 : 256); if (pcmpColorNum == 0) { FATAL_ERROR("colourNum=%d palette->bitDepth=%d\n", colourNum, bitdepth); @@ -1346,33 +1344,35 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in ++numSections; } - // NCLR header - WriteGenericNtrHeader(fp, ncpr ? "RPCN" : "RLCN", extSize + pcmpSize, !ncpr, false, numSections); + //NCLR header + WriteGenericNtrHeader(fp, (ncpr ? "RPCN" : "RLCN"), extSize + pcmpSize, !ncpr, false, numSections); - unsigned char palHeader[0x18] = { - 0x54, 0x54, 0x4C, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 - }; + unsigned char palHeader[0x18] = + { + 0x54, 0x54, 0x4C, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 + }; - // section size + //section size palHeader[4] = extSize & 0xFF; palHeader[5] = (extSize >> 8) & 0xFF; palHeader[6] = (extSize >> 16) & 0xFF; palHeader[7] = (extSize >> 24) & 0xFF; - if (!palette->bitDepth) { + if (!palette->bitDepth) palette->bitDepth = 4; - } bitdepth = bitdepth ? bitdepth : palette->bitDepth; - // bit depth - palHeader[8] = bitdepth == 4 ? 0x03 : 0x04; + //bit depth + palHeader[8] = bitdepth == 4 || convertTo4Bpp ? 0x03 : 0x04; - if (compNum) { - palHeader[10] = compNum; // assuming this is an indicator of compression, literally no docs for it though + if (compNum) + { + palHeader[10] = compNum; //assuming this is an indicator of compression, literally no docs for it though } - // size + //size int colorSize = inverted ? 0x200 - size : size; palHeader[16] = colorSize & 0xFF; palHeader[17] = (colorSize >> 8) & 0xFF; @@ -1381,10 +1381,12 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in fwrite(palHeader, 1, 0x18, fp); - unsigned char *colours = malloc(colourNum * 2); - // palette data - for (int i = 0; i < colourNum; i++) { - if (i < palette->numColors) { + unsigned char * colours = malloc(colourNum * 2); + //palette data + for (int i = 0; i < colourNum; i++) + { + if (i < palette->numColors) + { unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red); unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green); unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue); @@ -1393,13 +1395,16 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in colours[i * 2] = paletteEntry & 0xFF; colours[i * 2 + 1] = paletteEntry >> 8; - } else { + } + else + { colours[i * 2] = 0x00; colours[i * 2 + 1] = 0x00; } } - if (ir) { + if (ir) + { colours[colourNum * 2 - 2] = 'I'; colours[colourNum * 2 - 1] = 'R'; } @@ -1407,8 +1412,9 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in fwrite(colours, 1, colourNum * 2, fp); free(colours); - if (pcmp) { - uint8_t pcmp_header[16] = { 0x50, 0x4D, 0x43, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0x08, 0x00, 0x00, 0x00 }; + if (pcmp) + { + uint8_t pcmp_header[16] = {0x50, 0x4D, 0x43, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0x08, 0x00, 0x00, 0x00}; pcmp_header[4] = pcmpSize & 0xFF; pcmp_header[5] = (pcmpSize >> 8) & 0xFF; pcmp_header[6] = (pcmpSize >> 16) & 0xFF; @@ -1418,12 +1424,14 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in fwrite(pcmp_header, 1, 16, fp); uint8_t *pcmp_data = malloc(2 * pcmpColorNum); - if (pcmp_data == NULL) { + if (pcmp_data == NULL) + { FATAL_ERROR("failed to alloc pcmp_data\n"); } for (int i = 0; i < pcmpColorNum; ++i) { - pcmp_data[i * 2] = i & 0xFF; - pcmp_data[i * 2 + 1] = (i >> 8) & 0xFF; + int index = i + pcmpStartIndex; + pcmp_data[i * 2] = index & 0xFF; + pcmp_data[i * 2 + 1] = (index >> 8) & 0xFF; } fwrite(pcmp_data, 1, pcmpColorNum * 2, fp); free(pcmp_data); @@ -1432,24 +1440,23 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in fclose(fp); } -void ReadNtrCell_CEBK(unsigned char *restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options) { +void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options) +{ options->cellCount = data[blockOffset + 0x8] | (data[blockOffset + 0x9] << 8); options->extended = data[blockOffset + 0xA] == 1; int vramTransferOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8); + unsigned int ucatOffset = (data[blockOffset + 0x1c] | data[blockOffset + 0x1d] << 8 | data[blockOffset + 0x1e] << 16 | data[blockOffset + 0x1f] << 24); 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->ucatEnabled = ucatOffset > 0; 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++) { + for (int i = 0; i < options->cellCount; i++) + { int offset = blockOffset + 0x20 + (i * celSize); if (offset + celSize > blockOffset + blockSize) { FATAL_ERROR("corrupted CEBK block\n"); @@ -1463,7 +1470,8 @@ void ReadNtrCell_CEBK(unsigned char *restrict data, unsigned int blockOffset, un options->cells[i]->attributes.boundingRect = (cellAttrs >> 11) & 1; options->cells[i]->attributes.boundingSphereRadius = cellAttrs & 0x3F; - if (options->extended) { + if (options->extended) + { options->cells[i]->maxX = data[offset + 8] | (data[offset + 9] << 8); options->cells[i]->maxY = data[offset + 10] | (data[offset + 11] << 8); options->cells[i]->minX = data[offset + 12] | (data[offset + 13] << 8); @@ -1472,107 +1480,130 @@ void ReadNtrCell_CEBK(unsigned char *restrict data, unsigned int blockOffset, un } int offset = blockOffset + 0x20 + (options->cellCount * celSize); - for (int i = 0; i < options->cellCount; i++) { + for (int i = 0; i < options->cellCount; i++) + { options->cells[i]->oam = malloc(sizeof(struct OAM) * options->cells[i]->oamCount); - for (int j = 0; j < options->cells[i]->oamCount; j++) { - // Attr0 + for (int j = 0; j < options->cells[i]->oamCount; j++) + { + //Attr0 - // bits 0-7 Y coordinate + //bits 0-7 Y coordinate options->cells[i]->oam[j].attr0.YCoordinate = data[offset]; - // bit 8 rotation + //bit 8 rotation options->cells[i]->oam[j].attr0.Rotation = data[offset + 1] & 1; - // bit 9 Obj Size (if rotation) or Obj Disable (if not rotation) + //bit 9 Obj Size (if rotation) or Obj Disable (if not rotation) options->cells[i]->oam[j].attr0.SizeDisable = (data[offset + 1] >> 1) & 1; - // bits 10-11 Obj Mode + //bits 10-11 Obj Mode options->cells[i]->oam[j].attr0.Mode = (data[offset + 1] >> 2) & 3; - // bit 12 Obj Mosaic + //bit 12 Obj Mosaic options->cells[i]->oam[j].attr0.Mosaic = (data[offset + 1] >> 4) & 1; - // bit 13 Colours + //bit 13 Colours options->cells[i]->oam[j].attr0.Colours = ((data[offset + 1] >> 5) & 1) == 0 ? 16 : 256; - // bits 14-15 Obj Shape + //bits 14-15 Obj Shape options->cells[i]->oam[j].attr0.Shape = (data[offset + 1] >> 6) & 3; - // Attr1 + //Attr1 - // bits 0-8 X coordinate + //bits 0-8 X coordinate options->cells[i]->oam[j].attr1.XCoordinate = data[offset + 2] | ((data[offset + 3] & 1) << 8); - // bits 9-13 Rotation and scaling (if rotation) bit 12 Horizontal flip, bit 13 Vertical flip (if not rotation) + //bits 9-13 Rotation and scaling (if rotation) bit 12 Horizontal flip, bit 13 Vertical flip (if not rotation) options->cells[i]->oam[j].attr1.RotationScaling = (data[offset + 3] >> 1) & 0x1F; - // bits 14-15 Obj Size + //bits 14-15 Obj Size options->cells[i]->oam[j].attr1.Size = (data[offset + 3] >> 6) & 3; - // Attr2 + //Attr2 - // bits 0-9 Character Name? + //bits 0-9 Character Name? options->cells[i]->oam[j].attr2.CharName = data[offset + 4] | ((data[offset + 5] & 3) << 8); - // bits 10-11 Priority + //bits 10-11 Priority options->cells[i]->oam[j].attr2.Priority = (data[offset + 5] >> 2) & 3; - // bits 12-15 Palette Number + //bits 12-15 Palette Number options->cells[i]->oam[j].attr2.Palette = (data[offset + 5] >> 4) & 0xF; offset += 6; } } - if (options->vramTransferEnabled) { + if (options->vramTransferEnabled) + { 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++) { + for (int idx = 0; idx < options->cellCount; idx++) + { 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; } } + + if (options->ucatEnabled) + { + offset = blockOffset + 0x18 + ucatOffset + 0x04 * options->cellCount; + + options->ucatCellAttribtes = malloc(sizeof(uint32_t) * options->cellCount); + for (int i = 0; i < options->cellCount; i++) + { + options->ucatCellAttribtes[i] = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + offset += 0x04; + } + } } -void ReadNtrCell_LABL(unsigned char *restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options) { +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) { + while (textStart < blockOffset + blockSize) + { unsigned int labelOffset = data[textStart] | (data[textStart + 1] << 8) | (data[textStart + 2] << 16) | (data[textStart + 3] << 24); - if (labelOffset > blockSize) { + if (labelOffset > blockSize) + { break; - } else { + } + else { ++count; textStart += 4; } } options->labelCount = count; options->labels = malloc(sizeof(char *) * count); - for (int i = 0; i < count; ++i) { + 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) { + if (offset > blockOffset + blockSize) + { FATAL_ERROR("corrupted LABL block\n"); } - unsigned long slen = strnlen((char *)data + offset, blockSize - offset); + unsigned long slen = strnlen((char *)data + offset, (blockOffset + blockSize) - offset); options->labels[i] = malloc(slen + 1); strncpy(options->labels[i], (char *)data + offset, slen + 1); } } -void ReadNtrCell(char *path, struct JsonToCellOptions *options) { +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 + if (memcmp(data, "RECN", 4) != 0) //NCER { FATAL_ERROR("Not a valid NCER cell file.\n"); } @@ -1581,13 +1612,17 @@ void ReadNtrCell(char *path, struct JsonToCellOptions *options) { unsigned int blockSize; offset = FindNitroDataBlock(data, "KBEC", fileSize, &blockSize); - if (offset != -1u) { + if (offset != -1u) + { + options->dontPadKbec = blockSize % 4 != 0; ReadNtrCell_CEBK(data, offset, blockSize, options); - } else { + } + else { FATAL_ERROR("missing CEBK block"); } offset = FindNitroDataBlock(data, "LBAL", fileSize, &blockSize); - if (offset != -1u) { + if (offset != -1u) + { options->labelEnabled = true; ReadNtrCell_LABL(data, offset, blockSize, options); } @@ -1595,58 +1630,86 @@ void ReadNtrCell(char *path, struct JsonToCellOptions *options) { free(data); } -void WriteNtrCell(char *path, struct JsonToCellOptions *options) { +void WriteNtrCell(char *path, struct JsonToCellOptions *options) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } 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) { + if (options->vramTransferEnabled) + { kbecSize += 0x08 + (0x08 * options->cellCount); } + // if UCAT is enabled add size to KBEC + unsigned int ucatSize = 0; + if (options->ucatEnabled) + { + ucatSize = options->cellCount * 0x08 + 0x10; + kbecSize += ucatSize; + } // add 0x06 for number of OAMs - can be more than 1 - for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum) { + 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; + if (!options->dontPadKbec) + { + // KBEC size is padded to be 4-byte aligned + kbecSize = (kbecSize + 3) & ~3; + } unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + kbecSize; - if (options->labelEnabled) { - for (int j = 0; j < options->labelCount; j++) { - totalSize += (unsigned)strlen(options->labels[j]) + 5; // strlen + terminator + pointer + if (options->labelEnabled) + { + for (int j = 0; j < options->labelCount; j++) + { + totalSize += (unsigned)strlen(options->labels[j]) + 5; //strlen + terminator + pointer } } WriteGenericNtrHeader(fp, "RECN", totalSize, true, false, options->labelEnabled ? 3 : 1); - unsigned char KBECHeader[0x20] = { - 0x4B, 0x42, 0x45, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + unsigned char KBECHeader[0x20] = + { + 0x4B, 0x42, 0x45, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; - KBECHeader[8] = options->cellCount; // cell count + KBECHeader[8] = options->cellCount; //cell count - if (options->extended) { - KBECHeader[10] = 1; // extended + if (options->extended) + { + KBECHeader[10] = 1; //extended } - 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[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 + KBECHeader[16] = (options->mappingType & 0xFF); //not possible to be more than 8 bits, though 32 are allocated + + // offset to UCAT data within KBEC section (offset from KBEC start + 0x1c) + if (options->ucatEnabled) + { + unsigned int ucatOffset = (kbecSize + 0x20) - ucatSize - 0x08; + KBECHeader[28] = ucatOffset & 0xFF; + KBECHeader[29] = (ucatOffset >> 8) & 0xFF; + KBECHeader[30] = (ucatOffset >> 16) & 0xFF; + KBECHeader[31] = (ucatOffset >> 24) & 0xFF; + } // offset to VRAM transfer data within KBEC section (offset from KBEC start + 0x08) - if (options->vramTransferEnabled) { + if (options->vramTransferEnabled) + { unsigned int vramTransferLength = 0x08 + (0x08 * options->cellCount); - unsigned int vramTransferOffset = (kbecSize + 0x20) - vramTransferLength - 0x08; + unsigned int vramTransferOffset = (kbecSize + 0x20) - vramTransferLength - ucatSize - 0x08; KBECHeader[20] = vramTransferOffset & 0xFF; KBECHeader[21] = (vramTransferOffset >> 8) & 0xFF; KBECHeader[22] = (vramTransferOffset >> 16) & 0xFF; @@ -1659,94 +1722,99 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { memset(KBECContents, 0, kbecSize); - /*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"); - }*/ - int i; int totalOam = 0; - for (i = 0; i < options->cellCount * iterNum; i += iterNum) { - KBECContents[i] = options->cells[i / iterNum]->oamCount; // number of OAM entries + for (i = 0; i < options->cellCount * iterNum; i += iterNum) + { + KBECContents[i] = options->cells[i / iterNum]->oamCount; //number of OAM entries short cellAttrs = (options->cells[i / iterNum]->attributes.hFlip << 8) | (options->cells[i / iterNum]->attributes.vFlip << 9) - | (options->cells[i / iterNum]->attributes.hvFlip << 10) | (options->cells[i / iterNum]->attributes.boundingRect << 11) - | (options->cells[i / iterNum]->attributes.boundingSphereRadius & 0x3F); - KBECContents[i + 2] = cellAttrs & 0xff; // cell attributes + | (options->cells[i / iterNum]->attributes.hvFlip << 10) | (options->cells[i / iterNum]->attributes.boundingRect << 11) + | (options->cells[i / iterNum]->attributes.boundingSphereRadius & 0x3F); + KBECContents[i + 2] = cellAttrs & 0xff; //cell attributes KBECContents[i + 3] = cellAttrs >> 8; - KBECContents[i + 4] = (totalOam * 6) & 0xff; // pointer to OAM data - KBECContents[i + 5] = (totalOam * 6) >> 8; // unlikely to be more than 16 bits, but there are 32 allocated, change if necessary - if (options->extended) { - KBECContents[i + 8] = options->cells[i / iterNum]->maxX & 0xff; // maxX + KBECContents[i + 4] = (totalOam * 6) & 0xff; //pointer to OAM data + KBECContents[i + 5] = (totalOam * 6) >> 8; //unlikely to be more than 16 bits, but there are 32 allocated, change if necessary + if (options->extended) + { + KBECContents[i + 8] = options->cells[i / iterNum]->maxX & 0xff; //maxX KBECContents[i + 9] = options->cells[i / iterNum]->maxX >> 8; - KBECContents[i + 10] = options->cells[i / iterNum]->maxY & 0xff; // maxY + KBECContents[i + 10] = options->cells[i / iterNum]->maxY & 0xff; //maxY KBECContents[i + 11] = options->cells[i / iterNum]->maxY >> 8; - KBECContents[i + 12] = options->cells[i / iterNum]->minX & 0xff; // minX + KBECContents[i + 12] = options->cells[i / iterNum]->minX & 0xff; //minX KBECContents[i + 13] = options->cells[i / iterNum]->minX >> 8; - KBECContents[i + 14] = options->cells[i / iterNum]->minY & 0xff; // minY + KBECContents[i + 14] = options->cells[i / iterNum]->minY & 0xff; //minY KBECContents[i + 15] = options->cells[i / iterNum]->minY >> 8; } totalOam += options->cells[i / iterNum]->oamCount; } - // OAM data + //OAM data int offset = i; - for (int j = 0; j < options->cellCount; j++) { - for (int k = 0; k < options->cells[j]->oamCount; k++) { - // Attr0 + for (int j = 0; j < options->cellCount; j++) + { + for (int k = 0; k < options->cells[j]->oamCount; k++) + { + //Attr0 - // bits 0-7 Y coordinate + //bits 0-7 Y coordinate KBECContents[offset] = options->cells[j]->oam[k].attr0.YCoordinate & 0xff; - // bit 8 rotation + //bit 8 rotation KBECContents[offset + 1] = options->cells[j]->oam[k].attr0.Rotation; - // bit 9 Obj Size (if rotation) or Obj Disable (if not rotation) + //bit 9 Obj Size (if rotation) or Obj Disable (if not rotation) KBECContents[offset + 1] |= options->cells[j]->oam[k].attr0.SizeDisable << 1; - // bits 10-11 Obj Mode + //bits 10-11 Obj Mode KBECContents[offset + 1] |= options->cells[j]->oam[k].attr0.Mode << 2; - // bit 12 Obj Mosaic + //bit 12 Obj Mosaic KBECContents[offset + 1] |= options->cells[j]->oam[k].attr0.Mosaic << 4; - // bit 13 Colours + //bit 13 Colours KBECContents[offset + 1] |= (options->cells[j]->oam[k].attr0.Colours == 16 ? 0 : 1) << 5; - // bits 14-15 Obj Shape + //bits 14-15 Obj Shape KBECContents[offset + 1] |= options->cells[j]->oam[k].attr0.Shape << 6; - // Attr1 + //Attr1 - // bits 0-8 X coordinate + //bits 0-8 X coordinate KBECContents[offset + 2] = options->cells[j]->oam[k].attr1.XCoordinate & 0xff; KBECContents[offset + 3] = options->cells[j]->oam[k].attr1.XCoordinate >> 8; - // bits 9-13 Rotation and scaling (if rotation) bit 12 Horizontal flip, bit 13 Vertical flip (if not rotation) + //bits 9-13 Rotation and scaling (if rotation) bit 12 Horizontal flip, bit 13 Vertical flip (if not rotation) KBECContents[offset + 3] |= options->cells[j]->oam[k].attr1.RotationScaling << 1; - // bits 14-15 Obj Size + //bits 14-15 Obj Size KBECContents[offset + 3] |= options->cells[j]->oam[k].attr1.Size << 6; - // Attr2 + //Attr2 - // bits 0-9 Character Name? + //bits 0-9 Character Name? KBECContents[offset + 4] = options->cells[j]->oam[k].attr2.CharName & 0xff; KBECContents[offset + 5] = options->cells[j]->oam[k].attr2.CharName >> 8; - // bits 10-11 Priority + //bits 10-11 Priority KBECContents[offset + 5] |= options->cells[j]->oam[k].attr2.Priority << 2; - // bits 12-15 Palette Number + //bits 12-15 Palette Number KBECContents[offset + 5] |= options->cells[j]->oam[k].attr2.Palette << 4; offset += 6; } } + // word-aligned + if (offset % 4 > 0) + { + offset += 4 - (offset % 4); + } + // VRAM transfer data - if (options->vramTransferEnabled) { + if (options->vramTransferEnabled) + { // max transfer size + fixed offset 0x08 KBECContents[offset] = options->vramTransferMaxSize & 0xFF; KBECContents[offset + 1] = (options->vramTransferMaxSize >> 8) & 0xFF; @@ -1758,7 +1826,8 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { offset += 8; // write a VRAM transfer block for each cell - for (int idx = 0; idx < options->cellCount; idx++) { + 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; @@ -1774,13 +1843,66 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { } } + // UCAT data + if (options->ucatEnabled) + { + // UCAT magic + strcpy((char *) (KBECContents + offset), "TACU"); + offset += 0x04; + + // ucat size + KBECContents[offset] = ucatSize & 0xFF; + KBECContents[offset + 1] = (ucatSize >> 8) & 0xFF; + KBECContents[offset + 2] = (ucatSize >> 16) & 0xFF; + KBECContents[offset + 3] = (ucatSize >> 24) & 0xFF; + offset += 0x04; + + // num cells + KBECContents[offset] = options->cellCount & 0xFF; + KBECContents[offset + 1] = (options->cellCount >> 8) & 0xFF; + offset += 0x02; + + // num attributes per cell + KBECContents[offset] = 0x01; + offset += 0x02; + + // **attr + KBECContents[offset] = 0x08; + offset += 0x04; + + // *attr + unsigned int attributeAddress = options->cellCount * 0x04 + 0x08; + for (int i = 0; i < options->cellCount; i++) + { + KBECContents[offset] = attributeAddress & 0xFF; + KBECContents[offset + 1] = (attributeAddress >> 8) & 0xFF; + KBECContents[offset + 2] = (attributeAddress >> 16) & 0xFF; + KBECContents[offset + 3] = (attributeAddress >> 24) & 0xFF; + offset += 0x04; + attributeAddress += 0x04; + } + + // attr + for (int i = 0; i < options->cellCount; i++) + { + unsigned int ucatAttribute = options->ucatCellAttribtes[i]; + KBECContents[offset] = ucatAttribute & 0xFF; + KBECContents[offset + 1] = (ucatAttribute >> 8) & 0xFF; + KBECContents[offset + 2] = (ucatAttribute >> 16) & 0xFF; + KBECContents[offset + 3] = (ucatAttribute >> 24) & 0xFF; + offset += 0x04; + } + } + fwrite(KBECContents, 1, kbecSize, fp); free(KBECContents); - if (options->labelEnabled) { + if (options->labelEnabled) + { unsigned int lablSize = 8; - for (int j = 0; j < options->labelCount; j++) { + for (int j = 0; j < options->labelCount; j++) + { lablSize += (unsigned)strlen(options->labels[j]) + 5; } @@ -1788,14 +1910,15 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { memset(labl, 0, lablSize); - strcpy((char *)labl, "LBAL"); + strcpy((char *) labl, "LBAL"); labl[4] = lablSize & 0xff; labl[5] = lablSize >> 8; unsigned int position = 0; i = 0; - for (int j = 0; j < options->labelCount; j++) { + for (int j = 0; j < options->labelCount; j++) + { labl[i + 8] = position & 0xff; labl[i + 9] = position >> 8; @@ -1803,8 +1926,9 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { i += 4; } - for (int j = 0; j < options->labelCount; j++) { - strcpy((char *)(labl + (i + 8)), options->labels[j]); + for (int j = 0; j < options->labelCount; j++) + { + strcpy((char *) (labl + (i + 8)), options->labels[j]); i += (int)strlen(options->labels[j]) + 1; } @@ -1812,7 +1936,7 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { free(labl); - unsigned char txeu[0xc] = { 0x54, 0x58, 0x45, 0x55, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + unsigned char txeu[0xc] = {0x54, 0x58, 0x45, 0x55, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; fwrite(txeu, 1, 0xc, fp); } @@ -1820,18 +1944,19 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { fclose(fp); } -void WriteNtrScreen(char *path, struct JsonToScreenOptions *options) { +void WriteNtrScreen(char *path, struct JsonToScreenOptions *options) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } int totalSize = options->width * options->height * 2 + 0x14; WriteGenericNtrHeader(fp, "RCSN", totalSize, true, false, 1); - unsigned char NSCRHeader[0x14] = { 0x4E, 0x52, 0x43, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + unsigned char NSCRHeader[0x14] = { 0x4E, 0x52, 0x43, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; NSCRHeader[0x4] = totalSize & 0xff; NSCRHeader[0x5] = (totalSize >> 8) & 0xff; @@ -1858,18 +1983,19 @@ void WriteNtrScreen(char *path, struct JsonToScreenOptions *options) { fclose(fp); } -void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { +void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) +{ int fileSize; unsigned char *data = ReadWholeFile(path, &fileSize); - if (memcmp(data, "RNAN", 4) != 0 && memcmp(data, "RAMN", 4) != 0) // NANR/NMAR + if (memcmp(data, "RNAN", 4) != 0 && memcmp(data, "RAMN", 4) != 0) //NANR/NMAR { FATAL_ERROR("Not a valid NANR/NMAR animation file.\n"); } options->labelEnabled = data[0xE] != 1; - if (memcmp(data + 0x10, "KNBA", 4) != 0) // ABNK + if (memcmp(data + 0x10, "KNBA", 4) != 0 ) //ABNK { FATAL_ERROR("Not a valid ABNK animation file.\n"); } @@ -1877,9 +2003,13 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { options->sequenceCount = data[0x18] | (data[0x19] << 8); options->frameCount = data[0x1A] | (data[0x1B] << 8); + int uaatOffset = (data[0x2c] | data[0x2d] << 8 | data[0x2e] << 16 | data[0x2f] << 24); + options->uaatEnabled = uaatOffset > 0; + options->sequenceData = malloc(sizeof(struct SequenceData *) * options->sequenceCount); - for (int i = 0; i < options->sequenceCount; i++) { + for (int i = 0; i < options->sequenceCount; i++) + { options->sequenceData[i] = malloc(sizeof(struct SequenceData)); } @@ -1887,7 +2017,8 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { unsigned int *frameOffsets = malloc(sizeof(unsigned int) * options->sequenceCount); - for (int i = 0; i < options->sequenceCount; i++, offset += 0x10) { + for (int i = 0; i < options->sequenceCount; i++, offset += 0x10) + { options->sequenceData[i]->frameCount = data[offset] | (data[offset + 1] << 8); options->sequenceData[i]->loopStartFrame = data[offset + 2] | (data[offset + 3] << 8); options->sequenceData[i]->animationElement = data[offset + 4] | (data[offset + 5] << 8); @@ -1896,7 +2027,8 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { frameOffsets[i] = data[offset + 12] | (data[offset + 13] << 8) | (data[offset + 14] << 16) | (data[offset + 15] << 24); options->sequenceData[i]->frameData = malloc(sizeof(struct FrameData *) * options->sequenceData[i]->frameCount); - for (int j = 0; j < options->sequenceData[i]->frameCount; j++) { + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { options->sequenceData[i]->frameData[j] = malloc(sizeof(struct FrameData)); } } @@ -1904,28 +2036,35 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { int *resultOffsets = malloc(sizeof(int) * options->frameCount); memset(resultOffsets, -1, sizeof(int) * options->frameCount); - for (int i = 0; i < options->sequenceCount; i++) { - for (int j = 0; j < options->sequenceData[i]->frameCount; j++) { + for (int i = 0; i < options->sequenceCount; i++) + { + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { int frameOffset = offset + frameOffsets[i] + j * 0x8; options->sequenceData[i]->frameData[j]->resultOffset = data[frameOffset] | (data[frameOffset + 1] << 8) | (data[frameOffset + 2] << 16) | (data[frameOffset + 3] << 24); options->sequenceData[i]->frameData[j]->frameDelay = data[frameOffset + 4] | (data[frameOffset + 5] << 8); - // 0xBEEF + //0xBEEF - // the following is messy + //the following is messy bool present = false; - // check for offset in array - for (int k = 0; k < options->frameCount; k++) { - if (resultOffsets[k] == options->sequenceData[i]->frameData[j]->resultOffset) { + //check for offset in array + for (int k = 0; k < options->frameCount; k++) + { + if (resultOffsets[k] == options->sequenceData[i]->frameData[j]->resultOffset) + { options->sequenceData[i]->frameData[j]->resultId = k; present = true; break; } } - // add data if not present - if (!present) { - for (int k = 0; i < options->frameCount; k++) { - if (resultOffsets[k] == -1) { + //add data if not present + if (!present) + { + for (int k = 0; i < options->frameCount; k++) + { + if (resultOffsets[k] == -1) + { options->sequenceData[i]->frameData[j]->resultId = k; resultOffsets[k] = options->sequenceData[i]->frameData[j]->resultOffset; break; @@ -1937,14 +2076,33 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { free(frameOffsets); - offset = 0x18 + (data[0x24] | (data[0x25] << 8) | (data[0x26] << 16) | (data[0x27] << 24)); // start of animation results + if (options->uaatEnabled) + { + offset = 0x28 + uaatOffset + 0x0c * options->sequenceCount + 0x04 * options->frameCount; // index of first attribute + + options->uaatData.sequenceAttributes = malloc(sizeof(uint32_t) * options->sequenceCount); + for (int i = 0; i < options->sequenceCount; i++) + { + options->uaatData.sequenceAttributes[i] = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + offset += 0x04; + } + + options->uaatData.frameAttributes = malloc(sizeof(uint32_t) * options->frameCount); + for (int i = 0; i < options->frameCount; i++) + { + options->uaatData.frameAttributes[i] = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + offset += 0x04; + } + } + + offset = 0x18 + (data[0x24] | (data[0x25] << 8) | (data[0x26] << 16) | (data[0x27] << 24)); //start of animation results int k; - for (k = 0; k < options->frameCount; k++) { - if (resultOffsets[k] == -1) { + for (k = 0; k < options->frameCount; k++) + { + if (resultOffsets[k] == -1) break; - } } options->resultCount = k; @@ -1952,29 +2110,38 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { options->animationResults = malloc(sizeof(struct AnimationResults *) * options->resultCount); - for (int i = 0; i < options->resultCount; i++) { + for (int i = 0; i < options->resultCount; i++) + { 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++) { + 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++) { + for (int i = 0; i < options->resultCount; i++) + { // 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]->resultId == i) { - if (lastSequence != j) { + for (int j = 0; j < options->sequenceCount; j++) + { + for (int k = 0; k < options->sequenceData[j]->frameCount; k++) + { + if (options->sequenceData[j]->frameData[k]->resultId == i) + { + if (lastSequence != j) + { lastSequence = j; - if (resultOffset % 4 != 0) { + if (resultOffset % 4 != 0) + { resultOffset += 0x2; offset += 0x2; } @@ -1983,48 +2150,46 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { break; } } - if (found) { + if (found) break; + } + switch (options->animationResults[i]->resultType) + { + case 0: //index + options->animationResults[i]->index = data[offset] | (data[offset + 1] << 8); + resultOffset += 0x2; + offset += 0x2; break; - } - } - switch (options->animationResults[i]->resultType) { - case 0: // index - options->animationResults[i]->index = data[offset] | (data[offset + 1] << 8); - resultOffset += 0x2; - offset += 0x2; - break; - case 1: // SRT - options->animationResults[i]->dataSrt.index = data[offset] | (data[offset + 1] << 8); - options->animationResults[i]->dataSrt.rotation = data[offset + 2] | (data[offset + 3] << 8); - options->animationResults[i]->dataSrt.scaleX = data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24); - options->animationResults[i]->dataSrt.scaleY = data[offset + 8] | (data[offset + 9] << 8) | (data[offset + 10] << 16) | (data[offset + 11] << 24); - options->animationResults[i]->dataSrt.positionX = data[offset + 12] | (data[offset + 13] << 8); - options->animationResults[i]->dataSrt.positionY = data[offset + 14] | (data[offset + 15] << 8); - resultOffset += 0x10; - offset += 0x10; - break; + case 1: //SRT + options->animationResults[i]->dataSrt.index = data[offset] | (data[offset + 1] << 8); + options->animationResults[i]->dataSrt.rotation = data[offset + 2] | (data[offset + 3] << 8); + options->animationResults[i]->dataSrt.scaleX = data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24); + options->animationResults[i]->dataSrt.scaleY = data[offset + 8] | (data[offset + 9] << 8) | (data[offset + 10] << 16) | (data[offset + 11] << 24); + options->animationResults[i]->dataSrt.positionX = data[offset + 12] | (data[offset + 13] << 8); + options->animationResults[i]->dataSrt.positionY = data[offset + 14] | (data[offset + 15] << 8); + resultOffset += 0x10; + offset += 0x10; + break; - case 2: // T - options->animationResults[i]->dataT.index = data[offset] | (data[offset + 1] << 8); - options->animationResults[i]->dataT.positionX = data[offset + 4] | (data[offset + 5] << 8); - options->animationResults[i]->dataT.positionY = data[offset + 6] | (data[offset + 7] << 8); - resultOffset += 0x8; - offset += 0x8; - break; + case 2: //T + options->animationResults[i]->dataT.index = data[offset] | (data[offset + 1] << 8); + options->animationResults[i]->dataT.positionX = data[offset + 4] | (data[offset + 5] << 8); + options->animationResults[i]->dataT.positionY = data[offset + 6] | (data[offset + 7] << 8); + resultOffset += 0x8; + offset += 0x8; + break; } } - // add any missed padding from the final frame before processing labels - if (offset % 4 != 0) { - offset += 2; - } + offset = 0x10 + (data[0x14] | (data[0x15] << 8) | (data[0x26] << 16) | (data[0x17] << 24)); //start of label results - if (options->labelEnabled) { + if (options->labelEnabled) + { options->labelCount = options->sequenceCount; //*should* be the same options->labels = malloc(sizeof(char *) * options->labelCount); - offset += 0x8 + options->labelCount * 0x4; // skip to label data - for (int i = 0; i < options->labelCount; i++) { + offset += 0x8 + options->labelCount * 0x4; //skip to label data + for (int i = 0; i < options->labelCount; i++) + { options->labels[i] = malloc(strlen((char *)data + offset) + 1); strcpy(options->labels[i], (char *)data + offset); offset += strlen((char *)data + offset) + 1; @@ -2034,23 +2199,23 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { free(data); } -void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { +void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) +{ FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } unsigned int totalSize = 0x20 + options->sequenceCount * 0x10 + options->frameCount * 0x8; - for (int i = 0; i < options->resultCount; i++) { - if (options->animationResults[i]->resultType == 0) { + for (int i = 0; i < options->resultCount; i++) + { + if (options->animationResults[i]->resultType == 0) totalSize += 0x2; - } else if (options->animationResults[i]->resultType == 1) { + else if (options->animationResults[i]->resultType == 1) totalSize += 0x10; - } else if (options->animationResults[i]->resultType == 2) { + else if (options->animationResults[i]->resultType == 2) totalSize += 0x8; - } } // foreach sequence, need to check whether padding is applied for its results @@ -2059,21 +2224,26 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { // 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++) { + 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++) { + 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) { + 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) { + if (usedResults[resultIndex] == -1) + { usedResults[resultIndex] = options->sequenceData[i]->frameData[j]->resultId; lastNewResultIndex = options->sequenceData[i]->frameData[j]->resultId; break; @@ -2081,17 +2251,18 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { } // if not already used, add it to the result size for the sequence - if (!isUsed) { - if (options->animationResults[resultIndex]->resultType == 0) { + if (!isUsed) + { + if (options->animationResults[resultIndex]->resultType == 0) sequenceLen += 0x2; - } else if (options->animationResults[resultIndex]->resultType == 1) { + else if (options->animationResults[resultIndex]->resultType == 1) sequenceLen += 0x10; - } else if (options->animationResults[resultIndex]->resultType == 2) { + else if (options->animationResults[resultIndex]->resultType == 2) sequenceLen += 0x8; - } } } - if (sequenceLen % 4 != 0 && lastNewResultIndex != -1) { + 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; @@ -2100,20 +2271,31 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { free(usedResults); + unsigned int uaatSize = 0; + if (options->uaatEnabled) + { + uaatSize = 0x10 + 0x10 * options->sequenceCount + 0x08 * options->frameCount; + totalSize += uaatSize; + } + unsigned int KNBASize = totalSize; - if (options->labelEnabled) { + if (options->labelEnabled) + { totalSize += options->multiCell ? 0x8 : 0x14; - for (int j = 0; j < options->labelCount; j++) { - totalSize += (unsigned)strlen(options->labels[j]) + 5; // strlen + terminator + pointer + for (int j = 0; j < options->labelCount; j++) + { + totalSize += (unsigned)strlen(options->labels[j]) + 5; //strlen + terminator + pointer } } WriteGenericNtrHeader(fp, options->multiCell ? "RAMN" : "RNAN", totalSize, true, false, options->labelEnabled ? (options->multiCell ? 2 : 3) : 1); - unsigned char KBNAHeader[0x20] = { - 0x4B, 0x4E, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + unsigned char KBNAHeader[0x20] = + { + 0x4B, 0x4E, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; KBNAHeader[4] = KNBASize & 0xff; KBNAHeader[5] = (KNBASize >> 8) & 0xff; @@ -2140,14 +2322,26 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { KBNAHeader[22] = (resultsOffset >> 16) & 0xff; KBNAHeader[23] = resultsOffset >> 24; + unsigned int uaatOffset = 0; + if (options->uaatEnabled) + { + uaatOffset = KNBASize - uaatSize - 0x08; + KBNAHeader[28] = uaatOffset & 0xff; + KBNAHeader[29] = (uaatOffset >> 8) & 0xff; + KBNAHeader[30] = (uaatOffset >> 16) & 0xff; + KBNAHeader[31] = (uaatOffset >> 24) & 0xff; + } + fwrite(KBNAHeader, 1, 0x20, fp); int contentsSize = KNBASize - 0x20; unsigned char *KBNAContents = malloc(contentsSize); + memset(KBNAContents, 0, contentsSize); int i; int framePtrCounter = 0; - for (i = 0; i < options->sequenceCount * 0x10; i += 0x10) { + for (i = 0; i < options->sequenceCount * 0x10; i += 0x10) + { KBNAContents[i] = options->sequenceData[i / 0x10]->frameCount & 0xff; KBNAContents[i + 1] = options->sequenceData[i / 0x10]->frameCount >> 8; KBNAContents[i + 2] = options->sequenceData[i / 0x10]->loopStartFrame & 0xff; @@ -2169,21 +2363,19 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { int j; int m; - for (j = i, m = 0; m < options->sequenceCount; m++) { + for (j = i, m = 0; m < options->sequenceCount; m++) + { for (int k = 0; k < options->sequenceData[m]->frameCount; k++) { int resPtr = 0; for (int l = 0; l < options->sequenceData[m]->frameData[k]->resultId; l++) { - if (options->animationResults[l]->resultType == 0) { + if (options->animationResults[l]->resultType == 0) resPtr += 0x2; - } else if (options->animationResults[l]->resultType == 1) { + else if (options->animationResults[l]->resultType == 1) resPtr += 0x10; - } else if (options->animationResults[l]->resultType == 2) { + else if (options->animationResults[l]->resultType == 2) resPtr += 0x8; - } - - if (options->animationResults[l]->padded) { - resPtr += 0x02; - } + + if (options->animationResults[l]->padded) resPtr += 0x02; } KBNAContents[j + (k * 8)] = resPtr & 0xff; KBNAContents[j + (k * 8) + 1] = (resPtr >> 8) & 0xff; @@ -2198,62 +2390,161 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { } int resPtrCounter = j; - for (int k = 0; k < options->resultCount; k++) { - switch (options->animationResults[k]->resultType) { - case 0: - KBNAContents[resPtrCounter] = options->animationResults[k]->index & 0xff; - KBNAContents[resPtrCounter + 1] = options->animationResults[k]->index >> 8; - resPtrCounter += 0x2; - break; + for (int k = 0; k < options->resultCount; k++) + { + switch (options->animationResults[k]->resultType) + { + case 0: + KBNAContents[resPtrCounter] = options->animationResults[k]->index & 0xff; + KBNAContents[resPtrCounter + 1] = options->animationResults[k]->index >> 8; + resPtrCounter += 0x2; + break; - case 1: - KBNAContents[resPtrCounter] = options->animationResults[k]->dataSrt.index & 0xff; - KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataSrt.index >> 8; - KBNAContents[resPtrCounter + 2] = options->animationResults[k]->dataSrt.rotation & 0xff; - KBNAContents[resPtrCounter + 3] = options->animationResults[k]->dataSrt.rotation >> 8; - KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataSrt.scaleX & 0xff; - KBNAContents[resPtrCounter + 5] = (options->animationResults[k]->dataSrt.scaleX >> 8) & 0xff; - KBNAContents[resPtrCounter + 6] = (options->animationResults[k]->dataSrt.scaleX >> 16) & 0xff; - KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataSrt.scaleX >> 24; - KBNAContents[resPtrCounter + 8] = options->animationResults[k]->dataSrt.scaleY & 0xff; - KBNAContents[resPtrCounter + 9] = (options->animationResults[k]->dataSrt.scaleY >> 8) & 0xff; - KBNAContents[resPtrCounter + 10] = (options->animationResults[k]->dataSrt.scaleY >> 16) & 0xff; - KBNAContents[resPtrCounter + 11] = options->animationResults[k]->dataSrt.scaleY >> 24; - KBNAContents[resPtrCounter + 12] = options->animationResults[k]->dataSrt.positionX & 0xff; - KBNAContents[resPtrCounter + 13] = options->animationResults[k]->dataSrt.positionX >> 8; - KBNAContents[resPtrCounter + 14] = options->animationResults[k]->dataSrt.positionY & 0xff; - KBNAContents[resPtrCounter + 15] = options->animationResults[k]->dataSrt.positionY >> 8; - resPtrCounter += 0x10; - break; + case 1: + KBNAContents[resPtrCounter] = options->animationResults[k]->dataSrt.index & 0xff; + KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataSrt.index >> 8; + KBNAContents[resPtrCounter + 2] = options->animationResults[k]->dataSrt.rotation & 0xff; + KBNAContents[resPtrCounter + 3] = options->animationResults[k]->dataSrt.rotation >> 8; + KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataSrt.scaleX & 0xff; + KBNAContents[resPtrCounter + 5] = (options->animationResults[k]->dataSrt.scaleX >> 8) & 0xff; + KBNAContents[resPtrCounter + 6] = (options->animationResults[k]->dataSrt.scaleX >> 16) & 0xff; + KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataSrt.scaleX >> 24; + KBNAContents[resPtrCounter + 8] = options->animationResults[k]->dataSrt.scaleY & 0xff; + KBNAContents[resPtrCounter + 9] = (options->animationResults[k]->dataSrt.scaleY >> 8) & 0xff; + KBNAContents[resPtrCounter + 10] = (options->animationResults[k]->dataSrt.scaleY >> 16) & 0xff; + KBNAContents[resPtrCounter + 11] = options->animationResults[k]->dataSrt.scaleY >> 24; + KBNAContents[resPtrCounter + 12] = options->animationResults[k]->dataSrt.positionX & 0xff; + KBNAContents[resPtrCounter + 13] = options->animationResults[k]->dataSrt.positionX >> 8; + KBNAContents[resPtrCounter + 14] = options->animationResults[k]->dataSrt.positionY & 0xff; + KBNAContents[resPtrCounter + 15] = options->animationResults[k]->dataSrt.positionY >> 8; + resPtrCounter += 0x10; + break; - case 2: - KBNAContents[resPtrCounter] = options->animationResults[k]->dataT.index & 0xff; - KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataT.index >> 8; - KBNAContents[resPtrCounter + 2] = 0xEF; - KBNAContents[resPtrCounter + 3] = 0xBE; - KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataT.positionX & 0xff; - KBNAContents[resPtrCounter + 5] = options->animationResults[k]->dataT.positionX >> 8; - KBNAContents[resPtrCounter + 6] = options->animationResults[k]->dataT.positionY & 0xff; - KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataT.positionY >> 8; - resPtrCounter += 0x8; - break; + case 2: + KBNAContents[resPtrCounter] = options->animationResults[k]->dataT.index & 0xff; + KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataT.index >> 8; + KBNAContents[resPtrCounter + 2] = 0xEF; + KBNAContents[resPtrCounter + 3] = 0xBE; + KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataT.positionX & 0xff; + KBNAContents[resPtrCounter + 5] = options->animationResults[k]->dataT.positionX >> 8; + KBNAContents[resPtrCounter + 6] = options->animationResults[k]->dataT.positionY & 0xff; + KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataT.positionY >> 8; + resPtrCounter += 0x8; + break; } // use the `padded` flag which was stored earlier to inject padding - if (options->animationResults[k]->padded) { + if (options->animationResults[k]->padded) + { KBNAContents[resPtrCounter] = 0xCC; KBNAContents[resPtrCounter + 1] = 0xCC; resPtrCounter += 0x2; } } + // UAAT data + if (options->uaatEnabled) + { + int offset = uaatOffset - 0x18; + + // UAAT magic + strcpy((char *) (KBNAContents + offset), "TAAU"); + offset += 0x04; + + // uaat size + KBNAContents[offset] = uaatSize & 0xFF; + KBNAContents[offset + 1] = (uaatSize >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatSize >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatSize >> 24) & 0xFF; + offset += 0x04; + + // num sequences + KBNAContents[offset] = options->sequenceCount & 0xFF; + KBNAContents[offset + 1] = (options->sequenceCount >> 8) & 0xFF; + offset += 0x02; + + // num attributes per frame + KBNAContents[offset] = 0x01; + offset += 0x02; + + // fixed offset 0x08 + KBNAContents[offset] = 0x08; + offset += 0x04; + + unsigned int uaatSinglePointer = 0x08 + 0x0c * options->sequenceCount + 0x04 * options->frameCount; + unsigned int uaatDoublePointer = 0x08 + 0x0c * options->sequenceCount; + for (int i = 0; i < options->sequenceCount; i++) + { + // frame count in this sequence + KBNAContents[offset] = options->sequenceData[i]->frameCount & 0xFF; + KBNAContents[offset + 1] = (options->sequenceData[i]->frameCount >> 8) & 0xFF; + offset += 0x02; + + // 0xBEEF + KBNAContents[offset] = 0xEF; + KBNAContents[offset + 1] = 0xBE; + offset += 0x02; + + // sequence attributes * + KBNAContents[offset] = uaatSinglePointer & 0xFF; + KBNAContents[offset + 1] = (uaatSinglePointer >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatSinglePointer >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatSinglePointer >> 24) & 0xFF; + offset += 0x04; + uaatSinglePointer += 0x04; + + // frame attributes ** + KBNAContents[offset] = uaatDoublePointer & 0xFF; + KBNAContents[offset + 1] = (uaatDoublePointer >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatDoublePointer >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatDoublePointer >> 24) & 0xFF; + offset += 0x04; + uaatDoublePointer += options->sequenceData[i]->frameCount * 0x04; + } + + for (int i = 0; i < options->frameCount; i++) + { + // frame attributes * + KBNAContents[offset] = uaatSinglePointer & 0xFF; + KBNAContents[offset + 1] = (uaatSinglePointer >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatSinglePointer >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatSinglePointer >> 24) & 0xFF; + offset += 0x04; + uaatSinglePointer += 0x04; + } + + for (int i = 0; i < options->sequenceCount; i++) + { + // sequence attributes + unsigned int uaatSequenceAttribute = options->uaatData.sequenceAttributes[i]; + KBNAContents[offset] = uaatSequenceAttribute & 0xFF; + KBNAContents[offset + 1] = (uaatSequenceAttribute >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatSequenceAttribute >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatSequenceAttribute >> 24) & 0xFF; + offset += 0x04; + } + + for (int i = 0; i < options->frameCount; i++) + { + // frame attributes + unsigned int uaatFrameAttribute = options->uaatData.frameAttributes[i]; + KBNAContents[offset] = uaatFrameAttribute & 0xFF; + KBNAContents[offset + 1] = (uaatFrameAttribute >> 8) & 0xFF; + KBNAContents[offset + 2] = (uaatFrameAttribute >> 16) & 0xFF; + KBNAContents[offset + 3] = (uaatFrameAttribute >> 24) & 0xFF; + offset += 0x04; + } + } + fwrite(KBNAContents, 1, contentsSize, fp); free(KBNAContents); - if (options->labelEnabled) { + if (options->labelEnabled) + { unsigned int lablSize = 8; - for (int j = 0; j < options->labelCount; j++) { + for (int j = 0; j < options->labelCount; j++) + { lablSize += (unsigned)strlen(options->labels[j]) + 5; } @@ -2261,14 +2552,15 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { memset(labl, 0, lablSize); - strcpy((char *)labl, "LBAL"); + strcpy((char *) labl, "LBAL"); labl[4] = lablSize & 0xff; labl[5] = lablSize >> 8; unsigned int position = 0; i = 0; - for (int j = 0; j < options->labelCount; j++) { + for (int j = 0; j < options->labelCount; j++) + { labl[i + 8] = position & 0xff; labl[i + 9] = position >> 8; @@ -2276,8 +2568,9 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { i += 4; } - for (int j = 0; j < options->labelCount; j++) { - strcpy((char *)(labl + (i + 8)), options->labels[j]); + for (int j = 0; j < options->labelCount; j++) + { + strcpy((char *) (labl + (i + 8)), options->labels[j]); i += (int)strlen(options->labels[j]) + 1; } @@ -2285,8 +2578,9 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { free(labl); - if (!options->multiCell) { - unsigned char txeu[0xc] = { 0x54, 0x58, 0x45, 0x55, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(!options->multiCell) + { + unsigned char txeu[0xc] = {0x54, 0x58, 0x45, 0x55, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; fwrite(txeu, 1, 0xc, fp); } diff --git a/tools/nitrogfx/gfx.h b/tools/nitrogfx/gfx.h index b68bc0d31..a2ec65d5c 100644 --- a/tools/nitrogfx/gfx.h +++ b/tools/nitrogfx/gfx.h @@ -1,29 +1,28 @@ -// Copyright (c) 2015 YamaArashi, 2021-2024 red031000 +// Copyright (c) 2015 YamaArashi, 2021-2025 red031000 #ifndef GFX_H #define GFX_H -#include #include - +#include #include "options.h" struct Color { - unsigned char red; - unsigned char green; - unsigned char blue; + unsigned char red; + unsigned char green; + unsigned char blue; }; struct Palette { - struct Color colors[16 * 256]; - int numColors; - int bitDepth; + struct Color colors[256]; + int numColors; + int bitDepth; }; struct Image { - int width; - int height; - int bitDepth; + int width; + int height; + int bitDepth; /** * Pseudocode for converting index in pixels to coordinates in image is as follows * (where (0, 0) is the top left corner with the format (x, y) ): @@ -45,33 +44,22 @@ struct Image { * pixel = pixels[i] * pixel coordinates: (xCoord, yCoord) */ - unsigned char *pixels; - bool hasPalette; - struct Palette palette; - bool hasTransparency; - bool pixelsAreRGB; -}; - -struct NSCRFile { - uint16_t scrnWidth; // output width in pixels - uint16_t scrnHeight; // output height in pixels - uint16_t colorMode; // 4bpp or 8bpp - uint16_t scrnMode; // text, affine, or extpltt - uint32_t scrnSize; // total data length - uint8_t data[]; // data + unsigned char *pixels; + bool hasPalette; + struct Palette palette; + bool hasTransparency; }; void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors); -uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool scanFrontToBack); -void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG); -void ApplyScrnToImage(char *scrnFilePath, struct Image *image); +uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, uint32_t encodeMode, bool convertTo8Bpp, int palIndex, bool verbose); +void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG, bool snap); void WriteImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors); -void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, uint32_t scanMode, uint32_t mappingType, uint32_t key, bool wrongSize); +void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, bool scan, uint32_t encodeMode, uint32_t mappingType, uint32_t key, bool wrongSize, bool convertTo4Bpp); void FreeImage(struct Image *image); void ReadGbaPalette(char *path, struct Palette *palette); -void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted); +void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted, bool convertTo8Bpp); 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, bool inverted); +void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, int pcmpStartIndex, bool inverted, bool convertTo4Bpp); void ReadNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrScreen(char *path, struct JsonToScreenOptions *options); diff --git a/tools/nitrogfx/global.h b/tools/nitrogfx/global.h index 9c81a39f5..32378a9ff 100644 --- a/tools/nitrogfx/global.h +++ b/tools/nitrogfx/global.h @@ -8,37 +8,30 @@ #ifdef _MSC_VER -#define FATAL_ERROR(format, ...) \ - do { \ - fprintf(stderr, format, __VA_ARGS__); \ - exit(1); \ - } while (0) +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, __VA_ARGS__); \ + exit(1); \ +} while (0) #define UNUSED #else -#define FATAL_ERROR(format, ...) \ - do { \ - fprintf(stderr, format, ##__VA_ARGS__); \ - fprintf(stderr, " -- %s:%d\n", __FILE__, __LINE__); \ - abort(); \ - } while (0) +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, ##__VA_ARGS__); \ + exit(1); \ +} while (0) #define UNUSED __attribute__((__unused__)) #endif // _MSC_VER -#define PTR_ADD(ptr, value) ((void *)((uintptr_t)(ptr) + (value))) -#define PTR_SUB(ptr, value) ((void *)((uintptr_t)(ptr) - (value))) -#define PTR_IADD(ptr, value) \ - do { \ - (ptr) = PTR_ADD(ptr, value); \ - } while (0) -#define PTR_ISUB(ptr, value) \ - do { \ - (ptr) = PTR_SUB(ptr, value); \ - } while (0) +#define PTR_ADD(ptr, value) ((void*)((uintptr_t)(ptr) + (value))) +#define PTR_SUB(ptr, value) ((void*)((uintptr_t)(ptr) - (value))) +#define PTR_IADD(ptr, value) do { (ptr) = PTR_ADD(ptr, value); } while (0) +#define PTR_ISUB(ptr, value) do { (ptr) = PTR_SUB(ptr, value); } while (0) #define PTR_DIFF(right, left) ((uintptr_t)(right) - (uintptr_t)(left)) #endif // GLOBAL_H diff --git a/tools/nitrogfx/json.c b/tools/nitrogfx/json.c index 09bb07d3a..0f9c2394b 100644 --- a/tools/nitrogfx/json.c +++ b/tools/nitrogfx/json.c @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 red031000 +// Copyright (c) 2021-2025 red031000 #include "global.h" #include "cJSON.h" @@ -47,13 +47,17 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled"); + cJSON *dontPadKbecBool = cJSON_GetObjectItemCaseSensitive(json, "dontPadKbec"); cJSON *vramTransferBool = cJSON_GetObjectItemCaseSensitive(json, "vramTransferEnabled"); + cJSON *ucatBool = cJSON_GetObjectItemCaseSensitive(json, "ucatEnabled"); cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended"); cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount"); cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType"); options->labelEnabled = GetBool(labelBool); + options->dontPadKbec = GetBool(dontPadKbecBool); options->vramTransferEnabled = GetBool(vramTransferBool); + options->ucatEnabled = GetBool(ucatBool); options->extended = GetBool(extended); options->cellCount = GetInt(cellCount); options->mappingType = GetInt(mappingType); @@ -79,17 +83,19 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } } - if (options->vramTransferEnabled) { + 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_ArrayForEach(transfer, transfers) + { cJSON *vramTransferOffset = cJSON_GetObjectItemCaseSensitive(transfer, "offset"); cJSON *vramTransferSize = cJSON_GetObjectItemCaseSensitive(transfer, "size"); @@ -101,6 +107,16 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } } + if (options->ucatEnabled) { + cJSON *ucatCells = cJSON_GetObjectItemCaseSensitive(json, "cellAttributes"); + + options->ucatCellAttribtes = malloc(sizeof(uint32_t) * options->cellCount); + + for (int i = 0; i < options->cellCount; i++) { + options->ucatCellAttribtes[i] = GetInt(cJSON_GetArrayItem(ucatCells, i)); + } + } + for (int i = 0; i < options->cellCount; i++) { options->cells[i] = malloc(sizeof(struct Cell)); @@ -172,7 +188,11 @@ struct JsonToCellOptions *ParseNCERJson(char *path) cJSON *Colours = cJSON_GetObjectItemCaseSensitive(Attr0, "Colours"); cJSON *Shape = cJSON_GetObjectItemCaseSensitive(Attr0, "Shape"); - options->cells[i]->oam[j].attr0.YCoordinate = GetInt(YCoordinate); + int y = GetInt(YCoordinate); + if (y & (1 << 7)) { + y &= 0xFF; + } + options->cells[i]->oam[j].attr0.YCoordinate = y; options->cells[i]->oam[j].attr0.Rotation = GetBool(Rotation); options->cells[i]->oam[j].attr0.SizeDisable = GetBool(SizeDisable); options->cells[i]->oam[j].attr0.Mode = GetInt(Mode); @@ -187,7 +207,11 @@ struct JsonToCellOptions *ParseNCERJson(char *path) cJSON *RotationScaling = cJSON_GetObjectItemCaseSensitive(Attr1, "RotationScaling"); cJSON *Size = cJSON_GetObjectItemCaseSensitive(Attr1, "Size"); - options->cells[i]->oam[j].attr1.XCoordinate = GetInt(XCoordinate); + int x = GetInt(XCoordinate); + if (x & (1 << 8)) { + x &= 0x1FF; + } + options->cells[i]->oam[j].attr1.XCoordinate = x; options->cells[i]->oam[j].attr1.RotationScaling = GetInt(RotationScaling); options->cells[i]->oam[j].attr1.Size = GetInt(Size); @@ -218,8 +242,10 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON *ncer = cJSON_CreateObject(); cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled); + cJSON_AddBoolToObject(ncer, "dontPadKbec", options->dontPadKbec); cJSON_AddBoolToObject(ncer, "extended", options->extended); cJSON_AddBoolToObject(ncer, "vramTransferEnabled", options->vramTransferEnabled); + cJSON_AddBoolToObject(ncer, "ucatEnabled", options->ucatEnabled); cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount); cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType); @@ -255,7 +281,11 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON *Attr0 = cJSON_AddObjectToObject(OAM, "Attr0"); - cJSON_AddNumberToObject(Attr0, "YCoordinate", options->cells[i]->oam[j].attr0.YCoordinate); + int y = options->cells[i]->oam[j].attr0.YCoordinate; + if (y & (1 << 7)) { + y |= ~0xFF; + } + cJSON_AddNumberToObject(Attr0, "YCoordinate", y); cJSON_AddBoolToObject(Attr0, "Rotation", options->cells[i]->oam[j].attr0.Rotation); cJSON_AddBoolToObject(Attr0, "SizeDisable", options->cells[i]->oam[j].attr0.SizeDisable); cJSON_AddNumberToObject(Attr0, "Mode", options->cells[i]->oam[j].attr0.Mode); @@ -265,7 +295,11 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON *Attr1 = cJSON_AddObjectToObject(OAM, "Attr1"); - cJSON_AddNumberToObject(Attr1, "XCoordinate", options->cells[i]->oam[j].attr1.XCoordinate); + int x = options->cells[i]->oam[j].attr1.XCoordinate; + if (x & (1 << 8)) { + x |= ~0x1FF; + } + cJSON_AddNumberToObject(Attr1, "XCoordinate", x); cJSON_AddNumberToObject(Attr1, "RotationScaling", options->cells[i]->oam[j].attr1.RotationScaling); cJSON_AddNumberToObject(Attr1, "Size", options->cells[i]->oam[j].attr1.Size); @@ -288,11 +322,13 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount); } - if (options->vramTransferEnabled) { + if (options->vramTransferEnabled) + { cJSON_AddNumberToObject(ncer, "vramTransferMaxSize", options->vramTransferMaxSize); cJSON *transfers = cJSON_AddArrayToObject(ncer, "transferData"); - for (int idx = 0; idx < options->cellCount; idx++) { + 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); @@ -300,6 +336,14 @@ char *GetNCERJson(struct JsonToCellOptions *options) } } + if (options->ucatEnabled) { + cJSON *ucatCells = cJSON_AddArrayToObject(ncer, "cellAttributes"); + + for (int i = 0; i < options->cellCount; i++) { + cJSON_AddNumberToObject(ucatCells, "cellAttr", options->ucatCellAttribtes[i]); + } + } + char *jsonString = cJSON_Print(ncer); cJSON_Delete(ncer); return jsonString; @@ -473,6 +517,9 @@ struct JsonToAnimationOptions *ParseNANRJson(char *path) if (i > options->resultCount - 1) FATAL_ERROR("Frame count is incorrect.\n"); + // init padding to false, this is used in gfx.c to control padding, and is therefore checked there + options->animationResults[i]->padded = false; + cJSON *resultType = cJSON_GetObjectItemCaseSensitive(animationResult, "resultType"); options->animationResults[i]->resultType = GetInt(resultType); switch (options->animationResults[i]->resultType) { @@ -538,6 +585,27 @@ struct JsonToAnimationOptions *ParseNANRJson(char *path) } } + cJSON *uaatBool = cJSON_GetObjectItemCaseSensitive(json, "uaatEnabled"); + options->uaatEnabled = GetBool(uaatBool); + + if (options->uaatEnabled) { + cJSON *uaatData = cJSON_GetObjectItemCaseSensitive(json, "uaatData"); + + cJSON *uaatSequences = cJSON_GetObjectItemCaseSensitive(uaatData, "sequenceAttributes"); + options->uaatData.sequenceAttributes = malloc(sizeof(uint32_t) * options->sequenceCount); + for (int i = 0; i < options->sequenceCount; i++) { + cJSON *uaatSeq = cJSON_GetArrayItem(uaatSequences, i); + options->uaatData.sequenceAttributes[i] = GetInt(uaatSeq); + } + + cJSON *uaatFrames = cJSON_GetObjectItemCaseSensitive(uaatData, "frameAttributes"); + options->uaatData.frameAttributes = malloc(sizeof(uint32_t) * options->frameCount); + for (int i = 0; i < options->frameCount; i++) { + cJSON *uaatFra = cJSON_GetArrayItem(uaatFrames, i); + options->uaatData.frameAttributes[i] = GetInt(uaatFra); + } + } + cJSON_Delete(json); free(jsonString); return options; @@ -548,6 +616,7 @@ char *GetNANRJson(struct JsonToAnimationOptions *options) cJSON *nanr = cJSON_CreateObject(); cJSON_AddBoolToObject(nanr, "labelEnabled", options->labelEnabled); + cJSON_AddBoolToObject(nanr, "uaatEnabled", options->uaatEnabled); cJSON_AddNumberToObject(nanr, "sequenceCount", options->sequenceCount); cJSON_AddNumberToObject(nanr, "frameCount", options->frameCount); @@ -617,6 +686,20 @@ char *GetNANRJson(struct JsonToAnimationOptions *options) cJSON_AddNumberToObject(nanr, "labelCount", options->labelCount); } + if (options->uaatEnabled) { + cJSON *uaat = cJSON_AddObjectToObject(nanr, "uaatData"); + + cJSON *uaatSequences = cJSON_AddArrayToObject(uaat, "sequenceAttributes"); + for (int i = 0; i < options->sequenceCount; i++) { + cJSON_AddNumberToObject(uaatSequences, "seqAttr", options->uaatData.sequenceAttributes[i]); + } + + cJSON *uaatFrames = cJSON_AddArrayToObject(uaat, "frameAttributes"); + for (int i = 0; i < options->frameCount; i++) { + cJSON_AddNumberToObject(uaatFrames, "fraAttr", options->uaatData.frameAttributes[i]); + } + } + char *jsonString = cJSON_Print(nanr); cJSON_Delete(nanr); return jsonString; @@ -637,12 +720,17 @@ void FreeNCERCell(struct JsonToCellOptions *options) } free(options->labels); } - if (options->vramTransferEnabled) { - for (int j = 0; j < options->cellCount; j++) { + if (options->vramTransferEnabled) + { + for (int j = 0; j < options->cellCount; j++) + { free(options->transferData[j]); } free(options->transferData); } + if (options->ucatEnabled) { + free(options->ucatCellAttribtes); + } free(options->cells); free(options); } @@ -669,6 +757,10 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options) { free(options->animationResults[i]); } + if (options->uaatEnabled) { + free(options->uaatData.sequenceAttributes); + free(options->uaatData.frameAttributes); + } if (options->labelEnabled) { for (int j = 0; j < options->labelCount; j++) @@ -682,14 +774,16 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options) free(options); } -char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata) { +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++) { + for (int i = 0; i < metadata->numGlyphs; i++) + { cJSON *width = cJSON_CreateNumber(metadata->glyphWidthTable[i]); cJSON_AddItemToArray(glyphWidths, width); } @@ -699,18 +793,20 @@ char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata) { return jsonString; } -#define TILE_DIMENSION_PIXELS 8 +#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 +#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) { +struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path) +{ int fileLength; unsigned char *jsonString = ReadWholeFile(path, &fileLength); cJSON *json = cJSON_Parse((const char *)jsonString); - if (json == NULL) { + if (json == NULL) + { const char *errorPtr = cJSON_GetErrorPtr(); FATAL_ERROR("Error in line \"%s\"\n", errorPtr); } @@ -737,8 +833,10 @@ struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path) { uint8_t *glyphWidthCursor = metadata->glyphWidthTable; cJSON *glyphWidthIter = NULL; - cJSON_ArrayForEach(glyphWidthIter, labelGlyphWidths) { - if (!cJSON_IsNumber(glyphWidthIter)) { + cJSON_ArrayForEach(glyphWidthIter, labelGlyphWidths) + { + if (!cJSON_IsNumber(glyphWidthIter)) + { const char *errorPtr = cJSON_GetErrorPtr(); FATAL_ERROR("Error in line \"%s\"\n", errorPtr); } diff --git a/tools/nitrogfx/json.h b/tools/nitrogfx/json.h index 8bdf307ff..78a55a341 100644 --- a/tools/nitrogfx/json.h +++ b/tools/nitrogfx/json.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 red031000 +// Copyright (c) 2021-2025 red031000 #ifndef JSON_H #define JSON_H diff --git a/tools/nitrogfx/lz.c b/tools/nitrogfx/lz.c index fd75ac867..291d78ed8 100644 --- a/tools/nitrogfx/lz.c +++ b/tools/nitrogfx/lz.c @@ -1,69 +1,39 @@ // Copyright (c) 2015 YamaArashi -#include "lz.h" - -#include #include #include +#include #include - #include "global.h" +#include "lz.h" -static inline int getBlockSize(unsigned char *src, int *srcPos, int extFormat) { - int blockSize = (src[*srcPos] >> 4); - if (!extFormat) { - blockSize += 3; - } else { - if (blockSize > 1) { - blockSize += 1; - } else { - int isWide = (blockSize == 1 ? 1 : 0); - blockSize = (src[(*srcPos)++] & 0xF) << 4; - if (isWide) { - blockSize = (blockSize << 8) + (src[(*srcPos)++] << 4) + 0x100; - } - blockSize += 0x11 + (src[*srcPos] >> 4); - } - } - return blockSize; -} - -unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize) { - if (srcSize < 4) { +unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize) +{ + if (srcSize < 4) goto fail; - } - - if ((src[0] & 0xF0) != 0x10) { - goto fail; - } int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; unsigned char *dest = malloc(destSize); - if (dest == NULL) { + if (dest == NULL) goto fail; - } int srcPos = 4; int destPos = 0; - int extFormat = src[0] & 0xF ? 1 : 0; - for (;;) { - if (srcPos >= srcSize) { + if (srcPos >= srcSize) goto fail; - } unsigned char flags = src[srcPos++]; for (int i = 0; i < 8; i++) { if (flags & 0x80) { - if (srcPos + 1 >= srcSize) { + if (srcPos + 1 >= srcSize) goto fail; - } - int blockSize = getBlockSize(src, &srcPos, extFormat); + int blockSize = (src[srcPos] >> 4) + 3; int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1; srcPos += 2; @@ -76,17 +46,14 @@ unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSi fprintf(stderr, "Destination buffer overflow.\n"); } - if (blockPos < 0) { + if (blockPos < 0) goto fail; - } - for (int j = 0; j < blockSize; j++) { + for (int j = 0; j < blockSize; j++) dest[destPos++] = dest[blockPos + j]; - } } else { - if (srcPos >= srcSize || destPos >= destSize) { + if (srcPos >= srcSize || destPos >= destSize) goto fail; - } dest[destPos++] = src[srcPos++]; } @@ -104,66 +71,60 @@ fail: FATAL_ERROR("Fatal error while decompressing LZ file.\n"); } -static void FindBestBlockForwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize, bool extFormat) { +static void FindBestBlockForwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize) +{ int blockStart = srcPos < 0x1000 ? 0 : srcPos - 0x1000; - int maxBlockSize = extFormat ? 0xF210 : 18; - while (blockStart != srcPos) { int blockSize = 0; - while (blockSize < maxBlockSize + while (blockSize < 18 && srcPos + blockSize < srcSize - && src[blockStart + blockSize] == src[srcPos + blockSize]) { + && src[blockStart + blockSize] == src[srcPos + blockSize]) blockSize++; - } if (blockSize > *outBestBlockSize && srcPos - blockStart >= minDistance) { *outBestBlockDistance = srcPos - blockStart; *outBestBlockSize = blockSize; - if (blockSize == maxBlockSize) { + if (blockSize == 18) break; - } } blockStart++; } } -static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize, bool extFormat) { +static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize) +{ int blockDistance = minDistance; - int maxBlockSize = extFormat ? 0xF210 : 18; while (blockDistance <= srcPos && blockDistance <= 0x1000) { int blockStart = srcPos - blockDistance; int blockSize = 0; - while (blockSize < maxBlockSize + while (blockSize < 18 && srcPos + blockSize < srcSize - && src[blockStart + blockSize] == src[srcPos + blockSize]) { + && src[blockStart + blockSize] == src[srcPos + blockSize]) blockSize++; - } if (blockSize > *outBestBlockSize) { *outBestBlockDistance = blockDistance; *outBestBlockSize = blockSize; - if (blockSize == 18) { + if (blockSize == 18) break; - } } blockDistance++; } } -typedef void (*FindBestBlockFunc)(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize, bool extFormat); +typedef void (*FindBestBlockFunc)(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize); -unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration, bool pad, bool extFormat) { - if (srcSize <= 0) { +unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration, bool pad) { + if (srcSize <= 0) goto fail; - } int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8); @@ -172,12 +133,11 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, unsigned char *dest = malloc(worstCaseDestSize); - if (dest == NULL) { + if (dest == NULL) goto fail; - } // header - dest[0] = 0x10 | extFormat; // LZ compression type + dest[0] = 0x10; // LZ compression type dest[1] = (unsigned char)srcSize; dest[2] = (unsigned char)(srcSize >> 8); dest[3] = (unsigned char)(srcSize >> 16); @@ -194,48 +154,19 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, int bestBlockDistance = 0; int bestBlockSize = 0; - FindBestBlock(src, srcPos, srcSize, minDistance, &bestBlockDistance, &bestBlockSize, extFormat); + FindBestBlock(src, srcPos, srcSize, minDistance, &bestBlockDistance, &bestBlockSize); - if (extFormat) { - if (bestBlockSize >= 272) { - *flags |= (0x80 >> i); - srcPos += bestBlockSize; - bestBlockSize -= 273; - bestBlockDistance--; - dest[destPos++] = ((bestBlockSize >> 12) & 0xF) | 0x10; - dest[destPos++] = bestBlockSize >> 4; - dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8); - dest[destPos++] = (unsigned char)bestBlockDistance; - } else if (bestBlockSize >= 17) { - *flags |= (0x80 >> i); - srcPos += bestBlockSize; - bestBlockSize -= 17; - bestBlockDistance--; - dest[destPos++] = (bestBlockSize >> 4) & 0xF; - dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8); - dest[destPos++] = (unsigned char)bestBlockDistance; - } else if (bestBlockSize >= 3) { - *flags |= (0x80 >> i); - srcPos += bestBlockSize; - bestBlockSize -= 1; - bestBlockDistance--; - dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8); - dest[destPos++] = (unsigned char)bestBlockDistance; - } else { - dest[destPos++] = src[srcPos++]; - } + 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 { - 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++]; - } + dest[destPos++] = src[srcPos++]; } + if (srcPos == srcSize) { if (pad) { // Pad to multiple of 4 bytes. @@ -247,6 +178,7 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, } } } + *compressedSize = destPos; return dest; } diff --git a/tools/nitrogfx/lz.h b/tools/nitrogfx/lz.h index f8ea90e29..21f18a829 100644 --- a/tools/nitrogfx/lz.h +++ b/tools/nitrogfx/lz.h @@ -6,6 +6,6 @@ #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, bool forwardIteration, bool pad, bool extFormat); +unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration, bool pad); #endif // LZ_H diff --git a/tools/nitrogfx/main.c b/tools/nitrogfx/main.c index f69ded9f8..dc732271a 100644 --- a/tools/nitrogfx/main.c +++ b/tools/nitrogfx/main.c @@ -1,43 +1,45 @@ -// Copyright (c) 2015 YamaArashi, 2021-2024 red031000 +// Copyright (c) 2015 YamaArashi, 2021-2025 red031000 #include -#include #include #include - +#include #include "global.h" - -#include "convert_png.h" -#include "font.h" -#include "gfx.h" -#include "huff.h" -#include "jasc_pal.h" -#include "json.h" -#include "lz.h" -#include "options.h" -#include "rl.h" #include "util.h" +#include "options.h" +#include "gfx.h" +#include "convert_png.h" +#include "jasc_pal.h" +#include "lz.h" +#include "rl.h" +#include "font.h" +#include "huff.h" +#include "json.h" -struct CommandHandler { +struct CommandHandler +{ const char *inputFileExtension; const char *outputFileExtension; - void (*function)(char *inputPath, char *outputPath, int argc, char **argv); + void(*function)(char *inputPath, char *outputPath, int argc, char **argv); }; static int CountLzCompressArgs(int argc, char **argv); static void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv); static void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED); -void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *options) { +void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *options) +{ struct Image image; - if (options->paletteFilePath != NULL) { + if (options->paletteFilePath != NULL) + { ReadGbaPalette(options->paletteFilePath, &image.palette); image.hasPalette = true; - } else { + } + else + { image.hasPalette = false; } - image.pixelsAreRGB = false; ReadImage(inputPath, options->width, options->bitDepth, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette); @@ -48,15 +50,19 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions * FreeImage(&image); } -void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *options) { +void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *options) +{ // handle empty files if possible - if (options->handleEmpty) { + if (options->handleEmpty) + { FILE *fp = fopen(inputPath, "rb"); - if (fp != NULL) { + if (fp != NULL) + { fseek(fp, 0, SEEK_END); uint32_t size = ftell(fp); rewind(fp); - if (size == 0) { + if (size == 0) + { FILE *out = fopen(outputPath, "wb+"); if (out != NULL) { fclose(out); @@ -70,26 +76,25 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions * struct Image image; - if (options->paletteFilePath != NULL) { - ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false); + if (options->paletteFilePath != NULL) + { + ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false, options->convertTo8Bpp); image.hasPalette = true; - } else { + } + else + { image.hasPalette = false; } - image.pixelsAreRGB = false; - uint32_t key = ReadNtrImage(inputPath, options->width, 0, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->scanFrontToBack); - if (options->scrnFilePath == NULL && options->cellFilePath == NULL && image.bitDepth == 4) { - image.palette.numColors = 16; - } + uint32_t key = ReadNtrImage(inputPath, options->width, 0, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->encodeMode, options->convertTo8Bpp, options->palIndex, options->verbose); - if (key) { - char *string = malloc(strlen(outputPath) + 5); + if (key) + { + char* string = malloc(strlen(outputPath) + 5); sprintf(string, "%s.key", outputPath); FILE *fp = fopen(string, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open key file for writing.\n"); - } fwrite(&key, 4, 1, fp); fclose(fp); free(string); @@ -98,9 +103,7 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions * image.hasTransparency = options->hasTransparency; if (options->cellFilePath != NULL) { - ApplyCellsToImage(options->cellFilePath, &image, true); - } else if (options->scrnFilePath != NULL) { - ApplyScrnToImage(options->scrnFilePath, &image); + ApplyCellsToImage(options->cellFilePath, &image, true, options->cellSnap); } WritePng(outputPath, &image); @@ -108,7 +111,8 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions * FreeImage(&image); } -void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *options) { +void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *options) +{ struct Image image; image.bitDepth = options->bitDepth; @@ -120,15 +124,19 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions * FreeImage(&image); } -void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *options) { +void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *options) +{ // handle empty files if possible - if (options->handleEmpty) { + if (options->handleEmpty) + { FILE *fp = fopen(inputPath, "rb"); - if (fp != NULL) { + if (fp != NULL) + { fseek(fp, 0, SEEK_END); uint32_t size = ftell(fp); rewind(fp); - if (size == 0) { + if (size == 0) + { FILE *out = fopen(outputPath, "wb+"); if (out != NULL) { fclose(out); @@ -147,17 +155,16 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions * ReadPng(inputPath, &image); uint32_t key = 0; - if (options->scanMode) { - char *string = malloc(strlen(inputPath) + 5); + if (options->encodeMode) { + char* string = malloc(strlen(inputPath) + 5); sprintf(string, "%s.key", inputPath); FILE *fp = fopen(string, "rb"); if (fp == NULL) { FATAL_ERROR("Failed to open key file for reading.\n"); } size_t count = fread(&key, 4, 1, fp); - if (count != 1) { + if (count != 1) FATAL_ERROR("Not a valid key file.\n"); - } fclose(fp); free(string); } @@ -165,119 +172,120 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions * options->bitDepth = options->bitDepth == 0 ? image.bitDepth : options->bitDepth; if (options->cellFilePath != NULL) { - ApplyCellsToImage(options->cellFilePath, &image, false); - } else { - if (image.pixelsAreRGB || (image.bitDepth != 4 && image.bitDepth != 8)) { - FATAL_ERROR("PNG image %s has unsupported mapping type: bitDepth=%d and pixels %s RGB\n", inputPath, image.bitDepth, image.pixelsAreRGB ? "are" : "are not"); - } + ApplyCellsToImage(options->cellFilePath, &image, false, options->cellSnap); } - 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); + WriteNtrImage(outputPath, options->numTiles, options->bitDepth, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->clobberSize, options->byteOrder, options->version101, options->sopc, options->vramTransfer, options->scan, options->encodeMode, options->mappingType, key, options->wrongSize, options->convertTo4Bpp); FreeImage(&image); } -void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ char *inputFileExtension = GetFileExtension(inputPath); struct GbaToPngOptions options; options.paletteFilePath = NULL; - if (isdigit((unsigned char)inputFileExtension[0])) { + if (isdigit((unsigned char)inputFileExtension[0])) options.bitDepth = inputFileExtension[0] - '0'; - } else { + else options.bitDepth = 4; - } options.hasTransparency = false; options.width = 1; options.colsPerChunk = 1; options.rowsPerChunk = 1; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-palette") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-palette") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No palette file path following \"-palette\".\n"); - } i++; options.paletteFilePath = argv[i]; - } else if (strcmp(option, "-object") == 0) { + } + else if (strcmp(option, "-object") == 0) + { options.hasTransparency = true; - } else if (strcmp(option, "-width") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-width") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No width following \"-width\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.width)) { + if (!ParseNumber(argv[i], NULL, 10, &options.width)) FATAL_ERROR("Failed to parse width.\n"); - } - if (options.width < 1) { + if (options.width < 1) FATAL_ERROR("Width must be positive.\n"); - } - } else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No columns per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) FATAL_ERROR("Failed to parse columns per chunk.\n"); - } - if (options.colsPerChunk < 1) { + if (options.colsPerChunk < 1) FATAL_ERROR("columns per chunk must be positive.\n"); - } - } else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No rows per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) FATAL_ERROR("Failed to parse rows per chunk.\n"); - } - if (options.rowsPerChunk < 1) { + if (options.rowsPerChunk < 1) FATAL_ERROR("rows per chunk must be positive.\n"); - } - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } - if (options.colsPerChunk > options.width) { + if (options.colsPerChunk > options.width) options.width = options.colsPerChunk; - } ConvertGbaToPng(inputPath, outputPath, &options); } -void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ struct NtrToPngOptions options; options.paletteFilePath = NULL; options.cellFilePath = NULL; - options.scrnFilePath = NULL; + options.cellSnap = true; options.hasTransparency = false; options.width = 0; options.colsPerChunk = 1; options.rowsPerChunk = 1; options.palIndex = 1; - options.scanFrontToBack = false; options.handleEmpty = false; + options.encodeMode = 0; + options.convertTo8Bpp = false; + options.verbose = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-palette") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-palette") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No palette file path following \"-palette\".\n"); - } i++; @@ -290,87 +298,88 @@ void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **a i++; options.cellFilePath = argv[i]; - } else if (strcmp(option, "-scrn") == 0) { - if (i + 1 >= argc) { - FATAL_ERROR("No scrn file path following \"-scrn\".\n"); + + if (i + 1 < argc) { + if (strcmp(argv[i + 1], "-nosnap") == 0) { + options.cellSnap = false; + i++; + } } - - i++; - - options.scrnFilePath = argv[i]; } else if (strcmp(option, "-object") == 0) { options.hasTransparency = true; } else if (strcmp(option, "-palindex") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No palette index following \"-palindex\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.palIndex)) { + if (!ParseNumber(argv[i], NULL, 10, &options.palIndex)) FATAL_ERROR("Failed to parse palette index.\n"); - } - if (options.palIndex < 1) { + if (options.palIndex < 1) FATAL_ERROR("Palette index must be positive.\n"); - } } else if (strcmp(option, "-width") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No width following \"-width\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.width)) { + if (!ParseNumber(argv[i], NULL, 10, &options.width)) FATAL_ERROR("Failed to parse width.\n"); - } - if (options.width < 1) { + if (options.width < 1) FATAL_ERROR("Width must be positive.\n"); - } } else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No columns per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) FATAL_ERROR("Failed to parse columns per chunk.\n"); - } - if (options.colsPerChunk < 1) { + if (options.colsPerChunk < 1) FATAL_ERROR("columns per chunk must be positive.\n"); - } } else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No rows per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) FATAL_ERROR("Failed to parse rows per chunk.\n"); - } - if (options.rowsPerChunk < 1) { + if (options.rowsPerChunk < 1) FATAL_ERROR("rows per chunk must be positive.\n"); - } } else if (strcmp(option, "-scanfronttoback") == 0) { - options.scanFrontToBack = true; + // maintained for compatibility + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); + } + options.encodeMode = 2; + } else if (strcmp(option, "-encodebacktofront") == 0) { + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); + } + options.encodeMode = 1; + } else if (strcmp(option, "-encodefronttoback") == 0) { + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); + } + options.encodeMode = 2; } else if (strcmp(option, "-handleempty") == 0) { options.handleEmpty = true; + } else if (strcmp(option, "-convertTo8Bpp") == 0) { + options.convertTo8Bpp = true; + } else if (strcmp(option, "-verbose") == 0) { + options.verbose = true; } else { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } - if (options.width != 0 && options.colsPerChunk > options.width) { + if (options.width != 0 && options.colsPerChunk > options.width) options.width = options.colsPerChunk; - } - if (options.scrnFilePath != NULL) { - options.palIndex = 0; - } ConvertNtrToPng(inputPath, outputPath, &options); } @@ -381,66 +390,65 @@ void HandleNtrLzToPngCommand(char *inputPath, char *outputPath, int argc, char * HandleNtrToPngCommand(outputPath, outputPath, argc, argv); } -void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ char *outputFileExtension = GetFileExtension(outputPath); int bitDepth; - if (strcmp(outputFileExtension, "nbfc") == 0) { + if (strcmp(outputFileExtension, "nbfc") == 0) bitDepth = 4; - } else { + else bitDepth = outputFileExtension[0] - '0'; - } struct PngToGbaOptions options; options.numTiles = 0; options.bitDepth = bitDepth; options.colsPerChunk = 1; options.rowsPerChunk = 1; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-num_tiles") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-num_tiles") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No number of tiles following \"-num_tiles\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.numTiles)) { + if (!ParseNumber(argv[i], NULL, 10, &options.numTiles)) FATAL_ERROR("Failed to parse number of tiles.\n"); - } - if (options.numTiles < 1) { + if (options.numTiles < 1) FATAL_ERROR("Number of tiles must be positive.\n"); - } - } else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No columns per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) FATAL_ERROR("Failed to parse columns per chunk.\n"); - } - if (options.colsPerChunk < 1) { + if (options.colsPerChunk < 1) FATAL_ERROR("columns per chunk must be positive.\n"); - } - } else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No rows per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) FATAL_ERROR("Failed to parse rows per chunk.\n"); - } - if (options.rowsPerChunk < 1) { + if (options.rowsPerChunk < 1) FATAL_ERROR("rows per chunk must be positive.\n"); - } - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -448,9 +456,11 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a ConvertPngToGba(inputPath, outputPath, &options); } -void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ struct PngToNtrOptions options; options.cellFilePath = NULL; + options.cellSnap = true; options.numTiles = 0; options.bitDepth = 0; options.colsPerChunk = 1; @@ -460,28 +470,29 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a options.byteOrder = true; options.version101 = false; options.sopc = false; - options.scanMode = 0; + options.scan = false; options.handleEmpty = false; options.vramTransfer = false; options.mappingType = 0; + options.encodeMode = 0; + options.convertTo4Bpp = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-num_tiles") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-num_tiles") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No number of tiles following \"-num_tiles\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.numTiles)) { + if (!ParseNumber(argv[i], NULL, 10, &options.numTiles)) FATAL_ERROR("Failed to parse number of tiles.\n"); - } - if (options.numTiles < 1) { + if (options.numTiles < 1) FATAL_ERROR("Number of tiles must be positive.\n"); - } } else if (strcmp(option, "-cell") == 0) { if (i + 1 >= argc) { FATAL_ERROR("No cell file path following \"-cell\".\n"); @@ -490,48 +501,46 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a i++; options.cellFilePath = argv[i]; + + if (i + 1 < argc) { + if (strcmp(argv[i + 1], "-nosnap") == 0) { + options.cellSnap = false; + i++; + } + } } else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No columns per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.colsPerChunk)) FATAL_ERROR("Failed to parse columns per chunk.\n"); - } - if (options.colsPerChunk < 1) { + if (options.colsPerChunk < 1) FATAL_ERROR("columns per chunk must be positive.\n"); - } } else if (strcmp(option, "-mheight") == 0 || strcmp(option, "-rpc") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No rows per chunk value following \"%s\".\n", option); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) { + if (!ParseNumber(argv[i], NULL, 10, &options.rowsPerChunk)) FATAL_ERROR("Failed to parse rows per chunk.\n"); - } - if (options.rowsPerChunk < 1) { + if (options.rowsPerChunk < 1) FATAL_ERROR("rows per chunk must be positive.\n"); - } } else if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No bitdepth value following \"-bitdepth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.bitDepth)) { + if (!ParseNumber(argv[i], NULL, 10, &options.bitDepth)) FATAL_ERROR("Failed to parse bitdepth.\n"); - } - if (options.bitDepth != 4 && options.bitDepth != 8) { + if (options.bitDepth != 4 && options.bitDepth != 8) FATAL_ERROR("bitdepth must be either 4 or 8.\n"); - } } else if (strcmp(option, "-clobbersize") == 0) { options.clobberSize = true; } else if (strcmp(option, "-nobyteorder") == 0) { @@ -540,16 +549,32 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a options.version101 = true; } else if (strcmp(option, "-sopc") == 0) { options.sopc = true; + } else if (strcmp(option, "-scan") == 0) { + options.scan = true; } else if (strcmp(option, "-scanned") == 0) { - if (options.scanMode != 0) { - FATAL_ERROR("Scan mode specified more than once.\n-scanned goes back to front as in DP, -scanfronttoback goes front to back as in PtHGSS\n"); + // maintained for compatibility + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); } - options.scanMode = 1; + options.encodeMode = 1; + options.scan = true; } else if (strcmp(option, "-scanfronttoback") == 0) { - if (options.scanMode != 0) { - FATAL_ERROR("Scan mode specified more than once.\n-scanned goes back to front as in DP, -scanfronttoback goes front to back as in PtHGSS\n"); + // maintained for compatibility + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); } - options.scanMode = 2; + options.encodeMode = 2; + options.scan = true; + } else if (strcmp(option, "-encodebacktofront") == 0) { + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); + } + options.encodeMode = 1; + } else if (strcmp(option, "-encodefronttoback") == 0) { + if (options.encodeMode != 0) { + FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n"); + } + options.encodeMode = 2; } else if (strcmp(option, "-wrongsize") == 0) { options.wrongSize = true; } else if (strcmp(option, "-handleempty") == 0) { @@ -557,19 +582,18 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a } else if (strcmp(option, "-vram") == 0) { options.vramTransfer = true; } else if (strcmp(option, "-mappingtype") == 0) { - if (i + 1 >= argc) { + if (i + 1 >= argc) FATAL_ERROR("No mapping type value following \"-mappingtype\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &options.mappingType)) { + if (!ParseNumber(argv[i], NULL, 10, &options.mappingType)) FATAL_ERROR("Failed to parse mapping type.\n"); - } - if (options.mappingType != 0 && options.mappingType != 32 && options.mappingType != 64 && options.mappingType != 128 && options.mappingType != 256) { + if (options.mappingType != 0 && options.mappingType != 32 && options.mappingType != 64 && options.mappingType != 128 && options.mappingType != 256) FATAL_ERROR("bitdepth must be one of the following: 0, 32, 64, 128, or 256\n"); - } + } else if (strcmp(option, "-convertTo4Bpp") == 0) { + options.convertTo4Bpp = true; } else { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } @@ -586,134 +610,164 @@ void HandlePngToNtrLzCommand(char *inputPath, char *outputPath, int argc, char * HandleLZCompressCommand(outputPath, outputPath, 3 + numLzArgs, &(argv[argc - 3 - numLzArgs])); } -void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Palette palette; ReadPngPalette(inputPath, &palette); WriteGbaPalette(outputPath, &palette); } -void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ struct Palette palette; bool ncpr = false; bool ir = false; bool nopad = false; int bitdepth = 0; int compNum = 0; + int pcmpStartIndex = 0; bool pcmp = false; bool inverted = false; + bool convertTo4Bpp = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-ncpr") == 0) { + if (strcmp(option, "-ncpr") == 0) + { ncpr = true; - } else if (strcmp(option, "-ir") == 0) { + } + else if (strcmp(option, "-ir") == 0) + { ir = true; - } else if (strcmp(option, "-nopad") == 0) { + } + else if (strcmp(option, "-nopad") == 0) + { nopad = true; - } else if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-bitdepth") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No bitdepth following \"-bitdepth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) { + if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) FATAL_ERROR("Failed to parse bitdepth.\n"); - } - if (bitdepth != 4 && bitdepth != 8) { + if (bitdepth != 4 && bitdepth != 8) FATAL_ERROR("Bitdepth must be 4 or 8.\n"); - } - } else if (strcmp(option, "-comp") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-comp") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No compression value following \"-comp\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &compNum)) { + if (!ParseNumber(argv[i], NULL, 10, &compNum)) FATAL_ERROR("Failed to parse compression value.\n"); - } - if (compNum > 255) { + if (compNum > 255) FATAL_ERROR("Compression value must be 255 or below.\n"); - } - } else if (strcmp(option, "-pcmp") == 0) { + } + else if (strcmp(option, "-pcmp") == 0) + { pcmp = true; - } else if (strcmp(option, "-invertsize") == 0) { + + if (i + 2 < argc) { + if (strcmp(argv[i + 1], "-start") == 0) { + i += 2; + if (!ParseNumber(argv[i], NULL, 10, &pcmpStartIndex)) { + FATAL_ERROR("Failed to parse PCMP start index value.\n"); + } + } + } + } + else if (strcmp(option, "-invertsize") == 0) + { inverted = true; + } else if (strcmp(option, "-convertTo4Bpp") == 0) { + convertTo4Bpp = true; } else { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } ReadPngPalette(inputPath, &palette); - WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted); + WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, pcmpStartIndex, inverted, convertTo4Bpp); } -void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Palette palette; ReadGbaPalette(inputPath, &palette); WriteJascPalette(outputPath, &palette); } -void HandleNtrToJascPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleNtrToJascPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ struct Palette palette; int bitdepth = 0; bool inverted = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-bitdepth") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No bitdepth following \"-bitdepth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) { + if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) FATAL_ERROR("Failed to parse bitdepth.\n"); - } - if (bitdepth != 4 && bitdepth != 8) { + if (bitdepth != 4 && bitdepth != 8) FATAL_ERROR("Bitdepth must be 4 or 8.\n"); - } - } else if (strcmp(option, "-invertsize") == 0) { + } + else if (strcmp(option, "-invertsize") == 0) + { inverted = true; - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } - ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted); + ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted, false); WriteJascPalette(outputPath, &palette); } -void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ int numColors = 0; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-num_colors") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-num_colors") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No number of colors following \"-num_colors\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &numColors)) { + if (!ParseNumber(argv[i], NULL, 10, &numColors)) FATAL_ERROR("Failed to parse number of colors.\n"); - } - if (numColors < 1) { + if (numColors < 1) FATAL_ERROR("Number of colors must be positive.\n"); - } - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -722,79 +776,98 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, ReadJascPalette(inputPath, &palette); - if (numColors != 0) { + if (numColors != 0) palette.numColors = numColors; - } WriteGbaPalette(outputPath, &palette); } -void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ int numColors = 0; bool ncpr = false; bool ir = false; bool nopad = false; int bitdepth = 0; int compNum = 0; + int pcmpStartIndex = 0; bool pcmp = false; bool inverted = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-num_colors") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-num_colors") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No number of colors following \"-num_colors\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &numColors)) { + if (!ParseNumber(argv[i], NULL, 10, &numColors)) FATAL_ERROR("Failed to parse number of colors.\n"); - } - if (numColors < 1) { + if (numColors < 1) FATAL_ERROR("Number of colors must be positive.\n"); - } - } else if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-bitdepth") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No bitdepth following \"-bitdepth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) { + if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) FATAL_ERROR("Failed to parse bitdepth.\n"); - } - if (bitdepth != 4 && bitdepth != 8) { + if (bitdepth != 4 && bitdepth != 8) FATAL_ERROR("Bitdepth must be 4 or 8.\n"); - } - } else if (strcmp(option, "-comp") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-comp") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No compression value following \"-comp\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &compNum)) { + if (!ParseNumber(argv[i], NULL, 10, &compNum)) FATAL_ERROR("Failed to parse compression value.\n"); - } - if (compNum > 255) { + if (compNum > 255) FATAL_ERROR("Compression value must be 255 or below.\n"); - } - } else if (strcmp(option, "-ncpr") == 0) { + } + else if (strcmp(option, "-ncpr") == 0) + { ncpr = true; - } else if (strcmp(option, "-ir") == 0) { + } + else if (strcmp(option, "-ir") == 0) + { ir = true; - } else if (strcmp(option, "-nopad") == 0) { + } + else if (strcmp(option, "-nopad") == 0) + { nopad = true; - } else if (strcmp(option, "-pcmp") == 0) { + } + else if (strcmp(option, "-pcmp") == 0) + { pcmp = true; - } else if (strcmp(option, "-invertsize") == 0) { + + if (i + 2 < argc) { + if (strcmp(argv[i + 1], "-start") == 0) { + i += 2; + if (!ParseNumber(argv[i], NULL, 10, &pcmpStartIndex)) { + FATAL_ERROR("Failed to parse PCMP start index value.\n"); + } + } + } + } + else if (strcmp(option, "-invertsize") == 0) + { inverted = true; - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -803,14 +876,14 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, ReadJascPalette(inputPath, &palette); - if (numColors != 0) { + if (numColors != 0) palette.numColors = numColors; - } - WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted); + WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, pcmpStartIndex, inverted, false); } -void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToCellOptions *options; options = ParseNCERJson(inputPath); @@ -826,7 +899,8 @@ void HandleJsonToNtrCellLzCommand(char *inputPath, char *outputPath, int argc, c HandleLZCompressCommand(outputPath, outputPath, argc, argv); } -void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToCellOptions *options = malloc(sizeof(struct JsonToCellOptions)); ReadNtrCell(inputPath, options); @@ -844,31 +918,33 @@ void HandleNtrCellLzToJsonCommand(char *inputPath, char *outputPath, int argc UN HandleNtrCellToJsonCommand(outputPath, outputPath, argc, argv); } -void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToScreenOptions *options; options = ParseNSCRJson(inputPath); int bitdepth = 4; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-bitdepth") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No bitdepth following \"-bitdepth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) { + if (!ParseNumber(argv[i], NULL, 10, &bitdepth)) FATAL_ERROR("Failed to parse bitdepth.\n"); - } - if (bitdepth != 4 && bitdepth != 8) { + if (bitdepth != 4 && bitdepth != 8) FATAL_ERROR("Bitdepth must be 4 or 8.\n"); - } - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -880,7 +956,8 @@ void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UN FreeNSCRScreen(options); } -void HandleJsonToNtrAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleJsonToNtrAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToAnimationOptions *options; options = ParseNANRJson(inputPath); @@ -898,7 +975,8 @@ void HandleJsonToNtrAnimationLzCommand(char *inputPath, char *outputPath, int ar HandleLZCompressCommand(outputPath, outputPath, argc, argv); } -void HandleNtrAnimationToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleNtrAnimationToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToAnimationOptions *options = malloc(sizeof(struct JsonToAnimationOptions)); ReadNtrAnimation(inputPath, options); @@ -916,7 +994,8 @@ void HandleNtrAnimationLzToJsonCommand(char *inputPath, char *outputPath, int ar HandleNtrAnimationToJsonCommand(outputPath, outputPath, argc, argv); } -void HandleJsonToNtrMulticellAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleJsonToNtrMulticellAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct JsonToAnimationOptions *options; options = ParseNANRJson(inputPath); @@ -928,18 +1007,18 @@ void HandleJsonToNtrMulticellAnimationCommand(char *inputPath, char *outputPath, FreeNANRAnimation(options); } -void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; - image.pixelsAreRGB = false; - ReadLatinFont(inputPath, &image); WritePng(outputPath, &image); FreeImage(&image); } -void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; image.bitDepth = 2; @@ -950,18 +1029,18 @@ void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNU FreeImage(&image); } -void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; - image.pixelsAreRGB = false; - ReadHalfwidthJapaneseFont(inputPath, &image); WritePng(outputPath, &image); FreeImage(&image); } -void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; image.bitDepth = 2; @@ -972,18 +1051,18 @@ void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, FreeImage(&image); } -void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; - image.pixelsAreRGB = false; - ReadFullwidthJapaneseFont(inputPath, &image); WritePng(outputPath, &image); FreeImage(&image); } -void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ struct Image image; image.bitDepth = 2; @@ -1007,7 +1086,7 @@ static int CountLzCompressArgs(int argc, char **argv) { i++; - count++; + count += 2; } else if (strcmp(option, "-search") == 0) { if (i + 1 >= argc) { FATAL_ERROR("No size following \"-overflow\".\n"); @@ -1015,7 +1094,7 @@ static int CountLzCompressArgs(int argc, char **argv) { i++; - count++; + count += 2; } else if (strcmp(option, "-reverse") == 0) { count++; } else if (strcmp(option, "-nopad") == 0) { @@ -1030,44 +1109,41 @@ static void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, int overflowSize = 0; int minDistance = 2; // default, for compatibility with LZ77UnCompVram() bool forwardIteration = true; - bool extFormat = false; bool nopad = false; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-overflow") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-overflow") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No size following \"-overflow\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &overflowSize)) { + if (!ParseNumber(argv[i], NULL, 10, &overflowSize)) FATAL_ERROR("Failed to parse overflow size.\n"); - } - if (overflowSize < 1) { + if (overflowSize < 1) FATAL_ERROR("Overflow size must be positive.\n"); - } - } else if (strcmp(option, "-search") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-search") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No size following \"-overflow\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &minDistance)) { + if (!ParseNumber(argv[i], NULL, 10, &minDistance)) FATAL_ERROR("Failed to parse LZ min search distance.\n"); - } - if (minDistance < 1) { + if (minDistance < 1) FATAL_ERROR("LZ min search distance must be positive.\n"); - } - } else if (strcmp(option, "-reverse") == 0) { + } + else if (strcmp(option, "-reverse") == 0) + { forwardIteration = false; - } else if (strcmp(option, "-extfmt") == 0) { - extFormat = true; } else if (strcmp(option, "-nopad") == 0) { nopad = true; } else { @@ -1085,7 +1161,7 @@ static void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize); int compressedSize; - unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration, !nopad, extFormat); + unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration, !nopad); compressedData[1] = (unsigned char)fileSize; compressedData[2] = (unsigned char)(fileSize >> 8); @@ -1112,7 +1188,8 @@ static void HandleLZDecompressCommand(char *inputPath, char *outputPath, int arg free(uncompressedData); } -void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ int fileSize; unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); @@ -1126,7 +1203,8 @@ void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, free(compressedData); } -void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ int fileSize; unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); @@ -1140,28 +1218,30 @@ void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSE free(uncompressedData); } -void HandleHuffCompressCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleHuffCompressCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ int fileSize; int bitDepth = 4; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-depth") == 0) { - if (i + 1 >= argc) { + if (strcmp(option, "-depth") == 0) + { + if (i + 1 >= argc) FATAL_ERROR("No size following \"-depth\".\n"); - } i++; - if (!ParseNumber(argv[i], NULL, 10, &bitDepth)) { + if (!ParseNumber(argv[i], NULL, 10, &bitDepth)) FATAL_ERROR("Failed to parse bit depth.\n"); - } - if (bitDepth != 4 && bitDepth != 8) { + if (bitDepth != 4 && bitDepth != 8) FATAL_ERROR("GBA only supports bit depth of 4 or 8.\n"); - } - } else { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -1178,7 +1258,8 @@ void HandleHuffCompressCommand(char *inputPath, char *outputPath, int argc, char free(compressedData); } -void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ int fileSize; unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); @@ -1192,34 +1273,34 @@ void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNU free(uncompressedData); } -void HandleNtrFontToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) { +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++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-metadata") == 0) { - if (i + 1 >= argc) { + 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) { + } + else if (strcmp(option, "-subscreen") == 0) + { options.useSubscreenPalette = true; } } - if (options.metadataFilePath == NULL) { + if (options.metadataFilePath == NULL) FATAL_ERROR("No file path given for \"-metadata\".\n"); - } struct Image image; struct NtrFontMetadata metadata; - - image.pixelsAreRGB = false; - ReadNtrFont(inputPath, &image, &metadata, options.useSubscreenPalette); WritePng(outputPath, &image); @@ -1230,25 +1311,26 @@ void HandleNtrFontToPngCommand(char *inputPath, char *outputPath, int argc, char FreeImage(&image); } -void HandlePngToNtrFontCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandlePngToNtrFontCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ struct NtrFontOptions options; options.metadataFilePath = NULL; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { char *option = argv[i]; - if (strcmp(option, "-metadata") == 0) { - if (i + 1 >= argc) { + 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) { + if (options.metadataFilePath == NULL) FATAL_ERROR("No file path given for \"-metadata\".\n"); - } struct NtrFontMetadata *metadata = ParseNtrFontMetadataJson(options.metadataFilePath); struct Image image = { .bitDepth = 2 }; @@ -1260,10 +1342,10 @@ void HandlePngToNtrFontCommand(char *inputPath, char *outputPath, int argc, char FreeImage(&image); } -int main(int argc, char **argv) { - if (argc < 3) { +int main(int argc, char **argv) +{ + if (argc < 3) FATAL_ERROR("Usage: nitrogfx INPUT_PATH OUTPUT_PATH [options...]\n"); - } struct CommandHandler handlers[] = { { "1bpp", "png", HandleGbaToPngCommand }, @@ -1319,15 +1401,14 @@ int main(int argc, char **argv) { char *inputFileExtension = GetFileExtension(inputPath); char *outputFileExtension = GetFileExtension(outputPath); - if (inputFileExtension == NULL) { + if (inputFileExtension == NULL) FATAL_ERROR("Input file \"%s\" has no extension.\n", inputPath); - } - if (outputFileExtension == NULL) { + if (outputFileExtension == NULL) FATAL_ERROR("Output file \"%s\" has no extension.\n", outputPath); - } - for (int i = 0; handlers[i].function != NULL; i++) { + for (int i = 0; handlers[i].function != NULL; i++) + { if (((handlers[i].inputFileExtension == NULL || strcmp(handlers[i].inputFileExtension, inputFileExtension) == 0) && (handlers[i].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0)) || (handlers[i].inputFileExtension == NULL && strrchr(outputFileExtension, '.') && strstr(outputFileExtension, handlers[i].outputFileExtension)) diff --git a/tools/nitrogfx/options.h b/tools/nitrogfx/options.h index 9f2720a64..5dfaa7ea5 100644 --- a/tools/nitrogfx/options.h +++ b/tools/nitrogfx/options.h @@ -1,10 +1,10 @@ -// Copyright (c) 2018 huderlem, 2021-2024 red031000 +// Copyright (c) 2018 huderlem, 2021-2025 red031000 #ifndef OPTIONS_H #define OPTIONS_H -#include #include +#include struct GbaToPngOptions { char *paletteFilePath; @@ -25,6 +25,7 @@ struct PngToGbaOptions { struct PngToNtrOptions { char *cellFilePath; + bool cellSnap; int numTiles; int bitDepth; int colsPerChunk; @@ -33,25 +34,29 @@ struct PngToNtrOptions { bool byteOrder; bool version101; bool sopc; - uint32_t scanMode; + bool scan; bool wrongSize; bool handleEmpty; bool vramTransfer; int mappingType; + uint32_t encodeMode; + bool convertTo4Bpp; }; struct NtrToPngOptions { char *paletteFilePath; char *cellFilePath; - char *scrnFilePath; + bool cellSnap; int bitDepth; bool hasTransparency; int width; int colsPerChunk; int rowsPerChunk; int palIndex; - bool scanFrontToBack; bool handleEmpty; + uint32_t encodeMode; + bool convertTo8Bpp; + bool verbose; }; struct CellVramTransferData { @@ -88,10 +93,10 @@ struct OAM { }; struct CellAttributes { - bool hFlip; // 1 << 8 - bool vFlip; // 1 << 9 - bool hvFlip; // 1 << 10 - bool boundingRect; // 1 << 11 + bool hFlip; // 1 << 8 + bool vFlip; // 1 << 9 + bool hvFlip; // 1 << 10 + bool boundingRect; // 1 << 11 int boundingSphereRadius; // 1 << 0 (6 bits); }; @@ -107,8 +112,10 @@ struct Cell { struct JsonToCellOptions { bool labelEnabled; + bool dontPadKbec; bool extended; bool vramTransferEnabled; + bool ucatEnabled; int mappingType; int cellCount; struct Cell **cells; @@ -116,6 +123,7 @@ struct JsonToCellOptions { struct CellVramTransferData **transferData; char **labels; int labelCount; + int *ucatCellAttribtes; }; struct JsonToScreenOptions { @@ -151,7 +159,7 @@ struct AnimationDataSRT { struct AnimationDataT { short index; - // unsigned short rotation; + //unsigned short rotation; short positionX; short positionY; }; @@ -166,6 +174,11 @@ struct AnimationResults { }; }; +struct UaatData { + int *sequenceAttributes; + int *frameAttributes; +}; + struct JsonToAnimationOptions { bool multiCell; short sequenceCount; @@ -176,6 +189,8 @@ struct JsonToAnimationOptions { char **labels; int labelCount; short resultCount; + bool uaatEnabled; + struct UaatData uaatData; }; struct NtrFontOptions { diff --git a/tools/nitrogfx/util.c b/tools/nitrogfx/util.c index 5ca4ce1eb..88ee6c96f 100644 --- a/tools/nitrogfx/util.c +++ b/tools/nitrogfx/util.c @@ -14,29 +14,24 @@ bool ParseNumber(char *s, char **end, int radix, int *intValue) { char *localEnd; - if (end == NULL) { + if (end == NULL) end = &localEnd; - } errno = 0; const long longValue = strtol(s, end, radix); - if (*end == s) { + if (*end == s) return false; // not a number - } - if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) { + if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) return false; - } - if (longValue > INT_MAX) { + if (longValue > INT_MAX) return false; - } - if (longValue < INT_MIN) { + if (longValue < INT_MIN) return false; - } *intValue = (int)longValue; @@ -47,23 +42,19 @@ char *GetFileExtension(char *path) { char *extension = path; - while (*extension != 0) { + while (*extension != 0) extension++; - } - while (extension > path && *extension != '.') { + while (extension > path && *extension != '.') extension--; - } - if (extension == path) { + if (extension == path) return NULL; - } extension++; - if (*extension == 0) { + if (*extension == 0) return NULL; - } if (strcmp(extension, "lz") == 0) { char *plainName = malloc(strlen(path) + 1); @@ -83,9 +74,8 @@ unsigned char *ReadWholeFile(char *path, int *size) { FILE *fp = fopen(path, "rb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); - } fseek(fp, 0, SEEK_END); @@ -93,15 +83,13 @@ unsigned char *ReadWholeFile(char *path, int *size) unsigned char *buffer = malloc(*size); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); - } rewind(fp); - if (fread(buffer, *size, 1, fp) != 1) { + if (fread(buffer, *size, 1, fp) != 1) FATAL_ERROR("Failed to read \"%s\".\n", path); - } fclose(fp); @@ -112,9 +100,8 @@ unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) { FILE *fp = fopen(path, "rb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); - } fseek(fp, 0, SEEK_END); @@ -122,15 +109,13 @@ unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) unsigned char *buffer = calloc(*size + padAmount, 1); - if (buffer == NULL) { + if (buffer == NULL) FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); - } rewind(fp); - if (fread(buffer, *size, 1, fp) != 1) { + if (fread(buffer, *size, 1, fp) != 1) FATAL_ERROR("Failed to read \"%s\".\n", path); - } fclose(fp); @@ -141,13 +126,11 @@ void WriteWholeStringToFile(char *path, char *string) { FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } - if (fputs(string, fp) == EOF) { + if (fputs(string, fp) == EOF) FATAL_ERROR("Failed to write to \"%s\".\n", path); - } fclose(fp); } @@ -156,13 +139,11 @@ void WriteWholeFile(char *path, void *buffer, int bufferSize) { FILE *fp = fopen(path, "wb"); - if (fp == NULL) { + if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - } - if (fwrite(buffer, bufferSize, 1, fp) != 1) { + if (fwrite(buffer, bufferSize, 1, fp) != 1) FATAL_ERROR("Failed to write to \"%s\".\n", path); - } fclose(fp); }