From fa38e17f0d747fa15927bac31375802d58605209 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Thu, 17 Oct 2024 11:14:08 -0500 Subject: [PATCH 1/4] NCER KBEC section size depends on OAM count --- gfx.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/gfx.c b/gfx.c index f5108b1..b0877ba 100644 --- a/gfx.c +++ b/gfx.c @@ -1074,7 +1074,17 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) if (fp == NULL) FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + options->cellCount * (options->extended ? 0x16 : 0xe); + int iterNum = (options->extended ? 0x10 : 0x8); + + // KBEC base size: 0x08 per bank, or 0x10 per extended bank + unsigned int kbecSize = options->cellCount * (options->extended ? 0x10 : 0x08); + // add 0x06 for number of OAMs - can be more than 1 + for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum) + { + kbecSize += options->cells[idx / iterNum]->oamCount * 0x06; + } + + unsigned int totalSize = (options->labelEnabled > 0 ? 0x34 : 0x20) + kbecSize; if (options->labelEnabled) { @@ -1099,18 +1109,16 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) KBECHeader[10] = 1; //extended } - unsigned int size = options->cellCount * (options->extended ? 0x16 : 0xe); - - KBECHeader[4] = (size + 0x20) & 0xFF; //size - KBECHeader[5] = (size + 0x20) >> 8; //unlikely to be more than 16 bits, but there are 32 allocated, change if necessary + KBECHeader[4] = (kbecSize + 0x20) & 0xFF; //size + KBECHeader[5] = (kbecSize + 0x20) >> 8; //unlikely to be more than 16 bits, but there are 32 allocated, change if necessary KBECHeader[16] = (options->mappingType & 0xFF); //not possible to be more than 8 bits, though 32 are allocated fwrite(KBECHeader, 1, 0x20, fp); - unsigned char *KBECContents = malloc(size); + unsigned char *KBECContents = malloc(kbecSize); - memset(KBECContents, 0, size); + memset(KBECContents, 0, kbecSize); /*if (!options->extended) { @@ -1119,7 +1127,6 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) }*/ int i; - int iterNum = (options->extended ? 0x10 : 0x8); int totalOam = 0; for (i = 0; i < options->cellCount * iterNum; i += iterNum) { @@ -1203,7 +1210,7 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) } } - fwrite(KBECContents, 1, size, fp); + fwrite(KBECContents, 1, kbecSize, fp); free(KBECContents); From 8d191dbb9a69cac95ca98cab37ad56e13a370c0d Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Thu, 17 Oct 2024 13:25:00 -0500 Subject: [PATCH 2/4] support partition data in NCER JSON format --- gfx.c | 39 +++++++++++++++++++++++++++++++++++++++ json.c | 27 +++++++++++++++++++++++++++ options.h | 3 +++ 3 files changed, 69 insertions(+) diff --git a/gfx.c b/gfx.c index b0877ba..0ee426a 100644 --- a/gfx.c +++ b/gfx.c @@ -912,6 +912,9 @@ void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, u { options->cellCount = data[blockOffset + 0x8] | (data[blockOffset + 0x9] << 8); options->extended = data[blockOffset + 0xA] == 1; + + int partitionOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8); + options->partitionEnabled = partitionOffset > 0; /*if (!options->extended) { //in theory not extended should be implemented, however not 100% sure @@ -1002,6 +1005,20 @@ void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, u } } + if (options->partitionEnabled) + { + // FindNitroDataBlock returns a block size less 0x10 for header, but this requires the real block size + int realBlockSize = blockSize + 0x10; + offset = blockOffset + 0x08 + partitionOffset; + options->partitionData = malloc(realBlockSize - offset); + int index = 0; + while (offset < realBlockSize) + { + options->partitionData[index++] = (data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24); + offset += 4; + } + options->partitionCount = index; + } } void ReadNtrCell_LABL(unsigned char * restrict data, unsigned int blockOffset, unsigned int blockSize, struct JsonToCellOptions *options) @@ -1078,6 +1095,8 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) // KBEC base size: 0x08 per bank, or 0x10 per extended bank unsigned int kbecSize = options->cellCount * (options->extended ? 0x10 : 0x08); + // additional 0x04 for each partition. + kbecSize += options->partitionCount * 0x04; // add 0x06 for number of OAMs - can be more than 1 for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum) { @@ -1114,6 +1133,16 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) KBECHeader[16] = (options->mappingType & 0xFF); //not possible to be more than 8 bits, though 32 are allocated + // offset to partition data within KBEC section (offset from KBEC start + 0x08) + if (options->partitionEnabled) + { + unsigned int partitionStart = (kbecSize + 0x20) - (options->partitionCount * 0x04) - 0x08; + KBECHeader[20] = partitionStart & 0xFF; + KBECHeader[21] = (partitionStart >> 8) & 0xFF; + KBECHeader[22] = (partitionStart >> 16) & 0xFF; + KBECHeader[23] = (partitionStart >> 24) & 0xFF; + } + fwrite(KBECHeader, 1, 0x20, fp); unsigned char *KBECContents = malloc(kbecSize); @@ -1210,6 +1239,16 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) } } + // partition data + for (int idx = 0; idx < options->partitionCount; idx++) + { + KBECContents[offset] = options->partitionData[idx] & 0xFF; + KBECContents[offset + 1] = (options->partitionData[idx] >> 8) & 0xFF; + KBECContents[offset + 2] = (options->partitionData[idx] >> 16) & 0xFF; + KBECContents[offset + 3] = (options->partitionData[idx] >> 24) & 0xFF; + offset += 4; + } + fwrite(KBECContents, 1, kbecSize, fp); free(KBECContents); diff --git a/json.c b/json.c index 12bc8a9..e48334e 100644 --- a/json.c +++ b/json.c @@ -47,11 +47,13 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled"); + cJSON *partitionBool = cJSON_GetObjectItemCaseSensitive(json, "partitionEnabled"); cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended"); cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount"); cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType"); options->labelEnabled = GetBool(labelBool); + options->partitionEnabled = GetBool(partitionBool); options->extended = GetBool(extended); options->cellCount = GetInt(cellCount); options->mappingType = GetInt(mappingType); @@ -77,6 +79,23 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } } + if (options->partitionEnabled) + { + cJSON *partitionCount = cJSON_GetObjectItemCaseSensitive(json, "partitionCount"); + options->partitionCount = GetInt(partitionCount); + options->partitionData = malloc(sizeof(int) * options->partitionCount); + + cJSON *partitions = cJSON_GetObjectItemCaseSensitive(json, "partitions"); + cJSON *partition = NULL; + + int j = 0; + cJSON_ArrayForEach(partition, partitions) + { + int partitionValue = GetInt(partition); + options->partitionData[j++] = partitionValue; + } + } + for (int i = 0; i < options->cellCount; i++) { options->cells[i] = malloc(sizeof(struct Cell)); @@ -195,6 +214,7 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled); cJSON_AddBoolToObject(ncer, "extended", options->extended); + cJSON_AddBoolToObject(ncer, "partitionEnabled", options->partitionEnabled); cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount); cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType); @@ -263,6 +283,13 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount); } + if (options->partitionEnabled) + { + cJSON *partitions = cJSON_CreateIntArray(options->partitionData, options->partitionCount); + cJSON_AddItemToObject(ncer, "partitions", partitions); + cJSON_AddNumberToObject(ncer, "partitionCount", options->partitionCount); + } + char *jsonString = cJSON_Print(ncer); cJSON_Delete(ncer); return jsonString; diff --git a/options.h b/options.h index 4304f1e..ac0d80f 100644 --- a/options.h +++ b/options.h @@ -100,9 +100,12 @@ struct Cell { struct JsonToCellOptions { bool labelEnabled; bool extended; + bool partitionEnabled; int mappingType; int cellCount; struct Cell **cells; + int *partitionData; + int partitionCount; char **labels; int labelCount; }; From 819153913a476720b7773b357f8438aac4cf3cdd Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Sun, 20 Oct 2024 11:18:30 -0500 Subject: [PATCH 3/4] partitions -> vram transfer data, add struct/format to represent transfer data --- gfx.c | 85 +++++++++++++++++++++++++++++++++++++------------------ json.c | 46 +++++++++++++++++++----------- options.h | 11 +++++-- 3 files changed, 95 insertions(+), 47 deletions(-) diff --git a/gfx.c b/gfx.c index 0ee426a..68ab1bb 100644 --- a/gfx.c +++ b/gfx.c @@ -913,8 +913,8 @@ void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, u options->cellCount = data[blockOffset + 0x8] | (data[blockOffset + 0x9] << 8); options->extended = data[blockOffset + 0xA] == 1; - int partitionOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8); - options->partitionEnabled = partitionOffset > 0; + int vramTransferOffset = (data[blockOffset + 0x14] | data[blockOffset + 0x15] << 8); + options->vramTransferEnabled = vramTransferOffset > 0; /*if (!options->extended) { //in theory not extended should be implemented, however not 100% sure @@ -1005,19 +1005,23 @@ void ReadNtrCell_CEBK(unsigned char * restrict data, unsigned int blockOffset, u } } - if (options->partitionEnabled) + if (options->vramTransferEnabled) { - // FindNitroDataBlock returns a block size less 0x10 for header, but this requires the real block size - int realBlockSize = blockSize + 0x10; - offset = blockOffset + 0x08 + partitionOffset; - options->partitionData = malloc(realBlockSize - offset); - int index = 0; - while (offset < realBlockSize) + offset = blockOffset + 0x08 + vramTransferOffset; + + // first 2 dwords are max size and offset, offset *should* always be 0x08 since the transfer data list immediately follows this + options->vramTransferMaxSize = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + offset += 0x08; + + // read 1 VRAM transfer data block for each cell (this is an assumption based on the NCERs I looked at) + options->transferData = malloc(sizeof(struct CellVramTransferData *) * options->cellCount); + for (int idx = 0; idx < options->cellCount; idx++) { - options->partitionData[index++] = (data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24); - offset += 4; + options->transferData[idx] = malloc(sizeof(struct CellVramTransferData)); + options->transferData[idx]->sourceDataOffset = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + options->transferData[idx]->size = data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24); + offset += 8; } - options->partitionCount = index; } } @@ -1095,8 +1099,11 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) // KBEC base size: 0x08 per bank, or 0x10 per extended bank unsigned int kbecSize = options->cellCount * (options->extended ? 0x10 : 0x08); - // additional 0x04 for each partition. - kbecSize += options->partitionCount * 0x04; + // if VRAM transfer is enabled, add 0x08 for the header and 0x08 for each cell + if (options->vramTransferEnabled) + { + kbecSize += 0x08 + (0x08 * options->cellCount); + } // add 0x06 for number of OAMs - can be more than 1 for (int idx = 0; idx < options->cellCount * iterNum; idx += iterNum) { @@ -1133,14 +1140,15 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) KBECHeader[16] = (options->mappingType & 0xFF); //not possible to be more than 8 bits, though 32 are allocated - // offset to partition data within KBEC section (offset from KBEC start + 0x08) - if (options->partitionEnabled) + // offset to VRAM transfer data within KBEC section (offset from KBEC start + 0x08) + if (options->vramTransferEnabled) { - unsigned int partitionStart = (kbecSize + 0x20) - (options->partitionCount * 0x04) - 0x08; - KBECHeader[20] = partitionStart & 0xFF; - KBECHeader[21] = (partitionStart >> 8) & 0xFF; - KBECHeader[22] = (partitionStart >> 16) & 0xFF; - KBECHeader[23] = (partitionStart >> 24) & 0xFF; + unsigned int vramTransferLength = 0x08 + (0x08 * options->cellCount); + unsigned int vramTransferOffset = (kbecSize + 0x20) - vramTransferLength - 0x08; + KBECHeader[20] = vramTransferOffset & 0xFF; + KBECHeader[21] = (vramTransferOffset >> 8) & 0xFF; + KBECHeader[22] = (vramTransferOffset >> 16) & 0xFF; + KBECHeader[23] = (vramTransferOffset >> 24) & 0xFF; } fwrite(KBECHeader, 1, 0x20, fp); @@ -1239,14 +1247,35 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options) } } - // partition data - for (int idx = 0; idx < options->partitionCount; idx++) + // VRAM transfer data + if (options->vramTransferEnabled) { - KBECContents[offset] = options->partitionData[idx] & 0xFF; - KBECContents[offset + 1] = (options->partitionData[idx] >> 8) & 0xFF; - KBECContents[offset + 2] = (options->partitionData[idx] >> 16) & 0xFF; - KBECContents[offset + 3] = (options->partitionData[idx] >> 24) & 0xFF; - offset += 4; + // max transfer size + fixed offset 0x08 + KBECContents[offset] = options->vramTransferMaxSize & 0xFF; + KBECContents[offset + 1] = (options->vramTransferMaxSize >> 8) & 0xFF; + KBECContents[offset + 2] = (options->vramTransferMaxSize >> 16) & 0xFF; + KBECContents[offset + 3] = (options->vramTransferMaxSize >> 24) & 0xFF; + + KBECContents[offset + 4] = 0x08; + + offset += 8; + + // write a VRAM transfer block for each cell + for (int idx = 0; idx < options->cellCount; idx++) + { + // offset + KBECContents[offset] = options->transferData[idx]->sourceDataOffset & 0xFF; + KBECContents[offset + 1] = (options->transferData[idx]->sourceDataOffset >> 8) & 0xFF; + KBECContents[offset + 2] = (options->transferData[idx]->sourceDataOffset >> 16) & 0xFF; + KBECContents[offset + 3] = (options->transferData[idx]->sourceDataOffset >> 24) & 0xFF; + + // size + KBECContents[offset + 4] = options->transferData[idx]->size & 0xFF; + KBECContents[offset + 5] = (options->transferData[idx]->size >> 8) & 0xFF; + KBECContents[offset + 6] = (options->transferData[idx]->size >> 16) & 0xFF; + KBECContents[offset + 7] = (options->transferData[idx]->size >> 24) & 0xFF; + offset += 8; + } } fwrite(KBECContents, 1, kbecSize, fp); diff --git a/json.c b/json.c index e48334e..9b6bfc4 100644 --- a/json.c +++ b/json.c @@ -47,13 +47,13 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } cJSON *labelBool = cJSON_GetObjectItemCaseSensitive(json, "labelEnabled"); - cJSON *partitionBool = cJSON_GetObjectItemCaseSensitive(json, "partitionEnabled"); + cJSON *vramTransferBool = cJSON_GetObjectItemCaseSensitive(json, "vramTransferEnabled"); cJSON *extended = cJSON_GetObjectItemCaseSensitive(json, "extended"); cJSON *cellCount = cJSON_GetObjectItemCaseSensitive(json, "cellCount"); cJSON *mappingType = cJSON_GetObjectItemCaseSensitive(json, "mappingType"); options->labelEnabled = GetBool(labelBool); - options->partitionEnabled = GetBool(partitionBool); + options->vramTransferEnabled = GetBool(vramTransferBool); options->extended = GetBool(extended); options->cellCount = GetInt(cellCount); options->mappingType = GetInt(mappingType); @@ -79,20 +79,27 @@ struct JsonToCellOptions *ParseNCERJson(char *path) } } - if (options->partitionEnabled) + if (options->vramTransferEnabled) { - cJSON *partitionCount = cJSON_GetObjectItemCaseSensitive(json, "partitionCount"); - options->partitionCount = GetInt(partitionCount); - options->partitionData = malloc(sizeof(int) * options->partitionCount); + cJSON *vramTransferMaxSize = cJSON_GetObjectItemCaseSensitive(json, "vramTransferMaxSize"); + options->vramTransferMaxSize = GetInt(vramTransferMaxSize); + + options->transferData = malloc(sizeof(struct CellVramTransferData *) * options->cellCount); - cJSON *partitions = cJSON_GetObjectItemCaseSensitive(json, "partitions"); - cJSON *partition = NULL; + cJSON *transfers = cJSON_GetObjectItemCaseSensitive(json, "transferData"); + cJSON *transfer = NULL; int j = 0; - cJSON_ArrayForEach(partition, partitions) + cJSON_ArrayForEach(transfer, transfers) { - int partitionValue = GetInt(partition); - options->partitionData[j++] = partitionValue; + cJSON *vramTransferOffset = cJSON_GetObjectItemCaseSensitive(transfer, "offset"); + cJSON *vramTransferSize = cJSON_GetObjectItemCaseSensitive(transfer, "size"); + + options->transferData[j] = malloc(sizeof(struct CellVramTransferData)); + options->transferData[j]->sourceDataOffset = GetInt(vramTransferOffset); + options->transferData[j]->size = GetInt(vramTransferSize); + + j++; } } @@ -214,7 +221,7 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddBoolToObject(ncer, "labelEnabled", options->labelEnabled); cJSON_AddBoolToObject(ncer, "extended", options->extended); - cJSON_AddBoolToObject(ncer, "partitionEnabled", options->partitionEnabled); + cJSON_AddBoolToObject(ncer, "vramTransferEnabled", options->vramTransferEnabled); cJSON_AddNumberToObject(ncer, "cellCount", options->cellCount); cJSON_AddNumberToObject(ncer, "mappingType", options->mappingType); @@ -283,11 +290,18 @@ char *GetNCERJson(struct JsonToCellOptions *options) cJSON_AddNumberToObject(ncer, "labelCount", options->labelCount); } - if (options->partitionEnabled) + if (options->vramTransferEnabled) { - cJSON *partitions = cJSON_CreateIntArray(options->partitionData, options->partitionCount); - cJSON_AddItemToObject(ncer, "partitions", partitions); - cJSON_AddNumberToObject(ncer, "partitionCount", options->partitionCount); + cJSON_AddNumberToObject(ncer, "vramTransferMaxSize", options->vramTransferMaxSize); + cJSON *transfers = cJSON_AddArrayToObject(ncer, "transferData"); + + for (int idx = 0; idx < options->cellCount; idx++) + { + cJSON *transfer = cJSON_CreateObject(); + cJSON_AddNumberToObject(transfer, "offset", options->transferData[idx]->sourceDataOffset); + cJSON_AddNumberToObject(transfer, "size", options->transferData[idx]->size); + cJSON_AddItemToArray(transfers, transfer); + } } char *jsonString = cJSON_Print(ncer); diff --git a/options.h b/options.h index ac0d80f..3a84184 100644 --- a/options.h +++ b/options.h @@ -51,6 +51,11 @@ struct NtrToPngOptions { bool handleEmpty; }; +struct CellVramTransferData { + int sourceDataOffset; + int size; +}; + struct Attr0 { int YCoordinate; bool Rotation; @@ -100,12 +105,12 @@ struct Cell { struct JsonToCellOptions { bool labelEnabled; bool extended; - bool partitionEnabled; + bool vramTransferEnabled; int mappingType; int cellCount; struct Cell **cells; - int *partitionData; - int partitionCount; + int vramTransferMaxSize; + struct CellVramTransferData **transferData; char **labels; int labelCount; }; From ada2b83bc11e9ea30e4ab31b92a3a865d5c495d0 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Sun, 27 Oct 2024 18:50:43 -0500 Subject: [PATCH 4/4] ensure to free new VRAM transfer data in NCER parser --- json.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/json.c b/json.c index 9b6bfc4..0280ef9 100644 --- a/json.c +++ b/json.c @@ -641,6 +641,14 @@ void FreeNCERCell(struct JsonToCellOptions *options) } free(options->labels); } + if (options->vramTransferEnabled) + { + for (int j = 0; j < options->cellCount; j++) + { + free(options->transferData[j]); + } + free(options->transferData); + } free(options->cells); free(options); }