update nitrogfx to the latest version

This commit is contained in:
red031000 2025-11-03 22:50:42 +00:00
parent 69a73ea442
commit 5d39e7bd54
No known key found for this signature in database
GPG Key ID: D27E50C050AE0CE1
12 changed files with 1334 additions and 194 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2015 YamaArashi, 2021-2024 red031000
Copyright (c) 2015 YamaArashi, 2021-2025 red031000
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -117,7 +117,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
}
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 19)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
@ -308,9 +308,11 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu
{
double number = 0;
unsigned char *after_end = NULL;
unsigned char number_c_string[64];
unsigned char *number_c_string;
unsigned char decimal_point = get_decimal_point();
size_t i = 0;
size_t number_string_length = 0;
cJSON_bool has_decimal_point = false;
if ((input_buffer == NULL) || (input_buffer->content == NULL))
{
@ -320,7 +322,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu
/* copy the number into a temporary buffer and replace '.' with the decimal point
* of the current locale (for strtod)
* This also takes care of '\0' not necessarily being available for marking the end of the input */
for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
for (i = 0; can_access_at_index(input_buffer, i); i++)
{
switch (buffer_at_offset(input_buffer)[i])
{
@ -338,11 +340,12 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu
case '-':
case 'e':
case 'E':
number_c_string[i] = buffer_at_offset(input_buffer)[i];
number_string_length++;
break;
case '.':
number_c_string[i] = decimal_point;
number_string_length++;
has_decimal_point = true;
break;
default:
@ -350,11 +353,33 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu
}
}
loop_end:
number_c_string[i] = '\0';
/* malloc for temporary buffer, add 1 for '\0' */
number_c_string = (unsigned char *) input_buffer->hooks.allocate(number_string_length + 1);
if (number_c_string == NULL)
{
return false; /* allocation failure */
}
memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length);
number_c_string[number_string_length] = '\0';
if (has_decimal_point)
{
for (i = 0; i < number_string_length; i++)
{
if (number_c_string[i] == '.')
{
/* replace '.' with the decimal point of the current locale (for strtod) */
number_c_string[i] = decimal_point;
}
}
}
number = strtod((const char*)number_c_string, (char**)&after_end);
if (number_c_string == after_end)
{
/* free the temporary buffer */
input_buffer->hooks.deallocate(number_c_string);
return false; /* parse_error */
}
@ -377,6 +402,8 @@ loop_end:
item->type = cJSON_Number;
input_buffer->offset += (size_t)(after_end - number_c_string);
/* free the temporary buffer */
input_buffer->hooks.deallocate(number_c_string);
return true;
}

View File

@ -81,7 +81,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 18
#define CJSON_VERSION_PATCH 19
#include <stddef.h>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015 YamaArashi, 2021-2024 red031000
// Copyright (c) 2015 YamaArashi, 2021-2025 red031000
#ifndef GFX_H
#define GFX_H
@ -51,16 +51,17 @@ struct Image {
};
void ReadImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors);
uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, bool scanFrontToBack);
uint32_t ReadNtrImage(char *path, int tilesWide, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors, uint32_t encodeMode, bool convertTo8Bpp, int palIndex, bool verbose);
void ApplyCellsToImage(char *cellFilePath, struct Image *image, bool toPNG, bool snap);
void WriteImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image, bool invertColors);
void WriteNtrImage(char *path, int numTiles, int bitDepth, int colsPerChunk, int rowsPerChunk, struct Image *image,
bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, uint32_t scanMode,
uint32_t mappingType, uint32_t key, bool wrongSize);
bool invertColors, bool clobberSize, bool byteOrder, bool version101, bool sopc, bool vram, bool scan,
uint32_t encodeMode, uint32_t mappingType, uint32_t key, bool wrongSize, bool convertTo4Bpp);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted);
void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIndex, bool inverted, bool convertTo8Bpp);
void WriteGbaPalette(char *path, struct Palette *palette);
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, bool inverted);
void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, int bitdepth, bool pad, int compNum, bool pcmp, int pcmpStartIndex, bool inverted, bool convertTo4Bpp);
void ReadNtrCell(char *path, struct JsonToCellOptions *options);
void WriteNtrCell(char *path, struct JsonToCellOptions *options);
void WriteNtrScreen(char *path, struct JsonToScreenOptions *options);

View File

@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 red031000
// Copyright (c) 2021-2025 red031000
#include "global.h"
#include "cJSON.h"
@ -47,13 +47,17 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
}
cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled");
cJSON *dontPadKbecBool = cJSON_GetObjectItemCaseSensitive(json, "dontPadKbec");
cJSON *vramTransferBool = cJSON_GetObjectItemCaseSensitive(json, "vramTransferEnabled");
cJSON *ucatBool = cJSON_GetObjectItemCaseSensitive(json, "ucatEnabled");
cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended");
cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount");
cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType");
options->labelEnabled = GetBool(labelBool);
options->dontPadKbec = GetBool(dontPadKbecBool);
options->vramTransferEnabled = GetBool(vramTransferBool);
options->ucatEnabled = GetBool(ucatBool);
options->extended = GetBool(extended);
options->cellCount = GetInt(cellCount);
options->mappingType = GetInt(mappingType);
@ -103,6 +107,18 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
}
}
if (options->ucatEnabled)
{
cJSON *ucatCells = cJSON_GetObjectItemCaseSensitive(json, "cellAttributes");
options->ucatCellAttribtes = malloc(sizeof(uint32_t) * options->cellCount);
for (int i = 0; i < options->cellCount; i++)
{
options->ucatCellAttribtes[i] = GetInt(cJSON_GetArrayItem(ucatCells, i));
}
}
for (int i = 0; i < options->cellCount; i++)
{
options->cells[i] = malloc(sizeof(struct Cell));
@ -174,7 +190,12 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
cJSON *Colours = cJSON_GetObjectItemCaseSensitive(Attr0, "Colours");
cJSON *Shape = cJSON_GetObjectItemCaseSensitive(Attr0, "Shape");
options->cells[i]->oam[j].attr0.YCoordinate = GetInt(YCoordinate);
int y = GetInt(YCoordinate);
if (y & (1 << 7))
{
y &= 0xFF;
}
options->cells[i]->oam[j].attr0.YCoordinate = y;
options->cells[i]->oam[j].attr0.Rotation = GetBool(Rotation);
options->cells[i]->oam[j].attr0.SizeDisable = GetBool(SizeDisable);
options->cells[i]->oam[j].attr0.Mode = GetInt(Mode);
@ -189,7 +210,12 @@ struct JsonToCellOptions *ParseNCERJson(char *path)
cJSON *RotationScaling = cJSON_GetObjectItemCaseSensitive(Attr1, "RotationScaling");
cJSON *Size = cJSON_GetObjectItemCaseSensitive(Attr1, "Size");
options->cells[i]->oam[j].attr1.XCoordinate = GetInt(XCoordinate);
int x = GetInt(XCoordinate);
if (x & (1 << 8))
{
x &= 0x1FF;
}
options->cells[i]->oam[j].attr1.XCoordinate = x;
options->cells[i]->oam[j].attr1.RotationScaling = GetInt(RotationScaling);
options->cells[i]->oam[j].attr1.Size = GetInt(Size);
@ -220,8 +246,10 @@ char *GetNCERJson(struct JsonToCellOptions *options)
cJSON *ncer = cJSON_CreateObject();
cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled);
cJSON_AddBoolToObject(ncer, "dontPadKbec", options->dontPadKbec);
cJSON_AddBoolToObject(ncer, "extended", options->extended);
cJSON_AddBoolToObject(ncer, "vramTransferEnabled", options->vramTransferEnabled);
cJSON_AddBoolToObject(ncer, "ucatEnabled", options->ucatEnabled);
cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount);
cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType);
@ -257,7 +285,12 @@ char *GetNCERJson(struct JsonToCellOptions *options)
cJSON *Attr0 = cJSON_AddObjectToObject(OAM, "Attr0");
cJSON_AddNumberToObject(Attr0, "YCoordinate", options->cells[i]->oam[j].attr0.YCoordinate);
int y = options->cells[i]->oam[j].attr0.YCoordinate;
if (y & (1 << 7))
{
y |= ~0xFF;
}
cJSON_AddNumberToObject(Attr0, "YCoordinate", y);
cJSON_AddBoolToObject(Attr0, "Rotation", options->cells[i]->oam[j].attr0.Rotation);
cJSON_AddBoolToObject(Attr0, "SizeDisable", options->cells[i]->oam[j].attr0.SizeDisable);
cJSON_AddNumberToObject(Attr0, "Mode", options->cells[i]->oam[j].attr0.Mode);
@ -267,7 +300,12 @@ char *GetNCERJson(struct JsonToCellOptions *options)
cJSON *Attr1 = cJSON_AddObjectToObject(OAM, "Attr1");
cJSON_AddNumberToObject(Attr1, "XCoordinate", options->cells[i]->oam[j].attr1.XCoordinate);
int x = options->cells[i]->oam[j].attr1.XCoordinate;
if (x & (1 << 8))
{
x |= ~0x1FF;
}
cJSON_AddNumberToObject(Attr1, "XCoordinate", x);
cJSON_AddNumberToObject(Attr1, "RotationScaling", options->cells[i]->oam[j].attr1.RotationScaling);
cJSON_AddNumberToObject(Attr1, "Size", options->cells[i]->oam[j].attr1.Size);
@ -304,6 +342,16 @@ char *GetNCERJson(struct JsonToCellOptions *options)
}
}
if (options->ucatEnabled)
{
cJSON *ucatCells = cJSON_AddArrayToObject(ncer, "cellAttributes");
for (int i = 0; i < options->cellCount; i++)
{
cJSON_AddNumberToObject(ucatCells, "cellAttr", options->ucatCellAttribtes[i]);
}
}
char *jsonString = cJSON_Print(ncer);
cJSON_Delete(ncer);
return jsonString;
@ -477,6 +525,9 @@ struct JsonToAnimationOptions *ParseNANRJson(char *path)
if (i > options->resultCount - 1)
FATAL_ERROR("Frame count is incorrect.\n");
//init padding to false, this is used in gfx.c to control padding, and is therefore checked there
options->animationResults[i]->padded = false;
cJSON *resultType = cJSON_GetObjectItemCaseSensitive(animationResult, "resultType");
options->animationResults[i]->resultType = GetInt(resultType);
switch (options->animationResults[i]->resultType) {
@ -542,6 +593,30 @@ struct JsonToAnimationOptions *ParseNANRJson(char *path)
}
}
cJSON *uaatBool = cJSON_GetObjectItemCaseSensitive(json, "uaatEnabled");
options->uaatEnabled = GetBool(uaatBool);
if (options->uaatEnabled)
{
cJSON *uaatData = cJSON_GetObjectItemCaseSensitive(json, "uaatData");
cJSON *uaatSequences = cJSON_GetObjectItemCaseSensitive(uaatData, "sequenceAttributes");
options->uaatData.sequenceAttributes = malloc(sizeof(uint32_t) * options->sequenceCount);
for (int i = 0; i < options->sequenceCount; i++)
{
cJSON *uaatSeq = cJSON_GetArrayItem(uaatSequences, i);
options->uaatData.sequenceAttributes[i] = GetInt(uaatSeq);
}
cJSON *uaatFrames = cJSON_GetObjectItemCaseSensitive(uaatData, "frameAttributes");
options->uaatData.frameAttributes = malloc(sizeof(uint32_t) * options->frameCount);
for (int i = 0; i < options->frameCount; i++)
{
cJSON *uaatFra = cJSON_GetArrayItem(uaatFrames, i);
options->uaatData.frameAttributes[i] = GetInt(uaatFra);
}
}
cJSON_Delete(json);
free(jsonString);
return options;
@ -552,6 +627,7 @@ char *GetNANRJson(struct JsonToAnimationOptions *options)
cJSON *nanr = cJSON_CreateObject();
cJSON_AddBoolToObject(nanr, "labelEnabled", options->labelEnabled);
cJSON_AddBoolToObject(nanr, "uaatEnabled", options->uaatEnabled);
cJSON_AddNumberToObject(nanr, "sequenceCount", options->sequenceCount);
cJSON_AddNumberToObject(nanr, "frameCount", options->frameCount);
@ -621,6 +697,23 @@ char *GetNANRJson(struct JsonToAnimationOptions *options)
cJSON_AddNumberToObject(nanr, "labelCount", options->labelCount);
}
if (options->uaatEnabled)
{
cJSON *uaat = cJSON_AddObjectToObject(nanr, "uaatData");
cJSON *uaatSequences = cJSON_AddArrayToObject(uaat, "sequenceAttributes");
for (int i = 0; i < options->sequenceCount; i++)
{
cJSON_AddNumberToObject(uaatSequences, "seqAttr", options->uaatData.sequenceAttributes[i]);
}
cJSON *uaatFrames = cJSON_AddArrayToObject(uaat, "frameAttributes");
for (int i = 0; i < options->frameCount; i++)
{
cJSON_AddNumberToObject(uaatFrames, "fraAttr", options->uaatData.frameAttributes[i]);
}
}
char *jsonString = cJSON_Print(nanr);
cJSON_Delete(nanr);
return jsonString;
@ -649,6 +742,10 @@ void FreeNCERCell(struct JsonToCellOptions *options)
}
free(options->transferData);
}
if (options->ucatEnabled)
{
free(options->ucatCellAttribtes);
}
free(options->cells);
free(options);
}
@ -675,6 +772,11 @@ void FreeNANRAnimation(struct JsonToAnimationOptions *options)
{
free(options->animationResults[i]);
}
if (options->uaatEnabled)
{
free(options->uaatData.sequenceAttributes);
free(options->uaatData.frameAttributes);
}
if (options->labelEnabled)
{
for (int j = 0; j < options->labelCount; j++)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 red031000
// Copyright (c) 2021-2025 red031000
#ifndef JSON_H
#define JSON_H

View File

@ -122,7 +122,7 @@ static void FindBestBlockBackwards(unsigned char *src, int srcPos, int srcSize,
typedef void (*FindBestBlockFunc)(unsigned char *src, int srcPos, int srcSize, const int minDistance, int *outBestBlockDistance, int *outBestBlockSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration)
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration, bool pad)
{
if (srcSize <= 0)
goto fail;
@ -169,12 +169,14 @@ unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize,
}
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (pad) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
}
*compressedSize = destPos;

View File

@ -6,6 +6,6 @@
#include "stdbool.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance, bool forwardIteration, bool pad);
#endif // LZ_H

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015 YamaArashi, 2021-2024 red031000
// Copyright (c) 2015 YamaArashi, 2021-2025 red031000
#include <ctype.h>
#include <stdio.h>
@ -23,6 +23,10 @@ struct CommandHandler
void(*function)(char *inputPath, char *outputPath, int argc, char **argv);
};
static int CountLzCompressArgs(int argc, char **argv);
static void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv);
static void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED);
void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *options)
{
struct Image image;
@ -49,10 +53,9 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *options)
{
// handle empty files if possible
FILE *fp = fopen(inputPath, "rb");
if (options->handleEmpty)
{
FILE *fp = fopen(inputPath, "rb");
if (fp != NULL)
{
fseek(fp, 0, SEEK_END);
@ -61,20 +64,22 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *
if (size == 0)
{
FILE *out = fopen(outputPath, "wb+");
fclose(out);
if (out != NULL)
{
fclose(out);
}
fclose(fp);
return;
}
fclose(fp);
}
}
fclose(fp);
struct Image image;
if (options->paletteFilePath != NULL)
{
ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false);
ReadNtrPalette(options->paletteFilePath, &image.palette, options->bitDepth, options->palIndex, false, options->convertTo8Bpp);
image.hasPalette = true;
}
else
@ -82,7 +87,7 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *
image.hasPalette = false;
}
uint32_t key = ReadNtrImage(inputPath, options->width, 0, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->scanFrontToBack);
uint32_t key = ReadNtrImage(inputPath, options->width, 0, options->colsPerChunk, options->rowsPerChunk, &image, !image.hasPalette, options->encodeMode, options->convertTo8Bpp, options->palIndex, options->verbose);
if (key)
{
@ -98,6 +103,11 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct NtrToPngOptions *
image.hasTransparency = options->hasTransparency;
if (options->cellFilePath != NULL)
{
ApplyCellsToImage(options->cellFilePath, &image, true, options->cellSnap);
}
WritePng(outputPath, &image);
FreeImage(&image);
@ -119,10 +129,9 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *options)
{
// handle empty files if possible
FILE *fp = fopen(inputPath, "rb");
if (options->handleEmpty)
{
FILE *fp = fopen(inputPath, "rb");
if (fp != NULL)
{
fseek(fp, 0, SEEK_END);
@ -131,14 +140,17 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
if (size == 0)
{
FILE *out = fopen(outputPath, "wb+");
fclose(out);
if (out != NULL)
{
fclose(out);
}
fclose(fp);
return;
}
fclose(fp);
}
}
fclose(fp);
struct Image image;
image.bitDepth = options->bitDepth == 0 ? 4 : options->bitDepth;
@ -146,25 +158,31 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
ReadPng(inputPath, &image);
uint32_t key = 0;
if (options->scanMode)
if (options->encodeMode)
{
char* string = malloc(strlen(inputPath) + 5);
sprintf(string, "%s.key", inputPath);
FILE *fp2 = fopen(string, "rb");
if (fp2 == NULL)
FILE *fp = fopen(string, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open key file for reading.\n");
size_t count = fread(&key, 4, 1, fp2);
size_t count = fread(&key, 4, 1, fp);
if (count != 1)
FATAL_ERROR("Not a valid key file.\n");
fclose(fp2);
fclose(fp);
free(string);
}
options->bitDepth = options->bitDepth == 0 ? image.bitDepth : options->bitDepth;
if (options->cellFilePath != NULL)
{
ApplyCellsToImage(options->cellFilePath, &image, false, options->cellSnap);
}
WriteNtrImage(outputPath, options->numTiles, options->bitDepth, options->colsPerChunk, options->rowsPerChunk,
&image, !image.hasPalette, options->clobberSize, options->byteOrder, options->version101,
options->sopc, options->vramTransfer, options->scanMode, options->mappingType, key, options->wrongSize);
options->sopc, options->vramTransfer, options->scan, options->encodeMode, options->mappingType,
key, options->wrongSize, options->convertTo4Bpp);
FreeImage(&image);
}
@ -255,13 +273,17 @@ void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **a
{
struct NtrToPngOptions options;
options.paletteFilePath = NULL;
options.cellFilePath = NULL;
options.cellSnap = true;
options.hasTransparency = false;
options.width = 0;
options.colsPerChunk = 1;
options.rowsPerChunk = 1;
options.palIndex = 1;
options.scanFrontToBack = false;
options.handleEmpty = false;
options.encodeMode = 0;
options.convertTo8Bpp = false;
options.verbose = false;
for (int i = 3; i < argc; i++)
{
@ -276,6 +298,24 @@ void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **a
options.paletteFilePath = argv[i];
}
else if (strcmp(option, "-cell") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No cell file path following \"-cell\".\n");
i++;
options.cellFilePath = argv[i];
if (i + 1 < argc)
{
if (strcmp(argv[i+1], "-nosnap") == 0)
{
options.cellSnap = false;
i++;
}
}
}
else if (strcmp(option, "-object") == 0)
{
options.hasTransparency = true;
@ -334,12 +374,35 @@ void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **a
}
else if (strcmp(option, "-scanfronttoback") == 0)
{
options.scanFrontToBack = true;
// maintained for compatibility
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 2;
}
else if (strcmp(option, "-encodebacktofront") == 0)
{
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 1;
}
else if (strcmp(option, "-encodefronttoback") == 0)
{
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 2;
}
else if (strcmp(option, "-handleempty") == 0)
{
options.handleEmpty = true;
}
else if (strcmp(option, "-convertTo8Bpp") == 0)
{
options.convertTo8Bpp = true;
}
else if (strcmp(option, "-verbose") == 0)
{
options.verbose = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -352,6 +415,13 @@ void HandleNtrToPngCommand(char *inputPath, char *outputPath, int argc, char **a
ConvertNtrToPng(inputPath, outputPath, &options);
}
void HandleNtrLzToPngCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
HandleLZDecompressCommand(inputPath, outputPath, argc, argv);
HandleNtrToPngCommand(outputPath, outputPath, argc, argv);
}
void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
char *outputFileExtension = GetFileExtension(outputPath);
@ -421,6 +491,8 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
struct PngToNtrOptions options;
options.cellFilePath = NULL;
options.cellSnap = true;
options.numTiles = 0;
options.bitDepth = 0;
options.colsPerChunk = 1;
@ -430,10 +502,12 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
options.byteOrder = true;
options.version101 = false;
options.sopc = false;
options.scanMode = 0;
options.scan = false;
options.handleEmpty = false;
options.vramTransfer = false;
options.mappingType = 0;
options.encodeMode = 0;
options.convertTo4Bpp = false;
for (int i = 3; i < argc; i++)
{
@ -452,6 +526,24 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
if (options.numTiles < 1)
FATAL_ERROR("Number of tiles must be positive.\n");
}
else if (strcmp(option, "-cell") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No cell file path following \"-cell\".\n");
i++;
options.cellFilePath = argv[i];
if (i + 1 < argc)
{
if (strcmp(argv[i+1], "-nosnap") == 0)
{
options.cellSnap = false;
i++;
}
}
}
else if (strcmp(option, "-mwidth") == 0 || strcmp(option, "-cpc") == 0)
{
if (i + 1 >= argc)
@ -506,17 +598,37 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
{
options.sopc = true;
}
else if (strcmp(option, "-scan") == 0)
{
options.scan = true;
}
else if (strcmp(option, "-scanned") == 0)
{
if (options.scanMode != 0)
FATAL_ERROR("Scan mode specified more than once.\n-scanned goes back to front as in DP, -scanfronttoback goes front to back as in PtHGSS\n");
options.scanMode = 1;
// maintained for compatibility
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 1;
options.scan = true;
}
else if (strcmp(option, "-scanfronttoback") == 0)
{
if (options.scanMode != 0)
FATAL_ERROR("Scan mode specified more than once.\n-scanned goes back to front as in DP, -scanfronttoback goes front to back as in PtHGSS\n");
options.scanMode = 2;
// maintained for compatibility
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 2;
options.scan = true;
}
else if (strcmp(option, "-encodebacktofront") == 0)
{
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 1;
}
else if (strcmp(option, "-encodefronttoback") == 0)
{
if (options.encodeMode != 0)
FATAL_ERROR("Encode mode specified more than once.\n-encodebacktofront goes back to front as in DP, -encodefronttoback goes front to back as in PtHGSS\n");
options.encodeMode = 2;
}
else if (strcmp(option, "-wrongsize") == 0) {
options.wrongSize = true;
@ -541,6 +653,10 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
if (options.mappingType != 0 && options.mappingType != 32 && options.mappingType != 64 && options.mappingType != 128 && options.mappingType != 256)
FATAL_ERROR("bitdepth must be one of the following: 0, 32, 64, 128, or 256\n");
}
else if (strcmp(option, "-convertTo4Bpp") == 0)
{
options.convertTo4Bpp = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -550,6 +666,15 @@ void HandlePngToNtrCommand(char *inputPath, char *outputPath, int argc, char **a
ConvertPngToNtr(inputPath, outputPath, &options);
}
void HandlePngToNtrLzCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int numLzArgs = CountLzCompressArgs(argc, argv);
HandlePngToNtrCommand(inputPath, outputPath, argc - numLzArgs, argv);
HandleLZCompressCommand(outputPath, outputPath, 3 + numLzArgs, &(argv[argc - 3 - numLzArgs]));
}
void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette;
@ -566,8 +691,10 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
bool nopad = false;
int bitdepth = 0;
int compNum = 0;
int pcmpStartIndex = 0;
bool pcmp = false;
bool inverted = false;
bool convertTo4Bpp = false;
for (int i = 3; i < argc; i++)
{
@ -614,11 +741,25 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
else if (strcmp(option, "-pcmp") == 0)
{
pcmp = true;
if (i + 2 < argc)
{
if (strcmp(argv[i + 1], "-start") == 0)
{
i += 2;
if (!ParseNumber(argv[i], NULL, 10, &pcmpStartIndex))
FATAL_ERROR("Failed to parse PCMP start index value.\n");
}
}
}
else if (strcmp(option, "-invertsize") == 0)
{
inverted = true;
}
else if (strcmp(option, "-convertTo4Bpp") == 0)
{
convertTo4Bpp = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -626,7 +767,7 @@ void HandlePngToNtrPaletteCommand(char *inputPath, char *outputPath, int argc, c
}
ReadPngPalette(inputPath, &palette);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, pcmpStartIndex, inverted, convertTo4Bpp);
}
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
@ -670,7 +811,7 @@ void HandleNtrToJascPaletteCommand(char *inputPath, char *outputPath, int argc,
}
}
ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted);
ReadNtrPalette(inputPath, &palette, bitdepth, 0, inverted, false);
WriteJascPalette(outputPath, &palette);
}
@ -719,6 +860,7 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
bool nopad = false;
int bitdepth = 0;
int compNum = 0;
int pcmpStartIndex = 0;
bool pcmp = false;
bool inverted = false;
@ -780,6 +922,16 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
else if (strcmp(option, "-pcmp") == 0)
{
pcmp = true;
if (i + 2 < argc)
{
if (strcmp(argv[i + 1], "-start") == 0)
{
i += 2;
if (!ParseNumber(argv[i], NULL, 10, &pcmpStartIndex))
FATAL_ERROR("Failed to parse PCMP start index value.\n");
}
}
}
else if (strcmp(option, "-invertsize") == 0)
{
@ -798,7 +950,7 @@ void HandleJascToNtrPaletteCommand(char *inputPath, char *outputPath, int argc,
if (numColors != 0)
palette.numColors = numColors;
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, inverted);
WriteNtrPalette(outputPath, &palette, ncpr, ir, bitdepth, !nopad, compNum, pcmp, pcmpStartIndex, inverted, false);
}
void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
@ -812,6 +964,13 @@ void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUS
FreeNCERCell(options);
}
void HandleJsonToNtrCellLzCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
HandleJsonToNtrCellCommand(inputPath, outputPath, argc, argv);
HandleLZCompressCommand(outputPath, outputPath, argc, argv);
}
void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct JsonToCellOptions *options = malloc(sizeof(struct JsonToCellOptions));
@ -825,6 +984,13 @@ void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUS
FreeNCERCell(options);
}
void HandleNtrCellLzToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
HandleLZDecompressCommand(inputPath, outputPath, argc, argv);
HandleNtrCellToJsonCommand(outputPath, outputPath, argc, argv);
}
void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct JsonToScreenOptions *options;
@ -876,6 +1042,13 @@ void HandleJsonToNtrAnimationCommand(char *inputPath, char *outputPath, int argc
FreeNANRAnimation(options);
}
void HandleJsonToNtrAnimationLzCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
HandleJsonToNtrAnimationCommand(inputPath, outputPath, argc, argv);
HandleLZCompressCommand(outputPath, outputPath, argc, argv);
}
void HandleNtrAnimationToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct JsonToAnimationOptions *options = malloc(sizeof(struct JsonToAnimationOptions));
@ -889,6 +1062,13 @@ void HandleNtrAnimationToJsonCommand(char *inputPath, char *outputPath, int argc
FreeNANRAnimation(options);
}
void HandleNtrAnimationLzToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
HandleLZDecompressCommand(inputPath, outputPath, argc, argv);
HandleNtrAnimationToJsonCommand(outputPath, outputPath, argc, argv);
}
void HandleJsonToNtrMulticellAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct JsonToAnimationOptions *options;
@ -968,11 +1148,51 @@ void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath,
FreeImage(&image);
}
void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv)
static int CountLzCompressArgs(int argc, char **argv)
{
int count = 0;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-overflow") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-overflow\".\n");
i++;
count += 2;
}
else if (strcmp(option, "-search") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-overflow\".\n");
i++;
count += 2;
}
else if (strcmp(option, "-reverse") == 0)
{
count++;
}
else if (strcmp(option, "-nopad") == 0)
{
count++;
}
}
return count;
}
static void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int overflowSize = 0;
int minDistance = 2; // default, for compatibility with LZ77UnCompVram()
bool forwardIteration = true;
bool nopad = false;
for (int i = 3; i < argc; i++)
{
@ -1008,6 +1228,10 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
{
forwardIteration = false;
}
else if (strcmp(option, "-nopad") == 0)
{
nopad = true;
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
@ -1024,7 +1248,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration);
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance, forwardIteration, !nopad);
compressedData[1] = (unsigned char)fileSize;
compressedData[2] = (unsigned char)(fileSize >> 8);
@ -1037,7 +1261,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
free(compressedData);
}
void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
static void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
@ -1218,11 +1442,13 @@ int main(int argc, char **argv)
{ "8bpp", "png", HandleGbaToPngCommand },
{ "nbfc", "png", HandleGbaToPngCommand },
{ "NCGR", "png", HandleNtrToPngCommand },
{ "NCGR.lz", "png", HandleNtrLzToPngCommand },
{ "png", "1bpp", HandlePngToGbaCommand },
{ "png", "4bpp", HandlePngToGbaCommand },
{ "png", "nbfc", HandlePngToGbaCommand },
{ "png", "8bpp", HandlePngToGbaCommand },
{ "png", "NCGR", HandlePngToNtrCommand },
{ "png", "NCGR.lz", HandlePngToNtrLzCommand },
{ "png", "gbapal", HandlePngToGbaPaletteCommand },
{ "png", "nbfp", HandlePngToGbaPaletteCommand },
{ "png", "NCLR", HandlePngToNtrPaletteCommand },
@ -1238,10 +1464,14 @@ int main(int argc, char **argv)
{ "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand },
{ "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand },
{ "json", "NCER", HandleJsonToNtrCellCommand },
{ "json", "NCER.lz", HandleJsonToNtrCellLzCommand },
{ "NCER", "json", HandleNtrCellToJsonCommand },
{ "NCER.lz", "json", HandleNtrCellLzToJsonCommand },
{ "json", "NSCR", HandleJsonToNtrScreenCommand },
{ "json", "NANR", HandleJsonToNtrAnimationCommand },
{ "json", "NANR.lz", HandleJsonToNtrAnimationLzCommand },
{ "NANR", "json", HandleNtrAnimationToJsonCommand },
{ "NANR.lz", "json", HandleNtrAnimationLzToJsonCommand },
{ "json", "NMAR", HandleJsonToNtrMulticellAnimationCommand },
{ "NMAR", "json", HandleNtrAnimationToJsonCommand },
{ NULL, "huff", HandleHuffCompressCommand },
@ -1268,8 +1498,10 @@ int main(int argc, char **argv)
for (int i = 0; handlers[i].function != NULL; i++)
{
if ((handlers[i].inputFileExtension == NULL || strcmp(handlers[i].inputFileExtension, inputFileExtension) == 0)
if (((handlers[i].inputFileExtension == NULL || strcmp(handlers[i].inputFileExtension, inputFileExtension) == 0)
&& (handlers[i].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0))
|| (handlers[i].inputFileExtension == NULL && strrchr(outputFileExtension, '.') && strstr(outputFileExtension, handlers[i].outputFileExtension))
|| (handlers[i].outputFileExtension == NULL && strrchr(inputFileExtension, '.') && strstr(inputFileExtension, handlers[i].inputFileExtension)))
{
handlers[i].function(inputPath, outputPath, argc, argv);
return 0;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2018 huderlem, 2021-2024 red031000
// Copyright (c) 2018 huderlem, 2021-2025 red031000
#ifndef OPTIONS_H
#define OPTIONS_H
@ -24,6 +24,8 @@ struct PngToGbaOptions {
};
struct PngToNtrOptions {
char *cellFilePath;
bool cellSnap;
int numTiles;
int bitDepth;
int colsPerChunk;
@ -32,23 +34,29 @@ struct PngToNtrOptions {
bool byteOrder;
bool version101;
bool sopc;
uint32_t scanMode;
bool scan;
bool wrongSize;
bool handleEmpty;
bool vramTransfer;
int mappingType;
uint32_t encodeMode;
bool convertTo4Bpp;
};
struct NtrToPngOptions {
char *paletteFilePath;
char *cellFilePath;
bool cellSnap;
int bitDepth;
bool hasTransparency;
int width;
int colsPerChunk;
int rowsPerChunk;
int palIndex;
bool scanFrontToBack;
bool handleEmpty;
uint32_t encodeMode;
bool convertTo8Bpp;
bool verbose;
};
struct CellVramTransferData {
@ -104,8 +112,10 @@ struct Cell {
struct JsonToCellOptions {
bool labelEnabled;
bool dontPadKbec;
bool extended;
bool vramTransferEnabled;
bool ucatEnabled;
int mappingType;
int cellCount;
struct Cell **cells;
@ -113,6 +123,7 @@ struct JsonToCellOptions {
struct CellVramTransferData **transferData;
char **labels;
int labelCount;
int *ucatCellAttribtes;
};
struct JsonToScreenOptions {
@ -163,6 +174,11 @@ struct AnimationResults {
};
};
struct UaatData {
int *sequenceAttributes;
int *frameAttributes;
};
struct JsonToAnimationOptions {
bool multiCell;
short sequenceCount;
@ -173,6 +189,8 @@ struct JsonToAnimationOptions {
char **labels;
int labelCount;
short resultCount;
bool uaatEnabled;
struct UaatData uaatData;
};
struct NtrFontOptions {

View File

@ -56,6 +56,19 @@ char *GetFileExtension(char *path)
if (*extension == 0)
return NULL;
if (strcmp(extension,"lz") == 0)
{
char *plainName = malloc(strlen(path) + 1);
strcpy(plainName, path);
plainName[strlen(path) - 3] = 0;
char *newExtension = GetFileExtension(plainName);
if (newExtension != NULL)
{
extension -= strlen(newExtension) + 1;
}
free(plainName);
}
return extension;
}