mirror of
https://github.com/Garhoogin/NitroPaint.git
synced 2025-06-18 14:25:31 -04:00
Support viewing some AOB files
This commit is contained in:
parent
5a120b2f44
commit
43554ae349
@ -7,6 +7,7 @@
|
||||
#include "ncgr.h"
|
||||
#include "ncer.h"
|
||||
#include "nscr.h"
|
||||
#include "nanr.h"
|
||||
|
||||
extern const wchar_t *gComboFormats[] = {
|
||||
L"Invalid",
|
||||
@ -16,6 +17,7 @@ extern const wchar_t *gComboFormats[] = {
|
||||
L"5BG",
|
||||
L"MBB",
|
||||
L"BNCD",
|
||||
L"AOB",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -115,6 +117,10 @@ int combo2dGetObjMinCount(int comboType, int objType) {
|
||||
if (objType == FILE_TYPE_CHARACTER) return 1;
|
||||
if (objType == FILE_TYPE_CELL) return 1;
|
||||
return 0;
|
||||
case COMBO2D_TYPE_AOB:
|
||||
if (objType == FILE_TYPE_CELL) return 1;
|
||||
if (objType == FILE_TYPE_NANR) return 1;
|
||||
return 0;
|
||||
case COMBO2D_TYPE_DATAFILE:
|
||||
//no particular requirements
|
||||
return 0;
|
||||
@ -151,6 +157,10 @@ int combo2dGetObjMaxCount(int comboType, int objType) {
|
||||
if (objType == FILE_TYPE_CHARACTER) return 1;
|
||||
if (objType == FILE_TYPE_CELL) return 1;
|
||||
return 0;
|
||||
case COMBO2D_TYPE_AOB:
|
||||
if (objType == FILE_TYPE_CELL) return 1;
|
||||
if (objType == FILE_TYPE_NANR) return 1;
|
||||
return 0;
|
||||
case COMBO2D_TYPE_DATAFILE:
|
||||
//no particular requirements
|
||||
return INT_MAX;
|
||||
@ -360,8 +370,21 @@ int combo2dIsValidBncd(const unsigned char *file, unsigned int size) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int combo2dIsValidAob(const unsigned char *file, unsigned int size) {
|
||||
if (!IscadIsValidFooter(file, size)) return 0;
|
||||
|
||||
unsigned int agbSize, animSize, grpSize;
|
||||
const unsigned char *agb = IscadFindBlockBySignature(file, size, "AGB ", &agbSize);
|
||||
const unsigned char *anim = IscadFindBlockBySignature(file, size, "ANIM", &animSize);
|
||||
const unsigned char *grp = IscadFindBlockBySignature(file, size, "GRP ", &grpSize);
|
||||
if (agb == NULL || anim == NULL || grp == NULL) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int combo2dIsValid(const unsigned char *file, unsigned int size) {
|
||||
if (combo2dIsValid5bg(file, size)) return COMBO2D_TYPE_5BG;
|
||||
if (combo2dIsValidAob(file, size)) return COMBO2D_TYPE_AOB;
|
||||
if (combo2dIsValidBncd(file, size)) return COMBO2D_TYPE_BNCD;
|
||||
if (combo2dIsValidTimeAce(file, size)) return COMBO2D_TYPE_TIMEACE;
|
||||
if (combo2dIsValidBanner(file, size)) return COMBO2D_TYPE_BANNER;
|
||||
@ -612,6 +635,144 @@ int combo2dReadMbb(COMBO2D *combo, const unsigned char *buffer, unsigned int siz
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int combo2dReadAob(COMBO2D *combo, const unsigned char *buffer, unsigned int size) {
|
||||
unsigned int agbSize, animSize, achrSize, objSize, grpSize, cmntSize, anmcSize, p2dmSize, ccamSize, linkSize;
|
||||
unsigned char *agb = IscadFindBlockBySignature(buffer, size, "AGB ", &agbSize); // read
|
||||
unsigned char *anim = IscadFindBlockBySignature(buffer, size, "ANIM", &animSize);
|
||||
unsigned char *achr = IscadFindBlockBySignature(buffer, size, "ACHR", &achrSize);
|
||||
unsigned char *obj = IscadFindBlockBySignature(buffer, size, "OBJ ", &objSize);
|
||||
unsigned char *grp = IscadFindBlockBySignature(buffer, size, "GRP ", &grpSize);
|
||||
unsigned char *cmnt = IscadFindBlockBySignature(buffer, size, "CMNT", &cmntSize); // read
|
||||
unsigned char *anmc = IscadFindBlockBySignature(buffer, size, "ANMC", &anmcSize); // read
|
||||
unsigned char *p2dm = IscadFindBlockBySignature(buffer, size, "2DM ", &p2dmSize); // read
|
||||
unsigned char *ccam = IscadFindBlockBySignature(buffer, size, "CCAM", &ccamSize);
|
||||
unsigned char *link = IscadFindBlockBySignature(buffer, size, "LINK", &linkSize); // read
|
||||
|
||||
/*
|
||||
TODO: handling character compressed animation mode
|
||||
byte 0 of CCAM will be set nonzero, and an ACHR block will be present with 1D compressed character data
|
||||
OBJ block may be present
|
||||
*/
|
||||
|
||||
int mappingMode = GX_OBJVRAMMODE_CHAR_1D_32K;
|
||||
if (p2dm != NULL && p2dmSize > 0 && *p2dm) mappingMode = GX_OBJVRAMMODE_CHAR_2D;
|
||||
|
||||
unsigned int nObjEntry = *(const unsigned char *) (agb + 0x0);
|
||||
|
||||
NCER *ncer = (NCER *) calloc(1, sizeof(NCER));
|
||||
CellInit(ncer, NCER_TYPE_COMBO);
|
||||
ncer->mappingMode = mappingMode;
|
||||
ncer->nCells = nObjEntry;
|
||||
ncer->cells = (NCER_CELL *) calloc(ncer->nCells, sizeof(NCER_CELL));
|
||||
if (cmnt != NULL) {
|
||||
unsigned int commentLength = cmnt[1];
|
||||
ncer->header.comment = calloc(commentLength + 1, 1);
|
||||
memcpy(ncer->header.comment, cmnt + 2, commentLength);
|
||||
}
|
||||
|
||||
//read cell OBJ data
|
||||
const unsigned char *pCell = agb + 1;
|
||||
ANIM_DATA_SRT *trans = (ANIM_DATA_SRT *) calloc(nObjEntry, sizeof(ANIM_DATA_SRT));
|
||||
for (unsigned int i = 0; i < nObjEntry; i++) {
|
||||
trans[i].sx = trans[i].sy = 4096;
|
||||
}
|
||||
|
||||
for (unsigned int curCellCount = 0; curCellCount < nObjEntry;) {
|
||||
uint16_t nObj = *(const uint16_t *) (pCell + 0);
|
||||
pCell += 2;
|
||||
|
||||
if (nObj & 0x8000) {
|
||||
//msb=1: SRT data
|
||||
nObj &= ~0x8000;
|
||||
|
||||
for (unsigned int j = 0; j < nObj; j++) {
|
||||
int deg = *(const int16_t *) (pCell + 0);
|
||||
int grad = (deg * 65536 + (deg >= 0 ? 180 : -180)) / 360;
|
||||
|
||||
//TODO: support multiple transforms per cell?
|
||||
trans[curCellCount].rotZ = grad;
|
||||
trans[curCellCount].sx = (*(const int16_t *) (pCell + 2)) * 16; // 8-bit fraction -> 12-bit fraction
|
||||
trans[curCellCount].sy = (*(const int16_t *) (pCell + 4)) * 16; // 8-bit fraction -> 12-bit fraction
|
||||
pCell += 0x6;
|
||||
}
|
||||
} else {
|
||||
//msb=0: OBJ data
|
||||
int cellI = curCellCount;
|
||||
|
||||
ncer->cells[cellI].nAttribs = nObj;
|
||||
ncer->cells[cellI].attr = (uint16_t *) calloc(nObj, 6);
|
||||
|
||||
for (unsigned int j = 0; j < nObj; j++) {
|
||||
memcpy(ncer->cells[cellI].attr + j * 3, pCell, 6);
|
||||
|
||||
pCell += 0x6;
|
||||
}
|
||||
curCellCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NANR *nanr = (NANR *) calloc(1, sizeof(NANR));
|
||||
AnmInit(nanr, NANR_TYPE_COMBO);
|
||||
|
||||
//read animation sequences
|
||||
unsigned int nSeq = *(anim++);
|
||||
|
||||
nanr->nSequences = nSeq;
|
||||
nanr->sequences = (NANR_SEQUENCE *) calloc(nanr->nSequences, sizeof(NANR_SEQUENCE));
|
||||
|
||||
for (unsigned int i = 0; i < nSeq; i++) {
|
||||
//sequence properties
|
||||
NANR_SEQUENCE *seq = &nanr->sequences[i];
|
||||
int offsX = *(const int16_t *) (anim + 0x0);
|
||||
int offsY = *(const int16_t *) (anim + 0x2);
|
||||
unsigned int nFrame = *(const uint8_t *) (anim + 0x4);
|
||||
anim += 0x5;
|
||||
|
||||
seq->startFrameIndex = 0;
|
||||
seq->type = (NANR_SEQ_TYPE_CELL << 16) | NANR_SEQ_TYPE_INDEX_SRT;
|
||||
seq->mode = NANR_SEQ_MODE_FORWARD_LOOP;
|
||||
seq->nFrames = nFrame;
|
||||
seq->frames = (FRAME_DATA *) calloc(seq->nFrames, sizeof(FRAME_DATA));
|
||||
|
||||
for (unsigned int j = 0; j < nFrame; j++) {
|
||||
unsigned int cellno = *(const uint16_t *) (anim + 0x0);
|
||||
unsigned int dur = *(const uint16_t *) (anim + 0x2);
|
||||
|
||||
seq->frames[j].nFrames = dur;
|
||||
seq->frames[j].animationData = calloc(1, sizeof(ANIM_DATA_SRT));
|
||||
|
||||
ANIM_DATA_SRT *pSrt = (ANIM_DATA_SRT *) seq->frames[j].animationData;
|
||||
pSrt->index = cellno;
|
||||
pSrt->px = offsX;
|
||||
pSrt->py = offsY;
|
||||
pSrt->sx = trans[cellno].sx;
|
||||
pSrt->sy = trans[cellno].sy;
|
||||
pSrt->rotZ = trans[cellno].rotZ;
|
||||
|
||||
anim += 4;
|
||||
}
|
||||
}
|
||||
free(trans);
|
||||
|
||||
if (anmc != NULL) {
|
||||
unsigned int commentLength = anmc[1];
|
||||
nanr->header.comment = calloc(commentLength + 1, 1);
|
||||
memcpy(nanr->header.comment, anmc + 2, commentLength);
|
||||
}
|
||||
|
||||
combo2dLink(combo, &ncer->header);
|
||||
combo2dLink(combo, &nanr->header);
|
||||
|
||||
if (link != NULL) {
|
||||
unsigned int linkLength = link[1];
|
||||
combo->header.fileLink = calloc(linkLength + 1, 1);
|
||||
memcpy(combo->header.fileLink, link + 2, linkLength);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int combo2dRead(COMBO2D *combo, const unsigned char *buffer, unsigned int size) {
|
||||
int format = combo2dIsValid(buffer, size);
|
||||
if (format == COMBO2D_TYPE_INVALID) return 1;
|
||||
@ -628,6 +789,8 @@ int combo2dRead(COMBO2D *combo, const unsigned char *buffer, unsigned int size)
|
||||
return combo2dReadMbb(combo, buffer, size);
|
||||
case COMBO2D_TYPE_BNCD:
|
||||
return combo2dReadBncd(combo, buffer, size);
|
||||
case COMBO2D_TYPE_AOB:
|
||||
return combo2dReadAob(combo, buffer, size);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
#define COMBO2D_TYPE_5BG 4
|
||||
#define COMBO2D_TYPE_MBB 5
|
||||
#define COMBO2D_TYPE_BNCD 6
|
||||
#define COMBO2D_TYPE_MAX 7 //max +1
|
||||
#define COMBO2D_TYPE_AOB 7
|
||||
#define COMBO2D_TYPE_MAX 8 // max +1
|
||||
|
||||
extern const wchar_t *gComboFormats[];
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define NANR_TYPE_INVALID 0
|
||||
#define NANR_TYPE_NANR 1
|
||||
#define NANR_TYPE_GHOSTTRICK 2
|
||||
#define NANR_TYPE_COMBO 3
|
||||
|
||||
|
||||
#define NANR_SEQ_TYPE_INDEX 0
|
||||
|
@ -993,6 +993,12 @@ VOID OpenFileByName(HWND hWnd, LPCWSTR path) {
|
||||
h = CreateNcerViewerImmediate(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, data->hWndMdi, (NCER *) object);
|
||||
copy = EditorGetObject(h);
|
||||
break;
|
||||
|
||||
case FILE_TYPE_NANR:
|
||||
object->combo = (void *) combo;
|
||||
h = CreateNanrViewerImmediate(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, data->hWndMdi, (NANR *) object);
|
||||
copy = EditorGetObject(h);
|
||||
break;
|
||||
}
|
||||
|
||||
//if we created a copy, free the original and keep the copy
|
||||
|
@ -695,6 +695,44 @@ void NnsStreamFree(NnsStream *stream) {
|
||||
|
||||
// ----- ISCAD functions
|
||||
|
||||
int IscadIsValidFooter(const unsigned char *footer, unsigned int size) {
|
||||
//check blocks
|
||||
unsigned int pos = 0;
|
||||
while (pos < size) {
|
||||
const unsigned char *hdr = footer + pos;
|
||||
if ((size - pos) < 8) return 0;
|
||||
if (!NnsiSigNameIsValid((const char *) hdr)) return 0;
|
||||
|
||||
//check block size
|
||||
pos += 8;
|
||||
unsigned int blockSize = *(const uint32_t *) (hdr + 4);
|
||||
if ((size - pos) < blockSize) return 0;
|
||||
|
||||
pos += blockSize;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char *IscadFindBlockBySignature(const unsigned char *buffer, unsigned int size, const char *signature, unsigned int *pSize) {
|
||||
//check blocks
|
||||
unsigned int pos = 0;
|
||||
while (pos < size) {
|
||||
const unsigned char *hdr = buffer + pos;
|
||||
if (memcmp(hdr, signature, 4) == 0) {
|
||||
//return block
|
||||
*pSize = *(const uint32_t *) (hdr + 4);
|
||||
return (unsigned char *) (hdr + 8);
|
||||
}
|
||||
|
||||
//check block size
|
||||
pos += 8;
|
||||
unsigned int blockSize = *(const uint32_t *) (hdr + 4);
|
||||
pos += blockSize;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void IscadStreamCreate(IscadStream *stream) {
|
||||
stream->inFooter = 0;
|
||||
bstreamCreate(&stream->stream, NULL, 0);
|
||||
|
@ -74,6 +74,10 @@ void NnsStreamFree(NnsStream *stream);
|
||||
|
||||
// ----- ISCAD stream functions
|
||||
|
||||
int IscadIsValidFooter(const unsigned char *footer, unsigned int size);
|
||||
unsigned char *IscadFindBlockBySignature(const unsigned char *buffer, unsigned int size, const char *signature, unsigned int *pSize);
|
||||
|
||||
|
||||
typedef struct IscadStream_ {
|
||||
BSTREAM stream; // file stream
|
||||
int inFooter; // in footer
|
||||
|
Loading…
Reference in New Issue
Block a user