public bool DoIt(string[] args) { if (args.Length >= 2) { var mapFile = args[1]; var linkFile = (args.Length >= 3) ? args[2] : null; var map = new MapFile(mapFile); LinkFile link = null; if (linkFile != null) { link = new LinkFile(linkFile); } var debug_symbol_table = new HSDStruct((map.Entries.Count + 1) * 0xC); int symbol_index = 0; foreach (var e in map.Entries) { debug_symbol_table.SetInt32(symbol_index * 0xC, (int)e.Start); debug_symbol_table.SetInt32(symbol_index * 0xC + 4, (int)e.End); if (link != null && link.TryGetAddressSymbol(e.Start, out string sym)) { debug_symbol_table.SetString(symbol_index * 0xC + 8, sym, true); } else if (!e.Symbol.StartsWith("zz_")) { debug_symbol_table.SetString(symbol_index * 0xC + 8, e.Symbol, true); } symbol_index++; } var function = new HSDAccessor() { _s = new HSDStruct(0x10) }; function._s.SetInt32(0, map.Entries.Count); function._s.SetReferenceStruct(0x04, debug_symbol_table); HSDRawFile f = new HSDRawFile(); f.Roots.Add(new HSDRootNode() { Data = function, Name = "mexDebug" }); f.Save("MxDb.dat"); return(true); } return(false); }
/// <summary> /// /// </summary> /// <param name="root"></param> public static void AddRoot(string name, HSDAccessor accesor) { var root = new HSDRootNode() { Name = name, Data = accesor }; Instance.RawHSDFile.Roots.Add(root); Instance.treeView1.Nodes.Add(new DataNode(name, accesor, root: root)); }
/// <summary> /// /// </summary> public void ExtractDataFromMap(HSDAccessor acc) { foreach (var p in acc.GetType().GetProperties()) { if (dolMap.ContainsKey(p.Name)) { var i = Activator.CreateInstance(p.PropertyType); ((HSDAccessor)i)._s = GetStruct(dolMap[p.Name]); p.SetValue(acc, i); } } }
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonClone_Click(object sender, EventArgs e) { if (mxListBox1.SelectedItem is MEXCostume costume) { _costumes.Add(new MEXCostume() { Costume = HSDAccessor.DeepClone <HSDRaw.MEX.MEX_CostumeFileSymbol>(costume.Costume), Icon = HSDAccessor.DeepClone <HSD_TOBJ>(costume.Icon), CSP = costume.CSP == null ? null : HSDAccessor.DeepClone <HSD_TOBJ>(costume.CSP) }); } }
/// <summary> /// /// </summary> /// <param name="filePath"></param> private void LoadMnSlMap(HSDRawFile hsd) { if (StageMenuFile != null) { return; } var org = hsd["MnSelectStageDataTable"]; var mex = hsd["mexMapData"]; if (org != null && mex == null) { MessageBox.Show("MexMapData symbol not found. One will now be generated", "Symbol Not Found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); // load and convert data from vanilla MexMapGenerator.LoadIconDataFromVanilla(org.Data as SBM_MnSelectStageDataTable, Icons); // generate mex data node hsd.Roots.Add(new HSDRootNode() { Name = "mexMapData", Data = MexMapGenerator.GenerateMexMap(org.Data as SBM_MnSelectStageDataTable, Icons) }); mex = hsd["mexMapData"]; } if (org != null && mex != null) { var stage = org.Data as SBM_MnSelectStageDataTable; var mexMap = mex.Data as MEX_mexMapData; //StageMenuFilePath = filePath; StageMenuFile = hsd; // Load Data from Mex Symbol MexMapGenerator.LoadIconDataFromSymbol(mexMap, Icons); // Load Dummy Icon Model IconJOBJManager.RefreshRendering = true; var icon = HSDAccessor.DeepClone <HSD_JOBJ>(mexMap.IconModel); IconJOBJManager.SetJOBJ(icon); // Load Dummy Stage Name Model StageNameJOBJManager.RefreshRendering = true; var name = HSDAccessor.DeepClone <HSD_JOBJ>(stage.StageNameModel); StageNameJOBJManager.SetJOBJ(name); StageNameJOBJManager.SetAnimJoint(stage.StageNameAnimJoint); StageNameJOBJManager.Frame = 10; Enabled = true; } }
/// <summary> /// /// </summary> public static void ImportTextures(HSD_JOBJ jobj) { var folder = Tools.FileIO.OpenFolder(); if (!string.IsNullOrEmpty(folder)) { // get all tobjs Dictionary <int, HSD_TOBJ> hashToImage = new Dictionary <int, HSD_TOBJ>(); // load all textures from file foreach (var tf in System.IO.Directory.GetFiles(folder)) { if (tf.ToLower().EndsWith(".png")) { var fn = System.IO.Path.GetFileNameWithoutExtension(tf); if (fn.Length >= 8 && int.TryParse(fn.Substring(0, 8), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int hash) && TOBJConverter.FormatFromString(fn, out HSDRaw.GX.GXTexFmt texFmt, out HSDRaw.GX.GXTlutFmt tlutFmt)) { hashToImage.Add(hash, TOBJConverter.ImportTOBJFromFile(tf, texFmt, tlutFmt)); } } } // get all tobjs foreach (var j in jobj.BreathFirstList) { if (j.Dobj != null) { foreach (var dobj in j.Dobj.List) { if (dobj.Mobj != null && dobj.Mobj.Textures != null) { foreach (var tobj in dobj.Mobj.Textures.List) { // generate hashes and export textures and formatting var hash = ComputeHash(tobj.GetDecodedImageData()); if (hashToImage.ContainsKey(hash)) { var imgClone = HSDAccessor.DeepClone <HSD_TOBJ>(hashToImage[hash]); tobj.ImageData = imgClone.ImageData; tobj.TlutData = imgClone.TlutData; } } } } } } } }
/// <summary> /// /// </summary> private static void ExtractDataFromResource(HSDRawFile resourceFile, HSDAccessor acc) { foreach (var p in acc.GetType().GetProperties()) { var sym = resourceFile[p.Name]; if (sym != null) { var i = Activator.CreateInstance(p.PropertyType); ((HSDAccessor)i)._s = sym.Data._s; p.SetValue(acc, i); } } }
public void Render(JOBJManager jobjManager, List <SBM_Hurtbox> hurtboxes, HSDAccessor selected, Dictionary <int, int> states = null, int bodyState = -1) { foreach (SBM_Hurtbox v in hurtboxes) { var clr = HurtboxColor; var a = 0.25f; if (selected == v) { clr = SelectedHurtboxColor; a = 0.6f; } if (states != null) { if (states.ContainsKey(v.BoneIndex)) { switch (states[v.BoneIndex]) { case 1: clr = InvulColor; break; case 2: clr = IntanColor; break; } } } if (bodyState == 1) { clr = InvulColor; } if (bodyState == 2) { clr = IntanColor; } var transform = jobjManager.GetWorldTransform(v.BoneIndex); if (!HurtboxToCapsule.ContainsKey(v)) { HurtboxToCapsule.Add(v, new Capsule(new Vector3(v.X1, v.Y1, v.Z1), new Vector3(v.X2, v.Y2, v.Z2), v.Size)); } var cap = HurtboxToCapsule[v]; cap.SetParameters(new Vector3(v.X1, v.Y1, v.Z1), new Vector3(v.X2, v.Y2, v.Z2), v.Size); cap.Draw(transform, new Vector4(clr, a)); } }
public void Render(HSDAccessor a, int windowWidth, int windowHeight) { if (a is KAR_grCollisionNode cn && cn != Node) { Node = cn; Vertices = Node.Vertices; Triangles = Node.Triangles; Joints = Node.Joints; ZoneVertices = Node.ZoneVertices; ZoneTriangles = Node.ZoneTriangles; ZoneJoints = Node.ZoneJoints; } if (Node == null) { return; } GL.PushAttrib(AttribMask.AllAttribBits); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); GL.Begin(PrimitiveType.Triangles); foreach (var t in Triangles) { if ((t.Flags & 0x1) == 0x1) { GL.Color4(1f, 0f, 0f, 0.5f); } if ((t.Flags & 0x2) == 0x2) { GL.Color4(0f, 1f, 0f, 0.5f); } if ((t.Flags & 0x4) == 0x4) { GL.Color4(0f, 0f, 1f, 0.5f); } GL.Vertex3(GXTranslator.toVector3(Vertices[t.V1])); GL.Vertex3(GXTranslator.toVector3(Vertices[t.V2])); GL.Vertex3(GXTranslator.toVector3(Vertices[t.V3])); } GL.End(); GL.PopAttrib(); }
public void SetAccessor(HSDAccessor accessor) { if (accessor == null) { return; } propertyGrid1.SelectedObject = accessor; panel1.SetBytes(accessor._s.GetData()); if (this.accessor != accessor) { offsetBox.Text = "0"; } }
/// <summary> /// Decompiles subaction byte code into script /// </summary> /// <param name="commanddata"></param> /// <returns></returns> public string Decompile(string name, HSDAccessor commanddata) { if (commanddata == null) { return(""); } StringBuilder output = new StringBuilder(); tempStructToName.Clear(); DecompileGroup(output, name, commanddata._s); if (output.ToString() == "") { output.AppendLine("ref: " + structToFunctionName[commanddata._s]); } return(output.ToString()); }
/// <summary> /// Injects a given symbol into a dat /// If symbol already exists it is overwritten /// </summary> /// <param name="f"></param> /// <param name="symbolName"></param> /// <param name="function"></param> public static void InjectSymbolIntoDat(HSDRawFile f, string symbolName, HSDAccessor function) { // generate root var root = new HSDRootNode(); root.Name = symbolName; root.Data = function; // if this symbol already exists in file, replace it foreach (var ro in f.Roots) { if (ro.Name.Equals(root.Name)) { ro.Data = root.Data; } } // if symbol is not in file, then add it if (f.Roots.FindIndex(e => e.Name == root.Name) == -1) { f.Roots.Add(root); } }
/// <summary> /// /// </summary> /// <param name="mobj"></param> /// <returns></returns> public HSD_Material GetMaterialState(HSD_MOBJ mobj) { HSD_Material mat = HSDAccessor.DeepClone <HSD_Material>(mobj.Material); if (Nodes.Count > JOBJIndex && Nodes[JOBJIndex].Nodes.Count > DOBJIndex) { var node = Nodes[JOBJIndex].Nodes[DOBJIndex]; foreach (var t in node.Tracks) { switch ((MatTrackType)t.TrackType) { case MatTrackType.HSD_A_M_ALPHA: mat.Alpha = t.GetValue(node.Frame); break; case MatTrackType.HSD_A_M_AMBIENT_R: mat.AMB_R = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_AMBIENT_G: mat.AMB_G = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_AMBIENT_B: mat.AMB_B = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_DIFFUSE_R: mat.DIF_R = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_DIFFUSE_G: mat.DIF_G = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_DIFFUSE_B: mat.DIF_B = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_SPECULAR_R: mat.SPC_R = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_SPECULAR_G: mat.SPC_G = (byte)(t.GetValue(node.Frame) * 0xFF); break; case MatTrackType.HSD_A_M_SPECULAR_B: mat.SPC_B = (byte)(t.GetValue(node.Frame) * 0xFF); break; } } } return(mat); }
/// <summary> /// /// </summary> /// <param name="stage"></param> /// <param name="icons"></param> public static MEX_mexMapData GenerateMexMap(SBM_MnSelectStageDataTable stage, IEnumerable <MEXStageIconEntry> icons) { MEX_mexMapData mapData = new MEX_mexMapData(); var nameTexAnim = new HSD_TexAnim(); var nameKeys = new List <FOBJKey>(); var iconTexAnim = new HSD_TexAnim(); var iconKeys = new List <FOBJKey>(); HSD_JOBJ root = new HSD_JOBJ() { SX = 1, SY = 1, SZ = 1, Flags = JOBJ_FLAG.CLASSICAL_SCALING }; HSD_AnimJoint animRoot = new HSD_AnimJoint(); // extra { var tobjs = stage.IconLargeMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); var index = iconTexAnim.AddImage(tobjs[0]); iconKeys.Add(new FOBJKey() { Frame = index, Value = index, InterpolationType = GXInterpolationType.HSD_A_OP_CON }); index = iconTexAnim.AddImage(tobjs[1]); iconKeys.Add(new FOBJKey() { Frame = index, Value = index, InterpolationType = GXInterpolationType.HSD_A_OP_CON }); } foreach (var v in icons) { var index = iconTexAnim.AddImage(v.IconTOBJ); if (index == -1) { index = 0; } iconKeys.Add(new FOBJKey() { Frame = index, Value = index, InterpolationType = GXInterpolationType.HSD_A_OP_CON }); index = nameTexAnim.AddImage(v.NameTOBJ); if (index == -1) { index = 0; } nameKeys.Add(new FOBJKey() { Frame = index, Value = index, InterpolationType = GXInterpolationType.HSD_A_OP_CON }); v.Joint.Next = null; v.Joint.Child = null; v.AnimJoint.Next = null; v.AnimJoint.Child = null; root.AddChild(v.Joint); animRoot.AddChild(v.AnimJoint); } iconKeys.Add(new FOBJKey() { Frame = 1600, Value = 0, InterpolationType = GXInterpolationType.HSD_A_OP_CON }); iconTexAnim.GXTexMapID = HSDRaw.GX.GXTexMapID.GX_TEXMAP0; iconTexAnim.AnimationObject = new HSD_AOBJ(); iconTexAnim.AnimationObject.EndFrame = 1600; iconTexAnim.AnimationObject.FObjDesc = new HSD_FOBJDesc(); iconTexAnim.AnimationObject.FObjDesc.SetKeys(iconKeys, (byte)TexTrackType.HSD_A_T_TIMG); iconTexAnim.AnimationObject.FObjDesc.Next = new HSD_FOBJDesc(); iconTexAnim.AnimationObject.FObjDesc.Next.SetKeys(iconKeys, (byte)TexTrackType.HSD_A_T_TCLT); var iconJOBJ = HSDAccessor.DeepClone <HSD_JOBJ>(stage.IconDoubleModel); iconJOBJ.Child = iconJOBJ.Child.Next; var iconAnimJoint = HSDAccessor.DeepClone <HSD_AnimJoint>(stage.IconDoubleAnimJoint); iconAnimJoint.Child = iconAnimJoint.Child.Next; var iconMatAnimJoint = HSDAccessor.DeepClone <HSD_MatAnimJoint>(stage.IconDoubleMatAnimJoint); iconMatAnimJoint.Child = iconMatAnimJoint.Child.Next; iconMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation = iconTexAnim; var iconNameAnim = HSDAccessor.DeepClone <HSD_MatAnimJoint>(stage.StageNameMatAnimJoint); nameTexAnim.AnimationObject = new HSD_AOBJ(); nameTexAnim.AnimationObject.EndFrame = 1600; nameTexAnim.AnimationObject.FObjDesc = new HSD_FOBJDesc(); nameTexAnim.AnimationObject.FObjDesc.SetKeys(nameKeys, (byte)TexTrackType.HSD_A_T_TIMG); iconNameAnim.Child.Child.MaterialAnimation.TextureAnimation = nameTexAnim; mapData.IconModel = iconJOBJ; mapData.IconAnimJoint = iconAnimJoint; mapData.IconMatAnimJoint = iconMatAnimJoint; mapData.PositionModel = root; mapData.PositionAnimJoint = animRoot; mapData.StageNameMaterialAnimation = iconNameAnim; return(mapData); }
/// <summary> /// /// </summary> /// <returns></returns> public static HSDAccessor GenerateFunctionDAT(LinkedELF lelf, LinkFile link, string[] functions, bool debug, bool quiet = false) { // Generate Function DAT var function = new HSDAccessor() { _s = new HSDStruct(0x20) }; // Generate code section HSDStruct debug_symbol_table = null; int debug_symbol_count = 0; Dictionary <SymbolData, long> dataToOffset = new Dictionary <SymbolData, long>(); byte[] codedata; using (MemoryStream code = new MemoryStream()) { // create debug symbol table if (debug) { debug_symbol_table = new HSDStruct((lelf.AllSymbols.Count + 1) * 0xC); } // process all code foreach (var v in lelf.AllSymbols) { // align if (code.Length % 4 != 0) { code.Write(new byte[4 - (code.Length % 4)], 0, 4 - ((int)code.Length % 4)); } int code_start = (int)code.Position; // write code if (v.Data.Length == 0 && link.TryGetSymbolAddress(CppSanatize(v.Symbol), out uint addr)) { dataToOffset.Add(v, addr); } else { dataToOffset.Add(v, code.Length); code.Write(v.Data, 0, v.Data.Length); } int code_end = (int)code.Position; //Console.WriteLine($"{v.SectionName} {v.Symbol} Start: {code_start.ToString("X")} End: {code_end.ToString("X")} "); if (debug && code_start != code_end) { debug_symbol_table.SetInt32(debug_symbol_count * 0xC, code_start); debug_symbol_table.SetInt32(debug_symbol_count * 0xC + 4, code_end); debug_symbol_table.SetString(debug_symbol_count * 0xC + 8, string.IsNullOrEmpty(v.Symbol) ? v.SectionName : v.Symbol, true); debug_symbol_count++; } } codedata = code.ToArray(); // resize debug table if (debug) { debug_symbol_table.Resize(debug_symbol_count * 0xC); } } // generate function table HSDStruct functionTable = new HSDStruct(8); var funcCount = 0; var fl = functions.ToList(); foreach (var v in lelf.SymbolToData) { functionTable.Resize(8 * (funcCount + 1)); functionTable.SetInt32(funcCount * 8, fl.IndexOf(v.Key)); functionTable.SetInt32(funcCount * 8 + 4, (int)dataToOffset[v.Value]); funcCount++; } // set function table function._s.SetReferenceStruct(0x0C, functionTable); function._s.SetInt32(0x10, funcCount); // Generate Relocation Table HSDStruct relocationTable = new HSDStruct(0); var relocCount = 0; foreach (var v in lelf.AllSymbols) { // check data length if (v.Data.Length == 0) { if (link.ContainsSymbol(CppSanatize(v.Symbol))) { continue; } else { throw new Exception($"Error: {v.Symbol} length is {v.Data.Length.ToString("X")}"); } } // print debug info if (!quiet) { Console.WriteLine($"{v.Symbol,-30} {v.SectionName, -50} Offset: {dataToOffset[v].ToString("X8"), -16} Length: {v.Data.Length.ToString("X8")}"); if (v.Relocations.Count > 0) { Console.WriteLine($"\t {"Section:",-50} {"RelocType:",-20} {"FuncOffset:", -16} {"SectionOffset:"}"); } } // process and create relocation table foreach (var reloc in v.Relocations) { if (!quiet) { Console.WriteLine($"\t {reloc.Symbol.SectionName, -50} {reloc.Type, -20} {reloc.Offset.ToString("X8"), -16} {reloc.AddEnd.ToString("X8")}"); } // gather code positions var codeOffset = (int)(dataToOffset[v] + reloc.Offset); var toFunctionOffset = (int)(dataToOffset[reloc.Symbol] + reloc.AddEnd); // currently supported types check switch (reloc.Type) { case RelocType.R_PPC_REL32: case RelocType.R_PPC_REL24: case RelocType.R_PPC_ADDR32: case RelocType.R_PPC_ADDR16_LO: case RelocType.R_PPC_ADDR16_HA: break; case (RelocType)0x6D: break; default: // no exception, but not guarenteed to work Console.WriteLine($"Warning: unsupported reloc type {toFunctionOffset.ToString("X")} " + reloc.Type.ToString("X") + $" in {v.Symbol} to {reloc.Symbol.Symbol} send this to Ploaj or UnclePunch"); break; } bool addEntry = true; // only apply optimization if not external if (!reloc.Symbol.External) { // calculate relative offset var rel = toFunctionOffset - codeOffset; // apply relocation automatically if possible switch (reloc.Type) { case (RelocType)0x6D: case RelocType.R_PPC_REL32: codedata[codeOffset] = (byte)((rel >> 24) & 0xFF); codedata[codeOffset + 1] = (byte)((rel >> 16) & 0xFF); codedata[codeOffset + 2] = (byte)((rel >> 8) & 0xFF); codedata[codeOffset + 3] = (byte)((rel) & 0xFF); addEntry = false; break; case RelocType.R_PPC_REL24: var cur = ((codedata[codeOffset] & 0xFF) << 24) | ((codedata[codeOffset + 1] & 0xFF) << 16) | ((codedata[codeOffset + 2] & 0xFF) << 8) | ((codedata[codeOffset + 3] & 0xFF)); rel = cur | (rel & 0x03FFFFFC); codedata[codeOffset] = (byte)((rel >> 24) & 0xFF); codedata[codeOffset + 1] = (byte)((rel >> 16) & 0xFF); codedata[codeOffset + 2] = (byte)((rel >> 8) & 0xFF); codedata[codeOffset + 3] = (byte)((rel) & 0xFF); addEntry = false; break; } } // add relocation to table if (addEntry) { relocationTable.Resize((relocCount + 1) * 0x08); relocationTable.SetInt32(0x00 + relocCount * 8, codeOffset); relocationTable.SetByte(0x00 + relocCount * 8, (byte)reloc.Type); relocationTable.SetInt32(0x04 + relocCount * 8, toFunctionOffset); relocCount++; } } } function._s.SetReferenceStruct(0x00, new HSDStruct(codedata)); function._s.SetReferenceStruct(0x04, relocationTable); function._s.SetInt32(0x08, relocCount); if (debug_symbol_table != null) { function._s.SetInt32(0x14, codedata.Length); function._s.SetInt32(0x18, debug_symbol_count); function._s.SetReferenceStruct(0x1C, debug_symbol_table); } return(function); }
/// <summary> /// /// </summary> /// <param name="args"></param> /// <returns></returns> public bool DoIt(string[] args) { // Parse Args List <Tuple <int, List <string> > > itemInputs = new List <Tuple <int, List <string> > >(); List <string> inputs = new List <string>(); LinkFile linkFile = new LinkFile(); string output = ""; string symbolName = ""; string datFile = null; string[] fightFuncTable = null; string buildPath = null; bool quiet = true; bool yesOverwrite = false; bool disableWarnings = true; bool clean = false; bool debug = false; int opLevel = 2; List <string> includes = new List <string>(); for (int i = 0; i < args.Length; i++) { if (args[i] == "-i") { for (int j = i + 1; j < args.Length; j++) { if (File.Exists(args[j])) { inputs.Add(Path.GetFullPath(args[j])); } else { break; } } } if (args[i] == "-inc") { for (int j = i + 1; j < args.Length; j++) { if (Directory.Exists(args[j])) { includes.Add(Path.GetFullPath(args[j])); } else { break; } } } if (args[i] == "-item" && i + 2 < args.Length) { List <string> itemRefList = new List <string>(); for (int j = i + 2; j < args.Length; j++) { if (args[j] != "-item" && File.Exists(args[j])) { itemRefList.Add(Path.GetFullPath(args[j])); } else { break; } } itemInputs.Add(new Tuple <int, List <string> >(int.Parse(args[i + 1]), itemRefList)); } if (args[i] == "-op" && i + 1 < args.Length) { opLevel = int.Parse(args[i + 1]); } if (args[i] == "-o" && i + 1 < args.Length) { output = Path.GetFullPath(args[i + 1]); } if (args[i] == "-dat" && i + 1 < args.Length) { datFile = Path.GetFullPath(args[i + 1]); } if (args[i] == "-s" && i + 1 < args.Length) { symbolName = args[i + 1]; } if (args[i] == "-ow") { yesOverwrite = true; } if (args[i] == "-w") { disableWarnings = false; } if (args[i] == "-d") { debug = true; } if (args[i] == "-t" && i + 1 < args.Length) { fightFuncTable = File.ReadAllLines(args[i + 1]); } if (args[i] == "-v") { quiet = false; } if (args[i] == "-b" && i + 1 < args.Length) { buildPath = Path.GetFullPath(args[i + 1]); } if (args[i] == "-l" && i + 1 < args.Length) { linkFile.LoadLinkFile(args[i + 1]); } } // if (string.IsNullOrEmpty(symbolName)) { Console.WriteLine("Error: Symbol Required; please specify a symbol Ex:\"-s ftFunction\""); return(false); } // if output is null set the name to the symbol name if (string.IsNullOrEmpty(datFile) && string.IsNullOrEmpty(output) && !string.IsNullOrEmpty(symbolName)) { output = symbolName + ".dat"; } var symbolPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, symbolName + ".txt"); // if function table not specified attempt to get it from symbol name if (fightFuncTable == null && !string.IsNullOrEmpty(symbolName) && File.Exists(symbolPath)) { fightFuncTable = File.ReadAllLines(symbolPath); } // load link file in mex directory foreach (var f in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory)) { if (Path.GetExtension(f).ToLower().Equals(".link")) { linkFile.LoadLinkFile(f); } } // don't allow both item tables and normal tables if (inputs.Count > 0 && itemInputs.Count > 0) { Console.WriteLine("Only -i or -ii can be used at once, not both"); return(false); } // print instruction and exit if no input if ((inputs.Count == 0 && itemInputs.Count == 0) || args.Length == 0) { Console.WriteLine("No input files were given"); return(false); } // check if symbol name is given if (string.IsNullOrEmpty(symbolName)) { symbolName = "ftFunction"; Console.WriteLine("No symbol name given, defaulting to \"ftFunction\""); } // create output path if one isn't entered if (string.IsNullOrEmpty(output) && string.IsNullOrEmpty(datFile)) { output = Path.Combine(Path.GetDirectoryName(inputs[0]), Path.GetFileName(inputs[0]).Replace(Path.GetExtension(inputs[0]), ".dat")); } // check if output already exists if (File.Exists(output) && !yesOverwrite) { Console.WriteLine(output + " already exists, overwrite? (y/n)"); if (Console.ReadLine().Trim().ToLower() != "y") { return(false); } } // compile functions HSDAccessor function = null; // create file HSDRawFile newfile = null; HSDRawFile injectfile = null; SBM_FighterData ftData = null; // create new file if (!string.IsNullOrEmpty(output)) { newfile = new HSDRawFile(); } // inject existing dat file (or create new one if not found) if (!string.IsNullOrEmpty(datFile)) { injectfile = File.Exists(datFile) ? new HSDRawFile(datFile) : new HSDRawFile(); } // find fighter data if it exists if (injectfile != null && injectfile.Roots.Count > 0 && injectfile.Roots[0].Data is SBM_FighterData ftdata) { ftData = ftdata; } // static param table var param_table = new string[] { "param_ext" }; // single table compile if (itemInputs.Count == 0) { var elfs = CompileElfs(inputs.ToArray(), disableWarnings, clean, opLevel, includes.ToArray(), buildPath, debug, quiet); //foreach (var f in Directory.GetFiles(@"C:\devkitPro\libogc\lib\cube")) //foreach (var f in Directory.GetFiles(@"C:\devkitPro\devkitPPC\powerpc-eabi\lib")) //{ // if (Path.GetExtension(f).Equals(".a")) // elfs.AddRange(FighterFunction.LibArchive.GetElfs(f)); // if (Path.GetExtension(f).Equals(".o")) // elfs.Add(new RelocELF(File.ReadAllBytes(f))); //} //elfs.AddRange(FighterFunction.LibArchive.GetElfs(@"C:\devkitPro\devkitPPC\lib\gcc\powerpc-eabi\10.2.0\libgcc.a")); //elfs.AddRange(FighterFunction.LibArchive.GetElfs(@"C:\Users\ploaj\Desktop\Modlee\libgc\MemCardDemo\libogc.a")); var lelf = GenerateLinkedElf(elfs, fightFuncTable, linkFile, quiet); // check for special attribute symbol if (ftData != null) { foreach (var elf in elfs) { if (elf.SymbolEnumerator.Any(e => e.Symbol.Equals("param_ext"))) { var special_attr = CmdGenerateDatFile.BuildDatFile(elf, param_table); if (special_attr != null && special_attr.Roots.Count > 0 && special_attr["param_ext"] != null) { // Console.WriteLine("Found Param_Ext... adding to fighter data..."); ftData.Attributes2 = special_attr["param_ext"].Data; break; } } } } function = GenerateFunctionData(lelf, linkFile, fightFuncTable, quiet, debug); } else { // item table compile function = new HSDAccessor() { _s = new HSDStruct(4) }; int count = 0; foreach (var f in itemInputs) { count = Math.Max(count, f.Item1 + 1); if (4 + 4 * (f.Item1 + 1) > function._s.Length) { function._s.Resize(4 + 4 * (f.Item1 + 1)); } var elfs = CompileElfs(f.Item2.ToArray(), disableWarnings, clean, opLevel, includes.ToArray(), buildPath, debug, quiet); var lelf = GenerateLinkedElf(elfs, fightFuncTable, linkFile, quiet); // check for special attribute symbol if (ftData != null) { foreach (var elf in elfs) { if (elf.SymbolEnumerator.Any(e => e.Symbol.Equals("param_ext"))) { var special_attr = CmdGenerateDatFile.BuildDatFile(elf, param_table); if (special_attr != null && special_attr.Roots.Count > 0 && special_attr["param_ext"] != null) { // Console.WriteLine("Found Param_Ext... adding to fighter data..."); ftData.Articles.Articles[f.Item1].ParametersExt = special_attr["param_ext"].Data; break; } } } } var relocFunc = GenerateFunctionData(lelf, linkFile, fightFuncTable, quiet, debug); function._s.SetReference(4 + 0x04 * f.Item1, relocFunc); } function._s.SetInt32(0x00, count); } // inject symbol to dat file and save if (function != null) { if (newfile != null) { DatTools.InjectSymbolIntoDat(newfile, symbolName, function); newfile.Save(output); Console.WriteLine("saving " + output + "..."); } if (injectfile != null) { DatTools.InjectSymbolIntoDat(injectfile, symbolName, function); injectfile.Save(datFile); Console.WriteLine("saving " + datFile + "..."); } // We did it boys Console.WriteLine(); Console.WriteLine("Sucessfully Compiled and Converted to DAT!"); return(true); } return(false); }
/// <summary> /// /// </summary> /// <param name="table"></param> /// <param name="icons"></param> /// <returns></returns> private static MEX_mexSelectChr GenerateMexSelectChrSymbol(SBM_SelectChrDataTable table, MEX_CSSIcon[] cssIcons) { // create mexSelectChr struct MEX_mexSelectChr mex = new MEX_mexSelectChr(); // generate icon model var icon_joint = HSDAccessor.DeepClone <HSD_JOBJ>(table.MenuModel.Children[2].Child); icon_joint.TX = 0; icon_joint.TY = 0; icon_joint.TZ = 0; icon_joint.Next = null; var center = RegenerateIcon(icon_joint); // generate material_anim_joint var icon_matanim_joint = HSDAccessor.DeepClone <HSD_MatAnimJoint>(table.MenuMaterialAnimation.Children[2].Child); icon_matanim_joint.Next = null; // general base models HSD_JOBJ position_joint = new HSD_JOBJ(); position_joint.Flags = JOBJ_FLAG.CLASSICAL_SCALING | JOBJ_FLAG.ROOT_XLU; position_joint.SX = 1; position_joint.SY = 1; position_joint.SZ = 1; HSD_AnimJoint anim_joint = new HSD_AnimJoint(); HSD_MatAnimJoint matanim_joint = new HSD_MatAnimJoint(); // create icon data var joints = table.MenuModel.BreathFirstList; var matanims = table.MenuAnimation.BreathFirstList; foreach (var ico in cssIcons) { if (joints[ico.JointID].Dobj == null) { continue; } HSD_JOBJ joint = HSDAccessor.DeepClone <HSD_JOBJ>(icon_joint); joint.Dobj.Pobj.Attributes = icon_joint.Dobj.Pobj.Attributes; joint.Dobj.Next.Pobj.Attributes = icon_joint.Dobj.Pobj.Attributes; joint.Dobj.Next.Mobj.Textures = HSDAccessor.DeepClone <HSD_TOBJ>(joints[ico.JointID].Dobj.Next.Mobj.Textures); var worldPosition = new GXVector3(joints[ico.JointID].TX, joints[ico.JointID].TY, joints[ico.JointID].TZ); // get anim var anim = matanims[ico.JointID].AOBJ; // if it's a clone get parent location if (ico.JointID < 15) { worldPosition = new GXVector3(joints[ico.JointID - 1].TX, joints[ico.JointID - 1].TY, joints[ico.JointID - 1].TZ); anim = matanims[ico.JointID - 1].AOBJ; } // check animation for world position if (anim != null) { foreach (var v in anim.FObjDesc.List) { System.Diagnostics.Debug.WriteLine(v.JointTrackType); if (v.JointTrackType == JointTrackType.HSD_A_J_TRAX) { var keys = v.GetDecodedKeys(); worldPosition.X = keys[keys.Count - 1].Value; } } } joint.TX = worldPosition.X + center.X; joint.TY = worldPosition.Y + center.Y; joint.TZ = worldPosition.Z + center.Z; position_joint.AddChild(joint); anim_joint.AddChild(new HSD_AnimJoint()); matanim_joint.AddChild(HSDAccessor.DeepClone <HSD_MatAnimJoint>(icon_matanim_joint)); } mex.IconModel = position_joint; mex.IconAnimJoint = anim_joint; mex.IconMatAnimJoint = matanim_joint; mex.CSPMatAnim = HSDAccessor.DeepClone <HSD_MatAnim>(table.MenuMaterialAnimation.Children[6].Child.MaterialAnimation); var cspkeys = mex.CSPMatAnim.TextureAnimation.AnimationObject.FObjDesc.GetDecodedKeys(); foreach (var k in cspkeys) { if ((k.Frame % 30) >= 19) { k.Frame++; } } mex.CSPMatAnim.TextureAnimation.AnimationObject.FObjDesc.SetKeys(cspkeys, (byte)TexTrackType.HSD_A_T_TIMG); mex.CSPMatAnim.TextureAnimation.AnimationObject.FObjDesc.Next.SetKeys(cspkeys, (byte)TexTrackType.HSD_A_T_TCLT); mex.CSPStride = 30; return(mex); }
/// <summary> /// /// </summary> /// <param name="stage"></param> /// <returns></returns> public static MEX_mexMapData LoadIconDataFromVanilla(SBM_MnSelectStageDataTable stage) { List <HSD_TOBJ> nameTags = new List <HSD_TOBJ>(); List <HSD_TOBJ> iconTOBJs = new List <HSD_TOBJ>(); HSD_JOBJ root = new HSD_JOBJ() { SX = 1, SY = 1, SZ = 1, Flags = JOBJ_FLAG.CLASSICAL_SCALING }; HSD_AnimJoint animRoot = new HSD_AnimJoint(); var tex0 = stage.IconDoubleMatAnimJoint.Child.Next.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); var tex0_extra = stage.IconDoubleMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); var tex1 = stage.IconLargeMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); var tex2 = stage.IconSpecialMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); var nameTOBJs = stage.StageNameMatAnimJoint.Child.Child.MaterialAnimation.TextureAnimation.ToTOBJs(); var nameTOBJsAnim = stage.StageNameMatAnimJoint.Child.Child.MaterialAnimation.TextureAnimation.AnimationObject.FObjDesc.GetDecodedKeys(); var positionAnimation = new List <HSD_AnimJoint>(); foreach (var c in stage.PositionAnimation.Children) { var pos = new HSD_AnimJoint(); pos.AOBJ = HSDAccessor.DeepClone <HSD_AOBJ>(c.AOBJ); positionAnimation.Add(pos); } var g1 = tex0.Length - 2; var g2 = tex0.Length - 2 + tex1.Length - 2; var g3 = tex0.Length - 2 + tex1.Length - 2 + tex2.Length - 2; for (int i = 0; i < stage.PositionModel.Children.Length; i++) { var childIndex = i; if (unswizzle.ContainsKey(i)) { childIndex = unswizzle[i]; } HSD_TOBJ icon = null; HSD_TOBJ name = null; var keys = positionAnimation[childIndex].AOBJ.FObjDesc.GetDecodedKeys(); var Y = stage.PositionModel.Children[childIndex].TY; var Z = stage.PositionModel.Children[childIndex].TZ; var SX = 1f; var SY = 1f; if (i >= g3) { //RandomIcon name = nameTOBJs[(int)nameTOBJsAnim[nameTOBJsAnim.Count - 1].Value]; } else if (i >= g2) { name = nameTOBJs[(int)nameTOBJsAnim[24 + (i - g2)].Value]; icon = tex2[i - g2 + 2]; SX = 0.8f; SY = 0.8f; } else if (i >= g1) { name = nameTOBJs[(int)nameTOBJsAnim[22 + texunswizzle[i - g1]].Value]; icon = tex1[i - g1 + 2]; SY = 1.1f; } else { icon = tex0[texunswizzle[i] + 2]; name = nameTOBJs[(int)nameTOBJsAnim[texunswizzle[i]].Value * 2]; root.AddChild(new HSD_JOBJ() { TX = keys[keys.Count - 1].Value, TY = Y, TZ = Z, SX = SX, SY = SY, SZ = 1, Flags = JOBJ_FLAG.CLASSICAL_SCALING }); iconTOBJs.Add(icon); nameTags.Add(name); animRoot.AddChild(HSDAccessor.DeepClone <HSD_AnimJoint>(positionAnimation[childIndex])); Y -= 5.6f; Z = 0; icon = tex0_extra[texunswizzle[i] + 2]; name = nameTOBJs[(int)nameTOBJsAnim[texunswizzle[i]].Value * 2 + 1]; } root.AddChild(new HSD_JOBJ() { TX = keys[keys.Count - 1].Value, TY = Y, TZ = Z, SX = SX, SY = SY, SZ = 1, Flags = JOBJ_FLAG.CLASSICAL_SCALING }); iconTOBJs.Add(icon); nameTags.Add(name); animRoot.AddChild(HSDAccessor.DeepClone <HSD_AnimJoint>(positionAnimation[childIndex])); } var extraIcons = stage.IconLargeMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.ToTOBJs(); iconTOBJs.Insert(0, extraIcons[0]); iconTOBJs.Insert(0, extraIcons[1]); iconTOBJs.Add(extraIcons[0]); var iconJOBJ = HSDAccessor.DeepClone <HSD_JOBJ>(stage.IconDoubleModel); iconJOBJ.Child = iconJOBJ.Child.Next; var iconAnimJoint = HSDAccessor.DeepClone <HSD_AnimJoint>(stage.IconDoubleAnimJoint); iconAnimJoint.Child = iconAnimJoint.Child.Next; var iconMatAnimJoint = HSDAccessor.DeepClone <HSD_MatAnimJoint>(stage.IconDoubleMatAnimJoint); iconMatAnimJoint.Child = iconMatAnimJoint.Child.Next; iconMatAnimJoint.Child.MaterialAnimation.Next.TextureAnimation.FromTOBJs(iconTOBJs.ToArray(), true); var mapdata = new MEX_mexMapData(); mapdata.IconModel = iconJOBJ; mapdata.IconAnimJoint = iconAnimJoint; mapdata.IconMatAnimJoint = iconMatAnimJoint; mapdata.PositionModel = root; mapdata.PositionAnimJoint = animRoot; mapdata.StageNameMaterialAnimation = HSDAccessor.DeepClone <HSD_MatAnimJoint>(stage.StageNameMatAnimJoint); mapdata.StageNameMaterialAnimation.Child.Child.MaterialAnimation.TextureAnimation.FromTOBJs(nameTags, true); return(mapdata); }
/// <summary> /// /// </summary> private bool BuildDemoAJFile(string symbol, string ajpath, int actionstart, int actionend) { if (string.IsNullOrEmpty(symbol)) { return(false); } // get actions var actions = new Action[actionend - actionstart + 1]; for (int i = actionstart; i <= actionend; i++) { actions[i - actionstart] = AllActions[i]; } // rebuild aj file var data = AJManager.RebuildAJFile(actions.Select(e => e.Symbol).ToArray(), false); // update animation offset and sizes foreach (var a in actions) { // don't write subroutines if (a.Subroutine) { continue; } // update animation size and offset if (!string.IsNullOrEmpty(a.Symbol)) { var offsize = AJManager.GetOffsetSize(a.Symbol); a.AnimOffset = offsize.Item1; a.AnimSize = offsize.Item2; } } // save action changes to dat file SaveAllActionChanges(); // save aj file HSDRawFile file = new HSDRawFile(); if (File.Exists(ajpath)) { file = new HSDRawFile(ajpath); } var dataAccessor = new HSDAccessor() { _s = new HSDStruct(data) }; if (file[symbol] != null) { file[symbol].Data = dataAccessor; } else { file.Roots.Add(new HSDRootNode() { Name = symbol, Data = dataAccessor }); } file.Save(ajpath); return(true); }
/// <summary> /// /// </summary> private void InstallUI(ZipFile pack, MEXFighterEntry fighter) { Console.WriteLine($"Installing UI data..."); // LOAD Stock Icons var icons = pack.Where(e => Regex.IsMatch(e.FileName, "Icon/ico..\\.png")).ToArray(); HSD_TOBJ[] iconTOBJs = new HSD_TOBJ[icons.Length]; foreach (var c in icons) { var index = int.Parse(Regex.Match(c.FileName, @"\d\d").Value); using (MemoryStream stream = new MemoryStream(GetBytes(c))) using (var bmp = new System.Drawing.Bitmap(stream)) iconTOBJs[index] = TOBJConverter.BitmapToTOBJ(bmp, HSDRaw.GX.GXTexFmt.CI4, HSDRaw.GX.GXTlutFmt.RGB5A3); } // Load Menu Icon // Load Emblem var emblemPack = pack["UI/emblem.obj"]; HSD_TOBJ emblemTexture = null; HSD_JOBJ emblemModel = null; if (emblemPack != null) { EmblemModel model; using (MemoryStream stream = new MemoryStream()) { emblemPack.Extract(stream); stream.Position = 0; model = Converters.EmblemConverter.GenerateEmblemModelFromOBJ(stream); } emblemModel = Converters.EmblemConverter.GenerateEmblemModel(model); emblemTexture = Converters.EmblemConverter.GenerateEmblemIconImage(model); } // Load Misc Name Tags and icons var largeName = pack["UI/result_victory_name.png"]; var smallName = pack["UI/result_name.png"]; HSD_TOBJ largeNameTexture = null; HSD_TOBJ smallNameTexture = null; if (largeName != null) { using (MemoryStream stream = new MemoryStream(GetBytes(largeName))) using (var bmp = new System.Drawing.Bitmap(stream)) largeNameTexture = TOBJConverter.BitmapToTOBJ(bmp, HSDRaw.GX.GXTexFmt.I4, HSDRaw.GX.GXTlutFmt.IA8); } if (smallName != null) { using (MemoryStream stream = new MemoryStream(GetBytes(smallName))) using (var bmp = new System.Drawing.Bitmap(stream)) smallNameTexture = TOBJConverter.BitmapToTOBJ(bmp, HSDRaw.GX.GXTexFmt.I4, HSDRaw.GX.GXTlutFmt.IA8); } // -------------------------------------------------------------------------- var root = Path.GetDirectoryName(MainForm.Instance.FilePath); int stride = editor.FighterControl.FighterEntries.Count - 3; int internalID = editor.FighterControl.FighterEntries.IndexOf(fighter); var externalId = MEXIdConverter.ToExternalID(internalID, editor.FighterControl.FighterEntries.Count); int GroupID = externalId - (externalId > 18 ? 1 : 0); // Inject CSPs and Stock Icons into Character Select //var chrSelPath = Path.Combine(root, "MnSlChr.usd"); //if (File.Exists(chrSelPath)) // InjectCharSelectImages(pack, chrSelPath, iconTOBJs, emblemTexture, fighter, stride, GroupID); // Inject Stock Icons into IfAll, GmRst var ifallPath = Path.Combine(root, "IfAll.usd"); if (File.Exists(ifallPath)) { var datFile = new HSDRawFile(ifallPath); var mark = datFile.Roots.Find(e => e.Name.Equals("Stc_scemdls")).Data as HSDNullPointerArrayAccessor <HSD_JOBJDesc>; for (int i = 0; i < 7; i++) // first 7 { InjectIntoMatTexAnim(mark[0].MaterialAnimations[0].Children[i].MaterialAnimation.TextureAnimation, iconTOBJs, GroupID, stride, MAX_COSTUME_COUNT); } var emblemGroup = datFile.Roots.Find(e => e.Name.Equals("DmgMrk_scene_models")).Data as HSDNullPointerArrayAccessor <HSD_JOBJDesc>; InjectIntoMatTexAnim(emblemGroup[0].MaterialAnimations[0].Child.MaterialAnimation.TextureAnimation, new HSDRaw.Common.HSD_TOBJ[] { emblemTexture }, GroupID, stride, 1, fighter.InsigniaID); editedFiles.Add(new Tuple <HSDRawFile, string, bool>(datFile, ifallPath, true)); } var gmRst = Path.Combine(root, "GmRst.usd"); if (File.Exists(gmRst)) { var datFile = new HSDRawFile(gmRst); var flmsce = datFile.Roots.Find(e => e.Name.Equals("flmsce")).Data as HSD_SOBJ; var pnlsce = datFile.Roots.Find(e => e.Name.Equals("pnlsce")).Data as HSD_SOBJ; // Stock Icons------------------------------------- for (int i = 5; i <= 8; i++) // at 5-8, 2nd mat anim { InjectIntoMatTexAnim( pnlsce.JOBJDescs[0].MaterialAnimations[0].Children[i].MaterialAnimation.Next.TextureAnimation, iconTOBJs, GroupID, stride, MAX_COSTUME_COUNT); } // Emblem Textures-------------------------------------- var matgroup = pnlsce.JOBJDescs[0].MaterialAnimations[0].Children[17]; for (int i = 0; i < 4; i++) { InjectIntoMatTexAnim(matgroup.Children[i].MaterialAnimation.TextureAnimation, new HSDRaw.Common.HSD_TOBJ[] { emblemTexture }, GroupID, stride, 1, fighter.InsigniaID); } // Emblem Model-------------------------------------- var emblemGroup = flmsce.JOBJDescs[0]; if (emblemModel != null) { var modelIndex = emblemGroup.RootJoint.Children[4].Children.Length; fighter.InsigniaID = (byte)modelIndex; var jointClone = HSDAccessor.DeepClone <HSDRaw.Common.Animation.HSD_AnimJoint>(emblemGroup.JointAnimations[0].Children[4].Child); jointClone.Next = null; var matjointClone = HSDAccessor.DeepClone <HSDRaw.Common.Animation.HSD_MatAnimJoint>(emblemGroup.MaterialAnimations[0].Children[4].Child); matjointClone.Next = null; emblemGroup.JointAnimations[0].Children[4].AddChild(jointClone); emblemGroup.MaterialAnimations[0].Children[4].AddChild(matjointClone); emblemGroup.RootJoint.Children[4].AddChild(emblemModel); } // name textures var largenameGroup = pnlsce.JOBJDescs[0].MaterialAnimations[0].Children[0].Children[2].MaterialAnimation.Next.TextureAnimation; InjectIntoMatTexAnim(largenameGroup, new HSD_TOBJ[] { largeNameTexture }, GroupID, stride, 1); var smallnameGroup = pnlsce.JOBJDescs[0].MaterialAnimations[0]; for (int i = 9; i < 13; i++) { InjectIntoMatTexAnim(smallnameGroup.Children[i].Children[1].MaterialAnimation.TextureAnimation, new HSD_TOBJ[] { smallNameTexture }, GroupID, stride, 1); } editedFiles.Add(new Tuple <HSDRawFile, string, bool>(datFile, gmRst, true)); } // Inject Emblem // Generate Emblem Models and Inject // Inject Misc Name Tags and icons }
/// <summary> /// /// </summary> /// <param name="table"></param> /// <param name="icons"></param> /// <returns></returns> public static MEX_mexSelectChr GenerateMEXMapFromVanilla(SBM_SelectChrDataTable table, MEX_CSSIconEntry[] icons) { // generate icon model var icon_joint = HSDAccessor.DeepClone <HSD_JOBJ>(table.MenuModel.Children[2].Child); icon_joint.TX = 0; icon_joint.TY = 0; icon_joint.TZ = 0; icon_joint.Next = null; var center = RegenerateIcon(icon_joint); var icon_matanim_joint = HSDAccessor.DeepClone <HSD_MatAnimJoint>(table.MenuMaterialAnimation.Children[2].Child); icon_matanim_joint.Next = null; // generate mat anim node var joints = table.MenuModel.BreathFirstList; HSD_TOBJ[] tobjs = new HSD_TOBJ[icons.Max(e => e.FighterExternalID) + 1]; JOBJManager m = new JOBJManager(); m.SetJOBJ(table.MenuModel); m.SetAnimJoint(table.MenuAnimation); m.Frame = 600; m.UpdateNoRender(); var csps = table.MenuMaterialAnimation.Children[6].Child.MaterialAnimation.TextureAnimation.ToTOBJs(); var cspKeys = table.MenuMaterialAnimation.Children[6].Child.MaterialAnimation.TextureAnimation.AnimationObject.FObjDesc.GetDecodedKeys(); var stride = 30; foreach (var ico in icons) { if (joints[ico.icon.JointID].Dobj == null) { continue; } HSD_JOBJ pos = HSDAccessor.DeepClone <HSD_JOBJ>(icon_joint); pos.Dobj.Pobj.Attributes = icon_joint.Dobj.Pobj.Attributes; pos.Dobj.Next.Pobj.Attributes = icon_joint.Dobj.Pobj.Attributes; pos.Dobj.Next.Mobj.Textures = HSDAccessor.DeepClone <HSD_TOBJ>(joints[ico.icon.JointID].Dobj.Next.Mobj.Textures); var worldPosition = Vector3.TransformPosition(Vector3.Zero, m.GetWorldTransform(ico.icon.JointID)); pos.TX = worldPosition.X + center.X; pos.TY = worldPosition.Y + center.Y; pos.TZ = worldPosition.Z + center.Z; ico.Joint = pos; ico.Animation = new MexMenuAnimation(); ico.MatAnimJoint = HSDAccessor.DeepClone <HSD_MatAnimJoint>(icon_matanim_joint); // load csps // find key at stride var icocsps = new List <HSD_TOBJ>(); int cspIndex = 0; while (true) { var key = ico.FighterExternalID + (cspIndex * stride); if (ico.FighterExternalID > 0x13) { key = ico.FighterExternalID + (cspIndex * stride) - 1; } var k = cspKeys.Find(e => e.Frame == key); if (k == null) { break; } icocsps.Add(csps[(int)k.Value]); cspIndex++; } ico.CSPs = icocsps.Select(e => new TOBJProxy() { TOBJ = e }).ToArray(); } m.RefreshRendering = true; MEX_mexSelectChr mex = new MEX_mexSelectChr(); SetMexNode(mex, icons); return(mex); }