From c567e1268e3afc3a6be19ee1bafc0618dcdfa477 Mon Sep 17 00:00:00 2001 From: red031000 Date: Tue, 4 Jul 2023 00:40:46 +0100 Subject: [PATCH] NCER to json decoding --- gfx.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gfx.h | 1 + json.c | 74 +++++++++++++++++++++++++++++-- json.h | 1 + main.c | 14 ++++++ options.h | 2 - util.c | 13 ++++++ util.h | 1 + 8 files changed, 229 insertions(+), 6 deletions(-) diff --git a/gfx.c b/gfx.c index 85a178b..ec6bede 100644 --- a/gfx.c +++ b/gfx.c @@ -774,6 +774,135 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in fclose(fp); } +void ReadNtrCell(char *path, struct JsonToCellOptions *options) +{ + int fileSize; + unsigned char *data = ReadWholeFile(path, &fileSize); + + if (memcmp(data, "RECN", 4) != 0) //NCER + { + FATAL_ERROR("Not a valid NCER cell file.\n"); + } + + options->labelEnabled = data[0xE] != 1; + + if (memcmp(data + 0x10, "KBEC", 4) != 0 ) //KBEC + { + FATAL_ERROR("Not a valid KBEC cell file.\n"); + } + + options->cellCount = data[0x18] | (data[0x19] << 8); + options->extended = data[0x1A] == 1; + if (!options->extended) + { + FATAL_ERROR("Don't know how to deal with not extended yet, bug red031000.\n"); + } + + options->mappingType = data[0x20]; + + options->cells = malloc(sizeof(struct Cell *) * options->cellCount); + + for (int i = 0; i < options->cellCount; i++) + { + int offset = 0x30 + (i * 0x10); + options->cells[i] = malloc(sizeof(struct Cell)); + short cellAttrs = data[offset + 2] | (data[offset + 3] << 8); + options->cells[i]->attributes.hFlip = (cellAttrs >> 8) & 1; + options->cells[i]->attributes.vFlip = (cellAttrs >> 9) & 1; + options->cells[i]->attributes.hvFlip = (cellAttrs >> 10) & 1; + options->cells[i]->attributes.boundingRect = (cellAttrs >> 11) & 1; + options->cells[i]->attributes.boundingSphereRadius = cellAttrs & 0x3F; + + options->cells[i]->maxX = data[offset + 8] | (data[offset + 9] << 8); + options->cells[i]->maxY = data[offset + 10] | (data[offset + 11] << 8); + options->cells[i]->minX = data[offset + 12] | (data[offset + 13] << 8); + options->cells[i]->minY = data[offset + 14] | (data[offset + 15] << 8); + } + + for (int i = 0; i < options->cellCount; i++) + { + int offset = 0x30 + (options->cellCount * 0x10) + (i * 0x6); + + //Attr0 + + //bits 0-7 Y coordinate + options->cells[i]->oam.attr0.YCoordinate = data[offset]; + + //bit 8 rotation + options->cells[i]->oam.attr0.Rotation = data[offset + 1] & 1; + + //bit 9 Obj Size (if rotation) or Obj Disable (if not rotation) + options->cells[i]->oam.attr0.SizeDisable = (data[offset + 1] >> 1) & 1; + + //bits 10-11 Obj Mode + options->cells[i]->oam.attr0.Mode = (data[offset + 1] >> 2) & 3; + + //bit 12 Obj Mosaic + options->cells[i]->oam.attr0.Mosaic = (data[offset + 1] >> 4) & 1; + + //bit 13 Colours + options->cells[i]->oam.attr0.Colours = ((data[offset + 1] >> 5) & 1) == 0 ? 16 : 256; + + //bits 14-15 Obj Shape + options->cells[i]->oam.attr0.Shape = (data[offset + 1] >> 6) & 3; + + //Attr1 + + //bits 0-8 X coordinate + options->cells[i]->oam.attr1.XCoordinate = data[offset + 2] | ((data[offset + 3] & 1) << 8); + + //bits 9-13 Rotation and scaling (if rotation) bit 12 Horizontal flip, bit 13 Vertical flip (if not rotation) + options->cells[i]->oam.attr1.RotationScaling = (data[offset + 3] >> 1) & 0x1F; + + //bits 14-15 Obj Size + options->cells[i]->oam.attr1.Size = (data[offset + 3] >> 6) & 3; + + //Attr2 + + //bits 0-9 Character Name? + options->cells[i]->oam.attr2.CharName = data[offset + 4] | ((data[offset + 5] & 3) << 8); + + //bits 10-11 Priority + options->cells[i]->oam.attr2.Priority = (data[offset + 5] >> 2) & 3; + + //bits 12-15 Palette Number + options->cells[i]->oam.attr2.Palette = (data[offset + 5] >> 4) & 0xF; + } + + if (options->labelEnabled) + { + int count = 0; + int offset = 0x30 + (options->cellCount * 0x16) + 0x8; + bool flag = false; + //this entire thing is a huge assumption, it will not work with labels that are less than 2 characters long + while (!flag) + { + if (strlen((char *) data + offset) < 2) + { + //probably a pointer, maybe? + count++; + offset += 4; + } + else + { + //huzzah a string + flag = true; + } + } + options->labelCount = count; + options->labels = malloc(sizeof(char *) * count); + for (int i = 0; i < count; i++) + { + options->labels[i] = malloc(strlen((char *) data + offset) + 1); + strcpy(options->labels[i], (char *) data + offset); + offset += strlen(options->labels[i]) + 1; + } + //after this should be txeu, if everything was done right + } + + free(data); +} + void WriteNtrCell(char *path, struct JsonToCellOptions *options) { FILE *fp = fopen(path, "wb"); diff --git a/gfx.h b/gfx.h index cab792b..d894258 100644 --- a/gfx.h +++ b/gfx.h @@ -40,6 +40,7 @@ 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 ReadNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrCell(char *path, struct JsonToCellOptions *options); void WriteNtrScreen(char *path, struct JsonToScreenOptions *options); void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options); diff --git a/json.c b/json.c index 1f89d15..e8f4e27 100644 --- a/json.c +++ b/json.c @@ -48,15 +48,11 @@ struct JsonToCellOptions *ParseNCERJson(char *path) 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); @@ -177,6 +173,76 @@ struct JsonToCellOptions *ParseNCERJson(char *path) return options; } +char *GetNCERJson(struct JsonToCellOptions *options) +{ + cJSON *ncer = cJSON_CreateObject(); + + cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled); + cJSON_AddBoolToObject(ncer, "extended", options->extended); + cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount); + cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType); + + cJSON *cells = cJSON_AddArrayToObject(ncer, "cells"); + + for (int i = 0; i < options->cellCount; i++) + { + cJSON *cell = cJSON_CreateObject(); + + cJSON *cellAttrs = cJSON_AddObjectToObject(cell, "cellAttrs"); + + cJSON_AddBoolToObject(cellAttrs, "hFlip", options->cells[i]->attributes.hFlip); + cJSON_AddBoolToObject(cellAttrs, "vFlip", options->cells[i]->attributes.vFlip); + cJSON_AddBoolToObject(cellAttrs, "hvFlip", options->cells[i]->attributes.hvFlip); + cJSON_AddBoolToObject(cellAttrs, "boundingRect", options->cells[i]->attributes.boundingRect); + cJSON_AddNumberToObject(cellAttrs, "boundingSphereRadius", options->cells[i]->attributes.boundingSphereRadius); + + if (options->extended) + { + cJSON_AddNumberToObject(cell, "maxX", options->cells[i]->maxX); + cJSON_AddNumberToObject(cell, "maxY", options->cells[i]->maxY); + cJSON_AddNumberToObject(cell, "minX", options->cells[i]->minX); + cJSON_AddNumberToObject(cell, "minY", options->cells[i]->minY); + } + + cJSON *OAM = cJSON_AddObjectToObject(cell, "OAM"); + + cJSON *Attr0 = cJSON_AddObjectToObject(OAM, "Attr0"); + + cJSON_AddNumberToObject(Attr0, "YCoordinate", options->cells[i]->oam.attr0.YCoordinate); + cJSON_AddBoolToObject(Attr0, "Rotation", options->cells[i]->oam.attr0.Rotation); + cJSON_AddBoolToObject(Attr0, "SizeDisable", options->cells[i]->oam.attr0.SizeDisable); + cJSON_AddNumberToObject(Attr0, "Mode", options->cells[i]->oam.attr0.Mode); + cJSON_AddBoolToObject(Attr0, "Mosaic", options->cells[i]->oam.attr0.Mosaic); + cJSON_AddNumberToObject(Attr0, "Colours", options->cells[i]->oam.attr0.Colours); + cJSON_AddNumberToObject(Attr0, "Shape", options->cells[i]->oam.attr0.Shape); + + cJSON *Attr1 = cJSON_AddObjectToObject(OAM, "Attr1"); + + cJSON_AddNumberToObject(Attr1, "XCoordinate", options->cells[i]->oam.attr1.XCoordinate); + cJSON_AddNumberToObject(Attr1, "RotationScaling", options->cells[i]->oam.attr1.RotationScaling); + cJSON_AddNumberToObject(Attr1, "Size", options->cells[i]->oam.attr1.Size); + + cJSON *Attr2 = cJSON_AddObjectToObject(OAM, "Attr2"); + + cJSON_AddNumberToObject(Attr2, "CharName", options->cells[i]->oam.attr2.CharName); + cJSON_AddNumberToObject(Attr2, "Priority", options->cells[i]->oam.attr2.Priority); + cJSON_AddNumberToObject(Attr2, "Palette", options->cells[i]->oam.attr2.Palette); + + cJSON_AddItemToArray(cells, cell); + } + + if (options->labelEnabled) + { + cJSON *labels = cJSON_CreateStringArray((const char * const*)options->labels, options->labelCount); + cJSON_AddItemToObject(ncer, "labels", labels); + cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount); + } + + char *jsonString = cJSON_Print(ncer); + cJSON_Delete(ncer); + return jsonString; +} + struct JsonToScreenOptions *ParseNSCRJson(char *path) { int fileLength; diff --git a/json.h b/json.h index aebe4b5..e751801 100644 --- a/json.h +++ b/json.h @@ -6,6 +6,7 @@ #include "options.h" struct JsonToCellOptions *ParseNCERJson(char *path); +char *GetNCERJson(struct JsonToCellOptions *options); struct JsonToScreenOptions *ParseNSCRJson(char *path); struct JsonToAnimationOptions *ParseNANRJson(char *path); void FreeNCERCell(struct JsonToCellOptions *options); diff --git a/main.c b/main.c index dac7f81..bed85df 100644 --- a/main.c +++ b/main.c @@ -768,6 +768,19 @@ void HandleJsonToNtrCellCommand(char *inputPath, char *outputPath, int argc UNUS FreeNCERCell(options); } +void HandleNtrCellToJsonCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ + struct JsonToCellOptions *options = malloc(sizeof(struct JsonToCellOptions)); + + ReadNtrCell(inputPath, options); + + char *json = GetNCERJson(options); + + WriteWholeStringToFile(outputPath, json); + + FreeNCERCell(options); +} + void HandleJsonToNtrScreenCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) { struct JsonToScreenOptions *options; @@ -1094,6 +1107,7 @@ int main(int argc, char **argv) { "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand }, { "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand }, { "json", "NCER", HandleJsonToNtrCellCommand }, + { "NCER", "json", HandleNtrCellToJsonCommand }, { "json", "NSCR", HandleJsonToNtrScreenCommand }, { "json", "NANR", HandleJsonToNtrAnimationCommand }, { "json", "NMAR", HandleJsonToNtrMulticellAnimationCommand }, diff --git a/options.h b/options.h index 45015fc..1a78f6c 100644 --- a/options.h +++ b/options.h @@ -98,8 +98,6 @@ struct JsonToCellOptions { bool labelEnabled; bool extended; int mappingType; - int imageHeight; - int imageWidth; int cellCount; struct Cell **cells; char **labels; diff --git a/util.c b/util.c index 7dc4ca8..65df5e2 100644 --- a/util.c +++ b/util.c @@ -111,6 +111,19 @@ unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) return buffer; } +void WriteWholeStringToFile(char *path, char *string) +{ + FILE *fp = fopen(path, "wb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); + + if (fputs(string, fp) == EOF) + FATAL_ERROR("Failed to write to \"%s\".\n", path); + + fclose(fp); +} + void WriteWholeFile(char *path, void *buffer, int bufferSize) { FILE *fp = fopen(path, "wb"); diff --git a/util.h b/util.h index 080dd4b..a7bf3f1 100644 --- a/util.h +++ b/util.h @@ -10,6 +10,7 @@ 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 WriteWholeStringToFile(char *path, char *string); 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);