public static void Reconstruct(string filepath, string animPath, string directory, string rootName) { var path = Path.GetDirectoryName(directory); HSDRawFile file = new HSDRawFile(); HSDRootNode root = new HSDRootNode(); root.Name = rootName; var ftData = new SBM_FighterData(); root.Data = ftData; file.Roots.Add(root); var prop = root.Data.GetType().GetProperties().ToList(); foreach (var f in Directory.GetFiles(directory)) { if (f.EndsWith(".dat")) { HSDRawFile chunk = new HSDRawFile(f); Console.WriteLine(f + " " + chunk.Roots.Count); var property = prop.Find(e => e.Name == chunk.Roots[0].Name); if (property != null) { var newt = Activator.CreateInstance(property.PropertyType); { var dset = newt as HSDAccessor; if (dset != null) { dset._s = chunk.Roots[0].Data._s; } } property.SetValue(root.Data, newt); } } else if (f.EndsWith(".ini")) { SBM_CommonFighterAttributes attr = new SBM_CommonFighterAttributes(); using (StreamReader r = new StreamReader(new FileStream(f, FileMode.Open))) { foreach (var v in attr.GetType().GetProperties()) { if (v.Name.Equals("TrimmedSize")) { continue; } var line = r.ReadLine().Split('='); if (line.Length < 2 || line[0] != v.Name) { throw new InvalidDataException("Invalid Attribute " + string.Join("=", line)); } if (v.PropertyType == typeof(int)) { v.SetValue(attr, int.Parse(line[1].Trim())); } if (v.PropertyType == typeof(float)) { v.SetValue(attr, float.Parse(line[1].Trim())); } } } ftData.Attributes = attr; } else if (f.EndsWith(".txt")) { XmlSerializer writer = new XmlSerializer(typeof(ScriptFile)); var script = (ScriptFile)writer.Deserialize(new FileStream(f, FileMode.Open)); Dictionary <string, Tuple <int, int> > animationToOffset = new Dictionary <string, Tuple <int, int> >(); List <SBM_FighterAction> SubActions = new List <SBM_FighterAction>(); Dictionary <SBM_FighterAction, string> subActionToScript = new Dictionary <SBM_FighterAction, string>(); Dictionary <string, HSDStruct> stringToStruct = new Dictionary <string, HSDStruct>(); using (BinaryWriter w = new BinaryWriter(new FileStream(animPath, FileMode.Create))) { foreach (var s in script.Actions) { SBM_FighterAction subaction = new SBM_FighterAction(); subaction.Flags = (uint)s.flags; if (s.animation_name != null) { if (!stringToStruct.ContainsKey(s.animation_name)) { var namestruct = new HSDStruct(); byte[] data = new byte[s.animation_name.Length + 1]; var bytes = UTF8Encoding.UTF8.GetBytes(s.animation_name); for (int i = 0; i < s.animation_name.Length; i++) { data[i] = bytes[i]; } namestruct.SetData(data); stringToStruct.Add(s.animation_name, namestruct); } subaction._s.SetReferenceStruct(0, stringToStruct[s.animation_name]); //subaction.Name = s.animation_name; if (!animationToOffset.ContainsKey(s.animation_name)) { if (File.Exists(path + "\\Animations\\" + s.animation_name + ".dat")) { var data = File.ReadAllBytes(path + "\\Animations\\" + s.animation_name + ".dat"); animationToOffset.Add(s.animation_name, new Tuple <int, int>((int)w.BaseStream.Position, data.Length)); w.Write(data); if (w.BaseStream.Length % 0x20 != 0) { var padd = new byte[0x20 - (w.BaseStream.Position % 0x20)]; for (int i = 0; i < padd.Length; i++) { padd[i] = 0xFF; } w.Write(padd); } } else { throw new FileNotFoundException("Could not find animation " + path + "\\Animations\\" + s.animation_name + ".dat"); } } subaction.AnimationOffset = animationToOffset[s.animation_name].Item1; subaction.AnimationSize = animationToOffset[s.animation_name].Item2; } if (s.script != null) { ActionCompiler.Compile(s.script); subActionToScript.Add(subaction, s.script); } SubActions.Add(subaction); } } ftData.FighterActionTable = new SBM_FighterActionTable(); ActionCompiler.LinkStructs(); ftData.FighterActionTable.Commands = SubActions.ToArray(); Console.WriteLine("recompiled count " + ftData.FighterActionTable._s.GetSubStructs().Count); } } file.Save(filepath); }
/// <summary> /// Generates and saves a SEM file /// </summary> public static void SaveSEMFile(string path, List <SEMEntry> Entries, MEX_Data mexData) { if (mexData != null) { mexData.SSMTable.SSM_SSMFiles.Array = new HSD_String[0]; mexData.SSMTable.SSM_BufferSizes.Array = new MEX_SSMSizeAndFlags[0]; mexData.SSMTable.SSM_LookupTable.Array = new MEX_SSMLookup[0]; mexData.SSMTable.SSM_BufferSizes.Set(Entries.Count, new MEX_SSMSizeAndFlags()); // blank entry at end mexData.SSMTable.SSM_LookupTable.Set(Entries.Count, new MEX_SSMLookup()); // blank entry at beginning // generate runtime struct mexData.MetaData.NumOfSSMs = Entries.Count; HSDStruct rtTable = new HSDStruct(6 * 4); rtTable.SetReferenceStruct(0x00, new HSDStruct(GeneratePaddedBuffer(0x180, 0x01))); rtTable.SetReferenceStruct(0x04, new HSDStruct(GeneratePaddedBuffer(Entries.Count * 4, 0x02))); rtTable.SetReferenceStruct(0x08, new HSDStruct(GeneratePaddedBuffer(Entries.Count * 4, 0x03))); rtTable.SetReferenceStruct(0x0C, new HSDStruct(GeneratePaddedBuffer(Entries.Count * 4, 0x04))); rtTable.SetReferenceStruct(0x10, new HSDStruct(GeneratePaddedBuffer(Entries.Count * 4, 0x05))); rtTable.SetReferenceStruct(0x14, new HSDStruct(GeneratePaddedBuffer(Entries.Count * 4, 0x06))); mexData.SSMTable._s.SetReferenceStruct(0x0C, rtTable); } var soundOffset = 0; using (BinaryWriterExt w = new BinaryWriterExt(new FileStream(path, FileMode.Create))) { w.BigEndian = true; w.Write(0); w.Write(0); w.Write(Entries.Count); int index = 0; foreach (var e in Entries) { w.Write(index); index += e.Scripts.Length; } w.Write(index); var offset = w.BaseStream.Position + 4 * index + 4; var dataindex = 0; foreach (var e in Entries) { foreach (var v in e.Scripts) { w.Write((int)(offset + dataindex)); dataindex += v.CommandData.Length; } } w.Write(0); int entryIndex = -1; foreach (var e in Entries) { entryIndex++; // fix sound offset ids if (e.SoundBank != null) { // set start offset in sem and save e.SoundBank.StartIndex = soundOffset; int bufSize; e.SoundBank.Save(Path.GetDirectoryName(path) + "\\" + e.SoundBank.Name, out bufSize); if (mexData != null) { mexData.SSMTable.SSM_SSMFiles.Set(entryIndex, new HSD_String() { Value = e.SoundBank.Name }); mexData.SSMTable.SSM_BufferSizes.Set(entryIndex, new MEX_SSMSizeAndFlags() { Flag = e.SoundBank.Flag, SSMFileSize = bufSize }); var lu = new MEX_SSMLookup(); lu._s.SetInt32(0x00, e.SoundBank.GroupFlags); mexData.SSMTable.SSM_LookupTable.Set(entryIndex, lu); } // add sound offset foreach (var v in e.Scripts) { v.SoundCommandIndex += soundOffset; } foreach (var v in e.Scripts) { w.Write(v.CommandData); } //return to normal foreach (var v in e.Scripts) { v.SoundCommandIndex -= soundOffset; } soundOffset += e.SoundBank.Sounds.Length; } else { foreach (var v in e.Scripts) { w.Write(v.CommandData); } } } } }
public int GetStructLocation(HSDStruct str) { return(RawHSDFile.GetOffsetFromStruct(str)); }
/// <summary> /// /// </summary> /// <param name="output"></param> /// <param name="name"></param> /// <param name="datas"></param> /// <param name="structToFunctionName"></param> private void DecompileGroup(StringBuilder output, string name, HSDStruct datas) { if (structToFunctionName.ContainsKey(datas)) { return; } structToFunctionName.Add(datas, name); output.AppendLine(name); output.AppendLine("{"); using (BinaryReaderExt r = new BinaryReaderExt(new MemoryStream(datas.GetData()))) { byte flag = (byte)(r.ReadByte() >> 2); var cmd = ActionCommon.GetMeleeCMDAction(flag); while (flag != 0) { r.BaseStream.Position -= 1; var size = cmd.ByteSize; var command = r.GetSection(r.Position, size); r.Skip((uint)size); if (flag == 5 || flag == 7) //goto { var re = datas.GetReference <HSDAccessor>((int)r.BaseStream.Position - 4); if (re != null) { if (!tempStructToName.ContainsKey(re._s)) { if (structToFunctionName.ContainsKey(re._s)) { tempStructToName.Add(re._s, structToFunctionName[re._s]); } else { tempStructToName.Add(re._s, name + "_" + ((int)r.BaseStream.Position - 4).ToString("X4")); } } var funcname = tempStructToName[re._s]; output.AppendLine("\t" + (flag == 5 ? "Goto" : "Subroutine") + "(" + funcname + ");"); } } else { output.AppendLine("\t" + DecompileCommand(command)); } if (r.BaseStream.Position >= r.BaseStream.Length) { break; } flag = (byte)(r.ReadByte() >> 2); cmd = ActionCommon.GetMeleeCMDAction(flag); } } output.AppendLine("}"); foreach (var re in datas.References) { DecompileGroup(output, name + "_" + re.Key.ToString("X4"), re.Value); } }
/// <summary> /// /// </summary> /// <param name="data"></param> private List <Command> GetCommands(HSDStruct str, SubactionGroup subGroup, Dictionary <HSDStruct, List <Command> > structToComman = null) { if (subGroup != SubactionGroup.Fighter) { return(new List <Command>()); } if (structToComman == null) { structToComman = new Dictionary <HSDStruct, List <Command> >(); } if (structToComman.ContainsKey(str)) { return(structToComman[str]); } var data = str.GetData(); var Commands = new List <Command>(); structToComman.Add(str, Commands); for (int i = 0; i < data.Length;) { var sa = SubactionManager.GetSubaction(data[i], subGroup); var cmd = new Command(); foreach (var r in str.References) { if (r.Key >= i && r.Key < i + sa.ByteSize) { if (cmd.Reference != null) { throw new NotSupportedException("Multiple References not supported"); } else { if (r.Value != str) // prevent self reference { cmd.Reference = r.Value; cmd.ReferenceCommands = GetCommands(cmd.Reference, subGroup, structToComman); } } } } var sub = new byte[sa.ByteSize]; if (i + sub.Length > data.Length) { break; } for (int j = 0; j < sub.Length; j++) { sub[j] = data[i + j]; } cmd.Parameters = sa.GetParameters(sub); cmd.Action = sa; Commands.Add(cmd); i += sa.ByteSize; if (sa.Code == 0) { break; } } return(Commands); }
/// <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> /// Compiles script into subaction byte code /// </summary> public static void Compile(string script) { if (scriptToBinary.ContainsKey(script) || scriptToReference.ContainsKey(script)) { return; } HSDStruct main = null; var stream = Regex.Replace(script, @"\s+", string.Empty); if (script.StartsWith("ref:")) { var name = stream.Split(':')[1]; scriptToReference.Add(script, name); return; } var functions = Regex.Matches(stream, @"([^\{])*\{([^\}]*)\}"); foreach (Match g in functions) { var name = Regex.Match(g.Value, @".+?(?={)").Value; var code = Regex.Match(g.Value, @"(?<=\{).+?(?=\})").Value.Split(';'); if (nameToFunction.ContainsKey(name)) { continue; } HSDStruct s = new HSDStruct(); nameToFunction.Add(name, s); structToOffsetToFunction.Add(s, new Dictionary <int, string>()); if (main == null) { main = s; } List <byte> output = new List <byte>(); bool returned = false; foreach (var c in code) { var cname = Regex.Match(c, @".+?(?=\()").Value; var cparameters = Regex.Match(c, @"(?<=\().+?(?=\))").Value.Split(','); if (cname == "Return") { returned = true; } byte flag = ActionCommon.GetMeleeCMDAction(cname).Command; if (flag == 0x5 || flag == 0x7) //goto and subroutine { structToOffsetToFunction[s].Add(output.Count + 4, cparameters[0]); output.AddRange(new byte[] { (byte)(flag << 2), 0, 0, 0, 0, 0, 0, 0 }); } else { output.AddRange(CompileCommand(cname, cparameters)); } // padd if (output.Count % 4 != 0) { output.AddRange(new byte[4 - (output.Count % 4)]); } } if (true) { output.Add(0); if (output.Count % 4 != 0) { output.AddRange(new byte[4 - (output.Count % 4)]); } } s.SetData(output.ToArray()); } if (main == null) { main = new HSDStruct(4); } scriptToBinary.Add(script, main); }