/// <summary> /// /// </summary> /// <param name="filePath"></param> public void Open(string name, Stream s) { Name = name; using (BinaryReaderExt r = new BinaryReaderExt(s)) { r.BigEndian = true; var headerLength = r.ReadInt32() + 0x10; var dataOff = r.ReadInt32(); var soundCount = r.ReadInt32(); StartIndex = r.ReadInt32(); Sounds = new DSP[soundCount]; for (int i = 0; i < soundCount; i++) { var sound = new DSP(); var ChannelCount = r.ReadInt32(); sound.Frequency = r.ReadInt32(); sound.Channels.Clear(); for (int j = 0; j < ChannelCount; j++) { var channel = new DSPChannel(); channel.LoopFlag = r.ReadInt16(); channel.Format = r.ReadInt16(); var LoopStartOffset = r.ReadInt32(); var LoopEndOffset = r.ReadInt32(); var CurrentAddress = r.ReadInt32(); for (int k = 0; k < 0x10; k++) { channel.COEF[k] = r.ReadInt16(); } channel.Gain = r.ReadInt16(); channel.InitialPredictorScale = r.ReadInt16(); channel.InitialSampleHistory1 = r.ReadInt16(); channel.InitialSampleHistory2 = r.ReadInt16(); channel.LoopPredictorScale = r.ReadInt16(); channel.LoopSampleHistory1 = r.ReadInt16(); channel.LoopSampleHistory2 = r.ReadInt16(); r.ReadInt16(); // padding channel.NibbleCount = LoopEndOffset - CurrentAddress; channel.LoopStart = LoopStartOffset - CurrentAddress; sound.Channels.Add(channel); var DataOffset = headerLength + (int)Math.Ceiling(CurrentAddress / 2d) - 1; channel.Data = r.GetSection((uint)DataOffset, (int)Math.Ceiling(channel.NibbleCount / 2d) + 1); } Sounds[i] = sound; } } }
/// <summary> /// Melee's sound format /// </summary> /// <param name="filePath"></param> private void OpenSSM(string filePath) { using (BinaryReaderExt r = new BinaryReaderExt(new FileStream(filePath, FileMode.Open))) { r.BigEndian = true; var headerLength = r.ReadInt32() + 0x10; var dataOff = r.ReadInt32(); var soundCount = r.ReadInt32(); Unknown = r.ReadInt32(); for (int i = 0; i < soundCount; i++) { var sound = new DSP(); sound.Index = i; var ChannelCount = r.ReadInt32(); sound.Frequency = r.ReadInt32(); sound.Channels.Clear(); for (int j = 0; j < ChannelCount; j++) { var channel = new DSPChannel(); channel.LoopFlag = r.ReadInt16(); channel.Format = r.ReadInt16(); var LoopStartOffset = r.ReadInt32(); var LoopEndOffset = r.ReadInt32(); var CurrentAddress = r.ReadInt32(); for (int k = 0; k < 0x10; k++) { channel.COEF[k] = r.ReadInt16(); } channel.Gain = r.ReadInt16(); channel.InitialPredictorScale = r.ReadInt16(); channel.InitialSampleHistory1 = r.ReadInt16(); channel.InitialSampleHistory2 = r.ReadInt16(); channel.LoopPredictorScale = r.ReadInt16(); channel.LoopSampleHistory1 = r.ReadInt16(); channel.LoopSampleHistory2 = r.ReadInt16(); r.ReadInt16(); // padding channel.NibbleCount = LoopEndOffset - CurrentAddress; channel.LoopStart = LoopStartOffset - CurrentAddress; sound.Channels.Add(channel); var DataOffset = headerLength + (int)Math.Ceiling(CurrentAddress / 2d) - 1; channel.Data = r.GetSection((uint)DataOffset, (int)Math.Ceiling(channel.NibbleCount / 2d) + 1); } Sounds.Add(sound); } } }
/// <summary> /// /// </summary> public void LoadFromStream(Stream s) { using (BinaryReaderExt r = new BinaryReaderExt(s)) { if (s.Length < 0x14) { return; } if (new string(r.ReadChars(4)) != "SPKG") { return; } GroupFlags = r.ReadUInt32(); Flags = r.ReadUInt32(); var ssmSize = r.ReadInt32(); ScriptBank = new SEMBank(); ScriptBank.Scripts = new SEMBankScript[r.ReadInt32()]; for (int i = 0; i < ScriptBank.Scripts.Length; i++) { ScriptBank.Scripts[i] = new SEMBankScript(); ScriptBank.Scripts[i].Decompile(r.GetSection(r.ReadUInt32(), r.ReadInt32())); } var name = r.ReadString(r.ReadByte()); if (ssmSize == 0) { SoundBank = null; } else { SoundBank = new SSM(); using (MemoryStream ssmStream = new MemoryStream(r.ReadBytes(ssmSize))) SoundBank.Open(name, ssmStream); } } }
/// <summary> /// /// </summary> private void LoadFighterAnimationFiles() { var aFile = MainForm.Instance.FilePath.Replace(".dat", "AJ.dat"); var cFile = MainForm.Instance.FilePath.Replace(".dat", "Nr.dat"); // try to automatically locate files bool openFiles = true; if (File.Exists(aFile) && File.Exists(cFile)) { var r = MessageBox.Show($"Load {System.IO.Path.GetFileName(aFile)} and {System.IO.Path.GetFileName(cFile)}", "Open Files", MessageBoxButtons.YesNoCancel); if (r == DialogResult.Cancel) { return; } if (r == DialogResult.Yes) { openFiles = false; } } // find files to open if (openFiles) { cFile = FileIO.OpenFile("Fighter Costume (Pl**Nr.dat)|*.dat"); if (cFile == null) { return; } aFile = FileIO.OpenFile("Fighter Animation (Pl**AJ.dat)|*.dat"); if (aFile == null) { return; } } // load model var modelFile = new HSDRawFile(cFile); if (modelFile.Roots.Count > 0 && modelFile.Roots[0].Data is HSD_JOBJ jobj) { JointManager.SetJOBJ(jobj); // load material animation if it exists if (modelFile.Roots.Count > 1 && modelFile.Roots[1].Data is HSD_MatAnimJoint matanim) { JointManager.SetMatAnimJoint(matanim); JointManager.EnableMaterialFrame = true; } } else { return; } // set model scale JointManager.ModelScale = ModelScale; // clear hidden dobjs JointManager.DOBJManager.HiddenDOBJs.Clear(); // don't render bones by default JointManager._settings.RenderBones = false; // reset model visibility ResetModelVis(); // load the model parts LoadModelParts(); // populate animation dictionary AJFilePath = aFile; SymbolToAnimation.Clear(); using (BinaryReaderExt r = new BinaryReaderExt(new FileStream(aFile, FileMode.Open))) foreach (var a in AllActions) { if (a.Symbol != null && !SymbolToAnimation.ContainsKey(a.Symbol)) { SymbolToAnimation.Add(a.Symbol, r.GetSection((uint)a.AnimOffset, a.AnimSize)); } } // enable preview box previewBox.Visible = true; // reselect action if (actionList.SelectedItem is Action action) { SelectAction(action); } }
public static void Deconstruct(string plFilePath, string ajFilePath, string outputFolder) { var plfile = new HSDRawFile(plFilePath); var path = Path.GetDirectoryName(plFilePath) + "\\" + outputFolder + "\\"; Directory.CreateDirectory(path); var data = plfile.Roots[0].Data as SBM_FighterData; if (data == null) { return; } var ajfile = new HSDRawFile(ajFilePath); foreach (var prop in data.GetType().GetProperties()) { var val = prop.GetValue(data) as HSDAccessor; if (val == null) { continue; } if (prop.PropertyType == typeof(SBM_CommonFighterAttributes)) { var attr = prop.GetValue(data) as SBM_CommonFighterAttributes; using (StreamWriter w = new StreamWriter(new FileStream(path + prop.Name + ".ini", FileMode.Create))) { foreach (var v in attr.GetType().GetProperties()) { if (v.Name.Equals("TrimmedSize")) { continue; } w.WriteLine($"{v.Name}={v.GetValue(attr)}"); } } } else if (prop.Name.Equals("SubActionTable")) { ScriptFile f = new ScriptFile(); f.Actions = new ActionGroup[val._s.Length / 0x18]; var ActionDecompiler = new ActionDecompiler(); HashSet <string> ExportedAnimations = new HashSet <string>(); for (int i = 0; i < f.Actions.Length; i++) { SBM_FighterAction subaction = new HSDRaw.Melee.Pl.SBM_FighterAction(); subaction._s = val._s.GetEmbeddedStruct(0x18 * i, 0x18); if (!ExportedAnimations.Contains(subaction.Name) && subaction.Name != null && subaction.Name != "") { ExportedAnimations.Add(subaction.Name); if (ajFilePath != null && File.Exists(ajFilePath)) { using (BinaryReaderExt r = new BinaryReaderExt(new FileStream(ajFilePath, FileMode.Open))) { var animdata = r.GetSection((uint)subaction.AnimationOffset, subaction.AnimationSize); File.WriteAllBytes(path + "Animations\\" + subaction.Name + ".dat", animdata); } } } ActionGroup g = new ActionGroup(); g.animation_name = subaction.Name; g.flags = (int)subaction.Flags; g.script = ActionDecompiler.Decompile("Func_" + i.ToString("X3"), subaction.SubAction); //g.script = g.off = subaction.AnimationOffset; g.size = subaction.AnimationSize; f.Actions[i] = g; Console.WriteLine(i + " " + subaction.Name + " " + subaction._s.GetReference <HSDAccessor>(0x0C)._s.References.Count); } XmlSerializer writer = new XmlSerializer(typeof(ScriptFile)); using (var w = new XmlTextWriter(new FileStream(path + prop.Name + ".txt", FileMode.Create), Encoding.UTF8)) { w.Formatting = Formatting.Indented; writer.Serialize(w, f); } } else { HSDRootNode root = new HSDRootNode(); root.Name = prop.Name; root.Data = val; HSDRawFile file = new HSDRawFile(); file.Roots.Add(root); file.Save(path + prop.Name + ".dat"); Console.WriteLine(prop.Name + " " + val._s.GetSubStructs().Count); } } }
/// <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); } }
private static void FromBRSTM(this DSP dsp, string filePath) { using (FileStream s = new FileStream(filePath, FileMode.Open)) using (BinaryReaderExt r = new BinaryReaderExt(s)) { if (new string(r.ReadChars(4)) != "RSTM") { throw new NotSupportedException("File is not a valid BRSTM file"); } r.BigEndian = true; r.BigEndian = r.ReadUInt16() == 0xFEFF; r.Skip(2); // 01 00 version r.Skip(4); // filesize r.Skip(2); // 00 40 - header length r.Skip(2); // 00 02 - header version var headOffset = r.ReadUInt32(); var headSize = r.ReadUInt32(); var adpcOffset = r.ReadUInt32(); var adpcSize = r.ReadUInt32(); var dataOffset = r.ReadUInt32(); var dataSize = r.ReadUInt32(); // can skip adpc section when reading because it just contains sample history // parse head section // -------------------------------------------------------------- r.Position = headOffset; if (new string(r.ReadChars(4)) != "HEAD") { throw new NotSupportedException("BRSTM does not have a valid HEAD"); } r.Skip(4); // section size r.Skip(4); // 01 00 00 00 marker var chunk1Offset = r.ReadUInt32() + 8 + headOffset; r.Skip(4); // 01 00 00 00 marker var chunk2Offset = r.ReadUInt32() + 8 + headOffset; r.Skip(4); // 01 00 00 00 marker var chunk3Offset = r.ReadUInt32() + 8 + headOffset; // -------------------------------------------------------------- r.Seek(chunk1Offset); var codec = (BRSTM_CODEC)r.ReadByte(); var loopFlag = r.ReadByte(); var channelCount = r.ReadByte(); r.Skip(1); // padding if (codec != BRSTM_CODEC.ADPCM_4bit) { throw new NotSupportedException("only 4bit ADPCM files currently supported"); } var sampleRate = r.ReadUInt16(); r.Skip(2); // padding dsp.Frequency = sampleRate; var loopStart = r.ReadUInt32(); var totalSamples = r.ReadUInt32(); var dataPointer = r.ReadUInt32(); // DATA offset int blockCount = r.ReadInt32(); var blockSize = r.ReadUInt32(); var samplesPerBlock = r.ReadInt32(); var sizeOfFinalBlock = r.ReadUInt32(); var samplesInFinalBlock = r.ReadInt32(); var sizeOfFinalBlockWithPadding = r.ReadUInt32(); var samplesPerEntry = r.ReadInt32(); var bytesPerEntry = r.ReadInt32(); // -------------------------------------------------------------- r.Seek(chunk2Offset); var numOfTracks = r.ReadByte(); var trackDescType = r.ReadByte(); r.Skip(2); // padding for (uint i = 0; i < numOfTracks; i++) { r.Seek(chunk1Offset + 4 + 8 * i); r.Skip(1); // 01 padding var descType = r.ReadByte(); r.Skip(2); // padding var descOffset = r.ReadUInt32() + 8 + headOffset; r.Seek(descOffset); switch (descType) { case 0: { int channelsInTrack = r.ReadByte(); int leftChannelID = r.ReadByte(); int rightChannelID = r.ReadByte(); r.Skip(1); // padding } break; case 1: { var volume = r.ReadByte(); var panning = r.ReadByte(); r.Skip(2); // padding r.Skip(4); // padding int channelsInTrack = r.ReadByte(); int leftChannelID = r.ReadByte(); int rightChannelID = r.ReadByte(); r.Skip(1); // 01 padding } break; } } // -------------------------------------------------------------- r.Seek(chunk3Offset); var channelCountAgain = r.ReadByte(); r.Skip(3); for (uint i = 0; i < channelCountAgain; i++) { r.Seek(chunk3Offset + 4 + 8 * i); r.Skip(4); // 01000000 marker var offset = r.ReadUInt32() + headOffset + 8; r.Seek(offset); // channel information var channel = new DSPChannel(); dsp.Channels.Add(channel); channel.LoopFlag = loopFlag; channel.LoopStart = (int)loopStart; r.Skip(4); // 01000000 marker r.Skip(4); // offset to coefficients (they follow directly after) for (int k = 0; k < 0x10; k++) { channel.COEF[k] = r.ReadInt16(); } channel.Gain = r.ReadInt16(); channel.InitialPredictorScale = r.ReadInt16(); channel.InitialSampleHistory1 = r.ReadInt16(); channel.InitialSampleHistory2 = r.ReadInt16(); channel.LoopPredictorScale = r.ReadInt16(); channel.LoopSampleHistory1 = r.ReadInt16(); channel.LoopSampleHistory2 = r.ReadInt16(); r.Skip(2); // padding // get channel data using (MemoryStream channelStream = new MemoryStream()) { for (uint j = 0; j < blockCount; j++) { var bs = blockSize; var actualBlockSize = blockSize; if (j == blockCount - 1) { bs = sizeOfFinalBlockWithPadding; actualBlockSize = sizeOfFinalBlock; } channelStream.Write(r.GetSection(dataPointer + j * (blockSize * channelCountAgain) + bs * i, (int)actualBlockSize), 0, (int)actualBlockSize); } channel.Data = channelStream.ToArray(); channel.NibbleCount = channel.Data.Length * 2; } } dsp.LoopPoint = TimeSpan.FromMilliseconds(loopStart / (double)sampleRate * 1000).ToString(); } }
/// <summary> /// /// </summary> /// <param name="offset"></param> /// <param name="size"></param> /// <returns></returns> public byte[] GetSection(uint doloffset, int size) { return(r.GetSection(doloffset, size)); }
/// <summary> /// /// </summary> /// <param name="elfFile"></param> public RelocELF(byte[] elfFile) { using (MemoryStream mstream = new MemoryStream(elfFile)) using (BinaryReaderExt r = new BinaryReaderExt(mstream)) { // Parse Header if (!(r.ReadByte() == 0x7F && r.ReadByte() == 0x45 && r.ReadByte() == 0x4C && r.ReadByte() == 0x46)) { throw new InvalidDataException("Not a valid ELF file"); } byte bitType = r.ReadByte(); // 1 - 32, 2 - 64 if (bitType != 1) { throw new NotSupportedException("Only 32 bit ELF files are currently supported"); } r.BigEndian = r.ReadByte() == 2; // I only care about the sections r.Seek(0x20); var sectionOffset = r.ReadUInt32(); r.Seek(0x2E); var sectionHeaderSize = r.ReadUInt16(); var numOfSections = r.ReadInt16(); var StringSectionIndex = r.ReadUInt16(); List <SectionData> DataSections = new List <SectionData>(); // Parse Sections var Sections = new ELFSection[numOfSections]; for (uint i = 0; i < numOfSections; i++) { r.Seek(sectionOffset + sectionHeaderSize * i); Sections[i] = new ELFSection() { sh_name = r.ReadUInt32(), sh_type = (SectionType)r.ReadInt32(), sh_flags = r.ReadUInt32(), sh_addr = r.ReadUInt32(), sh_offset = r.ReadUInt32(), sh_size = r.ReadUInt32(), sh_link = r.ReadUInt32(), sh_info = r.ReadUInt32(), sh_addralign = r.ReadUInt32(), sh_entsize = r.ReadUInt32() }; DataSections.Add(new SectionData()); } // Parse Symbols var symbolSection = Array.Find(Sections, e => r.ReadString((int)(Sections[StringSectionIndex].sh_offset + e.sh_name), -1) == ".symtab"); var Symbols = new ELFSymbol[symbolSection.sh_size / 0x10]; for (uint i = 0; i < Symbols.Length; i++) { r.Seek(symbolSection.sh_offset + 0x10 * i); Symbols[i] = new ELFSymbol() { st_name = r.ReadUInt32(), st_value = r.ReadUInt32(), st_size = r.ReadUInt32(), st_info = r.ReadByte(), st_other = r.ReadByte(), st_shndx = r.ReadInt16() }; SymbolSections.Add(new SymbolData()); } // Grab Relocation Data for (int i = 0; i < Sections.Length; i++) { var section = Sections[i]; var data = DataSections[i]; data.Name = r.ReadString((int)(Sections[StringSectionIndex].sh_offset + Sections[i].sh_name), -1); data.Data = r.GetSection(section.sh_offset, (int)section.sh_size); if (section.sh_type == SectionType.SHT_RELA || section.sh_type == SectionType.SHT_REL) { var relocs = ParseRelocationSection(r, section); foreach (var v in relocs) { DataSections[(int)section.sh_info].Relocations.Add(new RelocData() { Offset = v.r_offset, AddEnd = v.r_addend, Symbol = SymbolSections[(int)v.R_SYM], Type = (RelocType)v.R_TYP, SymbolIndex = v.R_SYM }); } } } var symbolStringSection = Sections[symbolSection.sh_link]; // rip out symbol data for (int i = 0; i < Symbols.Length; i++) { var sym = Symbols[i]; var section = sym.st_shndx >= 0 ? DataSections[sym.st_shndx] : null; byte[] symbolData = new byte[sym.st_size]; List <RelocData> relocations = new List <RelocData>(); if (section != null) { SymbolSections[i].SectionName = section.Name; if (Sections[sym.st_shndx].sh_type == SectionType.SHT_NOBITS) { symbolData = new byte[section.Data.Length]; #if DEBUG // Console.WriteLine($"{section.Name} {(Sections[sym.st_shndx].sh_offset + sym.st_value).ToString("X")} {sym.st_size} {sym.st_value} {symbolData.Length} {Sections[sym.st_shndx].sh_type}"); #endif } else { // If size of section is 0, get all data? if (sym.st_size == 0) { symbolData = section.Data; } //else //if ((sym.st_value & 0x80000000) != 0) //{ // Array.Copy(section.Data, sym.st_value - 0x80000000 - Sections[sym.st_shndx].sh_offset, symbolData, 0, sym.st_size); // Debug.WriteLine($"LONG CALL {section.Relocations.Count} Off: {(sym.st_value - 0x80000000).ToString("X")} SectionOff: {Sections[sym.st_shndx].sh_offset.ToString("X")} {sym.st_value.ToString("X")} Size: {sym.st_size.ToString("X")} Total Size: {section.Data.Length.ToString("X")}"); //} else { Array.Copy(section.Data, sym.st_value, symbolData, 0, sym.st_size); } // TODO: when to get relocations? relocations = section.Relocations.Where( e => e.Offset >= sym.st_value && (e.Offset < sym.st_value + symbolData.Length) ).ToList(); // make relative foreach (var rel in relocations) { rel.Offset -= sym.st_value; } } // if the offset is 0 the function is usually in another file SymbolSections[i].External = Sections[sym.st_shndx].sh_offset == 0; #if DEBUG //Console.WriteLine(section.Name + " " + r.ReadString((int)(symbolStringSection.sh_offset + sym.st_name), -1) // + " " + Sections[sym.st_shndx].sh_info + " " + Sections[sym.st_shndx].sh_addr + " " + relocations.Count); //Debug.WriteLine($"{section.Name} {r.ReadString((int)(symbolStringSection.sh_offset + sym.st_name), -1)} {(Sections[sym.st_shndx].sh_offset + + sym.st_value).ToString("X")} {sym.st_size.ToString("X")}"); if (section.Name == ".debug_line") { //r.Seek(Sections[sym.st_shndx].sh_offset + +sym.st_value); //ParseDebugLine(r); } #endif } SymbolSections[i].Symbol = r.ReadString((int)(symbolStringSection.sh_offset + sym.st_name), -1); SymbolSections[i].Data = symbolData; SymbolSections[i].Relocations = relocations; } } }