From d55fbd6ff4ce2dab93afe4a6894e07e63f335758 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Sun, 20 Oct 2024 12:51:53 -0500 Subject: [PATCH] update NANR parser to handle resultType better and handle padding --- gfx.c | 125 +++++++++++++++++++++++++++++++++++++++++------------- json.c | 6 +-- options.h | 3 +- 3 files changed, 101 insertions(+), 33 deletions(-) diff --git a/gfx.c b/gfx.c index 0bc7150..c4db346 100644 --- a/gfx.c +++ b/gfx.c @@ -1327,8 +1327,8 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { options->sequenceData[i]->frameCount = data[offset] | (data[offset + 1] << 8); options->sequenceData[i]->loopStartFrame = data[offset + 2] | (data[offset + 3] << 8); - options->sequenceData[i]->animationElement = data[offset + 4] | (data[offset + 5] << 8); - options->sequenceData[i]->animationType = data[offset + 6] | (data[offset + 7] << 8); + options->sequenceData[i]->animationType = data[offset + 4] | (data[offset + 5] << 8); + options->sequenceData[i]->animationType2 = data[offset + 6] | (data[offset + 7] << 8); options->sequenceData[i]->playbackMode = data[offset + 8] | (data[offset + 9] << 8) | (data[offset + 10] << 16) | (data[offset + 11] << 24); frameOffsets[i] = data[offset + 12] | (data[offset + 13] << 8) | (data[offset + 14] << 16) | (data[offset + 15] << 24); @@ -1358,6 +1358,7 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { if (resultOffsets[k] == options->sequenceData[i]->frameData[j]->resultOffset) { + options->sequenceData[i]->frameData[j]->resultId = k; present = true; break; } @@ -1370,6 +1371,7 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) { if (resultOffsets[k] == -1) { + options->sequenceData[i]->frameData[j]->resultId = k; resultOffsets[k] = options->sequenceData[i]->frameData[j]->resultOffset; break; } @@ -1400,37 +1402,49 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) options->animationResults[i] = malloc(sizeof(struct AnimationResults)); } + // store the animationType of the corresponding sequence as this result's resultType + for (int i = 0; i < options->sequenceCount; i++) + { + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { + options->animationResults[options->sequenceData[i]->frameData[j]->resultId]->resultType = options->sequenceData[i]->animationType; + } + } + int resultOffset = 0; + int lastSequence = 0; for (int i = 0; i < options->resultCount; i++) { - if (data[offset + 2] == 0xCC && data[offset + 3] == 0xCC) - { - options->animationResults[i]->resultType = 0; - } - else if (data[offset + 2] == 0xEF && data[offset + 3] == 0xBE) - { - options->animationResults[i]->resultType = 2; - } - else - { - options->animationResults[i]->resultType = 1; - } + // find the earliest sequence matching this animation result, + // and add padding if the sequence changes + the total offset is not 4-byte aligned. + bool found = false; for (int j = 0; j < options->sequenceCount; j++) { for (int k = 0; k < options->sequenceData[j]->frameCount; k++) { - if (options->sequenceData[j]->frameData[k]->resultOffset == resultOffset) + if (options->sequenceData[j]->frameData[k]->resultId == i) { - options->sequenceData[j]->frameData[k]->resultId = i; + if (lastSequence != j) + { + lastSequence = j; + if (resultOffset % 4 != 0) + { + resultOffset += 0x2; + offset += 0x2; + } + } + found = true; + break; } } + if (found) break; } switch (options->animationResults[i]->resultType) { case 0: //index options->animationResults[i]->index = data[offset] | (data[offset + 1] << 8); - resultOffset += 0x4; - offset += 0x4; + resultOffset += 0x2; + offset += 0x2; break; case 1: //SRT @@ -1454,6 +1468,9 @@ void ReadNtrAnimation(char *path, struct JsonToAnimationOptions *options) } } + // add any missed padding from the final frame before processing labels + if (offset % 4 != 0) offset += 2; + if (options->labelEnabled) { options->labelCount = options->sequenceCount; //*should* be the same @@ -1479,17 +1496,60 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) unsigned int totalSize = 0x20 + options->sequenceCount * 0x10 + options->frameCount * 0x8; - //todo: check these for (int i = 0; i < options->resultCount; i++) { if (options->animationResults[i]->resultType == 0) - totalSize += 0x4; + totalSize += 0x2; else if (options->animationResults[i]->resultType == 1) totalSize += 0x10; else if (options->animationResults[i]->resultType == 2) totalSize += 0x8; } + // foreach sequence, need to check whether padding is applied for its results + // then add 0x02 to totalSize if padding exists. + // padding exists if the animation results for that sequence are not 4-byte aligned. + // also flag the last result for the sequence with `padded` to save having to redo this same step later. + int *usedResults = malloc(sizeof(int) * options->frameCount); + memset(usedResults, -1, sizeof(int) * options->frameCount); + for (int i = 0; i < options->sequenceCount; i++) + { + int sequenceLen = 0; + int resultIndex = 0; + for (int j = 0; j < options->sequenceData[i]->frameCount; j++) + { + // check if the result has already been used + for (resultIndex = 0; resultIndex < options->resultCount; resultIndex++) + { + if (usedResults[resultIndex] == options->sequenceData[i]->frameData[j]->resultId) + { + break; + } + + // if not already used, add it to the list + if (usedResults[resultIndex] == -1) + { + usedResults[resultIndex] = options->sequenceData[i]->frameData[j]->resultId; + break; + } + } + + // if not already used, add it to the result size for the sequence + if (options->animationResults[resultIndex]->resultType == 0) + sequenceLen += 0x2; + else if (options->animationResults[resultIndex]->resultType == 1) + sequenceLen += 0x10; + else if (options->animationResults[resultIndex]->resultType == 2) + sequenceLen += 0x8; + } + if (sequenceLen % 4 != 0) + { + totalSize += 0x02; + // mark the last animationResult for the sequence as padded, this saves needing to check this again later + options->animationResults[resultIndex]->padded = true; + } + } + unsigned int KNBASize = totalSize; if (options->labelEnabled) @@ -1547,10 +1607,10 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) KBNAContents[i + 1] = options->sequenceData[i / 0x10]->frameCount >> 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 + 4] = options->sequenceData[i / 0x10]->animationType & 0xff; + KBNAContents[i + 5] = (options->sequenceData[i / 0x10]->animationType >> 8) & 0xff; + KBNAContents[i + 6] = options->sequenceData[i / 0x10]->animationType2 & 0xff; + KBNAContents[i + 7] = (options->sequenceData[i / 0x10]->animationType2 >> 8) & 0xff; 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; @@ -1570,11 +1630,13 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) int resPtr = 0; for (int l = 0; l < options->sequenceData[m]->frameData[k]->resultId; l++) { if (options->animationResults[l]->resultType == 0) - resPtr += 0x4; + resPtr += 0x2; else if (options->animationResults[l]->resultType == 1) resPtr += 0x10; else if (options->animationResults[l]->resultType == 2) resPtr += 0x8; + + if (options->animationResults[l]->padded) resPtr += 0x02; } KBNAContents[j + (k * 8)] = resPtr & 0xff; KBNAContents[j + (k * 8) + 1] = (resPtr >> 8) & 0xff; @@ -1588,7 +1650,6 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) j += options->sequenceData[m]->frameCount * 8; } - //todo: these are extrapolated, need confirming int resPtrCounter = j; for (int k = 0; k < options->resultCount; k++) { @@ -1597,9 +1658,7 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) 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; + resPtrCounter += 0x2; break; case 1: @@ -1634,6 +1693,14 @@ void WriteNtrAnimation(char *path, struct JsonToAnimationOptions *options) resPtrCounter += 0x8; break; } + + // use the `padded` flag which was stored earlier to inject padding + if (options->animationResults[k]->padded) + { + KBNAContents[resPtrCounter] = 0xCC; + KBNAContents[resPtrCounter + 1] = 0xCC; + resPtrCounter += 0x2; + } } fwrite(KBNAContents, 1, contentsSize, fp); diff --git a/json.c b/json.c index 12bc8a9..772bcc9 100644 --- a/json.c +++ b/json.c @@ -376,14 +376,14 @@ struct JsonToAnimationOptions *ParseNANRJson(char *path) 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 *animationType2 = cJSON_GetObjectItemCaseSensitive(sequence, "animationType2"); 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]->animationType2 = GetInt(animationType2); options->sequenceData[i]->playbackMode = GetInt(playbackMode); options->sequenceData[i]->frameData = malloc(sizeof(struct FrameData *) * options->sequenceData[i]->frameCount); @@ -521,8 +521,8 @@ char *GetNANRJson(struct JsonToAnimationOptions *options) cJSON *sequence = cJSON_CreateObject(); cJSON_AddNumberToObject(sequence, "frameCount", options->sequenceData[i]->frameCount); cJSON_AddNumberToObject(sequence, "loopStartFrame", options->sequenceData[i]->loopStartFrame); - cJSON_AddNumberToObject(sequence, "animationElement", options->sequenceData[i]->animationElement); cJSON_AddNumberToObject(sequence, "animationType", options->sequenceData[i]->animationType); + cJSON_AddNumberToObject(sequence, "animationType2", options->sequenceData[i]->animationType2); cJSON_AddNumberToObject(sequence, "playbackMode", options->sequenceData[i]->playbackMode); cJSON *frameData = cJSON_AddArrayToObject(sequence, "frameData"); diff --git a/options.h b/options.h index 4304f1e..d0870ea 100644 --- a/options.h +++ b/options.h @@ -123,8 +123,8 @@ struct FrameData { struct SequenceData { short frameCount; short loopStartFrame; - short animationElement; short animationType; + short animationType2; int playbackMode; struct FrameData **frameData; }; @@ -147,6 +147,7 @@ struct AnimationDataT { struct AnimationResults { short resultType; + bool padded; union { short index; struct AnimationDataSRT dataSrt;