From 3189ca5efc5826405bf2e9c7d9949ad5ea9a8a10 Mon Sep 17 00:00:00 2001 From: Treeki Date: Wed, 12 Sep 2012 14:57:44 +0200 Subject: initial un-exporter version --- lsrlytexporter.cpp | 1176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1176 insertions(+) create mode 100644 lsrlytexporter.cpp (limited to 'lsrlytexporter.cpp') diff --git a/lsrlytexporter.cpp b/lsrlytexporter.cpp new file mode 100644 index 0000000..42fb587 --- /dev/null +++ b/lsrlytexporter.cpp @@ -0,0 +1,1176 @@ +#include "lsrlytexporter.h" +#include "lyt/layout.h" +#include "wii/texpalette.h" +#include +#include +#include +#include +#include + +static void collectPanesForList(QList *list, LYTPane *pane) { + list->append(pane); + + foreach (LYTPane *p, pane->children) + collectPanesForList(list, p); +} + +static void outputPaneTree(QXmlStreamWriter *w, LYTPane *pane) { + w->writeStartElement("paneTree"); + w->writeAttribute("name", pane->name); + + foreach (LYTPane *p, pane->children) + outputPaneTree(w, p); + + w->writeEndElement(); +} + +static void writeTexCoordSet(QXmlStreamWriter &w, const LYTTexCoords &set) { + w.writeStartElement("texCoord"); + + static const char *vnames[] = { + "texLT", "texRT", "texLB", "texRB" + }; + for (int i = 0; i < 4; i++) { + w.writeEmptyElement(vnames[i]); + w.writeAttribute("s", QString::number(set.coord[i].x())); + w.writeAttribute("t", QString::number(set.coord[i].y())); + } + + w.writeEndElement(); // texCoord +} + +static void writeAttribColour(QXmlStreamWriter &w, const QColor &c, bool hasAlpha=true) { + w.writeAttribute("r", QString::number(c.red())); + w.writeAttribute("g", QString::number(c.green())); + w.writeAttribute("b", QString::number(c.blue())); + if (hasAlpha) + w.writeAttribute("a", QString::number(c.alpha())); +} + +static void writeAttribColourNoAlpha(QXmlStreamWriter &w, const QColor &c) { + writeAttribColour(w, c, false); +} + +static bool isMaterialDetailed(const LYTMaterial &mat) { + int chk = 0; + if (mat.hasAlphaCompare) chk += 1; + if (mat.hasBlendMode) chk += 1; + if (mat.hasChanCtrl) chk += 1; + if (mat.hasMatCol) chk += 1; + if (mat.hasTevSwapTable) chk += 1; + if (chk == 0) return false; + if (chk == 5) return true; + qWarning("HUH????"); + return false; +} + +static void writeMaterial(QXmlStreamWriter &w, const LYTMaterial &mat, QSet &usedNames) { + bool isDetail = mat.hasChanCtrl; + + QString matname = mat.name; + while (usedNames.contains(matname)) { + matname[0] = QChar(matname.at(0).unicode() + 1); + if (matname[0] == ('Z'+1)) + matname[0] = 'a'; + if (matname[0] == ('z'+1)) + matname[0] = 'A'; + } + usedNames.insert(matname); + + w.writeStartElement("material"); + w.writeAttribute("name", matname); + { + w.writeEmptyElement("blackColor"); + writeAttribColourNoAlpha(w, mat.colours[0]); + w.writeEmptyElement("whiteColor"); + writeAttribColourNoAlpha(w, mat.colours[1]); + + static const char *Filters[] = {"Near", "Linear"}; + static const char *Wraps[] = {"Clamp", "Repeat", "Mirror"}; + + foreach (const LYTTexMap &tm, mat.texMaps) { + w.writeEmptyElement("texMap"); + + QString niceName = tm.textureName; + niceName.replace(".tpl", ""); + w.writeAttribute("imageName", niceName); + w.writeAttribute("wrap_s", Wraps[tm.wrap_s]); + w.writeAttribute("wrap_t", Wraps[tm.wrap_t]); + w.writeAttribute("filterMin", Filters[tm.min_filter]); + w.writeAttribute("filterMax", Filters[tm.mag_filter]); + // Left out on purpose: paletteName + } + + foreach (const LYTTexSRT &srt, mat.texSRTs) { + w.writeStartElement("texMatrix"); + w.writeAttribute("rotate", QString::number(srt.rotate)); + + w.writeEmptyElement("scale"); + w.writeAttribute("x", QString::number(srt.xScale)); + w.writeAttribute("y", QString::number(srt.yScale)); + + w.writeEmptyElement("translate"); + w.writeAttribute("x", QString::number(srt.xTrans)); + w.writeAttribute("y", QString::number(srt.yTrans)); + + w.writeEndElement(); // texMatrix + } + + foreach (const LYTTexCoordGen &cg, mat.texCoordGens) { + w.writeEmptyElement("texCoordGen"); + w.writeAttribute("func", (cg.genType == 1 ? "Mtx2x4" : "ERROR")); + w.writeAttribute("srcParam", (cg.src>=4 && cg.src<=11)?QString("Tex%1").arg(cg.src-4):"ERROR"); + int fixMtx = (cg.mtx == 60) ? -1 : ((cg.mtx / 3) - 10); + w.writeAttribute("matrix", QString::number(fixMtx)); + } + + // this is most likely INCORRECT. + w.writeEmptyElement("textureStage"); + w.writeAttribute("texMap", "0"); + w.writeAttribute("texCoordGen", "0"); + + w.writeEmptyElement("texBlendRatio"); + w.writeAttribute("color", "255"); + + // TODO: textureStage, texBlendRatio, indirectStages(MaterialWarp),..? + } + w.writeEndElement(); // material + + w.writeStartElement("materialRevo"); + w.writeAttribute("name", matname); + if (isDetail) { + w.writeAttribute("tevStageNum", QString::number(mat.tevStages.count())); + w.writeAttribute("indirectStageNum", QString::number(mat.indTexStages.count())); + } + { + qDebug("Chk: %c%c%c%c%c %d,%d,%d,%d,%d,%d", + mat.hasAlphaCompare?'x':'_', + mat.hasBlendMode?'x':'_', + mat.hasChanCtrl?'x':'_', + mat.hasMatCol?'x':'_', + mat.hasTevSwapTable?'x':'_', + mat.indTexSRTs.count(), + mat.indTexStages.count(), + mat.tevStages.count(), + mat.texCoordGens.count(), + mat.texMaps.count(), + mat.texSRTs.count()); + + w.writeEmptyElement("channelControl"); + w.writeAttribute("channel", "Color0"); + w.writeAttribute("materialSource", mat.hasChanCtrl?(mat.chanCtrl.colourMatSrc==0?"Register":"Vertex"):"Vertex"); + + w.writeEmptyElement("channelControl"); + w.writeAttribute("channel", "Alpha0"); + w.writeAttribute("materialSource", mat.hasChanCtrl?(mat.chanCtrl.colourMatSrc==0?"Register":"Vertex"):"Vertex"); + + w.writeEmptyElement("matColReg"); + writeAttribColour(w, mat.hasMatCol ? mat.matCol : QColor::fromRgb(255,255,255,255)); + + for (int i = 0; i < 3; i++) { + w.writeEmptyElement("tevColReg"); + writeAttribColour(w, mat.colours[i]); + } + + for (int i = 0; i < 4; i++) { + w.writeEmptyElement("tevConstReg"); + writeAttribColour(w, mat.tevKColour[i]); + } + + // blehhhhhhhhhhhh DRY!!! + static const char *Filters[] = {"Near", "Linear"}; + static const char *Wraps[] = {"Clamp", "Repeat", "Mirror"}; + + foreach (const LYTTexMap &tm, mat.texMaps) { + w.writeEmptyElement("texMap"); + + QString niceName = tm.textureName; + niceName.replace(".tpl", ""); + w.writeAttribute("imageName", niceName); + w.writeAttribute("wrap_s", Wraps[tm.wrap_s]); + w.writeAttribute("wrap_t", Wraps[tm.wrap_t]); + w.writeAttribute("filterMin", Filters[tm.min_filter]); + w.writeAttribute("filterMax", Filters[tm.mag_filter]); + // Left out on purpose: paletteName + } + + foreach (const LYTTexSRT &srt, mat.texSRTs) { + w.writeStartElement("texMatrix"); + w.writeAttribute("rotate", QString::number(srt.rotate)); + + w.writeEmptyElement("scale"); + w.writeAttribute("x", QString::number(srt.xScale)); + w.writeAttribute("y", QString::number(srt.yScale)); + + w.writeEmptyElement("translate"); + w.writeAttribute("x", QString::number(srt.xTrans)); + w.writeAttribute("y", QString::number(srt.yTrans)); + + w.writeEndElement(); // texMatrix + } + + foreach (const LYTTexCoordGen &cg, mat.texCoordGens) { + w.writeEmptyElement("texCoordGen"); + w.writeAttribute("func", (cg.genType == 1 ? "Mtx2x4" : "ERROR")); + w.writeAttribute("srcParam", (cg.src>=4 && cg.src<=11)?QString("Tex%1").arg(cg.src-4):"ERROR"); + int fixMtx = (cg.mtx == 60) ? -1 : ((cg.mtx / 3) - 10); + w.writeAttribute("matrix", QString::number(fixMtx)); + } + // end horrible lack of DRY + + if (mat.hasTevSwapTable) { + static const char *Swaps[] = {"Red","Green","Blue","Alpha"}; + for (int i = 0; i < 4; i++) { + w.writeEmptyElement("swapTable"); + w.writeAttribute("r", Swaps[mat.tevSwapTable.mode[i].red]); + w.writeAttribute("g", Swaps[mat.tevSwapTable.mode[i].green]); + w.writeAttribute("b", Swaps[mat.tevSwapTable.mode[i].blue]); + w.writeAttribute("a", Swaps[mat.tevSwapTable.mode[i].alpha]); + } + } else { + for (int i = 0; i < 4; i++) { + w.writeEmptyElement("swapTable"); + w.writeAttribute("r", "Red"); + w.writeAttribute("g", "Green"); + w.writeAttribute("b", "Blue"); + w.writeAttribute("a", "Alpha"); + } + } + + foreach (const LYTTexSRT &srt, mat.indTexSRTs) { + w.writeStartElement("indirectMatrix"); + w.writeAttribute("rotate", QString::number(srt.rotate)); + + w.writeEmptyElement("scale"); + w.writeAttribute("x", QString::number(srt.xScale)); + w.writeAttribute("y", QString::number(srt.yScale)); + + w.writeEmptyElement("translate"); + w.writeAttribute("x", QString::number(srt.xTrans)); + w.writeAttribute("y", QString::number(srt.yTrans)); + + w.writeEndElement(); // texMatrix + } + + static const char *IndScale[] = { + "V1","V2","V4","V8","V16","V32","V64","V128","V256" + }; + foreach (const LYTIndirectStage &ist, mat.indTexStages) { + w.writeEmptyElement("indirectStage"); + w.writeAttribute("texMap", QString::number(ist.texMap)); + w.writeAttribute("texCoord", QString::number(ist.texCoord)); + w.writeAttribute("scale_s", IndScale[ist.wrap_s]); + w.writeAttribute("scale_t", IndScale[ist.wrap_t]); + } + + // for no indirects? no idea + if (mat.indTexSRTs.count() == 0) { + for (int i = 0; i < 3; i++) { + w.writeStartElement("indirectMatrix"); + w.writeAttribute("rotate", "0"); + + w.writeEmptyElement("scale"); + w.writeAttribute("x", "1"); + w.writeAttribute("y", "1"); + + w.writeEmptyElement("translate"); + w.writeAttribute("x", "0"); + w.writeAttribute("y", "0"); + + w.writeEndElement(); // indirectMatrix + } + } + + if (mat.indTexStages.count() == 0) { + for (int i = 0; i < 4; i++) { + w.writeEmptyElement("indirectStage"); + w.writeAttribute("texMap", "0"); + w.writeAttribute("texCoordGen", "0"); + w.writeAttribute("scale_s", "V1"); + w.writeAttribute("scale_t", "V1"); + } + } + + // the rest + + static const char *TevCChan[] = { + "xERR","xERR","xERR","xERR","Color0a0","xERR", + "ColorZero","xERR","xERR" + }; + static const char *CArg[] = { + "CPrev","APrev","C0","A0","C1","A1","C2","A2", + "TexC","TexA","RasC","RasA","V1_0","V0_5","Konst","V0" + }; + static const char *AArg[] = { + "APrev","A0","A1","A2","TexA","RasA","Konst","V0" + }; + static const char *KCSel[] = { + "V8_8","V7_8","V6_8","V5_8","V4_8","V3_8","V2_8","V1_8", + "xERR","xERR","xERR","xERR", + "K0","K1","K2","K3", + "K0_r","K1_r","K2_r","K3_r", + "K0_g","K1_g","K2_g","K3_g", + "K0_b","K1_b","K2_b","K3_b", + "K0_a","K1_a","K2_a","K3_a" + }; + static const char *KASel[] = { + "V8_8","V7_8","V6_8","V5_8","V4_8","V3_8","V2_8","V1_8", + "xERR","xERR","xERR","xERR","xERR","xERR","xERR","xERR", + "K0_r","K1_r","K2_r","K3_r", + "K0_g","K1_g","K2_g","K3_g", + "K0_b","K1_b","K2_b","K3_b", + "K0_a","K1_a","K2_a","K3_a" + }; + static const char *ColOp[] = { + "Add","Sub","x","x","x","x","x","x", + "Comp_r8_gt","Comp_r8_eq", + "Comp_gr16_gt","Comp_gr16_eq", + "Comp_bgr24_gt","Comp_bgr24_eq", + "Comp_rgb8_gt","Comp_rgb8_eq" + }; + static const char *AlOp[] = { + "Add","Sub","x","x","x","x","x","x", + "Comp_r8_gt","Comp_r8_eq", + "Comp_gr16_gt","Comp_gr16_eq", + "Comp_bgr24_gt","Comp_bgr24_eq", + "Comp_a8_gt","Comp_a8_eq" + }; + static const char *Bias[] = {"V0", "P0_5", "M0_5"}; + static const char *Scale[] = {"V1", "V2", "V4", "V1_2"}; + static const char *Reg[] = {"Prev", "Reg0", "Reg1", "Reg2"}; + foreach (const LYTTevStage &st, mat.tevStages) { + w.writeStartElement("tevStage"); + w.writeAttribute("colorChannel", (st.colour==-1)?"ColorNull":TevCChan[st.colour]); + w.writeAttribute("texMap", QString::number(st.texMap)); + w.writeAttribute("texCoordGen", QString::number(st.texCoord)); + w.writeAttribute("rasColSwap", QString::number(st.rasSwapMode)); + w.writeAttribute("texColSwap", QString::number(st.texSwapMode)); + + w.writeEmptyElement("color"); + w.writeAttribute("a", CArg[st.colourInA]); + w.writeAttribute("b", CArg[st.colourInB]); + w.writeAttribute("c", CArg[st.colourInC]); + w.writeAttribute("d", CArg[st.colourInD]); + w.writeAttribute("konst", KCSel[st.colourConst]); + w.writeAttribute("op", ColOp[st.colourOp]); + w.writeAttribute("bias", Bias[st.colourBias]); + w.writeAttribute("scale", Scale[st.colourScale]); + w.writeAttribute("clamp", st.colourClamp?"true":"false"); + w.writeAttribute("outReg", Reg[st.colourOutReg]); + + w.writeEmptyElement("alpha"); + w.writeAttribute("a", AArg[st.alphaInA]); + w.writeAttribute("b", AArg[st.alphaInB]); + w.writeAttribute("c", AArg[st.alphaInC]); + w.writeAttribute("d", AArg[st.alphaInD]); + w.writeAttribute("konst", KASel[st.alphaConst]); + w.writeAttribute("op", AlOp[st.alphaOp]); + w.writeAttribute("bias", Bias[st.alphaBias]); + w.writeAttribute("scale", Scale[st.alphaScale]); + w.writeAttribute("clamp", st.alphaClamp?"true":"false"); + w.writeAttribute("outReg", Reg[st.alphaOutReg]); + + static const char *IFormat[] = {"V8","V5","V4","V3"}; + static const char *IBias[] = {"None","S","T","ST","U","SU","TU","STU"}; + static const char *IMtx[] = {"Off","V0","V1","V2","x","S0","S1","S2","x","T0","T1","T2"}; + static const char *IWrap[] = {"Off","V256","V128","V64","V32","V16","V0"}; + + w.writeEmptyElement("indirect"); + w.writeAttribute("indStage", QString::number(st.indStage)); + w.writeAttribute("format", IFormat[st.indFormat]); + w.writeAttribute("bias", IBias[st.indBias]); + w.writeAttribute("matrix", IMtx[st.indMatrix]); + w.writeAttribute("wrap_s", IWrap[st.indWrapS]); + w.writeAttribute("wrap_t", IWrap[st.indWrapT]); + w.writeAttribute("addPrev", st.indAddPrev?"true":"false"); + w.writeAttribute("utcLod", st.indUtcLod?"true":"false"); + if (st.indAlphaSel != 0) + qFatal("indAlphaSel not off!"); + w.writeAttribute("alpha", "Off"); + + w.writeEndElement(); // tevStage + } + + // wtf bbq + if (mat.tevStages.count() == 0) { + // todo: different for >1 texture? must check. + w.writeStartElement("tevStage"); + w.writeAttribute("colorChannel", "Color0a0"); + w.writeAttribute("texMap", "-1"); + w.writeAttribute("texCoordGen", "-1"); + w.writeAttribute("rasColSwap", "0"); + w.writeAttribute("texColSwap", "0"); + + w.writeEmptyElement("color"); + w.writeAttribute("a", "RasC"); + w.writeAttribute("b", "V0"); + w.writeAttribute("c", "V0"); + w.writeAttribute("d", "V0"); + w.writeAttribute("konst", "K0"); + w.writeAttribute("op", "Add"); + w.writeAttribute("bias", "V0"); + w.writeAttribute("scale", "V1"); + w.writeAttribute("clamp", "true"); + w.writeAttribute("outReg", "Prev"); + + w.writeEmptyElement("alpha"); + w.writeAttribute("a", "RasA"); + w.writeAttribute("b", "V0"); + w.writeAttribute("c", "V0"); + w.writeAttribute("d", "V0"); + w.writeAttribute("konst", "K0_a"); + w.writeAttribute("op", "Add"); + w.writeAttribute("bias", "V0"); + w.writeAttribute("scale", "V1"); + w.writeAttribute("clamp", "true"); + w.writeAttribute("outReg", "Prev"); + + w.writeEmptyElement("indirect"); + w.writeAttribute("indStage", "0"); + w.writeAttribute("format", "V8"); + w.writeAttribute("bias", "STU"); + w.writeAttribute("matrix", "Off"); + w.writeAttribute("wrap_s", "Off"); + w.writeAttribute("wrap_t", "Off"); + w.writeAttribute("addPrev", "false"); + w.writeAttribute("alpha", "Off"); + w.writeAttribute("utcLod", "false"); + + w.writeEndElement(); // tevStage + } + + if (mat.hasAlphaCompare) { + static const char *Compare[] = {"Never","Less","Equal","LEqual","Greater","NEqual","GEqual","Always"}; + static const char *AlphaOp[] = {"And","Or","Xor","Xnor"}; + w.writeEmptyElement("alphaCompare"); + w.writeAttribute("comp0", Compare[mat.alphaCompare.comp0]); + w.writeAttribute("ref0", QString::number(mat.alphaCompare.ref0)); + w.writeAttribute("op", AlphaOp[mat.alphaCompare.op]); + w.writeAttribute("comp1", Compare[mat.alphaCompare.comp1]); + w.writeAttribute("ref1", QString::number(mat.alphaCompare.ref1)); + } else { + w.writeEmptyElement("alphaCompare"); + w.writeAttribute("comp0", "Always"); + w.writeAttribute("ref0", "0"); + w.writeAttribute("op", "And"); + w.writeAttribute("comp1", "Always"); + w.writeAttribute("ref1", "0"); + } + + if (mat.hasBlendMode) { + static const char *BmType[] = {"None","Blend","Logic","Subtract"}; + static const char *SrcFactor[] = {"V0","V1_0","DstClr","InvDstClr","SrcAlpha","InvSrcAlpha","DstAlpha","InvDstAlpha"}; + static const char *DstFactor[] = {"V0","V1_0","SrcClr","InvSrcClr","SrcAlpha","InvSrcAlpha","DstAlpha","InvDstAlpha"}; + static const char *LogicOp[] = { + "Clear", "And", "RevAnd", "Copy", + "InvAnd", "NoOp", "Xor", "Or", + "Nor", "Equiv", "Inv", "RevOr", + "InvCopy", "InvOr", "Nand", "Set" + }; + w.writeEmptyElement("blendMode"); + w.writeAttribute("type", BmType[mat.blendMode.type]); + w.writeAttribute("srcFactor", SrcFactor[mat.blendMode.srcFactor]); + w.writeAttribute("dstFactor", DstFactor[mat.blendMode.destFactor]); + w.writeAttribute("op", LogicOp[mat.blendMode.op]); + } else { + w.writeEmptyElement("blendMode"); + w.writeAttribute("type", "Blend"); + w.writeAttribute("srcFactor", "SrcAlpha"); + w.writeAttribute("dstFactor", "InvSrcAlpha"); + w.writeAttribute("op", "Copy"); + } + + // what have I missed...? tevStageNum attrib?, displayFace attrib?, + // indirectStageNum attrib? + } + w.writeEndElement(); // materialRevo +} + +static void exportRlyt(LYTLayout *layout, QIODevice *output, const QMap &texFormats) { + QXmlStreamWriter w(output); + w.setAutoFormatting(true); + + w.writeStartDocument(); + + w.writeStartElement("nw4r_layout"); + w.writeDefaultNamespace("http://www.nintendo.co.jp/NW4R/LayoutEditor"); + w.writeAttribute("version", "1.2.0"); + + w.writeStartElement("head"); + { + w.writeEmptyElement("create"); + w.writeAttribute("user", "someone"); + w.writeAttribute("host", "somewhere"); + w.writeAttribute("date", "2006-07-26T10:20:54.551+09:00"); + w.writeAttribute("source", ""); + + w.writeEmptyElement("title"); + + w.writeEmptyElement("comment"); + + w.writeEmptyElement("generator"); + w.writeAttribute("name", "LayoutStudio"); + w.writeAttribute("version", "NONE_OF_YOUR_BUSINESS"); + } + w.writeEndElement(); // head + + w.writeStartElement("body"); + w.writeStartElement("rlyt"); + + + // Stuff begins here + QSet usedNames; + + static const char *PaneKinds[] = { + "Null", "Picture", "TextBox", "Window", "Bounding" + }; + static const char *HorzOrigin[] = {"Left", "Center", "Right"}; + static const char *VertOrigin[] = {"Top", "Center", "Bottom"}; + + w.writeStartElement("paneSet"); + { + // Collect pane list + QList paneList; + collectPanesForList(&paneList, layout->rootPane); + + // now write them all + foreach (LYTPane *p, paneList) { + w.writeStartElement("pane"); + w.writeAttribute("kind", PaneKinds[(int)p->type()]); + w.writeAttribute("name", p->name); + w.writeAttribute("visible", p->visible?"true":"false"); + w.writeAttribute("locked", "false"); + w.writeAttribute("alpha", QString::number(p->alpha)); + if (p->influencedAlpha) + w.writeAttribute("influencedAlpha", "true"); + if (p->isWidescreen) + w.writeAttribute("locationAdjust", "true"); + { + w.writeEmptyElement("comment"); + + w.writeEmptyElement("basePositionType"); + w.writeAttribute("x", HorzOrigin[(int)p->horzOrigin]); + w.writeAttribute("y", VertOrigin[(int)p->vertOrigin]); + + w.writeEmptyElement("translate"); + w.writeAttribute("x", QString::number(p->xTrans)); + w.writeAttribute("y", QString::number(p->yTrans)); + w.writeAttribute("z", QString::number(p->zTrans)); + + w.writeEmptyElement("rotate"); + w.writeAttribute("x", QString::number(p->xRot)); + w.writeAttribute("y", QString::number(p->yRot)); + w.writeAttribute("z", QString::number(p->zRot)); + + w.writeEmptyElement("scale"); + w.writeAttribute("x", QString::number(p->xScale)); + w.writeAttribute("y", QString::number(p->yScale)); + + w.writeEmptyElement("size"); + w.writeAttribute("x", QString::number(p->width)); + w.writeAttribute("y", QString::number(p->height)); + + + if (p->type() == LYTPane::PictureType) { + LYTPicture *pic = (LYTPicture*)p; + + const LYTMaterial &mat = *(layout->materials.getMaterialByName(pic->materialName)); + + w.writeStartElement("picture"); + w.writeAttribute("detailSetting", isMaterialDetailed(mat)?"true":"false"); + + static const char *vnames[] = { + "vtxColLT", "vtxColRT", "vtxColLB", "vtxColRB" + }; + for (int i = 0; i < 4; i++) { + w.writeEmptyElement(vnames[i]); + writeAttribColour(w, pic->vtxColours[i]); + } + + for (int i = 0; i < pic->texCoords.count(); i++) + writeTexCoordSet(w, pic->texCoords.at(i)); + + writeMaterial(w, mat, usedNames); + + w.writeEndElement(); // picture + + } else if (p->type() == LYTPane::TextBoxType) { + LYTTextBox *tb = (LYTTextBox*)p; + + static const char *TextAlignments[] = { + "Synchronous", "Left", "Center", "Right" + }; + + w.writeStartElement("textBox"); + w.writeAttribute("font", tb->fontName); + w.writeAttribute("textAlignment", TextAlignments[(int)tb->alignment]); + w.writeAttribute("lineSpace", QString::number(tb->lineSpace)); + w.writeAttribute("charSpace", QString::number(tb->charSpace)); + w.writeAttribute("allocateStringLength", QString::number(tb->bufferLength)); + + w.writeEmptyElement("fontSize"); + w.writeAttribute("x", QString::number(tb->fontSizeX)); + w.writeAttribute("y", QString::number(tb->fontSizeY)); + + w.writeTextElement("text", tb->text); + + w.writeEmptyElement("topColor"); + writeAttribColour(w, tb->colour1); + + w.writeEmptyElement("bottomColor"); + writeAttribColour(w, tb->colour2); + + w.writeEmptyElement("positionType"); + w.writeAttribute("x", HorzOrigin[(int)tb->textHorzPos]); + w.writeAttribute("y", VertOrigin[(int)tb->textVertPos]); + + writeMaterial(w, *(layout->materials.getMaterialByName(tb->materialName)), usedNames); + + w.writeEndElement(); // textBox + + } else if (p->type() == LYTPane::WindowType) { + LYTWindow *win = (LYTWindow*)p; + + w.writeStartElement("window"); + + w.writeStartElement("content"); + { + const LYTMaterial &mat = *(layout->materials.getMaterialByName(win->contentMaterialName)); + w.writeAttribute("detailSetting", isMaterialDetailed(mat)?"true":"false"); + + static const char *vnames[] = { + "vtxColLT", "vtxColRT", "vtxColLB", "vtxColRB" + }; + for (int i = 0; i < 4; i++) { + w.writeEmptyElement(vnames[i]); + writeAttribColour(w, win->contentVtxColours[i]); + } + + foreach (const LYTTexCoords &set, win->contentTexCoords) + writeTexCoordSet(w, set); + + writeMaterial(w, mat, usedNames); + } + w.writeEndElement(); // content + + const char *wframetypes[] = { + "CornerLT", "CornerRT", "CornerLB", "CornerRB", + "FrameT", "FrameB", "FrameL", "FrameR" + }; + int frameidx = 0; + foreach (const LYTWindowFrame *fr, win->frames) { + const LYTMaterial &fmat = *(layout->materials.getMaterialByName(fr->materialName)); + + w.writeStartElement("frame"); + w.writeAttribute("frameType", wframetypes[frameidx]); + w.writeAttribute("detailSetting", isMaterialDetailed(fmat)?"true":"false"); + + static const char *tflipNames[] = { + "None", "FlipH", "FlipV", "Rotate90", "Rotate180", "Rotate270" + }; + w.writeTextElement("textureFlip", tflipNames[(int)fr->type]); + + writeMaterial(w, fmat, usedNames); + + w.writeEndElement(); // frame + + frameidx++; + } + + w.writeEmptyElement("contentInflation"); + w.writeAttribute("l", QString::number(win->contentOverflowLeft)); + w.writeAttribute("r", QString::number(win->contentOverflowRight)); + w.writeAttribute("t", QString::number(win->contentOverflowTop)); + w.writeAttribute("b", QString::number(win->contentOverflowBottom)); + + w.writeEndElement(); // window + + } else if (p->type() == LYTPane::BoundingType) { + w.writeEmptyElement("bounding"); + } + + + w.writeStartElement("userData"); + w.writeEmptyElement("string"); + w.writeEndElement(); + } + w.writeEndElement(); // pane + } + } + w.writeEndElement(); // paneSet + + + // Now, the hierarchy + w.writeStartElement("paneHierarchy"); + { + outputPaneTree(&w, layout->rootPane); + } + w.writeEndElement(); // paneHierarchy + + + w.writeStartElement("groupSet"); + { + w.writeStartElement("group"); + w.writeAttribute("name", "RootGroup"); + { + foreach (LYTGroup *g, layout->groups) { + w.writeStartElement("group"); + w.writeAttribute("name", g->name); + { + foreach (LYTPane *p, g->panes) { + w.writeEmptyElement("paneRef"); + w.writeAttribute("name", p->name); + } + w.writeEmptyElement("comment"); + } + w.writeEndElement(); // group + } + + w.writeEmptyElement("comment"); + } + w.writeEndElement(); // group + } + w.writeEndElement(); // groupSet + + + w.writeStartElement("screenSetting"); + w.writeAttribute("origin", "Normal"); + { + w.writeEmptyElement("layoutSize"); + w.writeAttribute("x", QString::number(layout->width)); + w.writeAttribute("y", QString::number(layout->height)); + + w.writeStartElement("backGround"); + { + w.writeEmptyElement("color"); + w.writeAttribute("r", "169"); + w.writeAttribute("g", "169"); + w.writeAttribute("b", "169"); + w.writeAttribute("a", "255"); + } + w.writeEndElement(); // backGround + + w.writeStartElement("grid"); + w.writeAttribute("thickLineInterval", "40"); + w.writeAttribute("thinDivisionNum", "4"); + w.writeAttribute("visible", "true"); + w.writeAttribute("moveMethod", "Grid"); + { + w.writeEmptyElement("color"); + w.writeAttribute("r", "128"); + w.writeAttribute("g", "128"); + w.writeAttribute("b", "64"); + w.writeAttribute("a", "128"); + } + w.writeEndElement(); // grid + } + w.writeEndElement(); // screenSetting + + + const char *TexFormats[] = { + "I4", "I8", "IA4", "IA8", "RGB565", "RGB5A3", "RGBA8", + "xxx", "C4x", "C8x", "C14x", "xxx", "xxx", "xxx", "CMPR" + }; + + QStringList texfiles = layout->generateTextureRefs(); + foreach (const QString &texfile, texfiles) { + QString stripname = texfile; + stripname.replace(".tpl", ""); + + w.writeEmptyElement("textureFile"); + w.writeAttribute("imagePath", QString("texture\\%1.tga").arg(stripname)); + w.writeAttribute("format", TexFormats[(int)texFormats.value(stripname)]); + } + + + // find out all the fonts that are used + QStringList fonts; + layout->rootPane->addFontRefsToList(fonts); + + foreach (const QString &fontfile, fonts) { + w.writeEmptyElement("fontFile"); + w.writeAttribute("path", QString("..\\fonts\\%1").arg(fontfile)); + } + + + // Finish up + + + w.writeEndElement(); // rlyt + w.writeEndElement(); // body + + w.writeEndDocument(); +} + + +static void exportTexture(const WiiTPLTexture &tex, QIODevice *outputDevice) { + QDataStream out(outputDevice); + out.setByteOrder(QDataStream::LittleEndian); + + out << (quint8)0x14; // ID length + out << (quint8)0; // no colour map + out << (quint8)2; // uncompressed true colour + WritePadding(5, out); // colour map spec, not used + out << (quint16)0; // X origin + out << (quint16)0; // Y origin + out << (quint16)tex.image.width(); // width + out << (quint16)tex.image.height(); // height + out << (quint8)32; // bit depth + out << (quint8)8; // alpha channel, direction (dir not used) + + out.writeRawData("NW4R_Tga Ver1.0\0\0\0\xc1\xb0", 0x14); + + // put together the image + for (int y = tex.image.height() - 1; y >= 0; y--) { + const QRgb *line = (const QRgb*)tex.image.scanLine(y); + for (int x = 0; x < tex.image.width(); x++) { + out << (quint32)line[x]; + } + } + + // now, data... + static const char *TexFormats[] = { + "i4", "i8", "ia4", "ia8", "rgb565", "rgb5a3", "rgba8", + "XXX", "c4", "c8", "c14", "XXX", "XXX", "XXX", "cmpr" + }; + + out.setByteOrder(QDataStream::BigEndian); + + // format block + out.writeRawData("nw4r_tfm", 8); + const char *tfm = TexFormats[(int)tex.format]; + quint32 tfm_size = 12 + qstrlen(tfm); + out << tfm_size; + out.writeRawData(tfm, qstrlen(tfm)); + + // end block + out.writeRawData("nw4r_end", 8); + out << (quint32)12; +} + +#include "lyt/animation.h" + +static int AnimKeyFor(LYTAnimPiece::Type type, const LYTAnimEntry &entry) { + switch (type) { + case LYTAnimPiece::TexSRTAnim: + case LYTAnimPiece::IndTexSRTAnim: + case LYTAnimPiece::TexPatAnim: + return (entry.id << 8) | entry.target; + default: + return entry.target; + } +} + +struct AnimExportThing { + QVector hermites; + QVector steps; + QStringList refRes; +}; + +void LSExportPackage(LYTPackageBase *pkg, QString dirStr) { + QDir dir(dirStr); + if (!dir.exists()) + dir.mkpath("."); + + if (!dir.exists("texture")) + dir.mkdir("texture"); + + QMap texFormats; + + QStringList texes = pkg->listTextures(); + foreach (const QString &tplName, texes) { + if (!tplName.endsWith(".tpl")) + continue; + + QByteArray tplData = pkg->getTexture(tplName); + QDataStream ds(tplData); + WiiTexPalette tpl(ds); + + const WiiTPLTexture &tex = tpl.textures.first(); + + QString texName = tplName; + texName.replace(".tpl", ""); + + texFormats.insert(texName, tex.format); + + QFile tgaFile(dir.filePath(QString("texture/%1.tga").arg(texName))); + tgaFile.open(QFile::WriteOnly); + exportTexture(tex, &tgaFile); + tgaFile.close(); + } + + QString brlytName = pkg->listLayouts().first(); + QString lytName = brlytName; + lytName.replace(".brlyt", ""); + + LYTLayout layout(*pkg, brlytName); + + QFile rlytFile(dir.filePath(QString("%1.rlyt").arg(lytName))); + rlytFile.open(QFile::WriteOnly); + exportRlyt(&layout, &rlytFile, texFormats); + rlytFile.close(); + + + // Now handle the rlan + QStringList brlans = pkg->listAnims(); + QMap animPtrsByTag; + foreach (const QString &brlanName, brlans) { + if (!brlanName.endsWith(".brlan")) + continue; + + QByteArray brlanData = pkg->getAnim(brlanName); + LYTAnimation *anim = new LYTAnimation(brlanData); + + animPtrsByTag.insert(anim->sourceTagNumber, anim); + } + + QFile rlanFile(dir.filePath(QString("%1.rlan").arg(lytName))); + rlanFile.open(QFile::WriteOnly); + QXmlStreamWriter aw(&rlanFile); + + aw.setAutoFormatting(true); + aw.writeStartDocument(); + + aw.writeStartElement("nw4r_layout"); + aw.writeDefaultNamespace("http://www.nintendo.co.jp/NW4R/LayoutEditor"); + aw.writeAttribute("version", "1.2.0"); + + aw.writeStartElement("head"); + { + aw.writeEmptyElement("create"); + aw.writeAttribute("user", "someone"); + aw.writeAttribute("host", "somewhere"); + aw.writeAttribute("date", "2006-07-26T10:20:54.551+09:00"); + aw.writeAttribute("source", ""); + + aw.writeEmptyElement("title"); + + aw.writeEmptyElement("comment"); + + aw.writeEmptyElement("generator"); + aw.writeAttribute("name", "LayoutStudio"); + aw.writeAttribute("version", "NONE_OF_YOUR_BUSINESS"); + } + aw.writeEndElement(); // head + + aw.writeStartElement("body"); + { + int minFrame = 0x7fffffff, maxFrame = -1; + foreach (const LYTAnimation *anim, animPtrsByTag) { + aw.writeStartElement("animTag"); + aw.writeAttribute("name", anim->name); + aw.writeAttribute("startFrame", QString::number(anim->sourceStartFrame)); + aw.writeAttribute("endFrame", QString::number(anim->sourceEndFrame)); + aw.writeAttribute("fileName", anim->name); + aw.writeAttribute("descendingBind", anim->recursiveBind?"true":"false"); + aw.writeAttribute("animLoop", anim->loop?"Loop":"OneTime"); + + if (anim->sourceStartFrame < minFrame) + minFrame = anim->sourceStartFrame; + if (anim->sourceEndFrame > maxFrame) + maxFrame = anim->sourceEndFrame; + + aw.writeEmptyElement("comment"); + + foreach (const LYTAnimGroupInfo &grp, anim->groups) { + aw.writeEmptyElement("group"); + aw.writeAttribute("name", grp.name); + } + + aw.writeEndElement(); // animTag + } + + // This is probably not the most efficient way to do this + // but who gives a shit + QSet usedAnimTypes; + foreach (const LYTAnimation *anim, animPtrsByTag) { + foreach (const LYTAnimBlock &block, anim->blocks) { + foreach (const LYTAnimPiece &piece, block.pieces) { + usedAnimTypes.insert(piece.type); + } + } + } + + // Now do processing for each type + static const char *AnimTypes[] = { + "PainSRT", "TextureSRT", "Visibility", "VertexColor", + "MaterialColor", "TexturePattern", "IndTextureSRT" + }; + foreach (LYTAnimPiece::Type type, usedAnimTypes) { + aw.writeStartElement("rlan"); + aw.writeAttribute("animType", AnimTypes[(int)type]); + aw.writeAttribute("startFrame", QString::number(minFrame)); + aw.writeAttribute("endFrame", QString::number(maxFrame)); + aw.writeAttribute("convertStartFrame", QString::number(minFrame)); + aw.writeAttribute("convertEndFrame", QString::number(maxFrame)); + + // collect the contents + QMap contentsUsed; + + foreach (const LYTAnimation *anim, animPtrsByTag) { + foreach (const LYTAnimBlock &block, anim->blocks) { + foreach (const LYTAnimPiece &piece, block.pieces) { + if (piece.type == type) + contentsUsed.insert(block.name, true); + } + } + } + + // now put them together! + QMapIterator contentsUsedIter(contentsUsed); + while (contentsUsedIter.hasNext()) { + contentsUsedIter.next(); + + aw.writeStartElement("animContent"); + aw.writeAttribute("name", contentsUsedIter.key()); + + // Collect the targets being used + QMap targetsUsed; + foreach (const LYTAnimation *anim, animPtrsByTag) { + foreach (const LYTAnimBlock &block, anim->blocks) { + if (block.name != contentsUsedIter.key()) + continue; + + foreach (const LYTAnimPiece &piece, block.pieces) { + if (piece.type != type) + continue; + + foreach (const LYTAnimEntry &entry, piece.entries) { + int key = AnimKeyFor(type, entry); + if (!targetsUsed.contains(key)) + targetsUsed.insert(key, AnimExportThing()); + + AnimExportThing &thing = targetsUsed[key]; + if (entry.curveType == 0) + qFatal("what!"); + else if (entry.curveType == 1) { + // step keys + foreach (const LYTAnimStepKey &key, entry.stepKeys) { + LYTAnimStepKey newKey = key; + newKey.frame += anim->sourceStartFrame; + thing.steps.append(newKey); + } + } else if (entry.curveType == 2) { + // hermite keys + foreach (const LYTAnimHermiteKey &key, entry.keys) { + LYTAnimHermiteKey newKey = key; + newKey.frame += anim->sourceStartFrame; + thing.hermites.append(newKey); + } + } + } + } + } + } + + // Now export them... + QMapIterator tuIter(targetsUsed); + while (tuIter.hasNext()) { + tuIter.next(); + + // assemble the start tag + int ak = tuIter.key(); + + static const char *PainTargets[] = { + "TranslateX", "TranslateY", "TranslateZ", + "RotateX", "RotateY", "RotateZ", + "ScaleX", "ScaleY", + "SizeW", "SizeH" + }; + + static const char *VtxColTargets[] = { + "LT_r", "LT_g", "LT_b", "LT_a", + "RT_r", "RT_g", "RT_b", "RT_a", + "LB_r", "LB_g", "LB_b", "LB_a", + "RB_r", "RB_g", "RB_b", "RB_a", + "PaneAlpha" + }; + + static const char *MatColTargets[] = { + "MatColor0_r", "MatColor0_g", "MatColor0_b", "MatColor0_a", + "TevColor0_r", "TevColor0_g", "TevColor0_b", "TevColor0_a", + "TevColor1_r", "TevColor1_g", "TevColor1_b", "TevColor1_a", + "TevColor2_r", "TevColor2_g", "TevColor2_b", "TevColor2_a", + "TevKonst0_r", "TevKonst0_g", "TevKonst0_b", "TevKonst0_a", + "TevKonst1_r", "TevKonst1_g", "TevKonst1_b", "TevKonst1_a", + "TevKonst2_r", "TevKonst2_g", "TevKonst2_b", "TevKonst2_a", + "TevKonst3_r", "TevKonst3_g", "TevKonst3_b", "TevKonst3_a" + }; + + static const char *SRTTargets[] = { + "TranslateS", "TranslateT", "Rotate", "ScaleS", "ScaleT" + }; + + static const char *PatTargets[] = { + "Image", "Palette" + }; + + switch (type) { + case LYTAnimPiece::PaneAnim: + aw.writeStartElement("animPainSRTTarget"); + aw.writeAttribute("target", PainTargets[ak]); + break; + case LYTAnimPiece::VisAnim: + aw.writeStartElement("animVisibilityTarget"); + aw.writeAttribute("target", "Visibility"); + break; + case LYTAnimPiece::VtxClrAnim: + aw.writeStartElement("animVertexColorTarget"); + aw.writeAttribute("target", VtxColTargets[ak]); + break; + case LYTAnimPiece::MatClrAnim: + aw.writeStartElement("animMaterialColorTarget"); + aw.writeAttribute("target", MatColTargets[ak]); + break; + case LYTAnimPiece::TexSRTAnim: + aw.writeStartElement("animTexSRTTarget"); + aw.writeAttribute("id", QString::number(ak>>8)); + aw.writeAttribute("target", SRTTargets[ak&0xFF]); + break; + case LYTAnimPiece::IndTexSRTAnim: + aw.writeStartElement("animIndTexSRTTarget"); + aw.writeAttribute("id", QString::number(ak>>8)); + aw.writeAttribute("target", SRTTargets[ak&0xFF]); + break; + case LYTAnimPiece::TexPatAnim: + aw.writeStartElement("animTexPatternTarget"); + aw.writeAttribute("id", QString::number(ak>>8)); + aw.writeAttribute("target", PatTargets[ak&0xFF]); + + //foreach (const QString &bee, ) + break; + } + + foreach (const LYTAnimHermiteKey &key, tuIter.value().hermites) { + aw.writeEmptyElement("key"); + aw.writeAttribute("frame", QString::number(key.frame)); + aw.writeAttribute("value", QString::number(key.value)); + aw.writeAttribute("slope", QString::number(key.slope)); + } + + foreach (const LYTAnimStepKey &key, tuIter.value().steps) { + aw.writeEmptyElement("key"); + aw.writeAttribute("frame", QString::number(key.frame)); + aw.writeAttribute("value", QString::number(key.value)); + aw.writeAttribute("slopeType", "Step"); + } + + aw.writeEndElement(); // target thing + } + + aw.writeEndElement(); // animContent + } + + aw.writeEndElement(); // rlan + } + } + aw.writeEndElement(); // body + + aw.writeEndElement(); // nw4r_layout + + aw.writeEndDocument(); + + rlanFile.close(); + + qDeleteAll(animPtrsByTag.begin(), animPtrsByTag.end()); +} -- cgit v1.2.3