add NANR support to nitrogfx

This commit is contained in:
red031000
2023-07-03 07:14:59 +01:00
parent 7e1ced0c28
commit e9a56255ea
13 changed files with 496 additions and 7 deletions

View File

@@ -168,7 +168,7 @@ clean: mostlyclean clean-fs clean-tools
clean-fs:
$(RM) $(filter %.narc %.arc,$(HOSTFS_FILES))
$(RM) $(patsubst %.narc,%.naix,$(patsubst %.arc,%.naix,$(filter %.narc %.arc,$(HOSTFS_FILES))))
$(RM) $(NCGR_CLEAN_LIST) $(NCLR_CLEAN_LIST) $(NCER_CLEAN_LIST) $(FS_CLEAN_LIST)
$(RM) $(NCGR_CLEAN_LIST) $(NCLR_CLEAN_LIST) $(NCER_CLEAN_LIST) $(NANR_CLEAN_LIST) $(FS_CLEAN_LIST)
find . \( -iname '*.1bpp' -o -iname '*.4bpp' -o -iname '*.8bpp' -o -iname '*.gbapal' -o -iname '*.lz' \) -exec $(RM) {} +
$(RM) files/msgdata/msg/narc_*.bin
@@ -283,6 +283,9 @@ $(NCPR_NCLR_FILES): GFX_FLAGS = -ncpr
%.NCER: %.json
$(GFX) $< $@
%.NANR: %.json
$(GFX) $< $@
%.NSCR: %_map.json
$(GFX) $< $@ $(GFX_FLAGS)

View File

@@ -5,7 +5,7 @@
#include "gx.h"
#include "heap.h"
const u8 sTilemapWidthByBufferSize[] = {
static const u8 sTilemapWidthByBufferSize[] = {
[GF_BG_SCR_SIZE_128x128] = 0x10,
[GF_BG_SCR_SIZE_256x256] = 0x20,
[GF_BG_SCR_SIZE_256x512] = 0x20,

View File

@@ -1,4 +1,5 @@
narc_0000.NCLR
narc_0001.NANR
*.NCER
narc_0007.NCGR
narc_0008.NCGR

View File

@@ -0,0 +1,32 @@
{
"labelEnabled": true,
"sequenceCount": 2,
"frameCount": 2,
"sequences": [
{
"frameNumber": 1,
"loopStartFrame": 0,
"animationElement": 0,
"animationType": 1,
"playbackMode": 1
},
{
"frameNumber": 1,
"loopStartFrame": 0,
"animationElement": 0,
"animationType": 1,
"playbackMode": 1
}
],
"frameDelays": [1, 1],
"animationResults": [
{
"index": 0
},
{
"index": 1
}
],
"labels": ["CellAnime0", "CellAnime1"],
"labelCount": 2
}

View File

@@ -2671,6 +2671,7 @@ files/poketool/pokegra/pokegra.narc: \
files/poketool/icongra/poke_icon.narc: \
files/poketool/icongra/poke_icon/narc_0000.NCLR \
files/poketool/icongra/poke_icon/narc_0001.NANR \
files/poketool/icongra/poke_icon/narc_0002.NCER \
files/poketool/icongra/poke_icon/narc_0004.NCER \
files/poketool/icongra/poke_icon/narc_0006.NCER \

View File

@@ -1211,3 +1211,5 @@ NCLR_CLEAN_LIST := files/application/wifi_earth/wifi_earth/narc_0006.NCLR \
NCER_CLEAN_LIST := files/poketool/icongra/poke_icon/narc_0002.NCER \
files/poketool/icongra/poke_icon/narc_0004.NCER \
files/poketool/icongra/poke_icon/narc_0006.NCER
NANR_CLEAN_LIST := files/poketool/icongra/poke_icon/narc_0001.NANR

View File

@@ -909,12 +909,12 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
fclose(fp);
}
void WriteNtrScreen(char *outputPath, struct JsonToScreenOptions *options)
void WriteNtrScreen(char *path, struct JsonToScreenOptions *options)
{
FILE *fp = fopen(outputPath, "wb");
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", outputPath);
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
int totalSize = options->width * options->height * 2 + 0x14;
@@ -947,3 +947,211 @@ void WriteNtrScreen(char *outputPath, struct JsonToScreenOptions *options)
fclose(fp);
}
void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
unsigned int totalSize = 0x20 + options->sequenceCount * 0x10 + options->frameCount * 0x8;
//todo: check these
for (int i = 0; i < options->frameCount; i++)
{
if (options->sequenceData[i]->animationElement == 0)
totalSize += 0x4;
else if (options->sequenceData[i]->animationElement == 1)
totalSize += 0x10;
else if (options->sequenceData[i]->animationElement == 2)
totalSize += 0x8;
}
unsigned int KNBASize = totalSize;
if (options->labelEnabled)
{
totalSize += 0x14;
for (int j = 0; j < options->labelCount; j++)
{
totalSize += (unsigned)strlen(options->labels[j]) + 5; //strlen + terminator + pointer
}
}
WriteGenericNtrHeader(fp, "RNAN", totalSize, true, false, options->labelEnabled ? 3 : 1);
unsigned char KBNAHeader[0x20] =
{
0x4B, 0x4E, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
KBNAHeader[4] = KNBASize & 0xff;
KBNAHeader[5] = (KNBASize >> 8) & 0xff;
KBNAHeader[6] = (KNBASize >> 16) & 0xff;
KBNAHeader[7] = KNBASize >> 24;
KBNAHeader[8] = options->sequenceCount & 0xff;
KBNAHeader[9] = options->sequenceCount >> 8;
KBNAHeader[10] = options->frameCount & 0xff;
KBNAHeader[11] = options->frameCount >> 8;
unsigned int frameOffset = 0x18 + options->sequenceCount * 0x10;
KBNAHeader[16] = frameOffset & 0xff;
KBNAHeader[17] = (frameOffset >> 8) & 0xff;
KBNAHeader[18] = (frameOffset >> 16) & 0xff;
KBNAHeader[19] = frameOffset >> 24;
unsigned int resultsOffset = frameOffset + options->frameCount * 0x8;
KBNAHeader[20] = resultsOffset & 0xff;
KBNAHeader[21] = (resultsOffset >> 8) & 0xff;
KBNAHeader[22] = (resultsOffset >> 16) & 0xff;
KBNAHeader[23] = resultsOffset >> 24;
fwrite(KBNAHeader, 1, 0x20, fp);
int contentsSize = KNBASize - 0x20;
unsigned char *KBNAContents = malloc(contentsSize);
int i;
for (i = 0; i < options->sequenceCount * 0x10; i += 0x10)
{
KBNAContents[i] = options->sequenceData[i / 0x10]->frameNumber & 0xff;
KBNAContents[i + 1] = options->sequenceData[i / 0x10]->frameNumber >> 8;
KBNAContents[i + 2] = options->sequenceData[i / 0x10]->loopStartFrame & 0xff;
KBNAContents[i + 3] = options->sequenceData[i / 0x10]->loopStartFrame >> 8;
KBNAContents[i + 4] = options->sequenceData[i / 0x10]->animationElement & 0xff;
KBNAContents[i + 5] = options->sequenceData[i / 0x10]->animationElement >> 8;
KBNAContents[i + 6] = options->sequenceData[i / 0x10]->animationType & 0xff;
KBNAContents[i + 7] = options->sequenceData[i / 0x10]->animationType >> 8;
KBNAContents[i + 8] = options->sequenceData[i / 0x10]->playbackMode & 0xff;
KBNAContents[i + 9] = (options->sequenceData[i / 0x10]->playbackMode >> 8) & 0xff;
KBNAContents[i + 10] = (options->sequenceData[i / 0x10]->playbackMode >> 16) & 0xff;
KBNAContents[i + 11] = options->sequenceData[i / 0x10]->playbackMode >> 24;
KBNAContents[i + 12] = (i / 0x10 * 8) & 0xff;
KBNAContents[i + 13] = ((i / 0x10 * 8) >> 8) & 0xff;
KBNAContents[i + 14] = ((i / 0x10 * 8) >> 16) & 0xff;
KBNAContents[i + 15] = (i / 0x10 * 8) >> 24;
}
int j;
int resPtrCounter = 0;
for (j = i; j < options->frameCount * 8 + i; j += 8)
{
KBNAContents[j] = resPtrCounter & 0xff;
KBNAContents[j + 1] = (resPtrCounter >> 8) & 0xff;
KBNAContents[j + 2] = (resPtrCounter >> 16) & 0xff;
KBNAContents[j + 3] = resPtrCounter >> 24;
if (options->sequenceData[(j - i) / 8]->animationElement == 0)
resPtrCounter += 0x4;
else if (options->sequenceData[(j - i) / 8]->animationElement == 1)
resPtrCounter += 0x10;
else if (options->sequenceData[(j - i) / 8]->animationElement == 2)
resPtrCounter += 0x8;
KBNAContents[j + 4] = options->frameDelay[(j - i) / 8] & 0xff;
KBNAContents[j + 5] = options->frameDelay[(j - i) / 8] >> 8;
KBNAContents[j + 6] = 0xEF;
KBNAContents[j + 7] = 0xBE;
}
//todo: these are extrapolated, need confirming
resPtrCounter = j;
for (int k = 0; k < options->frameCount; k++)
{
switch (options->sequenceData[k]->animationElement)
{
case 0:
KBNAContents[resPtrCounter] = options->animationResults[k]->index & 0xff;
KBNAContents[resPtrCounter + 1] = options->animationResults[k]->index >> 8;
KBNAContents[resPtrCounter + 2] = 0xCC;
KBNAContents[resPtrCounter + 3] = 0xCC;
resPtrCounter += 0x4;
break;
case 1:
KBNAContents[resPtrCounter] = options->animationResults[k]->dataSrt.index & 0xff;
KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataSrt.index >> 8;
KBNAContents[resPtrCounter + 2] = options->animationResults[k]->dataSrt.rotation & 0xff;
KBNAContents[resPtrCounter + 3] = options->animationResults[k]->dataSrt.rotation >> 8;
KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataSrt.scaleX & 0xff;
KBNAContents[resPtrCounter + 5] = (options->animationResults[k]->dataSrt.scaleX >> 8) & 0xff;
KBNAContents[resPtrCounter + 6] = (options->animationResults[k]->dataSrt.scaleX >> 16) & 0xff;
KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataSrt.scaleX >> 24;
KBNAContents[resPtrCounter + 8] = options->animationResults[k]->dataSrt.scaleY & 0xff;
KBNAContents[resPtrCounter + 9] = (options->animationResults[k]->dataSrt.scaleY >> 8) & 0xff;
KBNAContents[resPtrCounter + 10] = (options->animationResults[k]->dataSrt.scaleY >> 16) & 0xff;
KBNAContents[resPtrCounter + 11] = options->animationResults[k]->dataSrt.scaleY >> 24;
KBNAContents[resPtrCounter + 12] = options->animationResults[k]->dataSrt.positionX & 0xff;
KBNAContents[resPtrCounter + 13] = options->animationResults[k]->dataSrt.positionX >> 8;
KBNAContents[resPtrCounter + 14] = options->animationResults[k]->dataSrt.positionY & 0xff;
KBNAContents[resPtrCounter + 15] = options->animationResults[k]->dataSrt.positionY >> 8;
resPtrCounter += 0x10;
break;
case 2:
KBNAContents[resPtrCounter] = options->animationResults[k]->dataT.index & 0xff;
KBNAContents[resPtrCounter + 1] = options->animationResults[k]->dataT.index >> 8;
KBNAContents[resPtrCounter + 2] = options->animationResults[k]->dataT.rotation & 0xff;
KBNAContents[resPtrCounter + 3] = options->animationResults[k]->dataT.rotation >> 8;
KBNAContents[resPtrCounter + 4] = options->animationResults[k]->dataT.positionX & 0xff;
KBNAContents[resPtrCounter + 5] = options->animationResults[k]->dataT.positionX >> 8;
KBNAContents[resPtrCounter + 6] = options->animationResults[k]->dataT.positionY & 0xff;
KBNAContents[resPtrCounter + 7] = options->animationResults[k]->dataT.positionY >> 8;
resPtrCounter += 0x8;
break;
}
}
fwrite(KBNAContents, 1, contentsSize, fp);
free(KBNAContents);
if (options->labelEnabled)
{
unsigned int lablSize = 8;
for (int j = 0; j < options->labelCount; j++)
{
lablSize += (unsigned)strlen(options->labels[j]) + 5;
}
unsigned char *labl = malloc(lablSize);
memset(labl, 0, lablSize);
strcpy((char *) labl, "LBAL");
labl[4] = lablSize & 0xff;
labl[5] = lablSize >> 8;
unsigned int position = 0;
i = 0;
for (int j = 0; j < options->labelCount; j++)
{
labl[i + 8] = position & 0xff;
labl[i + 9] = position >> 8;
position += (unsigned)strlen(options->labels[j]) + 1;
i += 4;
}
for (int j = 0; j < options->labelCount; j++)
{
strcpy((char *) (labl + (i + 8)), options->labels[j]);
i += (int)strlen(options->labels[j]) + 1;
}
fwrite(labl, 1, lablSize, fp);
free(labl);
unsigned char texu[0xc] = {0x54, 0x58, 0x45, 0x55, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
fwrite(texu, 1, 0xc, fp);
}
fclose(fp);
}

View File

@@ -41,6 +41,7 @@ void ReadNtrPalette(char *path, struct Palette *palette, int bitdepth, int palIn
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 *outputPath, struct JsonToScreenOptions *options);
void WriteNtrScreen(char *path, struct JsonToScreenOptions *options);
void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options);
#endif // GFX_H

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 red031000
// Copyright (c) 2023 red031000
#include "global.h"
#include "cJSON.h"
@@ -230,6 +230,165 @@ struct JsonToScreenOptions *ParseNSCRJson(char *path)
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 *frameNumber = cJSON_GetObjectItemCaseSensitive(sequence, "frameNumber");
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]->frameNumber = GetInt(frameNumber);
options->sequenceData[i]->loopStartFrame = GetInt(loopStartFrame);
options->sequenceData[i]->animationElement = GetInt(animationElement);
options->sequenceData[i]->animationType = GetInt(animationType);
options->sequenceData[i]->playbackMode = GetInt(playbackMode);
//TODO: see if frame data mapping is required
i++;
}
i = 0;
options->frameDelay = malloc(sizeof(short) * options->frameCount);
cJSON *frameDelay = NULL;
cJSON *frameDelays = cJSON_GetObjectItemCaseSensitive(json, "frameDelays");
cJSON_ArrayForEach(frameDelay, frameDelays)
{
if (i > options->frameCount - 1)
FATAL_ERROR("Frame count is incorrect.\n");
options->frameDelay[i] = GetInt(frameDelay);
//TODO: see if animation result data mapping is required
i++;
}
//todo implement extended attributes
options->animationResults = malloc(sizeof(struct AnimationResults *) * options->frameCount);
for (i = 0; i < options->frameCount; 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->frameCount - 1)
FATAL_ERROR("Frame count is incorrect.\n");
switch (options->sequenceData[i]->animationElement) {
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]->dataSrt.index = GetInt(index);
options->animationResults[i]->dataSrt.rotation = GetInt(rotation);
options->animationResults[i]->dataSrt.positionX = GetInt(positionX);
options->animationResults[i]->dataSrt.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++)
@@ -244,6 +403,7 @@ void FreeNCERCell(struct JsonToCellOptions *options)
}
free(options->labels);
}
free(options->cells);
free(options);
}
@@ -253,3 +413,27 @@ void FreeNSCRScreen(struct JsonToScreenOptions *options)
free(options);
}
void FreeNANRAnimation(struct JsonToAnimationOptions *options)
{
for (int i = 0; i < options->sequenceCount; i++)
{
free(options->sequenceData[i]);
}
for (int i = 0; i < options->frameCount; 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->frameDelay);
free(options);
}

View File

@@ -7,7 +7,9 @@
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

View File

@@ -732,6 +732,17 @@ void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UN
FreeNSCRScreen(options);
}
void HandleJsonToNtrAnimationCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct JsonToAnimationOptions *options;
options = ParseNANRJson(inputPath);
WriteNtrAnimation(outputPath, options);
FreeNANRAnimation(options);
}
void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Image image;
@@ -992,6 +1003,7 @@ int main(int argc, char **argv)
{ "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand },
{ "json", "NCER", HandleJsonToNtrCellCommand },
{ "json", "NSCR", HandleJsonToNtrScreenCommand },
{ "json", "NANR", HandleJsonToNtrAnimationCommand },
{ NULL, "huff", HandleHuffCompressCommand },
{ NULL, "lz", HandleLZCompressCommand },
{ "huff", NULL, HandleHuffDecompressCommand },

View File

@@ -91,4 +91,47 @@ struct JsonToScreenOptions {
int bitdepth;
};
struct SequenceData {
short frameNumber;
short loopStartFrame;
short animationElement;
short animationType;
int playbackMode;
};
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 {
union {
short index;
struct AnimationDataSRT dataSrt;
struct AnimationDataT dataT;
};
};
struct JsonToAnimationOptions {
short sequenceCount;
short frameCount;
struct SequenceData **sequenceData;
short *frameDelay;
struct AnimationResults **animationResults;
bool labelEnabled;
char **labels;
int labelCount;
};
#endif // OPTIONS_H