diff --git a/.clang-format-ignore b/.clang-format-ignore index 4607a0c8f..a49edb564 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -2,3 +2,4 @@ **/*.inc **/*.json lib/ +tools/nitrogfx/ diff --git a/tools/nitrogfx/.gitignore b/tools/nitrogfx/.gitignore index e00951e94..80659a2f7 100644 --- a/tools/nitrogfx/.gitignore +++ b/tools/nitrogfx/.gitignore @@ -1,2 +1,3 @@ nitrogfx nitrogfx-debug +.vscode/ diff --git a/tools/nitrogfx/Makefile b/tools/nitrogfx/Makefile index 8b3c7c8d8..948416ea1 100644 --- a/tools/nitrogfx/Makefile +++ b/tools/nitrogfx/Makefile @@ -19,7 +19,7 @@ all: nitrogfx @: nitrogfx-debug: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h json.h cJSON.h - $(CC) $(CFLAGS) -g -DDEBUG $(SRCS) -o $@ $(LDFLAGS) $(LIBS) -fsanitize=address,undefined + $(CC) $(CFLAGS) -g -DDEBUG $(SRCS) -o $@ $(LDFLAGS) $(LIBS) nitrogfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h json.h cJSON.h $(CC) $(CFLAGS) -O2 $(SRCS) -o $@ $(LDFLAGS) $(LIBS) diff --git a/tools/nitrogfx/cJSON.c b/tools/nitrogfx/cJSON.c index d1a76821b..d7c72363d 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,54 +91,61 @@ 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 != 15) -#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif -CJSON_PUBLIC(const char *) -cJSON_Version(void) { +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ static char version[15]; - snprintf(version, 15, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); return version; } /* 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) { - size_t length = 0; +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,37 +206,43 @@ 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.allocate = malloc; global_hooks.deallocate = free; global_hooks.reallocate = realloc; return; } 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,25 @@ 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; } global_hooks.deallocate(item); item = next; @@ -250,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 @@ -271,69 +298,79 @@ 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) { - double number = 0; +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 decimal_point = get_decimal_point(); - size_t i = 0; + size_t i = 0; - 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; (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; - case '.': - number_c_string[i] = decimal_point; - break; + case '.': + number_c_string[i] = decimal_point; + break; - default: - goto loop_end; + default: + goto loop_end; } } loop_end: number_c_string[i] = '\0'; - number = strtod((const char *)number_c_string, (char **)&after_end); - if (number_c_string == after_end) { + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { 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; } @@ -344,35 +381,61 @@ static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buff } /* 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; } return object->valuedouble = number; } -CJSON_PUBLIC(char *) -cJSON_SetValuestring(cJSON *object, const char *valuestring) { +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ char *copy = NULL; + size_t v1_len; + size_t v2_len; /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { return NULL; } - if (strlen(valuestring) <= strlen(object->valuestring)) { + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) + { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { + return NULL; + } strcpy(object->valuestring, valuestring); return object->valuestring; } - 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; @@ -392,26 +455,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; + 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; } @@ -420,31 +488,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; @@ -462,65 +541,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) { - 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 decimal_point = get_decimal_point(); - double test = 0.0; - - if (output_buffer == NULL) { +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 decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { return false; } /* This checks for NaN and Infinity */ - if (isnan(d) || isinf(d)) { - length = snprintf((char *)number_buffer, 26, "null"); - } 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 = snprintf((char *)number_buffer, 26, "%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 = snprintf((char *)number_buffer, 26, "%1.17g", d); + length = sprintf((char*)number_buffer, "%1.17g", d); } } - /* snprintf failed or buffer overrun occurred */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + /* sprintf failed or buffer overrun occurred */ + 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; } @@ -535,24 +631,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; + 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; } @@ -563,16 +668,18 @@ 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) { - long unsigned int codepoint = 0; - unsigned int first_code = 0; +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; - unsigned char utf8_length = 0; - unsigned char utf8_position = 0; - unsigned char sequence_length = 0; - unsigned char first_byte_mark = 0; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + 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; } @@ -581,22 +688,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 */ + 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; } @@ -604,51 +715,68 @@ 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; + codepoint = first_code; } /* 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; + 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; + 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; + 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); } @@ -661,25 +789,30 @@ static unsigned char utf16_literal_to_utf8(const unsigned char *const input_poin } /* 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; + 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; } { /* 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 != '\"')) { + size_t skipped_bytes = 0; + 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; } @@ -688,64 +821,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; - - /* 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 */ + 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 */ + goto fail; + } + break; + + default: goto fail; - } - break; - - default: - goto fail; } input_pointer += sequence_length; } @@ -754,20 +895,23 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff /* zero terminate the output */ *output_pointer = '\0'; - item->type = cJSON_String; - item->valuestring = (char *)output; + item->type = cJSON_String; + 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); } @@ -775,59 +919,68 @@ static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buff } /* 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; - size_t output_length = 0; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; /* 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] = '\"'; @@ -836,43 +989,48 @@ static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer return true; } - output[0] = '\"'; + 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 */ - snprintf((char *)output_pointer, output_buffer->length - (output_pointer - output), "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; } } } @@ -883,33 +1041,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--; } @@ -917,23 +1081,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; } @@ -944,25 +1112,24 @@ 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.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.length = buffer_length; - buffer.offset = 0; - buffer.hooks = global_hooks; + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; item = cJSON_New_Item(&global_hooks); if (item == NULL) /* memory fail */ @@ -970,42 +1137,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; @@ -1015,19 +1192,20 @@ cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char ** } /* 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; @@ -1035,31 +1213,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) { + buffer->hooks = *hooks; + 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)); @@ -1067,122 +1250,137 @@ static unsigned char *print(const cJSON *const item, cJSON_bool format, const in /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } 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; } return NULL; } /* 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; } - p.length = (size_t)prebuffer; - p.offset = 0; + p.length = (size_t)prebuffer; + p.offset = 0; p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; + 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.length = (size_t)length; - p.offset = 0; + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; p.noalloc = true; - p.format = format; - p.hooks = global_hooks; + p.format = format; + p.hooks = global_hooks; return print_value(item, &p); } /* 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)) { - item->type = cJSON_True; + 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); } @@ -1190,94 +1388,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; + 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; } - memcpy(output, item->valuestring, raw_length); - return true; - } - case cJSON_String: - return print_string(item, output_buffer); + case cJSON_String: + return print_string(item, output_buffer); - case cJSON_Array: - return print_array(item, output_buffer); + case cJSON_Array: + return print_array(item, output_buffer); - case cJSON_Object: - return print_object(item, output_buffer); + case cJSON_Object: + return print_object(item, output_buffer); - default: - return false; + default: + return false; } } /* Build an array from input text. */ -static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) { - cJSON *head = NULL; /* head of the linked list */ +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; } @@ -1285,34 +1497,42 @@ 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; - current_item = new_item; + new_item->prev = current_item; + current_item = new_item; } /* 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 */ } @@ -1323,7 +1543,7 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe head->prev = current_item; } - item->type = cJSON_Array; + item->type = cJSON_Array; item->child = head; input_buffer->offset++; @@ -1331,7 +1551,8 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe return true; fail: - if (head != NULL) { + if (head != NULL) + { cJSON_Delete(head); } @@ -1339,19 +1560,22 @@ static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffe } /* 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; + 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; } @@ -1359,19 +1583,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'; @@ -1381,38 +1610,44 @@ 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++ = ']'; - *output_pointer = '\0'; + *output_pointer = '\0'; output_buffer->depth--; return true; } /* Build an object from the text. */ -static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) { - cJSON *head = NULL; /* linked list head */ +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; } @@ -1420,50 +1655,65 @@ 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; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ } /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); - 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); /* swap valuestring and string, because we parsed the name */ - current_item->string = current_item->valuestring; + 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 */ } @@ -1474,14 +1724,15 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff head->prev = current_item; } - item->type = cJSON_Object; + item->type = cJSON_Object; item->child = head; input_buffer->offset++; return true; fail: - if (head != NULL) { + if (head != NULL) + { cJSON_Delete(head); } @@ -1489,76 +1740,91 @@ static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buff } /* 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; + 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); /* print comma if not last */ - length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + 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'; @@ -1568,35 +1834,40 @@ 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'; } } *output_pointer++ = '}'; - *output_pointer = '\0'; + *output_pointer = '\0'; output_buffer->depth--; return true; } /* 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; + 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; } @@ -1606,15 +1877,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; } @@ -1622,29 +1896,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; } } @@ -1656,36 +1938,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; } @@ -1696,10 +1982,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; } @@ -1707,14 +1995,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 { + item->prev = item; + item->next = NULL; + } + else + { /* append to the end */ - if (child->prev) { + if (child->prev) + { suffix_object(child->prev, item); array->child->prev = item; } @@ -1724,88 +2016,100 @@ 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; + 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); } item->string = new_key; - item->type = new_type; + item->type = new_type; 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; } @@ -1813,10 +2117,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; } @@ -1824,10 +2129,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; } @@ -1835,10 +2141,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; } @@ -1846,10 +2153,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; } @@ -1857,10 +2165,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; } @@ -1868,10 +2177,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; } @@ -1879,10 +2189,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; } @@ -1890,10 +2201,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; } @@ -1901,25 +2213,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)) { +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; } @@ -1931,98 +2249,118 @@ 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) { + 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); } - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; after_inserted->prev = newitem; - 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) || (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; } } @@ -2034,94 +2372,113 @@ 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); + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + replacement->type &= ~cJSON_StringIsConst; return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } -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) { - item->type = cJSON_Number; + 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; } } @@ -2129,13 +2486,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) { - item->type = cJSON_String; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); - if (!item->valuestring) { + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { cJSON_Delete(item); return NULL; } @@ -2144,46 +2503,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) { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char *)cast_away_const(string); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + 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->type = cJSON_Object | cJSON_IsReference; + 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->type = cJSON_Array | cJSON_IsReference; + 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) { - item->type = cJSON_Raw; - item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); - if (!item->valuestring) { + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { cJSON_Delete(item); return NULL; } @@ -2192,20 +2553,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; } @@ -2213,28 +2576,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; @@ -2247,28 +2616,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; @@ -2281,28 +2656,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; @@ -2315,29 +2696,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; } @@ -2350,79 +2737,105 @@ cJSON_CreateStringArray(const char *const *strings, int count) { } /* Duplication */ -CJSON_PUBLIC(cJSON *) -cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + return cJSON_Duplicate_rec(item, 0, recurse ); +} + +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + 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->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) { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) { + 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) + { goto fail; } - if (next != NULL) { + if (next != NULL) + { /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; + next->next = newchild; newchild->prev = next; - next = newchild; - } else { + next = newchild; + } + else + { /* Set newitem->child and move to it */ newitem->child = newchild; - next = 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; @@ -2430,11 +2843,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; } @@ -2446,6 +2862,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]; @@ -2462,41 +2879,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++; } } @@ -2504,209 +2927,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; - - default: - return false; + 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; } /* 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) { - 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)) { + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { return false; } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } - 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; - } + 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; + } - 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; + a_element = a_element->next; + b_element = b_element->next; } - if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + /* one of the arrays is longer than the other */ + if (a_element != b_element) { return false; } + + return true; } - /* 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; + 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; + } } - if (!cJSON_Compare(b_element, a_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; - } + return true; + } - default: - return false; + 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 92907a2cd..37520bbcf 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 15 +#define CJSON_VERSION_PATCH 18 #include @@ -137,6 +137,12 @@ typedef int cJSON_bool; #define CJSON_NESTING_LIMIT 1000 #endif +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); @@ -279,6 +285,13 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) diff --git a/tools/nitrogfx/font.c b/tools/nitrogfx/font.c index 0dd6fbc3e..f929e30ce 100644 --- a/tools/nitrogfx/font.c +++ b/tools/nitrogfx/font.c @@ -4,323 +4,486 @@ #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) }; static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - unsigned int srcPixelsOffset = 0; + unsigned int srcPixelsOffset = 0; - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { - unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; - unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - srcPixelsOffset += 2; - } - } - } - } + srcPixelsOffset += 2; + } + } + } + } } static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - unsigned int destPixelsOffset = 0; + unsigned int destPixelsOffset = 0; - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { - unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; - unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - destPixelsOffset += 2; - } - } - } - } + destPixelsOffset += 2; + } + } + } + } } static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - unsigned int glyphIndex = (row * 16) + column; - - for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { - unsigned int pixelsX = column * 8; - unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; - - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; - unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4); - - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - - srcPixelsOffset += 2; - } - } - } - } + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + unsigned int glyphIndex = (row * 16) + column; + + for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { + unsigned int pixelsX = column * 8; + unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; + unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + srcPixelsOffset += 2; + } + } + } + } } static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - unsigned int glyphIndex = (row * 16) + column; - - for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { - unsigned int pixelsX = column * 8; - unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; - - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; - unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4); - - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - - destPixelsOffset += 2; - } - } - } - } + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + unsigned int glyphIndex = (row * 16) + column; + + for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) { + unsigned int pixelsX = column * 8; + unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile; + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i; + unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + destPixelsOffset += 2; + } + } + } + } } static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - unsigned int glyphIndex = (row * 16) + column; - - for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { - unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); - unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); - - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; - unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); - - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - - srcPixelsOffset += 2; - } - } - } - } + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + unsigned int glyphIndex = (row * 16) + column; + + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + srcPixelsOffset += 2; + } + } + } + } } static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows) { - for (unsigned int row = 0; row < numRows; row++) { - for (unsigned int column = 0; column < 16; column++) { - unsigned int glyphIndex = (row * 16) + column; - - for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { - unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); - unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); - - for (unsigned int i = 0; i < 8; i++) { - unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; - unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); - - dest[destPixelsOffset] = src[srcPixelsOffset + 1]; - dest[destPixelsOffset + 1] = src[srcPixelsOffset]; - - destPixelsOffset += 2; - } - } - } - } + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16; column++) { + unsigned int glyphIndex = (row * 16) + column; + + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1); + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + destPixelsOffset += 2; + } + } + } + } +} + +static void ConvertFromNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) +{ + unsigned int srcPixelsOffset = 0; + unsigned int curGlyph = 0; + + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16 && curGlyph < metadata->numGlyphs; column++, curGlyph++) { + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + srcPixelsOffset += 2; + } + } + } + } +} + +static void ConvertToNitroFont(unsigned char *src, unsigned char *dest, unsigned int numRows, struct NtrFontMetadata *metadata) +{ + unsigned int destPixelsOffset = 0; + unsigned int curGlyph = 0; + + for (unsigned int row = 0; row < numRows; row++) { + for (unsigned int column = 0; column < 16 && curGlyph < metadata->numGlyphs; column++, curGlyph++) { + for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) { + unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8); + + for (unsigned int i = 0; i < 8; i++) { + unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i; + unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4); + + dest[destPixelsOffset] = src[srcPixelsOffset + 1]; + dest[destPixelsOffset + 1] = src[srcPixelsOffset]; + + destPixelsOffset += 2; + } + } + } + } } static void SetFontPalette(struct Image *image) { - image->hasPalette = true; + image->hasPalette = true; - image->palette.numColors = 4; + image->palette.numColors = 4; - for (int i = 0; i < image->palette.numColors; i++) { - image->palette.colors[i].red = gFontPalette[i][0]; - image->palette.colors[i].green = gFontPalette[i][1]; - image->palette.colors[i].blue = gFontPalette[i][2]; - } + for (int i = 0; i < image->palette.numColors; i++) { + image->palette.colors[i].red = gFontPalette[i][0]; + image->palette.colors[i].green = gFontPalette[i][1]; + image->palette.colors[i].blue = gFontPalette[i][2]; + } - image->hasTransparency = false; + image->hasTransparency = false; +} + +static void SetSubscreenFontPalette(struct Image *image) +{ + image->hasPalette = true; + + image->palette.numColors = 4; + + for (int i = 0; i < image->palette.numColors; i++) { + image->palette.colors[i].red = gFontPalette_Subscreen[i][0]; + image->palette.colors[i].green = gFontPalette_Subscreen[i][1]; + image->palette.colors[i].blue = gFontPalette_Subscreen[i][2]; + } + + image->hasTransparency = false; } void ReadLatinFont(char *path, struct Image *image) { - int fileSize; - unsigned char *buffer = ReadWholeFile(path, &fileSize); + int fileSize; + unsigned char *buffer = ReadWholeFile(path, &fileSize); - int numGlyphs = fileSize / 64; + int numGlyphs = fileSize / 64; - if (numGlyphs % 16 != 0) - FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); + if (numGlyphs % 16 != 0) + FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - int numRows = numGlyphs / 16; + int numRows = numGlyphs / 16; - image->width = 256; - image->height = numRows * 16; - image->bitDepth = 2; - image->pixels = malloc(fileSize); + image->width = 256; + image->height = numRows * 16; + image->bitDepth = 2; + image->pixels = malloc(fileSize); - if (image->pixels == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - ConvertFromLatinFont(buffer, image->pixels, numRows); + ConvertFromLatinFont(buffer, image->pixels, numRows); - free(buffer); + free(buffer); - SetFontPalette(image); + SetFontPalette(image); } void WriteLatinFont(char *path, struct Image *image) { - if (image->width != 256) - FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); + if (image->width != 256) + FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); - if (image->height % 16 != 0) - FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); + if (image->height % 16 != 0) + FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); - int numRows = image->height / 16; - int bufferSize = numRows * 16 * 64; - unsigned char *buffer = malloc(bufferSize); + int numRows = image->height / 16; + int bufferSize = numRows * 16 * 64; + unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - ConvertToLatinFont(image->pixels, buffer, numRows); + ConvertToLatinFont(image->pixels, buffer, numRows); - WriteWholeFile(path, buffer, bufferSize); + WriteWholeFile(path, buffer, bufferSize); - free(buffer); + free(buffer); } void ReadHalfwidthJapaneseFont(char *path, struct Image *image) { - int fileSize; - unsigned char *buffer = ReadWholeFile(path, &fileSize); + int fileSize; + unsigned char *buffer = ReadWholeFile(path, &fileSize); - int glyphSize = 32; + int glyphSize = 32; - if (fileSize % glyphSize != 0) - FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize); + if (fileSize % glyphSize != 0) + FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize); - int numGlyphs = fileSize / glyphSize; - - if (numGlyphs % 16 != 0) - FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); + int numGlyphs = fileSize / glyphSize; + + if (numGlyphs % 16 != 0) + FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - int numRows = numGlyphs / 16; + int numRows = numGlyphs / 16; - image->width = 128; - image->height = numRows * 16; - image->bitDepth = 2; - image->pixels = malloc(fileSize); + image->width = 128; + image->height = numRows * 16; + image->bitDepth = 2; + image->pixels = malloc(fileSize); - if (image->pixels == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows); + ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows); - free(buffer); + free(buffer); - SetFontPalette(image); + SetFontPalette(image); } void WriteHalfwidthJapaneseFont(char *path, struct Image *image) { - if (image->width != 128) - FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width); + if (image->width != 128) + FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width); - if (image->height % 16 != 0) - FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); + if (image->height % 16 != 0) + FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); - int numRows = image->height / 16; - int bufferSize = numRows * 16 * 32; - unsigned char *buffer = malloc(bufferSize); + int numRows = image->height / 16; + int bufferSize = numRows * 16 * 32; + unsigned char *buffer = malloc(bufferSize); - if (buffer == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows); + ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows); - WriteWholeFile(path, buffer, bufferSize); + WriteWholeFile(path, buffer, bufferSize); - free(buffer); + free(buffer); } void ReadFullwidthJapaneseFont(char *path, struct Image *image) { - int fileSize; - unsigned char *buffer = ReadWholeFile(path, &fileSize); + int fileSize; + unsigned char *buffer = ReadWholeFile(path, &fileSize); - int numGlyphs = fileSize / 64; + int numGlyphs = fileSize / 64; - if (numGlyphs % 16 != 0) - FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); + if (numGlyphs % 16 != 0) + FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs); - int numRows = numGlyphs / 16; + int numRows = numGlyphs / 16; - image->width = 256; - image->height = numRows * 16; - image->bitDepth = 2; - image->pixels = malloc(fileSize); + image->width = 256; + image->height = numRows * 16; + image->bitDepth = 2; + image->pixels = malloc(fileSize); - if (image->pixels == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows); + ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows); - free(buffer); + free(buffer); - SetFontPalette(image); + SetFontPalette(image); } void WriteFullwidthJapaneseFont(char *path, struct Image *image) { - if (image->width != 256) - FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); + if (image->width != 256) + FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); + + if (image->height % 16 != 0) + FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); + + int numRows = image->height / 16; + int bufferSize = numRows * 16 * 64; + unsigned char *buffer = malloc(bufferSize); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); + + ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows); + + WriteWholeFile(path, buffer, bufferSize); + + free(buffer); +} + +static inline uint32_t ReadLittleEndianWord(unsigned char *buffer, size_t start) +{ + return (buffer[start + 3] << 24) + | (buffer[start + 2] << 16) + | (buffer[start + 1] << 8) + | (buffer[start]); +} + +void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata, bool useSubscreenPalette) +{ + int filesize; + unsigned char *buffer = ReadWholeFile(path, &filesize); - if (image->height % 16 != 0) - FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); + metadata->size = ReadLittleEndianWord(buffer, 0x00); + metadata->widthTableOffset = ReadLittleEndianWord(buffer, 0x04); + metadata->numGlyphs = ReadLittleEndianWord(buffer, 0x08); + metadata->maxWidth = buffer[0x0C]; + metadata->maxHeight = buffer[0x0D]; + metadata->glyphWidth = buffer[0x0E]; + metadata->glyphHeight = buffer[0x0F]; - int numRows = image->height / 16; - int bufferSize = numRows * 16 * 64; - unsigned char *buffer = malloc(bufferSize); + int numRows = (metadata->numGlyphs + 15) / 16; // Round up to next multiple of 16. - if (buffer == NULL) - FATAL_ERROR("Failed to allocate memory for font.\n"); + metadata->glyphWidthTable = malloc(metadata->numGlyphs); + memcpy(metadata->glyphWidthTable, buffer + metadata->widthTableOffset, metadata->numGlyphs); - ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows); + image->width = 256; + image->height = numRows * 16; + image->bitDepth = 2; + image->pixels = malloc(filesize); - WriteWholeFile(path, buffer, bufferSize); + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); - free(buffer); + ConvertFromNitroFont(buffer + metadata->size, image->pixels, numRows, metadata); + + free(buffer); + + if (useSubscreenPalette) + SetSubscreenFontPalette(image); + else + SetFontPalette(image); +} + +void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata) +{ + if (image->width != 256) + FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width); + + if (image->height % 16 != 0) + FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height); + + int numRows = image->height / 16; + int bufferSize = metadata->widthTableOffset + metadata->numGlyphs; + unsigned char *buffer = malloc(bufferSize); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); + + buffer[0x00] = (metadata->size & 0x000000FF); + buffer[0x01] = (metadata->size & 0x0000FF00) >> 8; + buffer[0x02] = (metadata->size & 0x00FF0000) >> 16; + buffer[0x03] = (metadata->size & 0xFF000000) >> 24; + buffer[0x04] = (metadata->widthTableOffset & 0x000000FF); + buffer[0x05] = (metadata->widthTableOffset & 0x0000FF00) >> 8; + buffer[0x06] = (metadata->widthTableOffset & 0x00FF0000) >> 16; + buffer[0x07] = (metadata->widthTableOffset & 0xFF000000) >> 24; + buffer[0x08] = (metadata->numGlyphs & 0x000000FF); + buffer[0x09] = (metadata->numGlyphs & 0x0000FF00) >> 8; + buffer[0x0A] = (metadata->numGlyphs & 0x00FF0000) >> 16; + buffer[0x0B] = (metadata->numGlyphs & 0xFF000000) >> 24; + buffer[0x0C] = metadata->maxWidth; + buffer[0x0D] = metadata->maxHeight; + buffer[0x0E] = metadata->glyphWidth; + buffer[0x0F] = metadata->glyphHeight; + + ConvertToNitroFont(image->pixels, buffer + metadata->size, numRows, metadata); + memcpy(buffer + metadata->widthTableOffset, metadata->glyphWidthTable, metadata->numGlyphs); + + WriteWholeFile(path, buffer, bufferSize); + + free(buffer); +} + +void FreeNtrFontMetadata(struct NtrFontMetadata *metadata) +{ + free(metadata->glyphWidthTable); + free(metadata); } diff --git a/tools/nitrogfx/font.h b/tools/nitrogfx/font.h index 45086d02a..70dca0808 100644 --- a/tools/nitrogfx/font.h +++ b/tools/nitrogfx/font.h @@ -5,6 +5,7 @@ #include #include "gfx.h" +#include "options.h" void ReadLatinFont(char *path, struct Image *image); void WriteLatinFont(char *path, struct Image *image); @@ -12,5 +13,8 @@ void ReadHalfwidthJapaneseFont(char *path, struct Image *image); void WriteHalfwidthJapaneseFont(char *path, struct Image *image); void ReadFullwidthJapaneseFont(char *path, struct Image *image); void WriteFullwidthJapaneseFont(char *path, struct Image *image); +void ReadNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata, bool useSubscreenPalette); +void WriteNtrFont(char *path, struct Image *image, struct NtrFontMetadata *metadata); +void FreeNtrFontMetadata(struct NtrFontMetadata *metadata); #endif // FONT_H diff --git a/tools/nitrogfx/gfx.c b/tools/nitrogfx/gfx.c index 2b8c7b4b1..ffbece9ad 100644 --- a/tools/nitrogfx/gfx.c +++ b/tools/nitrogfx/gfx.c @@ -1,25 +1,26 @@ // Copyright (c) 2015 YamaArashi, 2021-2024 red031000 -#include "gfx.h" - -#include -#include +#include #include #include -#include - +#include +#include #include "global.h" - +#include "gfx.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; } @@ -28,8 +29,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)) @@ -38,7 +39,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; @@ -54,18 +56,19 @@ 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) { - int tilesSoFar = 0; - int rowsSoFar = 0; +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; int chunkStartY = 0; - int pitch = chunksWide * colsPerChunk; + int pitch = chunksWide * colsPerChunk; for (int i = 0; i < numTiles; i++) { for (int j = 0; j < 8; j++) { - int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; - int idxComponentX = chunkStartX * colsPerChunk + tilesSoFar; - unsigned char srcPixelOctet = *src++; + int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; + int idxComponentX = chunkStartX * colsPerChunk + tilesSoFar; + unsigned char srcPixelOctet = *src++; unsigned char *destPixelOctet = &dest[idxComponentY * pitch + idxComponentX]; for (int k = 0; k < 8; k++) { @@ -79,25 +82,26 @@ 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) { - int tilesSoFar = 0; - int rowsSoFar = 0; +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; int chunkStartY = 0; - int pitch = (chunksWide * colsPerChunk) * 4; + int pitch = (chunksWide * colsPerChunk) * 4; 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 < 4; k++) { - int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 4 + k; + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 4 + k; unsigned char srcPixelPair = *src++; - unsigned char leftPixel = srcPixelPair & 0xF; - unsigned char rightPixel = srcPixelPair >> 4; + unsigned char leftPixel = srcPixelPair & 0xF; + unsigned char rightPixel = srcPixelPair >> 4; if (invertColors) { - leftPixel = 15 - leftPixel; + leftPixel = 15 - leftPixel; rightPixel = 15 - rightPixel; } @@ -109,36 +113,40 @@ static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int nu } } -static uint32_t ConvertFromScanned4Bpp(unsigned char *src, unsigned char *dest, int fileSize, bool invertColours, bool scanFrontToBack) { +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) { + 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] = val; src[i + 1] = val >> 8; - encValue = encValue * 1103515245; - encValue = encValue + 24691; + encValue = encValue * 1103515245; + encValue = encValue + 24691; } } else { encValue = (src[fileSize - 1] << 8) | src[fileSize - 2]; - for (int i = fileSize; i > 0; i -= 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; + encValue = encValue * 1103515245; + encValue = encValue + 24691; } } - for (int i = 0; i < fileSize; i++) { + for (int i = 0; i < fileSize; i++) + { unsigned char srcPixelPair = src[i]; - unsigned char leftPixel = srcPixelPair & 0xF; - unsigned char rightPixel = srcPixelPair >> 4; + unsigned char leftPixel = srcPixelPair & 0xF; + unsigned char rightPixel = srcPixelPair >> 4; if (invertColours) { - leftPixel = 15 - leftPixel; + leftPixel = 15 - leftPixel; rightPixel = 15 - rightPixel; } @@ -147,24 +155,24 @@ static uint32_t ConvertFromScanned4Bpp(unsigned char *src, unsigned char *dest, return encValue; } -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; +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; int chunkStartY = 0; - int pitch = (chunksWide * colsPerChunk) * 8; + 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++) { - int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; unsigned char srcPixel = *src++; - if (invertColors) { + if (invertColors) srcPixel = 255 - srcPixel; - } dest[idxComponentY * pitch + idxComponentX] = srcPixel; } @@ -174,30 +182,34 @@ static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int nu } } -static uint32_t ConvertFromScanned8Bpp(unsigned char *src, unsigned char *dest, int fileSize, bool invertColours, bool scanFrontToBack) { +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) { + 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] = val; src[i + 1] = val >> 8; - encValue = encValue * 1103515245; - encValue = encValue + 24691; + encValue = encValue * 1103515245; + encValue = encValue + 24691; } } else { encValue = (src[fileSize - 1] << 8) | src[fileSize - 2]; - for (int i = fileSize; i > 0; i -= 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; + encValue = encValue * 1103515245; + encValue = encValue + 24691; } } - for (int i = 0; i < fileSize; i++) { + for (int i = 0; i < fileSize; i++) + { unsigned char srcPixel = src[i]; if (invertColours) { @@ -209,18 +221,19 @@ static uint32_t ConvertFromScanned8Bpp(unsigned char *src, unsigned char *dest, return encValue; } -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; +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; int chunkStartY = 0; - int pitch = chunksWide * colsPerChunk; + int pitch = chunksWide * colsPerChunk; for (int i = 0; i < numTiles; i++) { for (int j = 0; j < 8; j++) { - int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; - int idxComponentX = chunkStartX * colsPerChunk + tilesSoFar; - unsigned char srcPixelOctet = src[idxComponentY * pitch + idxComponentX]; + int idxComponentY = (chunkStartY * rowsPerChunk + rowsSoFar) * 8 + j; + int idxComponentX = chunkStartX * colsPerChunk + tilesSoFar; + unsigned char srcPixelOctet = src[idxComponentY * pitch + idxComponentX]; unsigned char *destPixelOctet = dest++; for (int k = 0; k < 8; k++) { @@ -234,25 +247,26 @@ 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) { - int tilesSoFar = 0; - int rowsSoFar = 0; +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; int chunkStartY = 0; - int pitch = (chunksWide * colsPerChunk) * 4; + int pitch = (chunksWide * colsPerChunk) * 4; 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 < 4; k++) { - int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 4 + k; + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 4 + k; unsigned char srcPixelPair = src[idxComponentY * pitch + idxComponentX]; - unsigned char leftPixel = srcPixelPair >> 4; - unsigned char rightPixel = srcPixelPair & 0xF; + unsigned char leftPixel = srcPixelPair >> 4; + unsigned char rightPixel = srcPixelPair & 0xF; if (invertColors) { - leftPixel = 15 - leftPixel; + leftPixel = 15 - leftPixel; rightPixel = 15 - rightPixel; } @@ -264,55 +278,60 @@ 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++) { +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; + unsigned char leftPixel = srcPixelPair & 0xF; + unsigned char rightPixel = srcPixelPair >> 4; if (invertColours) { - leftPixel = 15 - leftPixel; + 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) { + for (int i = fileSize - 1; i > 0; i -= 2) + { uint16_t val = dest[i - 1] | (dest[i] << 8); - encValue = (encValue - 24691) * 4005161829; + encValue = (encValue - 24691) * 4005161829; val ^= (encValue & 0xFFFF); - dest[i] = (val >> 8); + dest[i] = (val >> 8); dest[i - 1] = val; } - } else if (scanMode == 1) { - for (int i = 1; i < fileSize; i += 2) { + } + 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; + encValue = (encValue - 24691) * 4005161829; val ^= (encValue & 0xFFFF); - dest[i] = (val >> 8); + 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) { - int tilesSoFar = 0; - int rowsSoFar = 0; +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; int chunkStartY = 0; - int pitch = (chunksWide * colsPerChunk) * 8; + 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++) { - int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; + int idxComponentX = (chunkStartX * colsPerChunk + tilesSoFar) * 8 + k; unsigned char srcPixel = src[idxComponentY * pitch + idxComponentX]; - if (invertColors) { + if (invertColors) srcPixel = 255 - srcPixel; - } *dest++ = srcPixel; } @@ -322,7 +341,8 @@ 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) { +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; @@ -332,22 +352,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->width = tilesWide * 8; + image->height = tilesTall * 8; image->bitDepth = bitDepth; - image->pixels = calloc(tilesWide * tilesTall, tileSize); + 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 @@ -366,23 +383,27 @@ 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, bool scanFrontToBack) +{ 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"); } bitDepth = bitDepth ? bitDepth : (charHeader[0xC] == 3 ? 4 : 8); - if (bitDepth == 4) { + if (bitDepth == 4) + { image->palette.numColors = 16; } @@ -392,57 +413,61 @@ uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int tileSize = bitDepth * 8; // number of bytes per tile - int tilesWide_in = ReadS16(charHeader, 0xA); - if (tilesWide_in < 0 && tilesWide == 0) { - tilesWide = 1; - } else if (tilesWide == 0) { - tilesWide = tilesWide_in; + if (tilesWide == 0) { + tilesWide = ReadS16(charHeader, 0xA); + if (tilesWide < 0) { + tilesWide = 1; + } } int numTiles = ReadS32(charHeader, 0x18) / (64 / (8 / bitDepth)); int tilesTall = ReadS16(charHeader, 0x8); - if (tilesTall < 0 || tilesWide != tilesWide_in) { + 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->width = tilesWide * 8; + image->height = tilesTall * 8; image->bitDepth = bitDepth; - image->pixels = calloc(tilesWide * tilesTall, tileSize); + 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; + 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; } - } 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: + ConvertFromTiles4Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + break; + case 8: + ConvertFromTiles8Bpp(imageData, image->pixels, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + break; } } @@ -450,42 +475,37 @@ uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, return key; } -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; + 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 @@ -506,95 +526,103 @@ 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, uint32_t scanMode, + uint32_t mappingType, uint32_t key, bool wrongSize) +{ 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 (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; + 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 (scanMode) + { + switch (bitDepth) + { + case 4: + ConvertToScanned4Bpp(image->pixels, pixelBuffer, bufferSize, invertColors, key, scanMode); + break; + case 8: + FATAL_ERROR("8Bpp not supported yet.\n"); + 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: + ConvertToTiles8Bpp(image->pixels, pixelBuffer, numTiles, chunksWide, colsPerChunk, rowsPerChunk, + invertColors); + break; } } 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 { - charHeader[8] = 0xFF; - charHeader[9] = 0xFF; + } + 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 } } @@ -604,32 +632,34 @@ void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int 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 (scanMode) + { + 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; @@ -641,7 +671,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; @@ -657,91 +688,98 @@ 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; for (int i = 0; i < palette->numColors; i++) { - uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[i * 2]; - palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry)); + uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[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)); + palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry)); } free(data); } -void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex) { +void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted) +{ int fileSize; unsigned char *data = ReadWholeFile(path, &fileSize); - 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 (palIndex == 0) { palette->numColors = paletteSize / 2; } else { - palette->numColors = bitdepth == 4 ? 16 : 256; // remove header and divide by 2 + palette->numColors = bitdepth == 4 ? 16 : 256; //remove header and divide by 2 --palIndex; } unsigned char *paletteData = paletteHeader + 0x18; - for (int i = 0; i < 256; i++) { - if (i < palette->numColors) { - uint16_t paletteEntry = (paletteData[(32 * palIndex) + i * 2 + 1] << 8) | paletteData[(32 * palIndex) + i * 2]; - palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry)); + for (int i = 0; i < 256; i++) + { + if (i < palette->numColors) + { + uint16_t paletteEntry = (paletteData[(32 * palIndex) + i * 2 + 1] << 8) | paletteData[(32 * 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 { - palette->colors[i].red = 0; + palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry)); + } + else + { + palette->colors[i].red = 0; palette->colors[i].green = 0; - palette->colors[i].blue = 0; + palette->colors[i].blue = 0; } } 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); + 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); + unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue); uint16_t paletteEntry = SET_GBA_PAL(red, green, blue); @@ -752,22 +790,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) { +void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, bool inverted) +{ FILE *fp = fopen(path, "wb"); - 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; + 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); @@ -776,59 +815,67 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in ++numSections; } - // NCLR header + //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 ? 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 - palHeader[16] = size & 0xFF; - palHeader[17] = (size >> 8) & 0xFF; - palHeader[18] = (size >> 16) & 0xFF; - palHeader[19] = (size >> 24) & 0xFF; + //size + int colorSize = inverted ? 0x200 - size : size; + palHeader[16] = colorSize & 0xFF; + palHeader[17] = (colorSize >> 8) & 0xFF; + palHeader[18] = (colorSize >> 16) & 0xFF; + palHeader[19] = (colorSize >> 24) & 0xFF; fwrite(palHeader, 1, 0x18, fp); - 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 * 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); + unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue); uint16_t paletteEntry = SET_GBA_PAL(red, green, blue); - colours[i * 2] = paletteEntry & 0xFF; + colours[i * 2] = paletteEntry & 0xFF; colours[i * 2 + 1] = paletteEntry >> 8; - } else { - colours[i * 2] = 0x00; + } + else + { + colours[i * 2] = 0x00; colours[i * 2 + 1] = 0x00; } } - if (ir) { + if (ir) + { colours[colourNum * 2 - 2] = 'I'; colours[colourNum * 2 - 1] = 'R'; } @@ -836,22 +883,24 @@ 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 }; - pcmp_header[4] = pcmpSize & 0xFF; - pcmp_header[5] = (pcmpSize >> 8) & 0xFF; - pcmp_header[6] = (pcmpSize >> 16) & 0xFF; - pcmp_header[7] = (pcmpSize >> 24) & 0xFF; - pcmp_header[8] = pcmpColorNum & 0xFF; - pcmp_header[9] = (pcmpColorNum >> 8) & 0xFF; + 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; + pcmp_header[7] = (pcmpSize >> 24) & 0xFF; + pcmp_header[8] = pcmpColorNum & 0xFF; + pcmp_header[9] = (pcmpColorNum >> 8) & 0xFF; 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] = i & 0xFF; pcmp_data[i * 2 + 1] = (i >> 8) & 0xFF; } fwrite(pcmp_data, 1, pcmpColorNum * 2, fp); @@ -861,9 +910,13 @@ 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; + options->extended = data[blockOffset + 0xA] == 1; + + int vramTransferOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8); + options->vramTransferEnabled = vramTransferOffset > 0; /*if (!options->extended) { //in theory not extended should be implemented, however not 100% sure @@ -873,23 +926,25 @@ void ReadNtrCell_CEBK(unsigned char *restrict data, unsigned int blockOffset, un options->mappingType = data[blockOffset + 0x10]; options->cells = malloc(sizeof(struct Cell *) * options->cellCount); - int celSize = options->extended ? 0x10 : 0x8; + 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 > blockSize) { - FATAL_ERROR("corrupted CEBK block"); + if (offset + celSize > blockOffset + blockSize) { + FATAL_ERROR("corrupted CEBK block\n"); } - options->cells[i] = malloc(sizeof(struct Cell)); - options->cells[i]->oamCount = data[offset] | (data[offset + 1] << 8); - short cellAttrs = data[offset + 2] | (data[offset + 3] << 8); - options->cells[i]->attributes.hFlip = (cellAttrs >> 8) & 1; - options->cells[i]->attributes.vFlip = (cellAttrs >> 9) & 1; - options->cells[i]->attributes.hvFlip = (cellAttrs >> 10) & 1; - options->cells[i]->attributes.boundingRect = (cellAttrs >> 11) & 1; + options->cells[i] = malloc(sizeof(struct Cell)); + options->cells[i]->oamCount = data[offset] | (data[offset + 1] << 8); + short cellAttrs = data[offset + 2] | (data[offset + 3] << 8); + options->cells[i]->attributes.hFlip = (cellAttrs >> 8) & 1; + options->cells[i]->attributes.vFlip = (cellAttrs >> 9) & 1; + options->cells[i]->attributes.hvFlip = (cellAttrs >> 10) & 1; + 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); @@ -898,77 +953,104 @@ 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) + { + 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++) + { + 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; + } + } } -void ReadNtrCell_LABL(unsigned char *restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options) { - int count = 0; +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) { + options->labels = malloc(sizeof(char *) * count); + for (int i = 0; i < count; ++i) + { int offset = textStart + (data[blockOffset + 4 * i + 8] | (data[blockOffset + 4 * i + 9] << 8) | (data[blockOffset + 4 * i + 10] << 16) | (data[blockOffset + 4 * i + 11] << 24)); - if (offset > blockOffset + blockSize) { - FATAL_ERROR("corrupted LABL block"); + if (offset > blockOffset + blockSize) + { + FATAL_ERROR("corrupted LABL block\n"); } unsigned long slen = strnlen((char *)data + offset, blockSize - offset); options->labels[i] = malloc(slen + 1); @@ -976,72 +1058,109 @@ void ReadNtrCell_LABL(unsigned char *restrict data, unsigned int blockOffset, un } } -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"); } - options->labelEnabled = data[0xE] != 1; + options->labelEnabled = false; unsigned int blockSize; offset = FindNitroDataBlock(data, "KBEC", fileSize, &blockSize); - if (offset != -1u) { + if (offset != -1u) + { 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); } 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) + { + kbecSize += 0x08 + (0x08 * options->cellCount); + } + // add 0x06 for number of OAMs - can be more than 1 + for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum) + { + kbecSize += options->cells[idx / iterNum]->oamCount * 0x06; } - unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + options->cellCount * (options->extended ? 0x16 : 0xe); + // KBEC size is padded to be 4-byte aligned + kbecSize += kbecSize % 4; - if (options->labelEnabled) { - for (int j = 0; j < options->labelCount; j++) { - totalSize += (unsigned)strlen(options->labels[j]) + 5; // strlen + terminator + pointer + 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 } } 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 } - unsigned int size = options->cellCount * (options->extended ? 0x16 : 0xe); + 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] = (size + 0x20) & 0xFF; // size - KBECHeader[5] = (size + 0x20) >> 8; // unlikely to be more than 16 bits, but there are 32 allocated, change if necessary + KBECHeader[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 VRAM transfer data within KBEC section (offset from KBEC start + 0x08) + if (options->vramTransferEnabled) + { + unsigned int vramTransferLength = 0x08 + (0x08 * options->cellCount); + unsigned int vramTransferOffset = (kbecSize + 0x20) - vramTransferLength - 0x08; + KBECHeader[20] = vramTransferOffset & 0xFF; + KBECHeader[21] = (vramTransferOffset >> 8) & 0xFF; + KBECHeader[22] = (vramTransferOffset >> 16) & 0xFF; + KBECHeader[23] = (vramTransferOffset >> 24) & 0xFF; + } fwrite(KBECHeader, 1, 0x20, fp); - unsigned char *KBECContents = malloc(size); + unsigned char *KBECContents = malloc(kbecSize); - memset(KBECContents, 0, size); + memset(KBECContents, 0, kbecSize); /*if (!options->extended) { @@ -1050,93 +1169,129 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) { }*/ int i; - int iterNum = (options->extended ? 0x10 : 0x8); int totalOam = 0; - for (i = 0; i < options->cellCount * iterNum; i += iterNum) { - 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 + 9] = options->cells[i / iterNum]->maxX >> 8; - KBECContents[i + 10] = options->cells[i / iterNum]->maxY & 0xff; // maxY + 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 + 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; } } - fwrite(KBECContents, 1, size, fp); + // VRAM transfer data + if (options->vramTransferEnabled) + { + // max transfer size + fixed offset 0x08 + KBECContents[offset] = options->vramTransferMaxSize & 0xFF; + KBECContents[offset + 1] = (options->vramTransferMaxSize >> 8) & 0xFF; + KBECContents[offset + 2] = (options->vramTransferMaxSize >> 16) & 0xFF; + KBECContents[offset + 3] = (options->vramTransferMaxSize >> 24) & 0xFF; + + KBECContents[offset + 4] = 0x08; + + offset += 8; + + // write a VRAM transfer block for each cell + for (int idx = 0; idx < options->cellCount; idx++) + { + // offset + KBECContents[offset] = options->transferData[idx]->sourceDataOffset & 0xFF; + KBECContents[offset + 1] = (options->transferData[idx]->sourceDataOffset >> 8) & 0xFF; + KBECContents[offset + 2] = (options->transferData[idx]->sourceDataOffset >> 16) & 0xFF; + KBECContents[offset + 3] = (options->transferData[idx]->sourceDataOffset >> 24) & 0xFF; + + // size + KBECContents[offset + 4] = options->transferData[idx]->size & 0xFF; + KBECContents[offset + 5] = (options->transferData[idx]->size >> 8) & 0xFF; + KBECContents[offset + 6] = (options->transferData[idx]->size >> 16) & 0xFF; + KBECContents[offset + 7] = (options->transferData[idx]->size >> 24) & 0xFF; + offset += 8; + } + } + + fwrite(KBECContents, 1, kbecSize, fp); free(KBECContents); - 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; } @@ -1144,14 +1299,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; @@ -1159,8 +1315,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; } @@ -1168,7 +1325,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); } @@ -1176,18 +1333,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; @@ -1214,28 +1372,30 @@ 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"); } options->sequenceCount = data[0x18] | (data[0x19] << 8); - options->frameCount = data[0x1A] | (data[0x1B] << 8); + options->frameCount = data[0x1A] | (data[0x1B] << 8); 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)); } @@ -1243,16 +1403,18 @@ 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) { - options->sequenceData[i]->frameCount = data[offset] | (data[offset + 1] << 8); - options->sequenceData[i]->loopStartFrame = data[offset + 2] | (data[offset + 3] << 8); + 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); - options->sequenceData[i]->animationType = data[offset + 6] | (data[offset + 7] << 8); - options->sequenceData[i]->playbackMode = data[offset + 8] | (data[offset + 9] << 8) | (data[offset + 10] << 16) | (data[offset + 11] << 24); - frameOffsets[i] = data[offset + 12] | (data[offset + 13] << 8) | (data[offset + 14] << 16) | (data[offset + 15] << 24); + options->sequenceData[i]->animationType = data[offset + 6] | (data[offset + 7] << 8); + options->sequenceData[i]->playbackMode = data[offset + 8] | (data[offset + 9] << 8) | (data[offset + 10] << 16) | (data[offset + 11] << 24); + 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)); } } @@ -1260,27 +1422,36 @@ 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++) { - int frameOffset = offset + frameOffsets[i] + j * 0x8; + 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 + options->sequenceData[i]->frameData[j]->frameDelay = data[frameOffset + 4] | (data[frameOffset + 5] << 8); + //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; } @@ -1291,14 +1462,14 @@ 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 + 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; @@ -1306,59 +1477,87 @@ 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)); } - int resultOffset = 0; - for (int i = 0; i < options->resultCount; i++) { - if (data[offset + 2] == 0xCC && data[offset + 3] == 0xCC) { - options->animationResults[i]->resultType = 0; - } else if (data[offset + 2] == 0xEF && data[offset + 3] == 0xBE) { - options->animationResults[i]->resultType = 2; - } else { - options->animationResults[i]->resultType = 1; + // store the animationElement of the corresponding sequence as this result's resultType + for (int i = 0; i < options->sequenceCount; i++) + { + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { + options->animationResults[options->sequenceData[i]->frameData[j]->resultId]->resultType = options->sequenceData[i]->animationElement; } - for (int j = 0; j < options->sequenceCount; j++) { - for (int k = 0; k < options->sequenceData[j]->frameCount; k++) { - if (options->sequenceData[j]->frameData[k]->resultOffset == resultOffset) { - options->sequenceData[j]->frameData[k]->resultId = i; + } + + int resultOffset = 0; + int lastSequence = 0; + 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) + { + lastSequence = j; + if (resultOffset % 4 != 0) + { + resultOffset += 0x2; + offset += 0x2; + } + } + found = true; + break; } } + if (found) break; } - switch (options->animationResults[i]->resultType) { - case 0: // index - options->animationResults[i]->index = data[offset] | (data[offset + 1] << 8); - resultOffset += 0x4; - offset += 0x4; - 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; + 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 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; } } - if (options->labelEnabled) { + // add any missed padding from the final frame before processing labels + if (offset % 4 != 0) offset += 2; + + if (options->labelEnabled) + { options->labelCount = options->sequenceCount; //*should* be the same - options->labels = malloc(sizeof(char *) * options->labelCount); - offset += 0x8 + options->labelCount * 0x4; // skip to label data - for (int i = 0; i < options->labelCount; i++) { + options->labels = malloc(sizeof(char *) * options->labelCount); + 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; @@ -1368,40 +1567,96 @@ 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; - // todo: check these - for (int i = 0; i < options->resultCount; i++) { - if (options->animationResults[i]->resultType == 0) { - totalSize += 0x4; - } else if (options->animationResults[i]->resultType == 1) { + for (int i = 0; i < options->resultCount; i++) + { + if (options->animationResults[i]->resultType == 0) + totalSize += 0x2; + 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 + // then add 0x02 to totalSize if padding exists. + // padding exists if the animation results for that sequence are not 4-byte aligned. + // also flag the last result for the sequence with `padded` to save having to redo this same step later. + int *usedResults = malloc(sizeof(int) * options->frameCount); + memset(usedResults, -1, sizeof(int) * options->frameCount); + for (int i = 0; i < options->sequenceCount; i++) + { + int sequenceLen = 0; + int resultIndex = 0; + int lastNewResultIndex = -1; + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { + // check if the result has already been used + bool isUsed = false; + for (resultIndex = 0; resultIndex < options->resultCount; resultIndex++) + { + if (usedResults[resultIndex] == options->sequenceData[i]->frameData[j]->resultId) + { + isUsed = true; + break; + } + + // if not already used, add it to the list + if (usedResults[resultIndex] == -1) + { + usedResults[resultIndex] = options->sequenceData[i]->frameData[j]->resultId; + lastNewResultIndex = options->sequenceData[i]->frameData[j]->resultId; + break; + } + } + + // if not already used, add it to the result size for the sequence + if (!isUsed) + { + if (options->animationResults[resultIndex]->resultType == 0) + sequenceLen += 0x2; + else if (options->animationResults[resultIndex]->resultType == 1) + sequenceLen += 0x10; + else if (options->animationResults[resultIndex]->resultType == 2) + sequenceLen += 0x8; + } + } + if (sequenceLen % 4 != 0 && lastNewResultIndex != -1) + { + totalSize += 0x02; + // mark the last new animationResult index for the sequence as padded, this saves needing to check this again later + options->animationResults[lastNewResultIndex]->padded = true; } } + free(usedResults); + unsigned int KNBASize = totalSize; - if (options->labelEnabled) { + 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; @@ -1430,22 +1685,23 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { fwrite(KBNAHeader, 1, 0x20, fp); - int contentsSize = KNBASize - 0x20; + int contentsSize = KNBASize - 0x20; unsigned char *KBNAContents = malloc(contentsSize); int i; int framePtrCounter = 0; - 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; - KBNAContents[i + 3] = options->sequenceData[i / 0x10]->loopStartFrame >> 8; - KBNAContents[i + 4] = options->sequenceData[i / 0x10]->animationElement & 0xff; - KBNAContents[i + 5] = options->sequenceData[i / 0x10]->animationElement >> 8; - KBNAContents[i + 6] = options->sequenceData[i / 0x10]->animationType & 0xff; - KBNAContents[i + 7] = options->sequenceData[i / 0x10]->animationType >> 8; - KBNAContents[i + 8] = options->sequenceData[i / 0x10]->playbackMode & 0xff; - KBNAContents[i + 9] = (options->sequenceData[i / 0x10]->playbackMode >> 8) & 0xff; + 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; + KBNAContents[i + 3] = options->sequenceData[i / 0x10]->loopStartFrame >> 8; + KBNAContents[i + 4] = options->sequenceData[i / 0x10]->animationElement & 0xff; + KBNAContents[i + 5] = (options->sequenceData[i / 0x10]->animationElement >> 8) & 0xff; + KBNAContents[i + 6] = options->sequenceData[i / 0x10]->animationType & 0xff; + KBNAContents[i + 7] = (options->sequenceData[i / 0x10]->animationType >> 8) & 0xff; + KBNAContents[i + 8] = options->sequenceData[i / 0x10]->playbackMode & 0xff; + KBNAContents[i + 9] = (options->sequenceData[i / 0x10]->playbackMode >> 8) & 0xff; KBNAContents[i + 10] = (options->sequenceData[i / 0x10]->playbackMode >> 16) & 0xff; KBNAContents[i + 11] = options->sequenceData[i / 0x10]->playbackMode >> 24; KBNAContents[i + 12] = framePtrCounter & 0xff; @@ -1457,19 +1713,21 @@ 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) { - resPtr += 0x4; - } else if (options->animationResults[l]->resultType == 1) { + if (options->animationResults[l]->resultType == 0) + resPtr += 0x2; + 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; } - KBNAContents[j + (k * 8)] = resPtr & 0xff; + KBNAContents[j + (k * 8)] = resPtr & 0xff; KBNAContents[j + (k * 8) + 1] = (resPtr >> 8) & 0xff; KBNAContents[j + (k * 8) + 2] = (resPtr >> 16) & 0xff; KBNAContents[j + (k * 8) + 3] = resPtr >> 24; @@ -1481,49 +1739,56 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { j += options->sequenceData[m]->frameCount * 8; } - // todo: these are extrapolated, need confirming int resPtrCounter = j; - for (int k = 0; k < options->resultCount; k++) { - switch (options->animationResults[k]->resultType) { - case 0: - KBNAContents[resPtrCounter] = options->animationResults[k]->index & 0xff; - KBNAContents[resPtrCounter + 1] = options->animationResults[k]->index >> 8; - KBNAContents[resPtrCounter + 2] = 0xCC; - KBNAContents[resPtrCounter + 3] = 0xCC; - resPtrCounter += 0x4; - 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; + 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 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) + { + KBNAContents[resPtrCounter] = 0xCC; + KBNAContents[resPtrCounter + 1] = 0xCC; + resPtrCounter += 0x2; } } @@ -1531,9 +1796,11 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) { 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; } @@ -1541,14 +1808,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; @@ -1556,8 +1824,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; } @@ -1565,8 +1834,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 91f697875..705b98ab3 100644 --- a/tools/nitrogfx/gfx.h +++ b/tools/nitrogfx/gfx.h @@ -58,9 +58,9 @@ void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int uint32_t mappingType, uint32_t key, bool wrongSize); void FreeImage(struct Image *image); void ReadGbaPalette(char *path, struct Palette *palette); -void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex); +void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted); void WriteGbaPalette(char *path, struct Palette *palette); -void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp); +void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, bool inverted); void ReadNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrScreen(char *path, struct JsonToScreenOptions *options); diff --git a/tools/nitrogfx/json.c b/tools/nitrogfx/json.c index 12bc8a956..f0156700c 100644 --- a/tools/nitrogfx/json.c +++ b/tools/nitrogfx/json.c @@ -47,11 +47,13 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled"); + cJSON *vramTransferBool = cJSON_GetObjectItemCaseSensitive(json, "vramTransferEnabled"); cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended"); cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount"); cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType"); options->labelEnabled = GetBool(labelBool); + options->vramTransferEnabled = GetBool(vramTransferBool); options->extended = GetBool(extended); options->cellCount = GetInt(cellCount); options->mappingType = GetInt(mappingType); @@ -77,6 +79,30 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } } + if (options->vramTransferEnabled) + { + cJSON *vramTransferMaxSize = cJSON_GetObjectItemCaseSensitive(json, "vramTransferMaxSize"); + options->vramTransferMaxSize = GetInt(vramTransferMaxSize); + + options->transferData = malloc(sizeof(struct CellVramTransferData *) * options->cellCount); + + cJSON *transfers = cJSON_GetObjectItemCaseSensitive(json, "transferData"); + cJSON *transfer = NULL; + + int j = 0; + cJSON_ArrayForEach(transfer, transfers) + { + cJSON *vramTransferOffset = cJSON_GetObjectItemCaseSensitive(transfer, "offset"); + cJSON *vramTransferSize = cJSON_GetObjectItemCaseSensitive(transfer, "size"); + + options->transferData[j] = malloc(sizeof(struct CellVramTransferData)); + options->transferData[j]->sourceDataOffset = GetInt(vramTransferOffset); + options->transferData[j]->size = GetInt(vramTransferSize); + + j++; + } + } + for (int i = 0; i < options->cellCount; i++) { options->cells[i] = malloc(sizeof(struct Cell)); @@ -195,6 +221,7 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled); cJSON_AddBoolToObject(ncer, "extended", options->extended); + cJSON_AddBoolToObject(ncer, "vramTransferEnabled", options->vramTransferEnabled); cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount); cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType); @@ -263,6 +290,20 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount); } + if (options->vramTransferEnabled) + { + cJSON_AddNumberToObject(ncer, "vramTransferMaxSize", options->vramTransferMaxSize); + cJSON *transfers = cJSON_AddArrayToObject(ncer, "transferData"); + + for (int idx = 0; idx < options->cellCount; idx++) + { + cJSON *transfer = cJSON_CreateObject(); + cJSON_AddNumberToObject(transfer, "offset", options->transferData[idx]->sourceDataOffset); + cJSON_AddNumberToObject(transfer, "size", options->transferData[idx]->size); + cJSON_AddItemToArray(transfers, transfer); + } + } + char *jsonString = cJSON_Print(ncer); cJSON_Delete(ncer); return jsonString; @@ -600,6 +641,14 @@ void FreeNCERCell(struct JsonToCellOptions *options) } free(options->labels); } + if (options->vramTransferEnabled) + { + for (int j = 0; j < options->cellCount; j++) + { + free(options->transferData[j]); + } + free(options->transferData); + } free(options->cells); free(options); } @@ -638,3 +687,79 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options) free(options->animationResults); free(options); } + +char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata) +{ + cJSON *json = cJSON_CreateObject(); + + cJSON_AddNumberToObject(json, "maxGlyphWidth", metadata->maxWidth); + cJSON_AddNumberToObject(json, "maxGlyphHeight", metadata->maxHeight); + + cJSON *glyphWidths = cJSON_AddArrayToObject(json, "glyphWidths"); + for (int i = 0; i < metadata->numGlyphs; i++) + { + cJSON *width = cJSON_CreateNumber(metadata->glyphWidthTable[i]); + cJSON_AddItemToArray(glyphWidths, width); + } + + char *jsonString = cJSON_Print(json); + cJSON_Delete(json); + return jsonString; +} + +#define TILE_DIMENSION_PIXELS 8 +#define PIXELS_FOR_DIMENSION(dim) ((dim) * TILE_DIMENSION_PIXELS) +#define TILES_FOR_PIXELS(num) (((num) + TILE_DIMENSION_PIXELS - 1) / TILE_DIMENSION_PIXELS) +#define PIXELS_PER_BYTE_2BPP 4 +#define NTR_FONT_HEADER_SIZE 16 + +struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path) +{ + int fileLength; + unsigned char *jsonString = ReadWholeFile(path, &fileLength); + + cJSON *json = cJSON_Parse((const char *)jsonString); + if (json == NULL) + { + const char *errorPtr = cJSON_GetErrorPtr(); + FATAL_ERROR("Error in line \"%s\"\n", errorPtr); + } + + cJSON *labelMaxGlyphWidth = cJSON_GetObjectItemCaseSensitive(json, "maxGlyphWidth"); + cJSON *labelMaxGlyphHeight = cJSON_GetObjectItemCaseSensitive(json, "maxGlyphHeight"); + cJSON *labelGlyphWidths = cJSON_GetObjectItemCaseSensitive(json, "glyphWidths"); + int numGlyphs = cJSON_GetArraySize(labelGlyphWidths); + + struct NtrFontMetadata *metadata = malloc(sizeof(struct NtrFontMetadata)); + + metadata->size = NTR_FONT_HEADER_SIZE; + metadata->numGlyphs = numGlyphs; + metadata->maxWidth = GetInt(labelMaxGlyphWidth); + metadata->maxHeight = GetInt(labelMaxGlyphHeight); + + metadata->glyphWidth = TILES_FOR_PIXELS(metadata->maxWidth); + metadata->glyphHeight = TILES_FOR_PIXELS(metadata->maxHeight); + + int glyphBitmapSize = (PIXELS_FOR_DIMENSION(metadata->glyphWidth) * PIXELS_FOR_DIMENSION(metadata->glyphHeight)) / PIXELS_PER_BYTE_2BPP; + metadata->widthTableOffset = metadata->size + (metadata->numGlyphs * glyphBitmapSize); + + metadata->glyphWidthTable = malloc(metadata->numGlyphs); + + uint8_t *glyphWidthCursor = metadata->glyphWidthTable; + cJSON *glyphWidthIter = NULL; + cJSON_ArrayForEach(glyphWidthIter, labelGlyphWidths) + { + if (!cJSON_IsNumber(glyphWidthIter)) + { + const char *errorPtr = cJSON_GetErrorPtr(); + FATAL_ERROR("Error in line \"%s\"\n", errorPtr); + } + + *glyphWidthCursor = glyphWidthIter->valueint; + glyphWidthCursor++; + } + + cJSON_Delete(json); + free(jsonString); + return metadata; +} diff --git a/tools/nitrogfx/json.h b/tools/nitrogfx/json.h index eb7e2add7..8bdf307ff 100644 --- a/tools/nitrogfx/json.h +++ b/tools/nitrogfx/json.h @@ -13,5 +13,7 @@ char *GetNANRJson(struct JsonToAnimationOptions *options); void FreeNCERCell(struct JsonToCellOptions *options); void FreeNSCRScreen(struct JsonToScreenOptions *options); void FreeNANRAnimation(struct JsonToAnimationOptions *options); +char *GetNtrFontMetadataJson(struct NtrFontMetadata *metadata); +struct NtrFontMetadata *ParseNtrFontMetadataJson(char *path); #endif //JSON_H diff --git a/tools/nitrogfx/lz.c b/tools/nitrogfx/lz.c index 97434ce50..de553178b 100644 --- a/tools/nitrogfx/lz.c +++ b/tools/nitrogfx/lz.c @@ -1,153 +1,188 @@ // Copyright (c) 2015 YamaArashi +#include #include #include +#include #include "global.h" #include "lz.h" unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize) { - if (srcSize < 4) - goto fail; + if (srcSize < 4) + goto fail; - int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; + int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; - unsigned char *dest = malloc(destSize); + unsigned char *dest = malloc(destSize); - if (dest == NULL) - goto fail; + if (dest == NULL) + goto fail; - int srcPos = 4; - int destPos = 0; + int srcPos = 4; + int destPos = 0; - for (;;) { - if (srcPos >= srcSize) - goto fail; + for (;;) { + if (srcPos >= srcSize) + goto fail; - unsigned char flags = src[srcPos++]; + unsigned char flags = src[srcPos++]; - for (int i = 0; i < 8; i++) { - if (flags & 0x80) { - if (srcPos + 1 >= srcSize) - goto fail; + for (int i = 0; i < 8; i++) { + if (flags & 0x80) { + if (srcPos + 1 >= srcSize) + goto fail; - int blockSize = (src[srcPos] >> 4) + 3; - int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1; + int blockSize = (src[srcPos] >> 4) + 3; + int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1; - srcPos += 2; + srcPos += 2; - int blockPos = destPos - blockDistance; + int blockPos = destPos - blockDistance; - // Some Ruby/Sapphire tilesets overflow. - if (destPos + blockSize > destSize) { - blockSize = destSize - destPos; - fprintf(stderr, "Destination buffer overflow.\n"); - } + // Some Ruby/Sapphire tilesets overflow. + if (destPos + blockSize > destSize) { + blockSize = destSize - destPos; + fprintf(stderr, "Destination buffer overflow.\n"); + } - if (blockPos < 0) - goto fail; + if (blockPos < 0) + goto fail; - for (int j = 0; j < blockSize; j++) - dest[destPos++] = dest[blockPos + j]; - } else { - if (srcPos >= srcSize || destPos >= destSize) - goto fail; + for (int j = 0; j < blockSize; j++) + dest[destPos++] = dest[blockPos + j]; + } else { + if (srcPos >= srcSize || destPos >= destSize) + goto fail; - dest[destPos++] = src[srcPos++]; - } + dest[destPos++] = src[srcPos++]; + } - if (destPos == destSize) { - *uncompressedSize = destSize; - return dest; - } + if (destPos == destSize) { + *uncompressedSize = destSize; + return dest; + } - flags <<= 1; - } - } + flags <<= 1; + } + } fail: - FATAL_ERROR("Fatal error while decompressing LZ file.\n"); + FATAL_ERROR("Fatal error while decompressing LZ file.\n"); } -unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance) +static void FindBestBlockForwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize) { - if (srcSize <= 0) - goto fail; - - int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8); - - // Round up to the next multiple of four. - worstCaseDestSize = (worstCaseDestSize + 3) & ~3; - - unsigned char *dest = malloc(worstCaseDestSize); - - if (dest == NULL) - goto fail; - - // header - dest[0] = 0x10; // LZ compression type - dest[1] = (unsigned char)srcSize; - dest[2] = (unsigned char)(srcSize >> 8); - dest[3] = (unsigned char)(srcSize >> 16); - - int srcPos = 0; - int destPos = 4; - - for (;;) { - unsigned char *flags = &dest[destPos++]; - *flags = 0; - - for (int i = 0; i < 8; i++) { - int bestBlockDistance = 0; - int bestBlockSize = 0; - int blockDistance = minDistance; - - while (blockDistance <= srcPos && blockDistance <= 0x1000) { - int blockStart = srcPos - blockDistance; - int blockSize = 0; - - while (blockSize < 18 - && srcPos + blockSize < srcSize - && src[blockStart + blockSize] == src[srcPos + blockSize]) - blockSize++; - - if (blockSize > bestBlockSize) { - bestBlockDistance = blockDistance; - bestBlockSize = blockSize; - - if (blockSize == 18) - break; - } - - blockDistance++; - } - - if (bestBlockSize >= 3) { - *flags |= (0x80 >> i); - srcPos += bestBlockSize; - bestBlockSize -= 3; - bestBlockDistance--; - dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8); - dest[destPos++] = (unsigned char)bestBlockDistance; - } else { - dest[destPos++] = src[srcPos++]; - } - - if (srcPos == srcSize) { - // Pad to multiple of 4 bytes. - int remainder = destPos % 4; - - if (remainder != 0) { - for (int i = 0; i < 4 - remainder; i++) - dest[destPos++] = 0; - } - - *compressedSize = destPos; - return dest; - } - } - } + int blockStart = srcPos < 0x1000 ? 0 : srcPos - 0x1000; + while (blockStart != srcPos) { + int blockSize = 0; + + while (blockSize < 18 + && srcPos + blockSize < srcSize + && src[blockStart + blockSize] == src[srcPos + blockSize]) + blockSize++; + + if (blockSize > *outBestBlockSize + && srcPos - blockStart >= minDistance) { + *outBestBlockDistance = srcPos - blockStart; + *outBestBlockSize = blockSize; + + if (blockSize == 18) + break; + } + + blockStart++; + } +} + +static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize) +{ + int blockDistance = minDistance; + + while (blockDistance <= srcPos && blockDistance <= 0x1000) { + int blockStart = srcPos - blockDistance; + int blockSize = 0; + + while (blockSize < 18 + && srcPos + blockSize < srcSize + && src[blockStart + blockSize] == src[srcPos + blockSize]) + blockSize++; + + if (blockSize > *outBestBlockSize) { + *outBestBlockDistance = blockDistance; + *outBestBlockSize = blockSize; + + if (blockSize == 18) + break; + } + + blockDistance++; + } +} + +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) +{ + if (srcSize <= 0) + goto fail; + + int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8); + + // Round up to the next multiple of four. + worstCaseDestSize = (worstCaseDestSize + 3) & ~3; + + unsigned char *dest = malloc(worstCaseDestSize); + + if (dest == NULL) + goto fail; + + // header + dest[0] = 0x10; // LZ compression type + dest[1] = (unsigned char)srcSize; + dest[2] = (unsigned char)(srcSize >> 8); + dest[3] = (unsigned char)(srcSize >> 16); + + int srcPos = 0; + int destPos = 4; + FindBestBlockFunc FindBestBlock = forwardIteration ? FindBestBlockForwards : FindBestBlockBackwards; + + for (;;) { + unsigned char *flags = &dest[destPos++]; + *flags = 0; + + for (int i = 0; i < 8; i++) { + int bestBlockDistance = 0; + int bestBlockSize = 0; + + FindBestBlock(src, srcPos, srcSize, minDistance, &bestBlockDistance, &bestBlockSize); + + if (bestBlockSize >= 3) { + *flags |= (0x80 >> i); + srcPos += bestBlockSize; + bestBlockSize -= 3; + bestBlockDistance--; + dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8); + dest[destPos++] = (unsigned char)bestBlockDistance; + } else { + dest[destPos++] = src[srcPos++]; + } + + if (srcPos == srcSize) { + // Pad to multiple of 4 bytes. + int remainder = destPos % 4; + + if (remainder != 0) { + for (int i = 0; i < 4 - remainder; i++) + dest[destPos++] = 0; + } + + *compressedSize = destPos; + return dest; + } + } + } fail: - FATAL_ERROR("Fatal error while compressing LZ file.\n"); + FATAL_ERROR("Fatal error while compressing LZ file.\n"); } diff --git a/tools/nitrogfx/lz.h b/tools/nitrogfx/lz.h index 90f56b643..fdc137023 100644 --- a/tools/nitrogfx/lz.h +++ b/tools/nitrogfx/lz.h @@ -3,7 +3,9 @@ #ifndef LZ_H #define LZ_H +#include "stdbool.h" + unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize); -unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance); +unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration); #endif // LZ_H diff --git a/tools/nitrogfx/main.c b/tools/nitrogfx/main.c index eb831fb3d..88fc48715 100644 --- a/tools/nitrogfx/main.c +++ b/tools/nitrogfx/main.c @@ -1,36 +1,39 @@ // Copyright (c) 2015 YamaArashi, 2021-2024 red031000 #include -#include #include #include - +#include #include "global.h" - -#include "convert_png.h" -#include "font.h" +#include "util.h" +#include "options.h" #include "gfx.h" -#include "huff.h" +#include "convert_png.h" #include "jasc_pal.h" -#include "json.h" #include "lz.h" -#include "options.h" #include "rl.h" -#include "util.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); }; -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; } @@ -43,16 +46,20 @@ 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 FILE *fp = fopen(inputPath, "rb"); - if (options->handleEmpty) { - if (fp != NULL) { + if (options->handleEmpty) + { + 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+"); fclose(out); fclose(fp); @@ -65,23 +72,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); + if (options->paletteFilePath != NULL) + { + ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false); image.hasPalette = true; - } else { + } + else + { image.hasPalette = false; } uint32_t key = ReadNtrImage(inputPath, options->width, 0, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->scanFrontToBack); - if (key) { - size_t outputStringSize = strlen(outputPath) + 5; - char *string = malloc(outputStringSize); - snprintf(string, outputStringSize, "%s.key", outputPath); + 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); @@ -94,7 +103,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; @@ -106,16 +116,20 @@ 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 FILE *fp = fopen(inputPath, "rb"); - if (options->handleEmpty) { - if (fp != NULL) { + if (options->handleEmpty) + { + 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+"); fclose(out); fclose(fp); @@ -127,269 +141,276 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions * fclose(fp); struct Image image; - image.bitDepth = options->bitDepth; + image.bitDepth = options->bitDepth == 0 ? 4 : options->bitDepth; ReadPng(inputPath, &image); uint32_t key = 0; - if (options->scanMode) { - size_t inputPathSize = strlen(inputPath) + 5; - char *string = malloc(inputPathSize); - snprintf(string, inputPathSize, "%s.key", inputPath); + if (options->scanMode) + { + char* string = malloc(strlen(inputPath) + 5); + sprintf(string, "%s.key", inputPath); FILE *fp2 = fopen(string, "rb"); - if (fp2 == NULL) { + if (fp2 == NULL) FATAL_ERROR("Failed to open key file for reading.\n"); - } size_t count = fread(&key, 4, 1, fp2); - if (count != 1) { + if (count != 1) FATAL_ERROR("Not a valid key file.\n"); - } fclose(fp2); free(string); } - WriteNtrImage(outputPath, options->numTiles, image.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); + options->bitDepth = options->bitDepth == 0 ? image.bitDepth : options->bitDepth; + + WriteNtrImage(outputPath, options->numTiles, options->bitDepth, options->colsPerChunk, options->rowsPerChunk, + &image, !image.hasPalette, options->clobberSize, options->byteOrder, options->version101, + options->sopc, options->vramTransfer, options->scanMode, options->mappingType, key, options->wrongSize); 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; + 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.hasTransparency = false; - options.width = 0; - options.colsPerChunk = 1; - options.rowsPerChunk = 1; - options.palIndex = 1; + options.width = 0; + options.colsPerChunk = 1; + options.rowsPerChunk = 1; + options.palIndex = 1; options.scanFrontToBack = false; - options.handleEmpty = false; + options.handleEmpty = 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++; options.paletteFilePath = argv[i]; - } else if (strcmp(option, "-object") == 0) { + } + else if (strcmp(option, "-object") == 0) + { options.hasTransparency = true; - } else if (strcmp(option, "-palindex") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-palindex") == 0) + { + 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) { + } + 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 if (strcmp(option, "-scanfronttoback") == 0) { + } + else if (strcmp(option, "-scanfronttoback") == 0) + { options.scanFrontToBack = true; - } else if (strcmp(option, "-handleempty") == 0) { + } + else if (strcmp(option, "-handleempty") == 0) + { options.handleEmpty = true; - } else { + } + 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; - } ConvertNtrToPng(inputPath, outputPath, &options); } -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.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); } } @@ -397,120 +418,131 @@ 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.numTiles = 0; - options.bitDepth = 4; + options.numTiles = 0; + options.bitDepth = 0; options.colsPerChunk = 1; options.rowsPerChunk = 1; - options.wrongSize = false; - options.clobberSize = false; - options.byteOrder = true; - options.version101 = false; - options.sopc = false; - options.scanMode = 0; - options.handleEmpty = false; + options.wrongSize = false; + options.clobberSize = false; + options.byteOrder = true; + options.version101 = false; + options.sopc = false; + options.scanMode = 0; + options.handleEmpty = false; options.vramTransfer = false; - options.mappingType = 0; + options.mappingType = 0; - 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 if (strcmp(option, "-bitdepth") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-bitdepth") == 0) + { + 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) { + } + else if (strcmp(option, "-clobbersize") == 0) + { options.clobberSize = true; - } else if (strcmp(option, "-nobyteorder") == 0) { + } + else if (strcmp(option, "-nobyteorder") == 0) + { options.byteOrder = false; - } else if (strcmp(option, "-version101") == 0) { + } + else if (strcmp(option, "-version101") == 0) + { options.version101 = true; - } else if (strcmp(option, "-sopc") == 0) { + } + else if (strcmp(option, "-sopc") == 0) + { options.sopc = true; - } else if (strcmp(option, "-scanned") == 0) { - if (options.scanMode != 0) { + } + 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"); - } options.scanMode = 1; - } else if (strcmp(option, "-scanfronttoback") == 0) { - if (options.scanMode != 0) { + } + 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"); - } options.scanMode = 2; - } else if (strcmp(option, "-wrongsize") == 0) { + } + else if (strcmp(option, "-wrongsize") == 0) { options.wrongSize = true; - } else if (strcmp(option, "-handleempty") == 0) { + } + else if (strcmp(option, "-handleempty") == 0) + { options.handleEmpty = true; - } else if (strcmp(option, "-vram") == 0) { + } + else if (strcmp(option, "-vram") == 0) + { options.vramTransfer = true; - } else if (strcmp(option, "-mappingtype") == 0) { - if (i + 1 >= argc) { + } + else if (strcmp(option, "-mappingtype") == 0) { + 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 { + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -518,128 +550,153 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a ConvertPngToNtr(inputPath, outputPath, &options); } -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; + bool ncpr = false; + bool ir = false; + bool nopad = false; int bitdepth = 0; - int compNum = 0; - bool pcmp = false; + int compNum = 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, "-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 { + } + else if (strcmp(option, "-invertsize") == 0) + { + inverted = true; + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } ReadPngPalette(inputPath, &palette); - WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp); + WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted); } -void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +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 { + } + else if (strcmp(option, "-invertsize") == 0) + { + inverted = true; + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } - ReadNtrPalette(inputPath, &palette, bitdepth, 0); + ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted); WriteJascPalette(outputPath, &palette); } -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); } } @@ -648,76 +705,88 @@ 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; - bool pcmp = false; - - for (int i = 3; i < argc; i++) { + bool ncpr = false; + bool ir = false; + bool nopad = false; + int bitdepth = 0; + int compNum = 0; + bool pcmp = false; + bool inverted = false; + + 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 { + } + else if (strcmp(option, "-invertsize") == 0) + { + inverted = true; + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -726,14 +795,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); + WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted); } -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); @@ -743,7 +812,8 @@ void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUS FreeNCERCell(options); } -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); @@ -755,31 +825,33 @@ void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUS FreeNCERCell(options); } -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); } } @@ -791,7 +863,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); @@ -803,7 +876,8 @@ void HandleJsonToNtrAnimationCommand(char *inputPath, char *outputPath, int argc FreeNANRAnimation(options); } -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); @@ -815,7 +889,8 @@ void HandleNtrAnimationToJsonCommand(char *inputPath, char *outputPath, int argc FreeNANRAnimation(options); } -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); @@ -827,7 +902,8 @@ 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; ReadLatinFont(inputPath, &image); @@ -836,7 +912,8 @@ void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNU 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; @@ -847,7 +924,8 @@ 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; ReadHalfwidthJapaneseFont(inputPath, &image); @@ -856,7 +934,8 @@ void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, 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; @@ -867,7 +946,8 @@ 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; ReadFullwidthJapaneseFont(inputPath, &image); @@ -876,7 +956,8 @@ void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, 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; @@ -887,42 +968,48 @@ void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, FreeImage(&image); } -void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv) { +void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ int overflowSize = 0; - int minDistance = 2; // default, for compatibility with LZ77UnCompVram() + int minDistance = 2; // default, for compatibility with LZ77UnCompVram() + bool forwardIteration = true; - for (int i = 3; i < argc; i++) { + for (int i = 3; i < argc; i++) + { 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 { + } + else if (strcmp(option, "-reverse") == 0) + { + forwardIteration = false; + } + else + { FATAL_ERROR("Unrecognized option \"%s\".\n", option); } } @@ -937,7 +1024,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char * unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize); int compressedSize; - unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance); + unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration); compressedData[1] = (unsigned char)fileSize; compressedData[2] = (unsigned char)(fileSize >> 8); @@ -950,7 +1037,8 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char * free(compressedData); } -void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { +void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ int fileSize; unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); @@ -964,7 +1052,8 @@ void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSE 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); @@ -978,7 +1067,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); @@ -992,28 +1082,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); } } @@ -1030,7 +1122,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); @@ -1044,68 +1137,140 @@ void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNU free(uncompressedData); } -int main(int argc, char **argv) { - if (argc < 3) { - FATAL_ERROR("Usage: nitrogfx INPUT_PATH OUTPUT_PATH [options...]\n"); +void HandleNtrFontToPngCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ + struct NtrFontOptions options; + options.metadataFilePath = NULL; + options.useSubscreenPalette = false; + + for (int i = 3; i < argc; i++) + { + char *option = argv[i]; + + if (strcmp(option, "-metadata") == 0) + { + if (i + 1 >= argc) + FATAL_ERROR("No file path following \"-metadata\".\n"); + + options.metadataFilePath = argv[++i]; + } + else if (strcmp(option, "-subscreen") == 0) + { + options.useSubscreenPalette = true; + } } - struct CommandHandler handlers[] = { - { "1bpp", "png", HandleGbaToPngCommand }, - { "4bpp", "png", HandleGbaToPngCommand }, - { "8bpp", "png", HandleGbaToPngCommand }, - { "nbfc", "png", HandleGbaToPngCommand }, - { "NCGR", "png", HandleNtrToPngCommand }, - { "png", "1bpp", HandlePngToGbaCommand }, - { "png", "4bpp", HandlePngToGbaCommand }, - { "png", "nbfc", HandlePngToGbaCommand }, - { "png", "8bpp", HandlePngToGbaCommand }, - { "png", "NCGR", HandlePngToNtrCommand }, - { "png", "gbapal", HandlePngToGbaPaletteCommand }, - { "png", "nbfp", HandlePngToGbaPaletteCommand }, - { "png", "NCLR", HandlePngToNtrPaletteCommand }, - { "gbapal", "pal", HandleGbaToJascPaletteCommand }, - { "NCLR", "pal", HandleNtrToJascPaletteCommand }, - { "NCPR", "pal", HandleNtrToJascPaletteCommand }, - { "pal", "gbapal", HandleJascToGbaPaletteCommand }, - { "pal", "NCLR", HandleJascToNtrPaletteCommand }, - { "latfont", "png", HandleLatinFontToPngCommand }, - { "png", "latfont", HandlePngToLatinFontCommand }, - { "hwjpnfont", "png", HandleHalfwidthJapaneseFontToPngCommand }, - { "png", "hwjpnfont", HandlePngToHalfwidthJapaneseFontCommand }, - { "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand }, - { "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand }, - { "json", "NCER", HandleJsonToNtrCellCommand }, - { "NCER", "json", HandleNtrCellToJsonCommand }, - { "json", "NSCR", HandleJsonToNtrScreenCommand }, - { "json", "NANR", HandleJsonToNtrAnimationCommand }, - { "NANR", "json", HandleNtrAnimationToJsonCommand }, - { "json", "NMAR", HandleJsonToNtrMulticellAnimationCommand }, - { "NMAR", "json", HandleNtrAnimationToJsonCommand }, - { NULL, "huff", HandleHuffCompressCommand }, - { NULL, "lz", HandleLZCompressCommand }, - { "huff", NULL, HandleHuffDecompressCommand }, - { "lz", NULL, HandleLZDecompressCommand }, - { NULL, "rl", HandleRLCompressCommand }, - { "rl", NULL, HandleRLDecompressCommand }, - { NULL, NULL, NULL } + if (options.metadataFilePath == NULL) + FATAL_ERROR("No file path given for \"-metadata\".\n"); + + struct Image image; + struct NtrFontMetadata metadata; + ReadNtrFont(inputPath, &image, &metadata, options.useSubscreenPalette); + WritePng(outputPath, &image); + + char *metadataJson = GetNtrFontMetadataJson(&metadata); + WriteWholeStringToFile(options.metadataFilePath, metadataJson); + + free(metadata.glyphWidthTable); + FreeImage(&image); +} + +void HandlePngToNtrFontCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ + struct NtrFontOptions options; + options.metadataFilePath = NULL; + + for (int i = 3; i < argc; i++) + { + char *option = argv[i]; + + if (strcmp(option, "-metadata") == 0) + { + if (i + 1 >= argc) + FATAL_ERROR("No file path following \"-metadata\".\n"); + + options.metadataFilePath = argv[++i]; + } + } + + if (options.metadataFilePath == NULL) + FATAL_ERROR("No file path given for \"-metadata\".\n"); + + struct NtrFontMetadata *metadata = ParseNtrFontMetadataJson(options.metadataFilePath); + struct Image image = { .bitDepth = 2 }; + + ReadPng(inputPath, &image); + WriteNtrFont(outputPath, &image, metadata); + + FreeNtrFontMetadata(metadata); + FreeImage(&image); +} + +int main(int argc, char **argv) +{ + if (argc < 3) + FATAL_ERROR("Usage: nitrogfx INPUT_PATH OUTPUT_PATH [options...]\n"); + + struct CommandHandler handlers[] = + { + { "1bpp", "png", HandleGbaToPngCommand }, + { "4bpp", "png", HandleGbaToPngCommand }, + { "8bpp", "png", HandleGbaToPngCommand }, + { "nbfc", "png", HandleGbaToPngCommand }, + { "NCGR", "png", HandleNtrToPngCommand }, + { "png", "1bpp", HandlePngToGbaCommand }, + { "png", "4bpp", HandlePngToGbaCommand }, + { "png", "nbfc", HandlePngToGbaCommand }, + { "png", "8bpp", HandlePngToGbaCommand }, + { "png", "NCGR", HandlePngToNtrCommand }, + { "png", "gbapal", HandlePngToGbaPaletteCommand }, + { "png", "nbfp", HandlePngToGbaPaletteCommand }, + { "png", "NCLR", HandlePngToNtrPaletteCommand }, + { "gbapal", "pal", HandleGbaToJascPaletteCommand }, + { "NCLR", "pal", HandleNtrToJascPaletteCommand }, + { "NCPR", "pal", HandleNtrToJascPaletteCommand }, + { "pal", "gbapal", HandleJascToGbaPaletteCommand }, + { "pal", "NCLR", HandleJascToNtrPaletteCommand }, + { "latfont", "png", HandleLatinFontToPngCommand }, + { "png", "latfont", HandlePngToLatinFontCommand }, + { "hwjpnfont", "png", HandleHalfwidthJapaneseFontToPngCommand }, + { "png", "hwjpnfont", HandlePngToHalfwidthJapaneseFontCommand }, + { "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand }, + { "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand }, + { "json", "NCER", HandleJsonToNtrCellCommand }, + { "NCER", "json", HandleNtrCellToJsonCommand }, + { "json", "NSCR", HandleJsonToNtrScreenCommand }, + { "json", "NANR", HandleJsonToNtrAnimationCommand }, + { "NANR", "json", HandleNtrAnimationToJsonCommand }, + { "json", "NMAR", HandleJsonToNtrMulticellAnimationCommand }, + { "NMAR", "json", HandleNtrAnimationToJsonCommand }, + { NULL, "huff", HandleHuffCompressCommand }, + { NULL, "lz", HandleLZCompressCommand }, + { "huff", NULL, HandleHuffDecompressCommand }, + { "lz", NULL, HandleLZDecompressCommand }, + { NULL, "rl", HandleRLCompressCommand }, + { "rl", NULL, HandleRLDecompressCommand }, + { "NFGR", "png", HandleNtrFontToPngCommand }, + { "png", "NFGR", HandlePngToNtrFontCommand }, + { NULL, NULL, NULL } }; - char *inputPath = argv[1]; - char *outputPath = argv[2]; - char *inputFileExtension = GetFileExtension(inputPath); + char *inputPath = argv[1]; + char *outputPath = argv[2]; + 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].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0)) + { handlers[i].function(inputPath, outputPath, argc, argv); return 0; } diff --git a/tools/nitrogfx/nitrogfx-debug b/tools/nitrogfx/nitrogfx-debug deleted file mode 100755 index 98ce8b220..000000000 Binary files a/tools/nitrogfx/nitrogfx-debug and /dev/null differ diff --git a/tools/nitrogfx/options.h b/tools/nitrogfx/options.h index 4304f1eca..ed5dab6e2 100644 --- a/tools/nitrogfx/options.h +++ b/tools/nitrogfx/options.h @@ -51,6 +51,11 @@ struct NtrToPngOptions { bool handleEmpty; }; +struct CellVramTransferData { + int sourceDataOffset; + int size; +}; + struct Attr0 { int YCoordinate; bool Rotation; @@ -100,9 +105,12 @@ struct Cell { struct JsonToCellOptions { bool labelEnabled; bool extended; + bool vramTransferEnabled; int mappingType; int cellCount; struct Cell **cells; + int vramTransferMaxSize; + struct CellVramTransferData **transferData; char **labels; int labelCount; }; @@ -147,6 +155,7 @@ struct AnimationDataT { struct AnimationResults { short resultType; + bool padded; union { short index; struct AnimationDataSRT dataSrt; @@ -166,4 +175,21 @@ struct JsonToAnimationOptions { short resultCount; }; +struct NtrFontOptions { + char *metadataFilePath; + bool useSubscreenPalette; +}; + +struct NtrFontMetadata { + uint32_t size; + uint32_t widthTableOffset; + uint32_t numGlyphs; + uint8_t maxWidth; + uint8_t maxHeight; + uint8_t glyphWidth; + uint8_t glyphHeight; + + uint8_t *glyphWidthTable; +}; + #endif // OPTIONS_H diff --git a/tools/nitrogfx/util.c b/tools/nitrogfx/util.c index 65df5e229..4d668dc7a 100644 --- a/tools/nitrogfx/util.c +++ b/tools/nitrogfx/util.c @@ -12,129 +12,129 @@ bool ParseNumber(char *s, char **end, int radix, int *intValue) { - char *localEnd; + char *localEnd; - if (end == NULL) - end = &localEnd; + if (end == NULL) + end = &localEnd; - errno = 0; + errno = 0; - const long longValue = strtol(s, end, radix); + const long longValue = strtol(s, end, radix); - if (*end == s) - return false; // not a number + if (*end == s) + return false; // not a number - if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) - return false; + if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) + return false; - if (longValue > INT_MAX) - return false; + if (longValue > INT_MAX) + return false; - if (longValue < INT_MIN) - return false; + if (longValue < INT_MIN) + return false; - *intValue = (int)longValue; + *intValue = (int)longValue; - return true; + return true; } char *GetFileExtension(char *path) { - char *extension = path; + char *extension = path; - while (*extension != 0) - extension++; + while (*extension != 0) + extension++; - while (extension > path && *extension != '.') - extension--; + while (extension > path && *extension != '.') + extension--; - if (extension == path) - return NULL; + if (extension == path) + return NULL; - extension++; + extension++; - if (*extension == 0) - return NULL; + if (*extension == 0) + return NULL; - return extension; + return extension; } unsigned char *ReadWholeFile(char *path, int *size) { - FILE *fp = fopen(path, "rb"); + FILE *fp = fopen(path, "rb"); - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); - fseek(fp, 0, SEEK_END); + fseek(fp, 0, SEEK_END); - *size = ftell(fp); + *size = ftell(fp); - unsigned char *buffer = malloc(*size); + unsigned char *buffer = malloc(*size); - if (buffer == NULL) - FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); - rewind(fp); + rewind(fp); - if (fread(buffer, *size, 1, fp) != 1) - FATAL_ERROR("Failed to read \"%s\".\n", path); + if (fread(buffer, *size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path); - fclose(fp); + fclose(fp); - return buffer; + return buffer; } unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) { - FILE *fp = fopen(path, "rb"); + FILE *fp = fopen(path, "rb"); - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); - fseek(fp, 0, SEEK_END); + fseek(fp, 0, SEEK_END); - *size = ftell(fp); + *size = ftell(fp); - unsigned char *buffer = calloc(*size + padAmount, 1); + unsigned char *buffer = calloc(*size + padAmount, 1); - if (buffer == NULL) - FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); - rewind(fp); + rewind(fp); - if (fread(buffer, *size, 1, fp) != 1) - FATAL_ERROR("Failed to read \"%s\".\n", path); + if (fread(buffer, *size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path); - fclose(fp); + fclose(fp); - return buffer; + return buffer; } void WriteWholeStringToFile(char *path, char *string) { - FILE *fp = fopen(path, "wb"); + FILE *fp = fopen(path, "wb"); - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - if (fputs(string, fp) == EOF) - FATAL_ERROR("Failed to write to \"%s\".\n", path); + if (fputs(string, fp) == EOF) + FATAL_ERROR("Failed to write to \"%s\".\n", path); - fclose(fp); + fclose(fp); } void WriteWholeFile(char *path, void *buffer, int bufferSize) { - FILE *fp = fopen(path, "wb"); + FILE *fp = fopen(path, "wb"); - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - if (fwrite(buffer, bufferSize, 1, fp) != 1) - FATAL_ERROR("Failed to write to \"%s\".\n", path); + if (fwrite(buffer, bufferSize, 1, fp) != 1) + FATAL_ERROR("Failed to write to \"%s\".\n", path); - fclose(fp); + fclose(fp); } void WriteGenericNtrHeader(FILE* fp, const char* magicNumber, uint32_t size, bool byteorder, bool version101, uint16_t sectionCount)