mirror of
https://github.com/red031000/nitrogfx.git
synced 2025-06-18 21:25:38 -04:00
initial commit
This commit is contained in:
commit
a4e194fc9b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
nitrogfx
|
19
LICENSE
Normal file
19
LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2015 YamaArashi, 2021-2023 red031000
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
CC = gcc
|
||||||
|
|
||||||
|
HAVE_LIBPNG := $(shell pkg-config libpng; echo $?)
|
||||||
|
|
||||||
|
ifeq ($(HAVE_LIBPNG),1)
|
||||||
|
$(error No package 'libpng' found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK $(shell pkg-config --cflags libpng zlib)
|
||||||
|
|
||||||
|
LIBS = $(shell pkg-config --libs libpng zlib)
|
||||||
|
|
||||||
|
SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c huff.c json.c cJSON.c
|
||||||
|
OBJS = $(SRCS:%.c=%.o)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
nitrogfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h json.h cJSON.h
|
||||||
|
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -r nitrogfx nitrogfx.exe $(OBJS)
|
293
cJSON.h
Normal file
293
cJSON.h
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef cJSON__h
|
||||||
|
#define cJSON__h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||||
|
#define __WINDOWS__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
|
||||||
|
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||||
|
|
||||||
|
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||||
|
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||||
|
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||||
|
|
||||||
|
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||||
|
|
||||||
|
setting default visibility to hidden by adding
|
||||||
|
-fvisibility=hidden (for gcc)
|
||||||
|
or
|
||||||
|
-xldscope=hidden (for sun cc)
|
||||||
|
to CFLAGS
|
||||||
|
|
||||||
|
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CJSON_CDECL __cdecl
|
||||||
|
#define CJSON_STDCALL __stdcall
|
||||||
|
|
||||||
|
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||||
|
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||||
|
#define CJSON_EXPORT_SYMBOLS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CJSON_HIDE_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||||
|
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||||
|
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||||
|
#endif
|
||||||
|
#else /* !__WINDOWS__ */
|
||||||
|
#define CJSON_CDECL
|
||||||
|
#define CJSON_STDCALL
|
||||||
|
|
||||||
|
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||||
|
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||||
|
#else
|
||||||
|
#define CJSON_PUBLIC(type) type
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* project version */
|
||||||
|
#define CJSON_VERSION_MAJOR 1
|
||||||
|
#define CJSON_VERSION_MINOR 7
|
||||||
|
#define CJSON_VERSION_PATCH 15
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* cJSON Types: */
|
||||||
|
#define cJSON_Invalid (0)
|
||||||
|
#define cJSON_False (1 << 0)
|
||||||
|
#define cJSON_True (1 << 1)
|
||||||
|
#define cJSON_NULL (1 << 2)
|
||||||
|
#define cJSON_Number (1 << 3)
|
||||||
|
#define cJSON_String (1 << 4)
|
||||||
|
#define cJSON_Array (1 << 5)
|
||||||
|
#define cJSON_Object (1 << 6)
|
||||||
|
#define cJSON_Raw (1 << 7) /* raw json */
|
||||||
|
|
||||||
|
#define cJSON_IsReference 256
|
||||||
|
#define cJSON_StringIsConst 512
|
||||||
|
|
||||||
|
/* The cJSON structure: */
|
||||||
|
typedef struct cJSON
|
||||||
|
{
|
||||||
|
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||||
|
struct cJSON *next;
|
||||||
|
struct cJSON *prev;
|
||||||
|
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||||
|
struct cJSON *child;
|
||||||
|
|
||||||
|
/* The type of the item, as above. */
|
||||||
|
int type;
|
||||||
|
|
||||||
|
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||||
|
char *valuestring;
|
||||||
|
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||||
|
int valueint;
|
||||||
|
/* The item's number, if type==cJSON_Number */
|
||||||
|
double valuedouble;
|
||||||
|
|
||||||
|
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||||
|
char *string;
|
||||||
|
} cJSON;
|
||||||
|
|
||||||
|
typedef struct cJSON_Hooks
|
||||||
|
{
|
||||||
|
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
||||||
|
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||||
|
void (CJSON_CDECL *free_fn)(void *ptr);
|
||||||
|
} cJSON_Hooks;
|
||||||
|
|
||||||
|
typedef int cJSON_bool;
|
||||||
|
|
||||||
|
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||||
|
* This is to prevent stack overflows. */
|
||||||
|
#ifndef CJSON_NESTING_LIMIT
|
||||||
|
#define CJSON_NESTING_LIMIT 1000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* returns the version of cJSON as a string */
|
||||||
|
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||||
|
|
||||||
|
/* Supply malloc, realloc and free functions to cJSON */
|
||||||
|
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||||
|
|
||||||
|
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||||
|
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||||
|
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||||
|
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||||
|
|
||||||
|
/* Render a cJSON entity to text for transfer/storage. */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||||
|
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||||
|
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||||
|
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||||
|
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||||
|
/* Delete a cJSON entity and all subentities. */
|
||||||
|
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||||
|
|
||||||
|
/* Returns the number of items in an array (or object). */
|
||||||
|
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||||
|
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||||
|
/* Get item "string" from object. Case insensitive. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||||
|
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||||
|
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||||
|
|
||||||
|
/* Check item type and return its value */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
||||||
|
|
||||||
|
/* These functions check the type of an item */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||||
|
|
||||||
|
/* These calls create a cJSON item of the appropriate type. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||||
|
/* raw json */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||||
|
|
||||||
|
/* Create a string where valuestring references a string so
|
||||||
|
* it will not be freed by cJSON_Delete */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||||
|
/* Create an object/array that only references it's elements so
|
||||||
|
* they will not be freed by cJSON_Delete */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||||
|
|
||||||
|
/* These utilities create an Array of count items.
|
||||||
|
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||||
|
|
||||||
|
/* Append item to the specified array/object. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||||
|
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||||
|
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||||
|
* writing to `item->string` */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||||
|
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||||
|
|
||||||
|
/* Remove/Detach items from Arrays/Objects. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||||
|
|
||||||
|
/* Update array items. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||||
|
|
||||||
|
/* Duplicate a cJSON item */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||||
|
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||||
|
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||||
|
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||||
|
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||||
|
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||||
|
|
||||||
|
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||||
|
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||||
|
* but should point to a readable and writable address area. */
|
||||||
|
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||||
|
|
||||||
|
/* Helper functions for creating and adding items to an object at the same time.
|
||||||
|
* They return the added item or NULL on failure. */
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||||
|
|
||||||
|
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||||
|
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||||
|
/* helper for the cJSON_SetNumberValue macro */
|
||||||
|
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||||
|
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (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);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
|
||||||
|
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||||
|
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||||
|
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
256
convert_png.c
Normal file
256
convert_png.c
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <png.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "convert_png.h"
|
||||||
|
#include "gfx.h"
|
||||||
|
|
||||||
|
static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
|
||||||
|
|
||||||
|
unsigned char sig[8];
|
||||||
|
|
||||||
|
if (fread(sig, 8, 1, fp) != 1)
|
||||||
|
FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
|
||||||
|
|
||||||
|
if (png_sig_cmp(sig, 0, 8))
|
||||||
|
FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
|
||||||
|
|
||||||
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (!png_ptr)
|
||||||
|
FATAL_ERROR("Failed to create PNG read struct.\n");
|
||||||
|
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
if (!info_ptr)
|
||||||
|
FATAL_ERROR("Failed to create PNG info struct.\n");
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
|
||||||
|
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
png_set_sig_bytes(png_ptr, 8);
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
*pngStruct = png_ptr;
|
||||||
|
*pngInfo = info_ptr;
|
||||||
|
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int destBitDepth, int numPixels)
|
||||||
|
{
|
||||||
|
// Round the number of bits up to the next 8 and divide by 8 to get the number of bytes.
|
||||||
|
int srcSize = ((numPixels * srcBitDepth + 7) & ~7) / 8;
|
||||||
|
int destSize = ((numPixels * destBitDepth + 7) & ~7) / 8;
|
||||||
|
unsigned char *output = calloc(destSize, 1);
|
||||||
|
unsigned char *dest = output;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int destBit = 8 - destBitDepth;
|
||||||
|
|
||||||
|
for (i = 0; i < srcSize; i++)
|
||||||
|
{
|
||||||
|
unsigned char srcByte = src[i];
|
||||||
|
|
||||||
|
for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth)
|
||||||
|
{
|
||||||
|
unsigned char pixel = (srcByte >> j) % (1 << srcBitDepth);
|
||||||
|
|
||||||
|
if (pixel >= (1 << destBitDepth))
|
||||||
|
FATAL_ERROR("Image exceeds the maximum color value for a %ibpp image.\n", destBitDepth);
|
||||||
|
*dest |= pixel << destBit;
|
||||||
|
destBit -= destBitDepth;
|
||||||
|
if (destBit < 0)
|
||||||
|
{
|
||||||
|
dest++;
|
||||||
|
destBit = 8 - destBitDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPng(char *path, struct Image *image)
|
||||||
|
{
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
|
||||||
|
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
|
||||||
|
|
||||||
|
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
int color_type = png_get_color_type(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE)
|
||||||
|
FATAL_ERROR("\"%s\" has an unsupported color type.\n", path);
|
||||||
|
|
||||||
|
// Check if the image has a palette so that we can tell if the colors need to be inverted later.
|
||||||
|
// Don't read the palette because it's not needed for now.
|
||||||
|
image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
|
||||||
|
|
||||||
|
image->width = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
image->height = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
image->pixels = malloc(image->height * rowbytes);
|
||||||
|
|
||||||
|
if (image->pixels == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate pixel buffer.\n");
|
||||||
|
|
||||||
|
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
|
||||||
|
|
||||||
|
if (row_pointers == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate row pointers.\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < image->height; i++)
|
||||||
|
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Error reading from \"%s\".\n", path);
|
||||||
|
|
||||||
|
png_read_image(png_ptr, row_pointers);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
|
||||||
|
free(row_pointers);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (bit_depth != image->bitDepth)
|
||||||
|
{
|
||||||
|
unsigned char *src = image->pixels;
|
||||||
|
|
||||||
|
if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8)
|
||||||
|
FATAL_ERROR("Bit depth of image must be 1, 2, 4, or 8.\n");
|
||||||
|
image->pixels = ConvertBitDepth(image->pixels, bit_depth, image->bitDepth, image->width * image->height);
|
||||||
|
free(src);
|
||||||
|
image->bitDepth = bit_depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPngPalette(char *path, struct Palette *palette)
|
||||||
|
{
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
png_colorp colors;
|
||||||
|
int numColors;
|
||||||
|
|
||||||
|
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
|
||||||
|
|
||||||
|
if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE)
|
||||||
|
FATAL_ERROR("The image \"%s\" does not contain a palette.\n", path);
|
||||||
|
|
||||||
|
if (png_get_PLTE(png_ptr, info_ptr, &colors, &numColors) != PNG_INFO_PLTE)
|
||||||
|
FATAL_ERROR("Failed to retrieve palette from \"%s\".\n", path);
|
||||||
|
|
||||||
|
if (numColors > 256)
|
||||||
|
FATAL_ERROR("Images with more than 256 colors are not supported.\n");
|
||||||
|
|
||||||
|
palette->numColors = numColors;
|
||||||
|
for (int i = 0; i < numColors; i++) {
|
||||||
|
palette->colors[i].red = colors[i].red;
|
||||||
|
palette->colors[i].green = colors[i].green;
|
||||||
|
palette->colors[i].blue = colors[i].blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
palette->bitDepth = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette)
|
||||||
|
{
|
||||||
|
png_colorp colors = malloc(palette->numColors * sizeof(png_color));
|
||||||
|
|
||||||
|
if (colors == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate PNG palette.\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < palette->numColors; i++) {
|
||||||
|
colors[i].red = palette->colors[i].red;
|
||||||
|
colors[i].green = palette->colors[i].green;
|
||||||
|
colors[i].blue = palette->colors[i].blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors);
|
||||||
|
|
||||||
|
free(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePng(char *path, struct Image *image)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "wb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
|
||||||
|
|
||||||
|
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (!png_ptr)
|
||||||
|
FATAL_ERROR("Failed to create PNG write struct.\n");
|
||||||
|
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
if (!info_ptr)
|
||||||
|
FATAL_ERROR("Failed to create PNG info struct.\n");
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
|
||||||
|
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Error writing header for \"%s\".\n", path);
|
||||||
|
|
||||||
|
int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
|
||||||
|
|
||||||
|
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
|
||||||
|
image->bitDepth, color_type, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||||
|
|
||||||
|
if (image->hasPalette) {
|
||||||
|
SetPngPalette(png_ptr, info_ptr, &image->palette);
|
||||||
|
|
||||||
|
if (image->hasTransparency) {
|
||||||
|
png_byte trans = 0;
|
||||||
|
png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
|
||||||
|
|
||||||
|
if (row_pointers == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate row pointers.\n");
|
||||||
|
|
||||||
|
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
for (int i = 0; i < image->height; i++)
|
||||||
|
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Error writing \"%s\".\n", path);
|
||||||
|
|
||||||
|
png_write_image(png_ptr, row_pointers);
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
FATAL_ERROR("Error ending write of \"%s\".\n", path);
|
||||||
|
|
||||||
|
png_write_end(png_ptr, NULL);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
free(row_pointers);
|
||||||
|
}
|
12
convert_png.h
Normal file
12
convert_png.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef CONVERT_PNG_H
|
||||||
|
#define CONVERT_PNG_H
|
||||||
|
|
||||||
|
#include "gfx.h"
|
||||||
|
|
||||||
|
void ReadPng(char *path, struct Image *image);
|
||||||
|
void WritePng(char *path, struct Image *image);
|
||||||
|
void ReadPngPalette(char *path, struct Palette *palette);
|
||||||
|
|
||||||
|
#endif // CONVERT_PNG_H
|
326
font.c
Normal file
326
font.c
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "font.h"
|
||||||
|
#include "gfx.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)
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
||||||
|
{
|
||||||
|
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 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 ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
|
||||||
|
{
|
||||||
|
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 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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetFontPalette(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[i][0];
|
||||||
|
image->palette.colors[i].green = gFontPalette[i][1];
|
||||||
|
image->palette.colors[i].blue = gFontPalette[i][2];
|
||||||
|
}
|
||||||
|
|
||||||
|
image->hasTransparency = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadLatinFont(char *path, struct Image *image)
|
||||||
|
{
|
||||||
|
int fileSize;
|
||||||
|
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||||
|
|
||||||
|
int numGlyphs = fileSize / 64;
|
||||||
|
|
||||||
|
if (numGlyphs % 16 != 0)
|
||||||
|
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
|
||||||
|
|
||||||
|
int numRows = numGlyphs / 16;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
ConvertFromLatinFont(buffer, image->pixels, numRows);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
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->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");
|
||||||
|
|
||||||
|
ConvertToLatinFont(image->pixels, buffer, numRows);
|
||||||
|
|
||||||
|
WriteWholeFile(path, buffer, bufferSize);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadHalfwidthJapaneseFont(char *path, struct Image *image)
|
||||||
|
{
|
||||||
|
int fileSize;
|
||||||
|
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||||
|
|
||||||
|
int glyphSize = 32;
|
||||||
|
|
||||||
|
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 numRows = numGlyphs / 16;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
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->height % 16 != 0)
|
||||||
|
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
|
||||||
|
|
||||||
|
int numRows = image->height / 16;
|
||||||
|
int bufferSize = numRows * 16 * 32;
|
||||||
|
unsigned char *buffer = malloc(bufferSize);
|
||||||
|
|
||||||
|
if (buffer == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate memory for font.\n");
|
||||||
|
|
||||||
|
ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows);
|
||||||
|
|
||||||
|
WriteWholeFile(path, buffer, bufferSize);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadFullwidthJapaneseFont(char *path, struct Image *image)
|
||||||
|
{
|
||||||
|
int fileSize;
|
||||||
|
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||||
|
|
||||||
|
int numGlyphs = fileSize / 64;
|
||||||
|
|
||||||
|
if (numGlyphs % 16 != 0)
|
||||||
|
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
|
||||||
|
|
||||||
|
int numRows = numGlyphs / 16;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
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->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);
|
||||||
|
}
|
16
font.h
Normal file
16
font.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef FONT_H
|
||||||
|
#define FONT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "gfx.h"
|
||||||
|
|
||||||
|
void ReadLatinFont(char *path, struct Image *image);
|
||||||
|
void WriteLatinFont(char *path, struct Image *image);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif // FONT_H
|
47
gfx.h
Normal file
47
gfx.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi, 2021-2023 red031000
|
||||||
|
|
||||||
|
#ifndef GFX_H
|
||||||
|
#define GFX_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
unsigned char red;
|
||||||
|
unsigned char green;
|
||||||
|
unsigned char blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Palette {
|
||||||
|
struct Color colors[256];
|
||||||
|
int numColors;
|
||||||
|
int bitDepth;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Image {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int bitDepth;
|
||||||
|
unsigned char *pixels;
|
||||||
|
bool hasPalette;
|
||||||
|
struct Palette palette;
|
||||||
|
bool hasTransparency;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||||
|
uint32_t ReadNtrImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool scanFrontToBack);
|
||||||
|
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||||
|
void WriteNtrImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image,
|
||||||
|
bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, uint32_t scanMode,
|
||||||
|
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 WriteGbaPalette(char *path, struct Palette *palette);
|
||||||
|
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum);
|
||||||
|
void WriteNtrCell(char *path, struct JsonToCellOptions *options);
|
||||||
|
void WriteNtrScreen(char *path, struct JsonToScreenOptions *options);
|
||||||
|
void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options);
|
||||||
|
|
||||||
|
#endif // GFX_H
|
37
global.h
Normal file
37
global.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef GLOBAL_H
|
||||||
|
#define GLOBAL_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
#define FATAL_ERROR(format, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, format, __VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define UNUSED
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define FATAL_ERROR(format, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, format, ##__VA_ARGS__); \
|
||||||
|
exit(1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define UNUSED __attribute__((__unused__))
|
||||||
|
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#define PTR_ADD(ptr, value) ((void*)((uintptr_t)(ptr) + (value)))
|
||||||
|
#define PTR_SUB(ptr, value) ((void*)((uintptr_t)(ptr) - (value)))
|
||||||
|
#define PTR_IADD(ptr, value) do { (ptr) = PTR_ADD(ptr, value); } while (0)
|
||||||
|
#define PTR_ISUB(ptr, value) do { (ptr) = PTR_SUB(ptr, value); } while (0)
|
||||||
|
#define PTR_DIFF(right, left) ((uintptr_t)(right) - (uintptr_t)(left))
|
||||||
|
|
||||||
|
#endif // GLOBAL_H
|
398
huff.c
Normal file
398
huff.c
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "huff.h"
|
||||||
|
|
||||||
|
static int cmp_tree(const void * a0, const void * b0) {
|
||||||
|
return ((struct HuffData *)a0)->value - ((struct HuffData *)b0)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*cmpfun)(const void *, const void *);
|
||||||
|
|
||||||
|
int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) {
|
||||||
|
/*
|
||||||
|
* Out-of-place mergesort (stable sort)
|
||||||
|
* Returns 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
void * leftPtr;
|
||||||
|
void * rightPtr;
|
||||||
|
void * leftEnd;
|
||||||
|
void * rightEnd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (count) {
|
||||||
|
case 0:
|
||||||
|
// Should never be here
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// Nothing to do here
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Swap the two entries if the right one compares higher.
|
||||||
|
if (cmp(data, PTR_ADD(data, size)) > 0) {
|
||||||
|
memcpy(buffer, data, size);
|
||||||
|
memcpy(data, PTR_ADD(data, size), size);
|
||||||
|
memcpy(PTR_ADD(data, size), buffer, size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Merge sort out-of-place.
|
||||||
|
leftPtr = data;
|
||||||
|
leftEnd = rightPtr = PTR_ADD(data, count / 2 * size);
|
||||||
|
rightEnd = PTR_ADD(data, count * size);
|
||||||
|
|
||||||
|
// Sort the left half
|
||||||
|
if (!msort_r(leftPtr, count / 2, size, cmp, buffer))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Sort the right half
|
||||||
|
if (!msort_r(rightPtr, count / 2 + (count & 1), size, cmp, buffer))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Merge the sorted halves out of place
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
if (cmp(leftPtr, rightPtr) <= 0) {
|
||||||
|
memcpy(PTR_ADD(buffer, i * size), leftPtr, size);
|
||||||
|
PTR_IADD(leftPtr, size);
|
||||||
|
} else {
|
||||||
|
memcpy(PTR_ADD(buffer, i * size), rightPtr, size);
|
||||||
|
PTR_IADD(rightPtr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (++i < count && leftPtr < leftEnd && rightPtr < rightEnd);
|
||||||
|
|
||||||
|
// Copy the remainder
|
||||||
|
if (i < count) {
|
||||||
|
if (leftPtr < leftEnd) {
|
||||||
|
memcpy(PTR_ADD(buffer, i * size), leftPtr, PTR_DIFF(leftEnd, leftPtr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(PTR_ADD(buffer, i * size), rightPtr, PTR_DIFF(rightEnd, rightPtr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the merged data back
|
||||||
|
memcpy(data, buffer, count * size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msort(void * data, size_t count, size_t size, cmpfun cmp) {
|
||||||
|
void * buffer = malloc(count * size);
|
||||||
|
if (buffer == NULL) return 0;
|
||||||
|
int result = msort_r(data, count, size, cmp, buffer);
|
||||||
|
free(buffer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_tree(unsigned char * dest, HuffNode_t * tree, int nitems, struct BitEncoding * encoding) {
|
||||||
|
/*
|
||||||
|
* The example used to guide this function encodes the tree in a
|
||||||
|
* breadth-first manner. We attempt to emulate that here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
// There are (2 * nitems - 1) nodes in the binary tree. Allocate that.
|
||||||
|
HuffNode_t * traversal = calloc(2 * nitems - 1, sizeof(HuffNode_t));
|
||||||
|
if (traversal == NULL)
|
||||||
|
FATAL_ERROR("Fatal error while compressing Huff file.\n");
|
||||||
|
|
||||||
|
// The first node is the root of the tree.
|
||||||
|
traversal[0] = *tree;
|
||||||
|
i = 1;
|
||||||
|
|
||||||
|
// Copy the tree into a breadth-first ordering using brute force.
|
||||||
|
for (int depth = 1; i < 2 * nitems - 1; depth++) {
|
||||||
|
// Consider every possible path up to the current depth.
|
||||||
|
for (j = 0; i < 2 * nitems - 1 && j < 1 << depth; j++) {
|
||||||
|
// The index of the path is used to encode the path itself.
|
||||||
|
// Start from the most significant relevant bit and work our way down.
|
||||||
|
// Keep track of the current and previous nodes.
|
||||||
|
HuffNode_t * currNode = traversal;
|
||||||
|
HuffNode_t * parent = NULL;
|
||||||
|
for (k = 0; k < depth; k++) {
|
||||||
|
if (currNode->header.isLeaf)
|
||||||
|
break;
|
||||||
|
parent = currNode;
|
||||||
|
if ((j >> (depth - k - 1)) & 1)
|
||||||
|
currNode = currNode->branch.right;
|
||||||
|
else
|
||||||
|
currNode = currNode->branch.left;
|
||||||
|
}
|
||||||
|
// Check that the length of the current path equals the current depth.
|
||||||
|
if (k == depth) {
|
||||||
|
// Make sure we can encode the current branch.
|
||||||
|
// Bail here if we cannot.
|
||||||
|
// This is only applicable for 8-bit encodings.
|
||||||
|
if (traversal + i - parent > 128)
|
||||||
|
FATAL_ERROR("Fatal error while compressing Huff file: unable to encode binary tree.\n");
|
||||||
|
// Copy the current node, and update its parent.
|
||||||
|
traversal[i] = *currNode;
|
||||||
|
if (parent != NULL) {
|
||||||
|
if ((j & 1) == 1)
|
||||||
|
parent->branch.right = traversal + i;
|
||||||
|
else
|
||||||
|
parent->branch.left = traversal + i;
|
||||||
|
}
|
||||||
|
// Encode the path through the tree in the lookup table
|
||||||
|
if (traversal[i].header.isLeaf) {
|
||||||
|
encoding[traversal[i].leaf.key].nbits = depth;
|
||||||
|
encoding[traversal[i].leaf.key].bitstring = j;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the size of the tree.
|
||||||
|
// This is used by the decompressor to skip the tree.
|
||||||
|
dest[4] = nitems - 1;
|
||||||
|
|
||||||
|
// Encode each node in the tree.
|
||||||
|
for (i = 0; i < 2 * nitems - 1; i++) {
|
||||||
|
HuffNode_t * currNode = traversal + i;
|
||||||
|
if (currNode->header.isLeaf) {
|
||||||
|
dest[5 + i] = traversal[i].leaf.key;
|
||||||
|
} else {
|
||||||
|
dest[5 + i] = (unsigned char)(((currNode->branch.right - traversal - i) / 2) - 1);
|
||||||
|
if (currNode->branch.left->header.isLeaf)
|
||||||
|
dest[5 + i] |= 0x80;
|
||||||
|
if (currNode->branch.right->header.isLeaf)
|
||||||
|
dest[5 + i] |= 0x40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(traversal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write_32_le(unsigned char * dest, int * destPos, uint32_t * buff, int * buffPos) {
|
||||||
|
dest[*destPos] = *buff;
|
||||||
|
dest[*destPos + 1] = *buff >> 8;
|
||||||
|
dest[*destPos + 2] = *buff >> 16;
|
||||||
|
dest[*destPos + 3] = *buff >> 24;
|
||||||
|
*destPos += 4;
|
||||||
|
*buff = 0;
|
||||||
|
*buffPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void read_32_le(unsigned char * src, int * srcPos, uint32_t * buff) {
|
||||||
|
uint32_t tmp = src[*srcPos];
|
||||||
|
tmp |= src[*srcPos + 1] << 8;
|
||||||
|
tmp |= src[*srcPos + 2] << 16;
|
||||||
|
tmp |= src[*srcPos + 3] << 24;
|
||||||
|
*srcPos += 4;
|
||||||
|
*buff = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_bits(unsigned char * dest, int * destPos, struct BitEncoding * encoding, int value, uint32_t * buff, int * buffBits) {
|
||||||
|
int nbits = (int)encoding[value].nbits;
|
||||||
|
uint32_t bitstring = (uint32_t)encoding[value].bitstring;
|
||||||
|
|
||||||
|
if (*buffBits + nbits >= 32) {
|
||||||
|
int diff = *buffBits + nbits - 32;
|
||||||
|
*buff <<= nbits - diff;
|
||||||
|
*buff |= bitstring >> diff;
|
||||||
|
bitstring &= ~(1 << diff);
|
||||||
|
nbits = diff;
|
||||||
|
write_32_le(dest, destPos, buff, buffBits);
|
||||||
|
}
|
||||||
|
if (nbits != 0) {
|
||||||
|
*buff <<= nbits;
|
||||||
|
*buff |= bitstring;
|
||||||
|
*buffBits += nbits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=======================================
|
||||||
|
MAIN COMPRESSION/DECOMPRESSION ROUTINES
|
||||||
|
=======================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned char * HuffCompress(unsigned char * src, int srcSize, int * compressedSize_p, int bitDepth) {
|
||||||
|
if (srcSize <= 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int worstCaseDestSize = 4 + (2 << bitDepth) + srcSize * 3;
|
||||||
|
|
||||||
|
unsigned char *dest = malloc(worstCaseDestSize);
|
||||||
|
if (dest == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int nitems = 1 << bitDepth;
|
||||||
|
|
||||||
|
HuffNode_t * freqs = calloc(nitems, sizeof(HuffNode_t));
|
||||||
|
if (freqs == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
struct BitEncoding * encoding = calloc(nitems, sizeof(struct BitEncoding));
|
||||||
|
if (encoding == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
// Set up the frequencies table. This will inform the tree.
|
||||||
|
for (int i = 0; i < nitems; i++) {
|
||||||
|
freqs[i].header.isLeaf = 1;
|
||||||
|
freqs[i].header.value = 0;
|
||||||
|
freqs[i].leaf.key = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count each nybble or byte.
|
||||||
|
for (int i = 0; i < srcSize; i++) {
|
||||||
|
if (bitDepth == 8) {
|
||||||
|
freqs[src[i]].header.value++;
|
||||||
|
} else {
|
||||||
|
freqs[src[i] >> 4].header.value++;
|
||||||
|
freqs[src[i] & 0xF].header.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
for (int i = 0; i < nitems; i++) {
|
||||||
|
fprintf(stderr, "%d: %d\n", i, freqs[i].header.value);
|
||||||
|
}
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
// Sort the frequency table.
|
||||||
|
if (!msort(freqs, nitems, sizeof(HuffNode_t), cmp_tree))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
// Prune zero-frequency values.
|
||||||
|
for (int i = 0; i < nitems; i++) {
|
||||||
|
if (freqs[i].header.value != 0) {
|
||||||
|
if (i > 0) {
|
||||||
|
for (int j = i; j < nitems; j++) {
|
||||||
|
freqs[j - i] = freqs[j];
|
||||||
|
}
|
||||||
|
nitems -= i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// This should never happen:
|
||||||
|
if (i == nitems - 1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
HuffNode_t * tree = calloc(nitems * 2 - 1, sizeof(HuffNode_t));
|
||||||
|
if (tree == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
// Iteratively collapse the two least frequent nodes.
|
||||||
|
HuffNode_t * endptr = freqs + nitems - 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < nitems - 1; i++) {
|
||||||
|
HuffNode_t * left = freqs;
|
||||||
|
HuffNode_t * right = freqs + 1;
|
||||||
|
tree[i * 2] = *right;
|
||||||
|
tree[i * 2 + 1] = *left;
|
||||||
|
for (int j = 0; j < nitems - i - 2; j++)
|
||||||
|
freqs[j] = freqs[j + 2];
|
||||||
|
endptr->header.isLeaf = 0;
|
||||||
|
endptr->header.value = tree[i * 2].header.value + tree[i * 2 + 1].header.value;
|
||||||
|
endptr->branch.left = tree + i * 2;
|
||||||
|
endptr->branch.right = tree + i * 2 + 1;
|
||||||
|
endptr--;
|
||||||
|
if (i < nitems - 2 && !msort(freqs, nitems - i - 1, sizeof(HuffNode_t), cmp_tree))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the tree breadth-first, and create the path lookup table.
|
||||||
|
write_tree(dest, freqs, nitems, encoding);
|
||||||
|
|
||||||
|
free(tree);
|
||||||
|
free(freqs);
|
||||||
|
|
||||||
|
// Encode the data itself.
|
||||||
|
int destPos = 4 + nitems * 2;
|
||||||
|
uint32_t destBuf = 0;
|
||||||
|
uint32_t srcBuf = 0;
|
||||||
|
int destBitPos = 0;
|
||||||
|
|
||||||
|
for (int srcPos = 0; srcPos < srcSize;) {
|
||||||
|
read_32_le(src, &srcPos, &srcBuf);
|
||||||
|
for (int i = 0; i < 32 / bitDepth; i++) {
|
||||||
|
write_bits(dest, &destPos, encoding, srcBuf & (0xFF >> (8 - bitDepth)), &destBuf, &destBitPos);
|
||||||
|
srcBuf >>= bitDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destBitPos != 0) {
|
||||||
|
write_32_le(dest, &destPos, &destBuf, &destBitPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(encoding);
|
||||||
|
|
||||||
|
// Write the header.
|
||||||
|
dest[0] = bitDepth | 0x20;
|
||||||
|
dest[1] = srcSize;
|
||||||
|
dest[2] = srcSize >> 8;
|
||||||
|
dest[3] = srcSize >> 16;
|
||||||
|
*compressedSize_p = (destPos + 3) & ~3;
|
||||||
|
return dest;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
FATAL_ERROR("Fatal error while compressing Huff file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char * HuffDecompress(unsigned char * src, int srcSize, int * uncompressedSize_p) {
|
||||||
|
if (srcSize < 4)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int bitDepth = *src & 15;
|
||||||
|
if (bitDepth != 4 && bitDepth != 8)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
|
||||||
|
|
||||||
|
unsigned char *dest = malloc(destSize);
|
||||||
|
|
||||||
|
if (dest == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int treePos = 5;
|
||||||
|
int treeSize = (src[4] + 1) * 2;
|
||||||
|
int srcPos = 4 + treeSize;
|
||||||
|
int destPos = 0;
|
||||||
|
int curValPos = 0;
|
||||||
|
uint32_t destTmp = 0;
|
||||||
|
uint32_t window;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (srcPos >= srcSize)
|
||||||
|
goto fail;
|
||||||
|
read_32_le(src, &srcPos, &window);
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
int curBit = (window >> 31) & 1;
|
||||||
|
unsigned char treeView = src[treePos];
|
||||||
|
bool isLeaf = ((treeView << curBit) & 0x80) != 0;
|
||||||
|
treePos &= ~1; // align
|
||||||
|
treePos += ((treeView & 0x3F) + 1) * 2 + curBit;
|
||||||
|
if (isLeaf) {
|
||||||
|
destTmp >>= bitDepth;
|
||||||
|
destTmp |= (src[treePos] << (32 - bitDepth));
|
||||||
|
curValPos++;
|
||||||
|
if (curValPos == 32 / bitDepth) {
|
||||||
|
write_32_le(dest, &destPos, &destTmp, &curValPos);
|
||||||
|
if (destPos == destSize) {
|
||||||
|
*uncompressedSize_p = destSize;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
treePos = 5;
|
||||||
|
}
|
||||||
|
window <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
FATAL_ERROR("Fatal error while decompressing Huff file.\n");
|
||||||
|
}
|
38
huff.h
Normal file
38
huff.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef HUFF_H
|
||||||
|
#define HUFF_H
|
||||||
|
|
||||||
|
union HuffNode;
|
||||||
|
|
||||||
|
struct HuffData {
|
||||||
|
unsigned value:31;
|
||||||
|
unsigned isLeaf:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HuffLeaf {
|
||||||
|
struct HuffData header;
|
||||||
|
unsigned char key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HuffBranch {
|
||||||
|
struct HuffData header;
|
||||||
|
union HuffNode * left;
|
||||||
|
union HuffNode * right;
|
||||||
|
};
|
||||||
|
|
||||||
|
union HuffNode {
|
||||||
|
struct HuffData header;
|
||||||
|
struct HuffLeaf leaf;
|
||||||
|
struct HuffBranch branch;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union HuffNode HuffNode_t;
|
||||||
|
|
||||||
|
struct BitEncoding {
|
||||||
|
unsigned long long nbits:6;
|
||||||
|
unsigned long long bitstring:58;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char * HuffCompress(unsigned char * buffer, int srcSize, int * compressedSize_p, int bitDepth);
|
||||||
|
unsigned char * HuffDecompress(unsigned char * buffer, int srcSize, int * uncompressedSize_p);
|
||||||
|
|
||||||
|
#endif //HUFF_H
|
179
jasc_pal.c
Normal file
179
jasc_pal.c
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "gfx.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// Read/write Paint Shop Pro palette files.
|
||||||
|
|
||||||
|
// Format of a Paint Shop Pro palette file, line by line:
|
||||||
|
// "JASC-PAL\r\n" (signature)
|
||||||
|
// "0100\r\n" (version; seems to always be "0100")
|
||||||
|
// "<NUMBER_OF_COLORS>\r\n" (number of colors in decimal)
|
||||||
|
//
|
||||||
|
// <NUMBER_OF_COLORS> times:
|
||||||
|
// "<RED> <GREEN> <BLUE>\r\n" (color entry)
|
||||||
|
//
|
||||||
|
// Each color component is a decimal number from 0 to 255.
|
||||||
|
// Examples:
|
||||||
|
// Black - "0 0 0\r\n"
|
||||||
|
// Blue - "0 0 255\r\n"
|
||||||
|
// Brown - "150 75 0\r\n"
|
||||||
|
|
||||||
|
#define MAX_LINE_LENGTH 11
|
||||||
|
|
||||||
|
void ReadJascPaletteLine(FILE *fp, char *line)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
c = fgetc(fp);
|
||||||
|
|
||||||
|
if (c == '\r')
|
||||||
|
{
|
||||||
|
c = fgetc(fp);
|
||||||
|
|
||||||
|
if (c != '\n')
|
||||||
|
FATAL_ERROR("CR line endings aren't supported.\n");
|
||||||
|
|
||||||
|
line[length] = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\n')
|
||||||
|
FATAL_ERROR("LF line endings aren't supported.\n");
|
||||||
|
|
||||||
|
if (c == EOF)
|
||||||
|
FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n");
|
||||||
|
|
||||||
|
if (c == 0)
|
||||||
|
FATAL_ERROR("NUL character in file.\n");
|
||||||
|
|
||||||
|
if (length == MAX_LINE_LENGTH)
|
||||||
|
{
|
||||||
|
line[length] = 0;
|
||||||
|
FATAL_ERROR("The line \"%s\" is too long.\n", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
line[length++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadJascPalette(char *path, struct Palette *palette)
|
||||||
|
{
|
||||||
|
char line[MAX_LINE_LENGTH + 1];
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
FATAL_ERROR("Failed to open JASC-PAL file \"%s\" for reading.\n", path);
|
||||||
|
|
||||||
|
ReadJascPaletteLine(fp, line);
|
||||||
|
|
||||||
|
if (strcmp(line, "JASC-PAL") != 0)
|
||||||
|
FATAL_ERROR("Invalid JASC-PAL signature.\n");
|
||||||
|
|
||||||
|
ReadJascPaletteLine(fp, line);
|
||||||
|
|
||||||
|
if (strcmp(line, "0100") != 0)
|
||||||
|
FATAL_ERROR("Unsuported JASC-PAL version.\n");
|
||||||
|
|
||||||
|
ReadJascPaletteLine(fp, line);
|
||||||
|
|
||||||
|
if (!ParseNumber(line, NULL, 10, &palette->numColors))
|
||||||
|
FATAL_ERROR("Failed to parse number of colors.\n");
|
||||||
|
|
||||||
|
if (palette->numColors < 1 || palette->numColors > 256)
|
||||||
|
FATAL_ERROR("%d is an invalid number of colors. The number of colors must be in the range [1, 256].\n", palette->numColors);
|
||||||
|
|
||||||
|
palette->bitDepth = 4;
|
||||||
|
|
||||||
|
for (int i = 0; i < palette->numColors; i++)
|
||||||
|
{
|
||||||
|
ReadJascPaletteLine(fp, line);
|
||||||
|
|
||||||
|
char *s = line;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
int red;
|
||||||
|
int green;
|
||||||
|
int blue;
|
||||||
|
|
||||||
|
if (!ParseNumber(s, &end, 10, &red))
|
||||||
|
FATAL_ERROR("Failed to parse red color component.\n");
|
||||||
|
|
||||||
|
s = end;
|
||||||
|
|
||||||
|
if (*s != ' ')
|
||||||
|
FATAL_ERROR("Expected a space after red color component.\n");
|
||||||
|
|
||||||
|
s++;
|
||||||
|
|
||||||
|
if (*s < '0' || *s > '9')
|
||||||
|
FATAL_ERROR("Expected only a space between red and green color components.\n");
|
||||||
|
|
||||||
|
if (!ParseNumber(s, &end, 10, &green))
|
||||||
|
FATAL_ERROR("Failed to parse green color component.\n");
|
||||||
|
|
||||||
|
s = end;
|
||||||
|
|
||||||
|
if (*s != ' ')
|
||||||
|
FATAL_ERROR("Expected a space after green color component.\n");
|
||||||
|
|
||||||
|
s++;
|
||||||
|
|
||||||
|
if (*s < '0' || *s > '9')
|
||||||
|
FATAL_ERROR("Expected only a space between green and blue color components.\n");
|
||||||
|
|
||||||
|
if (!ParseNumber(s, &end, 10, &blue))
|
||||||
|
FATAL_ERROR("Failed to parse blue color component.\n");
|
||||||
|
|
||||||
|
if (*end != 0)
|
||||||
|
FATAL_ERROR("Garbage after blue color component.\n");
|
||||||
|
|
||||||
|
if (red < 0 || red > 255)
|
||||||
|
FATAL_ERROR("Red color component (%d) is outside the range [0, 255].\n", red);
|
||||||
|
|
||||||
|
if (green < 0 || green > 255)
|
||||||
|
FATAL_ERROR("Green color component (%d) is outside the range [0, 255].\n", green);
|
||||||
|
|
||||||
|
if (blue < 0 || blue > 255)
|
||||||
|
FATAL_ERROR("Blue color component (%d) is outside the range [0, 255].\n", blue);
|
||||||
|
|
||||||
|
palette->colors[i].red = red;
|
||||||
|
palette->colors[i].green = green;
|
||||||
|
palette->colors[i].blue = blue;
|
||||||
|
if (i >= 16)
|
||||||
|
{
|
||||||
|
if (red || green || blue)
|
||||||
|
palette->bitDepth = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgetc(fp) != EOF)
|
||||||
|
FATAL_ERROR("Garbage after color data.\n");
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteJascPalette(char *path, struct Palette *palette)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "wb");
|
||||||
|
|
||||||
|
fputs("JASC-PAL\r\n", fp);
|
||||||
|
fputs("0100\r\n", fp);
|
||||||
|
fprintf(fp, "%d\r\n", palette->numColors);
|
||||||
|
|
||||||
|
for (int i = 0; i < palette->numColors; i++)
|
||||||
|
{
|
||||||
|
struct Color *color = &palette->colors[i];
|
||||||
|
fprintf(fp, "%d %d %d\r\n", color->red, color->green, color->blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
9
jasc_pal.h
Normal file
9
jasc_pal.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef JASC_PAL_H
|
||||||
|
#define JASC_PAL_H
|
||||||
|
|
||||||
|
void ReadJascPalette(char *path, struct Palette *palette);
|
||||||
|
void WriteJascPalette(char *path, struct Palette *palette);
|
||||||
|
|
||||||
|
#endif // JASC_PAL_H
|
454
json.c
Normal file
454
json.c
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
// Copyright (c) 2021-2023 red031000
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "cJSON.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static inline bool GetBool(cJSON * in)
|
||||||
|
{
|
||||||
|
if (!cJSON_IsBool(in))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return cJSON_IsTrue(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int GetInt(cJSON * in)
|
||||||
|
{
|
||||||
|
if (!cJSON_IsNumber(in))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return in->valueint;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *GetString(cJSON * in)
|
||||||
|
{
|
||||||
|
if (!cJSON_IsString(in))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return in->valuestring;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JsonToCellOptions *ParseNCERJson(char *path)
|
||||||
|
{
|
||||||
|
int fileLength;
|
||||||
|
unsigned char *jsonString = ReadWholeFile(path, &fileLength);
|
||||||
|
|
||||||
|
cJSON *json = cJSON_Parse((const char *)jsonString);
|
||||||
|
|
||||||
|
struct JsonToCellOptions *options = malloc(sizeof(struct JsonToCellOptions));
|
||||||
|
|
||||||
|
if (json == NULL)
|
||||||
|
{
|
||||||
|
const char *errorPtr = cJSON_GetErrorPtr();
|
||||||
|
FATAL_ERROR("Error in line \"%s\"\n", errorPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled");
|
||||||
|
cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended");
|
||||||
|
cJSON *imageHeight = cJSON_GetObjectItemCaseSensitive(json, "imageHeight");
|
||||||
|
cJSON *imageWidth = cJSON_GetObjectItemCaseSensitive(json, "imageWidth");
|
||||||
|
cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount");
|
||||||
|
cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType");
|
||||||
|
|
||||||
|
options->labelEnabled = GetBool(labelBool);
|
||||||
|
options->extended = GetBool(extended);
|
||||||
|
options->imageHeight = GetInt(imageHeight);
|
||||||
|
options->imageWidth = GetInt(imageWidth);
|
||||||
|
options->cellCount = GetInt(cellCount);
|
||||||
|
options->mappingType = GetInt(mappingType);
|
||||||
|
|
||||||
|
options->cells = malloc(sizeof(struct Cell *) * options->cellCount);
|
||||||
|
|
||||||
|
if (options->labelEnabled)
|
||||||
|
{
|
||||||
|
cJSON *labelCount = cJSON_GetObjectItemCaseSensitive(json, "labelCount");
|
||||||
|
options->labelCount = GetInt(labelCount);
|
||||||
|
options->labels = malloc(sizeof(char *) * options->labelCount);
|
||||||
|
|
||||||
|
cJSON *labels = cJSON_GetObjectItemCaseSensitive(json, "labels");
|
||||||
|
cJSON *label = NULL;
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
cJSON_ArrayForEach(label, labels)
|
||||||
|
{
|
||||||
|
char *labelString = GetString(label);
|
||||||
|
options->labels[j] = malloc(strlen(labelString) + 1);
|
||||||
|
strcpy(options->labels[j], labelString);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < options->cellCount; i++)
|
||||||
|
{
|
||||||
|
options->cells[i] = malloc(sizeof(struct Cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
cJSON *cells = cJSON_GetObjectItemCaseSensitive(json, "cells");
|
||||||
|
cJSON *cell = NULL;
|
||||||
|
|
||||||
|
cJSON_ArrayForEach(cell, cells)
|
||||||
|
{
|
||||||
|
if (i > options->cellCount - 1)
|
||||||
|
FATAL_ERROR("Cell count is incorrect.\n");
|
||||||
|
|
||||||
|
cJSON *readOnly = cJSON_GetObjectItemCaseSensitive(cell, "readOnly");
|
||||||
|
|
||||||
|
options->cells[i]->readOnly = (short)GetInt(readOnly);
|
||||||
|
if (options->extended)
|
||||||
|
{
|
||||||
|
cJSON *maxX = cJSON_GetObjectItemCaseSensitive(cell, "maxX");
|
||||||
|
cJSON *maxY = cJSON_GetObjectItemCaseSensitive(cell, "maxY");
|
||||||
|
cJSON *minX = cJSON_GetObjectItemCaseSensitive(cell, "minX");
|
||||||
|
cJSON *minY = cJSON_GetObjectItemCaseSensitive(cell, "minY");
|
||||||
|
|
||||||
|
options->cells[i]->maxX = (short)GetInt(maxX);
|
||||||
|
options->cells[i]->maxY = (short)GetInt(maxY);
|
||||||
|
options->cells[i]->minX = (short)GetInt(minX);
|
||||||
|
options->cells[i]->minY = (short)GetInt(minY);
|
||||||
|
}
|
||||||
|
//OAM data
|
||||||
|
cJSON *OAM = cJSON_GetObjectItemCaseSensitive(cell, "OAM");
|
||||||
|
|
||||||
|
//Attr0
|
||||||
|
cJSON *Attr0 = cJSON_GetObjectItemCaseSensitive(OAM, "Attr0");
|
||||||
|
|
||||||
|
cJSON *YCoordinate = cJSON_GetObjectItemCaseSensitive(Attr0, "YCoordinate");
|
||||||
|
cJSON *Rotation = cJSON_GetObjectItemCaseSensitive(Attr0, "Rotation");
|
||||||
|
cJSON *SizeDisable = cJSON_GetObjectItemCaseSensitive(Attr0, "SizeDisable");
|
||||||
|
cJSON *Mode = cJSON_GetObjectItemCaseSensitive(Attr0, "Mode");
|
||||||
|
cJSON *Mosaic = cJSON_GetObjectItemCaseSensitive(Attr0, "Mosaic");
|
||||||
|
cJSON *Colours = cJSON_GetObjectItemCaseSensitive(Attr0, "Colours");
|
||||||
|
cJSON *Shape = cJSON_GetObjectItemCaseSensitive(Attr0, "Shape");
|
||||||
|
|
||||||
|
options->cells[i]->oam.attr0.YCoordinate = GetInt(YCoordinate);
|
||||||
|
options->cells[i]->oam.attr0.Rotation = GetBool(Rotation);
|
||||||
|
options->cells[i]->oam.attr0.SizeDisable = GetBool(SizeDisable);
|
||||||
|
options->cells[i]->oam.attr0.Mode = GetInt(Mode);
|
||||||
|
options->cells[i]->oam.attr0.Mosaic = GetBool(Mosaic);
|
||||||
|
options->cells[i]->oam.attr0.Colours = GetInt(Colours);
|
||||||
|
options->cells[i]->oam.attr0.Shape = GetInt(Shape);
|
||||||
|
|
||||||
|
//Attr1
|
||||||
|
cJSON *Attr1 = cJSON_GetObjectItemCaseSensitive(OAM, "Attr1");
|
||||||
|
|
||||||
|
cJSON *XCoordinate = cJSON_GetObjectItemCaseSensitive(Attr1, "XCoordinate");
|
||||||
|
cJSON *RotationScaling = cJSON_GetObjectItemCaseSensitive(Attr1, "RotationScaling");
|
||||||
|
cJSON *Size = cJSON_GetObjectItemCaseSensitive(Attr1, "Size");
|
||||||
|
|
||||||
|
options->cells[i]->oam.attr1.XCoordinate = GetInt(XCoordinate);
|
||||||
|
options->cells[i]->oam.attr1.RotationScaling = GetInt(RotationScaling);
|
||||||
|
options->cells[i]->oam.attr1.Size = GetInt(Size);
|
||||||
|
|
||||||
|
//Attr2
|
||||||
|
cJSON *Attr2 = cJSON_GetObjectItemCaseSensitive(OAM, "Attr2");
|
||||||
|
|
||||||
|
cJSON *CharName = cJSON_GetObjectItemCaseSensitive(Attr2, "CharName");
|
||||||
|
cJSON *Priority = cJSON_GetObjectItemCaseSensitive(Attr2, "Priority");
|
||||||
|
cJSON *Palette = cJSON_GetObjectItemCaseSensitive(Attr2, "Palette");
|
||||||
|
|
||||||
|
options->cells[i]->oam.attr2.CharName = GetInt(CharName);
|
||||||
|
options->cells[i]->oam.attr2.Priority = GetInt(Priority);
|
||||||
|
options->cells[i]->oam.attr2.Palette = GetInt(Palette);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
free(jsonString);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JsonToScreenOptions *ParseNSCRJson(char *path)
|
||||||
|
{
|
||||||
|
int fileLength;
|
||||||
|
unsigned char *jsonString = ReadWholeFile(path, &fileLength);
|
||||||
|
|
||||||
|
cJSON *json = cJSON_Parse((const char *)jsonString);
|
||||||
|
|
||||||
|
struct JsonToScreenOptions *options = malloc(sizeof(struct JsonToScreenOptions));
|
||||||
|
|
||||||
|
if (json == NULL)
|
||||||
|
{
|
||||||
|
const char *errorPtr = cJSON_GetErrorPtr();
|
||||||
|
FATAL_ERROR("Error in line \"%s\"\n", errorPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *Height = cJSON_GetObjectItemCaseSensitive(json, "height");
|
||||||
|
cJSON *Width = cJSON_GetObjectItemCaseSensitive(json, "width");
|
||||||
|
|
||||||
|
options->height = GetInt(Height);
|
||||||
|
options->width = GetInt(Width);
|
||||||
|
|
||||||
|
options->data = malloc(sizeof(unsigned short) * options->height * options->width);
|
||||||
|
|
||||||
|
cJSON *layer = NULL;
|
||||||
|
cJSON *layers = cJSON_GetObjectItemCaseSensitive(json, "layers");
|
||||||
|
int palette = 0;
|
||||||
|
cJSON *tilesets = cJSON_GetObjectItemCaseSensitive(json, "tilesets");
|
||||||
|
int tilesetSize = 0;
|
||||||
|
if (cJSON_GetArraySize(tilesets) != 1)
|
||||||
|
{
|
||||||
|
cJSON *tileset = cJSON_GetArrayItem(tilesets, 1);
|
||||||
|
cJSON *firstGid = cJSON_GetObjectItemCaseSensitive(tileset, "firstgid");
|
||||||
|
tilesetSize = GetInt(firstGid) - 1;
|
||||||
|
if (tilesetSize <= 1)
|
||||||
|
FATAL_ERROR("Wrong tileset index (tileset 0 should be added first)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_ArrayForEach(layer, layers)
|
||||||
|
{
|
||||||
|
cJSON *tile = NULL;
|
||||||
|
cJSON *data = cJSON_GetObjectItemCaseSensitive(layer, "data");
|
||||||
|
int i = 0;
|
||||||
|
cJSON_ArrayForEach(tile, data)
|
||||||
|
{
|
||||||
|
int tileInt = GetInt(tile) - 1;
|
||||||
|
if (tileInt != -1)
|
||||||
|
{
|
||||||
|
if (tilesetSize != 0)
|
||||||
|
{
|
||||||
|
palette = tileInt / tilesetSize;
|
||||||
|
tileInt %= tilesetSize;
|
||||||
|
}
|
||||||
|
bool vFlip = tileInt >> 30;
|
||||||
|
bool hFlip = tileInt >> 31;
|
||||||
|
tileInt |= vFlip << 11;
|
||||||
|
tileInt |= hFlip << 10;
|
||||||
|
tileInt |= palette << 12;
|
||||||
|
options->data[i] = (short) (tileInt & 0xFFFF);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
free(jsonString);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JsonToAnimationOptions *ParseNANRJson(char *path)
|
||||||
|
{
|
||||||
|
int filelength;
|
||||||
|
unsigned char *jsonString = ReadWholeFile(path, &filelength);
|
||||||
|
|
||||||
|
cJSON *json = cJSON_Parse((const char *)jsonString);
|
||||||
|
|
||||||
|
struct JsonToAnimationOptions *options = malloc(sizeof(struct JsonToAnimationOptions));
|
||||||
|
|
||||||
|
if (json == NULL)
|
||||||
|
{
|
||||||
|
const char *errorPtr = cJSON_GetErrorPtr();
|
||||||
|
FATAL_ERROR("Error in line \"%s\"\n", errorPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *sequenceCount = cJSON_GetObjectItemCaseSensitive(json, "sequenceCount");
|
||||||
|
cJSON *frameCount = cJSON_GetObjectItemCaseSensitive(json, "frameCount");
|
||||||
|
|
||||||
|
options->sequenceCount = GetInt(sequenceCount);
|
||||||
|
options->frameCount = GetInt(frameCount);
|
||||||
|
|
||||||
|
options->sequenceData = malloc(sizeof(struct SequenceData *) * options->sequenceCount);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < options->sequenceCount; i++)
|
||||||
|
{
|
||||||
|
options->sequenceData[i] = malloc(sizeof(struct SequenceData));
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *sequence = NULL;
|
||||||
|
cJSON *sequences = cJSON_GetObjectItemCaseSensitive(json, "sequences");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
cJSON_ArrayForEach(sequence, sequences)
|
||||||
|
{
|
||||||
|
if (i > options->sequenceCount - 1)
|
||||||
|
FATAL_ERROR("Sequence count is incorrect.\n");
|
||||||
|
|
||||||
|
cJSON *frameCount = cJSON_GetObjectItemCaseSensitive(sequence, "frameCount");
|
||||||
|
cJSON *loopStartFrame = cJSON_GetObjectItemCaseSensitive(sequence, "loopStartFrame");
|
||||||
|
cJSON *animationElement = cJSON_GetObjectItemCaseSensitive(sequence, "animationElement");
|
||||||
|
cJSON *animationType = cJSON_GetObjectItemCaseSensitive(sequence, "animationType");
|
||||||
|
cJSON *playbackMode = cJSON_GetObjectItemCaseSensitive(sequence, "playbackMode");
|
||||||
|
|
||||||
|
options->sequenceData[i]->frameCount = GetInt(frameCount);
|
||||||
|
options->sequenceData[i]->loopStartFrame = GetInt(loopStartFrame);
|
||||||
|
options->sequenceData[i]->animationElement = GetInt(animationElement);
|
||||||
|
options->sequenceData[i]->animationType = GetInt(animationType);
|
||||||
|
options->sequenceData[i]->playbackMode = GetInt(playbackMode);
|
||||||
|
|
||||||
|
options->sequenceData[i]->frameData = malloc(sizeof(struct FrameData *) * options->sequenceData[i]->frameCount);
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < options->sequenceData[i]->frameCount; j++)
|
||||||
|
{
|
||||||
|
options->sequenceData[i]->frameData[j] = malloc(sizeof(struct FrameData));
|
||||||
|
}
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
cJSON *frame = NULL;
|
||||||
|
cJSON *frameData = cJSON_GetObjectItemCaseSensitive(sequence, "frameData");
|
||||||
|
|
||||||
|
cJSON_ArrayForEach(frame, frameData)
|
||||||
|
{
|
||||||
|
if (j > options->sequenceData[i]->frameCount - 1)
|
||||||
|
FATAL_ERROR("Sequence frame count is incorrect.\n");
|
||||||
|
|
||||||
|
cJSON *frameDelay = cJSON_GetObjectItemCaseSensitive(frame, "frameDelay");
|
||||||
|
cJSON *resultId = cJSON_GetObjectItemCaseSensitive(frame, "resultId");
|
||||||
|
|
||||||
|
options->sequenceData[i]->frameData[j]->frameDelay = GetInt(frameDelay);
|
||||||
|
options->sequenceData[i]->frameData[j]->resultId = GetInt(resultId);
|
||||||
|
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo implement extended attributes
|
||||||
|
|
||||||
|
cJSON *resultCount = cJSON_GetObjectItemCaseSensitive(json, "resultCount");
|
||||||
|
|
||||||
|
options->resultCount = GetInt(resultCount);
|
||||||
|
|
||||||
|
options->animationResults = malloc(sizeof(struct AnimationResults *) * options->resultCount);
|
||||||
|
|
||||||
|
for (i = 0; i < options->resultCount; i++)
|
||||||
|
{
|
||||||
|
options->animationResults[i] = malloc(sizeof(struct AnimationResults));
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
cJSON *animationResult = NULL;
|
||||||
|
cJSON *animationResults = cJSON_GetObjectItemCaseSensitive(json, "animationResults");
|
||||||
|
cJSON_ArrayForEach(animationResult, animationResults)
|
||||||
|
{
|
||||||
|
if (i > options->resultCount - 1)
|
||||||
|
FATAL_ERROR("Frame count is incorrect.\n");
|
||||||
|
|
||||||
|
cJSON *resultType = cJSON_GetObjectItemCaseSensitive(animationResult, "resultType");
|
||||||
|
options->animationResults[i]->resultType = GetInt(resultType);
|
||||||
|
switch (options->animationResults[i]->resultType) {
|
||||||
|
case 0: { //index
|
||||||
|
cJSON *index = cJSON_GetObjectItemCaseSensitive(animationResult, "index");
|
||||||
|
options->animationResults[i]->index = GetInt(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1: { //SRT
|
||||||
|
cJSON *index = cJSON_GetObjectItemCaseSensitive(animationResult, "index");
|
||||||
|
cJSON *rotation = cJSON_GetObjectItemCaseSensitive(animationResult, "rotation");
|
||||||
|
cJSON *scaleX = cJSON_GetObjectItemCaseSensitive(animationResult, "scaleX");
|
||||||
|
cJSON *scaleY = cJSON_GetObjectItemCaseSensitive(animationResult, "scaleY");
|
||||||
|
cJSON *positionX = cJSON_GetObjectItemCaseSensitive(animationResult, "positionX");
|
||||||
|
cJSON *positionY = cJSON_GetObjectItemCaseSensitive(animationResult, "positionY");
|
||||||
|
|
||||||
|
options->animationResults[i]->dataSrt.index = GetInt(index);
|
||||||
|
options->animationResults[i]->dataSrt.rotation = GetInt(rotation);
|
||||||
|
options->animationResults[i]->dataSrt.scaleX = GetInt(scaleX);
|
||||||
|
options->animationResults[i]->dataSrt.scaleY = GetInt(scaleY);
|
||||||
|
options->animationResults[i]->dataSrt.positionX = GetInt(positionX);
|
||||||
|
options->animationResults[i]->dataSrt.positionY = GetInt(positionY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: { //T
|
||||||
|
cJSON *index = cJSON_GetObjectItemCaseSensitive(animationResult, "index");
|
||||||
|
//cJSON *rotation = cJSON_GetObjectItemCaseSensitive(animationResult, "rotation");
|
||||||
|
cJSON *positionX = cJSON_GetObjectItemCaseSensitive(animationResult, "positionX");
|
||||||
|
cJSON *positionY = cJSON_GetObjectItemCaseSensitive(animationResult, "positionY");
|
||||||
|
|
||||||
|
options->animationResults[i]->dataT.index = GetInt(index);
|
||||||
|
//options->animationResults[i]->dataSrt.rotation = GetInt(rotation);
|
||||||
|
options->animationResults[i]->dataT.positionX = GetInt(positionX);
|
||||||
|
options->animationResults[i]->dataT.positionY = GetInt(positionY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled");
|
||||||
|
options->labelEnabled = GetBool(labelBool);
|
||||||
|
|
||||||
|
if (options->labelEnabled)
|
||||||
|
{
|
||||||
|
cJSON *labelCount = cJSON_GetObjectItemCaseSensitive(json, "labelCount");
|
||||||
|
options->labelCount = GetInt(labelCount);
|
||||||
|
options->labels = malloc(sizeof(char *) * options->labelCount);
|
||||||
|
|
||||||
|
cJSON *labels = cJSON_GetObjectItemCaseSensitive(json, "labels");
|
||||||
|
cJSON *label = NULL;
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
cJSON_ArrayForEach(label, labels)
|
||||||
|
{
|
||||||
|
char *labelString = GetString(label);
|
||||||
|
options->labels[j] = malloc(strlen(labelString) + 1);
|
||||||
|
strcpy(options->labels[j], labelString);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
free(jsonString);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeNCERCell(struct JsonToCellOptions *options)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < options->cellCount; i++)
|
||||||
|
{
|
||||||
|
free(options->cells[i]);
|
||||||
|
}
|
||||||
|
if (options->labelEnabled)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < options->labelCount; j++)
|
||||||
|
{
|
||||||
|
free(options->labels[j]);
|
||||||
|
}
|
||||||
|
free(options->labels);
|
||||||
|
}
|
||||||
|
free(options->cells);
|
||||||
|
free(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeNSCRScreen(struct JsonToScreenOptions *options)
|
||||||
|
{
|
||||||
|
free(options->data);
|
||||||
|
free(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FreeNANRAnimation(struct JsonToAnimationOptions *options)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < options->sequenceCount; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < options->sequenceData[i]->frameCount; j++)
|
||||||
|
{
|
||||||
|
free(options->sequenceData[i]->frameData[j]);
|
||||||
|
}
|
||||||
|
free(options->sequenceData[i]->frameData);
|
||||||
|
free(options->sequenceData[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < options->resultCount; i++)
|
||||||
|
{
|
||||||
|
free(options->animationResults[i]);
|
||||||
|
}
|
||||||
|
if (options->labelEnabled)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < options->labelCount; j++)
|
||||||
|
{
|
||||||
|
free(options->labels[j]);
|
||||||
|
}
|
||||||
|
free(options->labels);
|
||||||
|
}
|
||||||
|
free(options->sequenceData);
|
||||||
|
free(options->animationResults);
|
||||||
|
free(options);
|
||||||
|
}
|
15
json.h
Normal file
15
json.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) 2021-2023 red031000
|
||||||
|
|
||||||
|
#ifndef JSON_H
|
||||||
|
#define JSON_H
|
||||||
|
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
struct JsonToCellOptions *ParseNCERJson(char *path);
|
||||||
|
struct JsonToScreenOptions *ParseNSCRJson(char *path);
|
||||||
|
struct JsonToAnimationOptions *ParseNANRJson(char *path);
|
||||||
|
void FreeNCERCell(struct JsonToCellOptions *options);
|
||||||
|
void FreeNSCRScreen(struct JsonToScreenOptions *options);
|
||||||
|
void FreeNANRAnimation(struct JsonToAnimationOptions *options);
|
||||||
|
|
||||||
|
#endif //JSON_H
|
153
lz.c
Normal file
153
lz.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "lz.h"
|
||||||
|
|
||||||
|
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
|
||||||
|
{
|
||||||
|
if (srcSize < 4)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
|
||||||
|
|
||||||
|
unsigned char *dest = malloc(destSize);
|
||||||
|
|
||||||
|
if (dest == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int srcPos = 4;
|
||||||
|
int destPos = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (srcPos >= srcSize)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
unsigned char flags = src[srcPos++];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
srcPos += 2;
|
||||||
|
|
||||||
|
int blockPos = destPos - blockDistance;
|
||||||
|
|
||||||
|
// Some Ruby/Sapphire tilesets overflow.
|
||||||
|
if (destPos + blockSize > destSize) {
|
||||||
|
blockSize = destSize - destPos;
|
||||||
|
fprintf(stderr, "Destination buffer overflow.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
dest[destPos++] = src[srcPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destPos == destSize) {
|
||||||
|
*uncompressedSize = destSize;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
FATAL_ERROR("Fatal error while decompressing LZ file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
FATAL_ERROR("Fatal error while compressing LZ file.\n");
|
||||||
|
}
|
9
lz.h
Normal file
9
lz.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef LZ_H
|
||||||
|
#define LZ_H
|
||||||
|
|
||||||
|
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
|
||||||
|
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance);
|
||||||
|
|
||||||
|
#endif // LZ_H
|
159
options.h
Normal file
159
options.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright (c) 2018 huderlem, 2021-2023 red031000
|
||||||
|
|
||||||
|
#ifndef OPTIONS_H
|
||||||
|
#define OPTIONS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct GbaToPngOptions {
|
||||||
|
char *paletteFilePath;
|
||||||
|
int bitDepth;
|
||||||
|
bool hasTransparency;
|
||||||
|
int width;
|
||||||
|
int metatileWidth;
|
||||||
|
int metatileHeight;
|
||||||
|
int palIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PngToGbaOptions {
|
||||||
|
int numTiles;
|
||||||
|
int bitDepth;
|
||||||
|
int metatileWidth;
|
||||||
|
int metatileHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PngToNtrOptions {
|
||||||
|
int numTiles;
|
||||||
|
int bitDepth;
|
||||||
|
int metatileWidth;
|
||||||
|
int metatileHeight;
|
||||||
|
bool clobberSize;
|
||||||
|
bool byteOrder;
|
||||||
|
bool version101;
|
||||||
|
bool sopc;
|
||||||
|
uint32_t scanMode;
|
||||||
|
bool wrongSize;
|
||||||
|
bool handleEmpty;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NtrToPngOptions {
|
||||||
|
char *paletteFilePath;
|
||||||
|
int bitDepth;
|
||||||
|
bool hasTransparency;
|
||||||
|
int width;
|
||||||
|
int metatileWidth;
|
||||||
|
int metatileHeight;
|
||||||
|
int palIndex;
|
||||||
|
bool scanFrontToBack;
|
||||||
|
bool handleEmpty;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Attr0 {
|
||||||
|
int YCoordinate;
|
||||||
|
bool Rotation;
|
||||||
|
bool SizeDisable;
|
||||||
|
int Mode;
|
||||||
|
bool Mosaic;
|
||||||
|
int Colours;
|
||||||
|
int Shape;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Attr1 {
|
||||||
|
int XCoordinate;
|
||||||
|
int RotationScaling;
|
||||||
|
int Size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Attr2 {
|
||||||
|
int CharName;
|
||||||
|
int Priority;
|
||||||
|
int Palette;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OAM {
|
||||||
|
struct Attr0 attr0;
|
||||||
|
struct Attr1 attr1;
|
||||||
|
struct Attr2 attr2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cell {
|
||||||
|
short readOnly;
|
||||||
|
short maxX;
|
||||||
|
short maxY;
|
||||||
|
short minX;
|
||||||
|
short minY;
|
||||||
|
struct OAM oam;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JsonToCellOptions {
|
||||||
|
bool labelEnabled;
|
||||||
|
bool extended;
|
||||||
|
int mappingType;
|
||||||
|
int imageHeight;
|
||||||
|
int imageWidth;
|
||||||
|
int cellCount;
|
||||||
|
struct Cell **cells;
|
||||||
|
char **labels;
|
||||||
|
int labelCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JsonToScreenOptions {
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
unsigned short *data;
|
||||||
|
int bitdepth;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameData {
|
||||||
|
int resultId;
|
||||||
|
short frameDelay;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SequenceData {
|
||||||
|
short frameCount;
|
||||||
|
short loopStartFrame;
|
||||||
|
short animationElement;
|
||||||
|
short animationType;
|
||||||
|
int playbackMode;
|
||||||
|
struct FrameData **frameData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationDataSRT {
|
||||||
|
short index;
|
||||||
|
unsigned short rotation;
|
||||||
|
int scaleX;
|
||||||
|
int scaleY;
|
||||||
|
short positionX;
|
||||||
|
short positionY;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationDataT {
|
||||||
|
short index;
|
||||||
|
//unsigned short rotation;
|
||||||
|
short positionX;
|
||||||
|
short positionY;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationResults {
|
||||||
|
short resultType;
|
||||||
|
union {
|
||||||
|
short index;
|
||||||
|
struct AnimationDataSRT dataSrt;
|
||||||
|
struct AnimationDataT dataT;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JsonToAnimationOptions {
|
||||||
|
bool multiCell;
|
||||||
|
short sequenceCount;
|
||||||
|
short frameCount;
|
||||||
|
struct SequenceData **sequenceData;
|
||||||
|
struct AnimationResults **animationResults;
|
||||||
|
bool labelEnabled;
|
||||||
|
char **labels;
|
||||||
|
int labelCount;
|
||||||
|
short resultCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPTIONS_H
|
149
rl.c
Normal file
149
rl.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Copyright (c) 2016 YamaArashi
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "rl.h"
|
||||||
|
|
||||||
|
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
|
||||||
|
{
|
||||||
|
if (srcSize < 4)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
|
||||||
|
|
||||||
|
unsigned char *dest = malloc(destSize);
|
||||||
|
|
||||||
|
if (dest == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int srcPos = 4;
|
||||||
|
int destPos = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (srcPos >= srcSize)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
unsigned char flags = src[srcPos++];
|
||||||
|
bool compressed = ((flags & 0x80) != 0);
|
||||||
|
|
||||||
|
if (compressed)
|
||||||
|
{
|
||||||
|
int length = (flags & 0x7F) + 3;
|
||||||
|
unsigned char data = src[srcPos++];
|
||||||
|
|
||||||
|
if (destPos + length > destSize)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
dest[destPos++] = data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int length = (flags & 0x7F) + 1;
|
||||||
|
|
||||||
|
if (destPos + length > destSize)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
dest[destPos++] = src[srcPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destPos == destSize)
|
||||||
|
{
|
||||||
|
*uncompressedSize = destSize;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
FATAL_ERROR("Fatal error while decompressing RL file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize)
|
||||||
|
{
|
||||||
|
if (srcSize <= 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
int worstCaseDestSize = 4 + srcSize * 2;
|
||||||
|
|
||||||
|
// 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] = 0x30; // RL 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 (;;)
|
||||||
|
{
|
||||||
|
bool compress = false;
|
||||||
|
int uncompressedStart = srcPos;
|
||||||
|
int uncompressedLength = 0;
|
||||||
|
|
||||||
|
while (srcPos < srcSize && uncompressedLength < (0x7F + 1))
|
||||||
|
{
|
||||||
|
compress = (srcPos + 2 < srcSize && src[srcPos] == src[srcPos + 1] && src[srcPos] == src[srcPos + 2]);
|
||||||
|
|
||||||
|
if (compress)
|
||||||
|
break;
|
||||||
|
|
||||||
|
srcPos++;
|
||||||
|
uncompressedLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uncompressedLength > 0)
|
||||||
|
{
|
||||||
|
dest[destPos++] = uncompressedLength - 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < uncompressedLength; i++)
|
||||||
|
dest[destPos++] = src[uncompressedStart + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compress)
|
||||||
|
{
|
||||||
|
unsigned char data = src[srcPos];
|
||||||
|
int compressedLength = 0;
|
||||||
|
|
||||||
|
while (compressedLength < (0x7F + 3)
|
||||||
|
&& srcPos + compressedLength < srcSize
|
||||||
|
&& src[srcPos + compressedLength] == data)
|
||||||
|
{
|
||||||
|
compressedLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[destPos++] = 0x80 | (compressedLength - 3);
|
||||||
|
dest[destPos++] = data;
|
||||||
|
|
||||||
|
srcPos += compressedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 RL file.\n");
|
||||||
|
}
|
9
rl.h
Normal file
9
rl.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2016 YamaArashi
|
||||||
|
|
||||||
|
#ifndef RL_H
|
||||||
|
#define RL_H
|
||||||
|
|
||||||
|
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
|
||||||
|
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize);
|
||||||
|
|
||||||
|
#endif // RL_H
|
157
util.c
Normal file
157
util.c
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
bool ParseNumber(char *s, char **end, int radix, int *intValue)
|
||||||
|
{
|
||||||
|
char *localEnd;
|
||||||
|
|
||||||
|
if (end == NULL)
|
||||||
|
end = &localEnd;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
const long longValue = strtol(s, end, radix);
|
||||||
|
|
||||||
|
if (*end == s)
|
||||||
|
return false; // not a number
|
||||||
|
|
||||||
|
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (longValue > INT_MAX)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (longValue < INT_MIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*intValue = (int)longValue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *GetFileExtension(char *path)
|
||||||
|
{
|
||||||
|
char *extension = path;
|
||||||
|
|
||||||
|
while (*extension != 0)
|
||||||
|
extension++;
|
||||||
|
|
||||||
|
while (extension > path && *extension != '.')
|
||||||
|
extension--;
|
||||||
|
|
||||||
|
if (extension == path)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
extension++;
|
||||||
|
|
||||||
|
if (*extension == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *ReadWholeFile(char *path, int *size)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
|
||||||
|
*size = ftell(fp);
|
||||||
|
|
||||||
|
unsigned char *buffer = malloc(*size);
|
||||||
|
|
||||||
|
if (buffer == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
|
||||||
|
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
if (fread(buffer, *size, 1, fp) != 1)
|
||||||
|
FATAL_ERROR("Failed to read \"%s\".\n", path);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
|
||||||
|
*size = ftell(fp);
|
||||||
|
|
||||||
|
unsigned char *buffer = calloc(*size + padAmount, 1);
|
||||||
|
|
||||||
|
if (buffer == NULL)
|
||||||
|
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
|
||||||
|
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
if (fread(buffer, *size, 1, fp) != 1)
|
||||||
|
FATAL_ERROR("Failed to read \"%s\".\n", path);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteWholeFile(char *path, void *buffer, int bufferSize)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "wb");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteGenericNtrHeader(FILE* fp, const char* magicNumber, uint32_t size, bool byteorder, bool version101, uint16_t sectionCount)
|
||||||
|
{
|
||||||
|
unsigned char header[0x10] =
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00 };
|
||||||
|
//magic number
|
||||||
|
memcpy(header, magicNumber, 4);
|
||||||
|
|
||||||
|
if (version101)
|
||||||
|
{
|
||||||
|
header[6] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//byte order
|
||||||
|
if (!byteorder)
|
||||||
|
{
|
||||||
|
memset(header + 4, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//size
|
||||||
|
size += 0x10; //add header size
|
||||||
|
header[8] = size & 0xFF;
|
||||||
|
header[9] = (size >> 8) & 0xFF;
|
||||||
|
header[10] = (size >> 16) & 0xFF;
|
||||||
|
header[11] = (size >> 24) & 0xFF;
|
||||||
|
|
||||||
|
//section count
|
||||||
|
header[14] = sectionCount & 0xFF;
|
||||||
|
header[15] = (sectionCount >> 8) & 0xFF;
|
||||||
|
|
||||||
|
fwrite(header, 1, 0x10, fp);
|
||||||
|
}
|
16
util.h
Normal file
16
util.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2015 YamaArashi
|
||||||
|
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
bool ParseNumber(char *s, char **end, int radix, int *intValue);
|
||||||
|
char *GetFileExtension(char *path);
|
||||||
|
unsigned char *ReadWholeFile(char *path, int *size);
|
||||||
|
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount);
|
||||||
|
void WriteWholeFile(char *path, void *buffer, int bufferSize);
|
||||||
|
void WriteGenericNtrHeader(FILE* fp, const char* magicNumber, uint32_t size, bool byteorder, bool version101, uint16_t sectionCount);
|
||||||
|
|
||||||
|
#endif // UTIL_H
|
Loading…
Reference in New Issue
Block a user