#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()); }