/// <summary> /// Unpack the specified File, returns unpacked dir. /// </summary> /// <param name="sourceFileName">Source file path.</param> /// <param name="savePath">Save path.</param> /// <param name="decodeAudio">If set to <c>true</c> decode audio.</param> /// <param name="overwriteSongXml">If set to <c>true</c> overwrite existing song (EOF) xml with SNG data</param> /// <param name="predefinedPlatform">Predefined source platform.</param> public static string Unpack(string sourceFileName, string savePath, bool decodeAudio = false, bool overwriteSongXml = false, Platform predefinedPlatform = null) { Platform platform = sourceFileName.GetPlatform(); if (predefinedPlatform != null && predefinedPlatform.platform != GamePlatform.None && predefinedPlatform.version != GameVersion.None) platform = predefinedPlatform; var fnameWithoutExt = Path.GetFileNameWithoutExtension(sourceFileName); if (platform.platform == GamePlatform.PS3) fnameWithoutExt = fnameWithoutExt.Substring(0, fnameWithoutExt.LastIndexOf(".")); var unpackedDir = Path.Combine(savePath, String.Format("{0}_{1}", fnameWithoutExt, platform.platform)); if (Directory.Exists(unpackedDir)) DirectoryExtension.SafeDelete(unpackedDir); var useCryptography = platform.version == GameVersion.RS2012; // Cryptography way is used only for PC in Rocksmith 1 switch (platform.platform) { case GamePlatform.Pc: case GamePlatform.Mac: if (platform.version == GameVersion.RS2014) using (var inputStream = File.OpenRead(sourceFileName)) ExtractPSARC(sourceFileName, savePath, inputStream, platform); else { using (var inputFileStream = File.OpenRead(sourceFileName)) using (var inputStream = new MemoryStream()) { if (useCryptography) RijndaelEncryptor.DecryptFile(inputFileStream, inputStream, RijndaelEncryptor.DLCKey); else inputFileStream.CopyTo(inputStream); ExtractPSARC(sourceFileName, savePath, inputStream, platform); } } break; case GamePlatform.XBox360: UnpackXBox360Package(sourceFileName, savePath, platform); break; case GamePlatform.PS3: UnpackPS3Package(sourceFileName, savePath, platform); break; case GamePlatform.None: throw new InvalidOperationException("Platform not found :("); } // DECODE AUDIO if (decodeAudio) { GlobalExtension.ShowProgress("Decoding Audio ...", 50); var audioFiles = Directory.EnumerateFiles(unpackedDir, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".ogg") || s.EndsWith(".wem")); foreach (var file in audioFiles) { var outputAudioFileName = Path.Combine(Path.GetDirectoryName(file), String.Format("{0}_fixed{1}", Path.GetFileNameWithoutExtension(file), ".ogg")); OggFile.Revorb(file, outputAudioFileName, Path.GetDirectoryName(Application.ExecutablePath), Path.GetExtension(file).GetWwiseVersion()); } //GlobalExtension.HideProgress(); } // for debugging //overwriteSongXml = false; // Extract XML from SNG and check it against the EOF XML (correct bass tuning from older toolkit/EOF xml files) if (platform.version == GameVersion.RS2014) { var sngFiles = Directory.EnumerateFiles(unpackedDir, "*.sng", SearchOption.AllDirectories).ToList(); var step = Math.Round(1.0 / (sngFiles.Count + 2) * 100, 3); double progress = 0; GlobalExtension.ShowProgress("Extracting XML from SNG ..."); foreach (var sngFile in sngFiles) { var xmlEofFile = Path.Combine(Path.GetDirectoryName(sngFile), String.Format("{0}.xml", Path.GetFileNameWithoutExtension(sngFile))); xmlEofFile = xmlEofFile.Replace(String.Format("bin{0}{1}", Path.DirectorySeparatorChar, platform.GetPathName()[1].ToLower()), "arr"); var xmlSngFile = xmlEofFile.Replace(".xml", ".sng.xml"); var arrType = ArrangementType.Guitar; if (Path.GetFileName(xmlSngFile).ToLower().Contains("vocal")) arrType = ArrangementType.Vocal; Attributes2014 att = null; if (arrType != ArrangementType.Vocal) { var jsonFiles = Directory.EnumerateFiles(unpackedDir, String.Format("{0}.json", Path.GetFileNameWithoutExtension(sngFile)), SearchOption.AllDirectories).FirstOrDefault(); if (!String.IsNullOrEmpty(jsonFiles) && jsonFiles.Any()) att = Manifest2014<Attributes2014>.LoadFromFile(jsonFiles).Entries.ToArray()[0].Value.ToArray()[0].Value; } var sngContent = Sng2014File.LoadFromFile(sngFile, platform); using (var outputStream = new FileStream(xmlSngFile, FileMode.Create, FileAccess.ReadWrite)) { dynamic xmlContent = null; if (arrType == ArrangementType.Vocal) xmlContent = new Vocals(sngContent); else xmlContent = new Song2014(sngContent, att); xmlContent.Serialize(outputStream); } // correct old toolkit/EOF xml (tuning) issues ... sync with SNG data if (File.Exists(xmlEofFile) && !overwriteSongXml && arrType != ArrangementType.Vocal) { var eofSong = Song2014.LoadFromFile(xmlEofFile); var sngSong = Song2014.LoadFromFile(xmlSngFile); if (eofSong.Tuning != sngSong.Tuning) { eofSong.Tuning = sngSong.Tuning; var xmlComments = Song2014.ReadXmlComments(xmlEofFile); using (var stream = File.Open(xmlEofFile, FileMode.Create)) eofSong.Serialize(stream, true); Song2014.WriteXmlComments(xmlEofFile, xmlComments, customComment: "Synced with SNG file"); } File.Delete(xmlSngFile); } else { if (arrType != ArrangementType.Vocal) Song2014.WriteXmlComments(xmlSngFile, customComment: "Generated from SNG file"); File.Copy(xmlSngFile, xmlEofFile, true); File.Delete(xmlSngFile); } progress += step; GlobalExtension.UpdateProgress.Value = (int)progress; } //GlobalExtension.HideProgress(); } return unpackedDir; }
// COMPLETE private static void WriteRocksmithVocalsFile(Vocals vocals, string outputFile, EndianBitConverter bitConverter) { // WRITE THE .SNG FILE using (FileStream fs = new FileStream(outputFile, FileMode.Create)) using (EndianBinaryWriter w = new EndianBinaryWriter(bitConverter, fs)) { // file header WriteRocksmithSngHeader(w, ArrangementType.Vocal); // unused filler w.Write(new byte[16]); // vocal count if (vocals.Count != vocals.Vocal.Length) throw new InvalidDataException("XML vocals header count does not match number of vocal items."); w.Write(vocals.Count); // vocals for (int i = 0; i < vocals.Vocal.Length; i++) { // vocal time w.Write(vocals.Vocal[i].Time); // vocal note w.Write(vocals.Vocal[i].Note); // vocal length w.Write(vocals.Vocal[i].Length); // vocal lyric string lyric = vocals.Vocal[i].Lyric; if (lyric.Length > 32) throw new InvalidDataException(string.Format("Vocal lyric '{0}' at position {1} exceeded the maximum width of 32 bytes.", lyric, i)); foreach (char c in lyric) { w.Write(Convert.ToByte(c)); } // padding after name w.Write(new byte[32 - lyric.Length]); } // unused w.Write(new byte[254]); } }
/// <summary> /// Unpack the specified File, returns unpacked dir. /// </summary> /// <param name="sourceFileName">Source file path.</param> /// <param name="savePath">Save path.</param> /// <param name="decodeAudio">If set to <c>true</c> decode audio.</param> /// <param name="extractSongXml">If set to <c>true</c> extract song xml from sng.</param> /// <param name="overwriteSongXml">If set to <c>true</c> overwrite existing song xml with produced.</param> /// <param name="predefinedPlatform">Predefined source platform.</param> public static string Unpack(string sourceFileName, string savePath, bool decodeAudio = false, bool extractSongXml = false, bool overwriteSongXml = true, Platform predefinedPlatform = null) { Platform platform = sourceFileName.GetPlatform(); if (predefinedPlatform != null && predefinedPlatform.platform != GamePlatform.None && predefinedPlatform.version != GameVersion.None) platform = predefinedPlatform; var fnameWithoutExt = Path.GetFileNameWithoutExtension(sourceFileName); if (platform.platform == GamePlatform.PS3) fnameWithoutExt = fnameWithoutExt.Substring(0, fnameWithoutExt.LastIndexOf(".")); var unpackedDir = Path.Combine(savePath, String.Format("{0}_{1}", fnameWithoutExt, platform.platform)); if (Directory.Exists(unpackedDir)) DirectoryExtension.SafeDelete(unpackedDir); var useCryptography = platform.version == GameVersion.RS2012; // Cryptography way is used only for PC in Rocksmith 1 switch (platform.platform) { case GamePlatform.Pc: case GamePlatform.Mac: if (platform.version == GameVersion.RS2014) using (var inputStream = File.OpenRead(sourceFileName)) ExtractPSARC(sourceFileName, savePath, inputStream, platform); else { using (var inputFileStream = File.OpenRead(sourceFileName)) using (var inputStream = new MemoryStream()) { if (useCryptography) RijndaelEncryptor.DecryptFile(inputFileStream, inputStream, RijndaelEncryptor.DLCKey); else inputFileStream.CopyTo(inputStream); ExtractPSARC(sourceFileName, savePath, inputStream, platform); } } break; case GamePlatform.XBox360: UnpackXBox360Package(sourceFileName, savePath, platform); break; case GamePlatform.PS3: UnpackPS3Package(sourceFileName, savePath, platform); break; case GamePlatform.None: throw new InvalidOperationException("Platform not found :("); } // DECODE AUDIO if (decodeAudio) { var audioFiles = Directory.EnumerateFiles(unpackedDir, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".ogg") || s.EndsWith(".wem")); foreach (var file in audioFiles) { var outputAudioFileName = Path.Combine(Path.GetDirectoryName(file), String.Format("{0}_fixed{1}", Path.GetFileNameWithoutExtension(file), ".ogg")); OggFile.Revorb(file, outputAudioFileName, Path.GetDirectoryName(Application.ExecutablePath), Path.GetExtension(file).GetWwiseVersion()); } } // EXTRACT XML FROM SNG if (extractSongXml && platform.version == GameVersion.RS2014) { var sngFiles = Directory.EnumerateFiles(unpackedDir, "*.sng", SearchOption.AllDirectories); foreach (var sngFile in sngFiles) { var xmlOutput = Path.Combine(Path.GetDirectoryName(sngFile), String.Format("{0}.xml", Path.GetFileNameWithoutExtension(sngFile))); xmlOutput = xmlOutput.Replace(String.Format("bin{0}{1}", Path.DirectorySeparatorChar, platform.GetPathName()[1].ToLower()), "arr"); if (File.Exists(xmlOutput) && !overwriteSongXml) continue; var arrType = ArrangementType.Guitar; if (Path.GetFileName(xmlOutput).ToLower().Contains("vocal")) arrType = ArrangementType.Vocal; Attributes2014 att = null; if (arrType != ArrangementType.Vocal) { var jsonFiles = Directory.EnumerateFiles(unpackedDir, String.Format("{0}.json", Path.GetFileNameWithoutExtension(sngFile)), SearchOption.AllDirectories).FirstOrDefault(); if (jsonFiles.Any() && !String.IsNullOrEmpty(jsonFiles)) att = Manifest2014<Attributes2014>.LoadFromFile(jsonFiles).Entries.ToArray()[0].Value.ToArray()[0].Value; } var sngContent = Sng2014File.LoadFromFile(sngFile, platform); using (var outputStream = new FileStream(xmlOutput, FileMode.Create, FileAccess.ReadWrite)) { dynamic xmlContent = null; if (arrType == ArrangementType.Vocal) xmlContent = new Vocals(sngContent); else xmlContent = new Song2014(sngContent, att); xmlContent.Serialize(outputStream); } } } return unpackedDir; }
private void convertSngXmlButton_Click(object sender, EventArgs e) { if (String.IsNullOrEmpty(ConverterSngXmlFile)) { MessageBox.Show(String.Format("File not found: {0}: ", ConverterSngXmlFile), MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Error); sngXmlTB.Focus(); return; } if (sng2xmlRadio.Checked) { if (String.IsNullOrEmpty(ConverterManifestFile)) MessageBox.Show("No manifest file was entered. The song xml file will be generated without song informations like song title, album, artist, tone names, etc.", MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Error); Attributes2014 att = null; if (ConverterArrangementType != ArrangementType.Vocal && !String.IsNullOrEmpty(ConverterManifestFile)) att = Manifest2014<Attributes2014>.LoadFromFile(ConverterManifestFile).Entries.ToArray()[0].Value.ToArray()[0].Value; var sng = Sng2014File.LoadFromFile(ConverterSngXmlFile, ConverterPlatform); var outputFile = Path.Combine(Path.GetDirectoryName(ConverterSngXmlFile), String.Format("{0}.xml", Path.GetFileNameWithoutExtension(ConverterSngXmlFile))); using (FileStream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) { dynamic xml = null; if (ConverterArrangementType == ArrangementType.Vocal) xml = new Vocals(sng); else xml = new Song2014(sng, att ?? null); xml.Serialize(outputStream); MessageBox.Show(String.Format("XML file was generated! {0}It was saved on same location of sng file specified.", Environment.NewLine), MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Information); } } else if (xml2sngRadio.Checked) { var outputFile = Path.Combine(Path.GetDirectoryName(ConverterSngXmlFile), String.Format("{0}.sng", Path.GetFileNameWithoutExtension(ConverterSngXmlFile))); using (FileStream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) { Sng2014File sng = Sng2014File.ConvertXML(ConverterSngXmlFile, ConverterArrangementType); sng.WriteSng(outputStream, ConverterPlatform); } MessageBox.Show(String.Format("SNG file was generated! {0}It was saved on same location of xml file specified.", Environment.NewLine), MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Information); } }
private static void parseVocals(Vocals xml, Sng2014File sng) { sng.Vocals = new VocalSection(); sng.Vocals.Count = xml.Vocal.Length; sng.Vocals.Vocals = new Vocal[sng.Vocals.Count]; for (int i = 0; i < sng.Vocals.Count; i++) { var vcl = xml.Vocal[i]; var v = new Vocal(); v.Time = vcl.Time; v.Note = vcl.Note; v.Length = vcl.Length; readString(vcl.Lyric, v.Lyric); sng.Vocals.Vocals[i] = v; } }
static int Main(string[] args) { var arguments = DefaultArguments(); var options = GetOptions(arguments); try { options.Parse(args); if (arguments.ShowHelp) { options.WriteOptionDescriptions(Console.Out); return 0; } if (!arguments.Pack && !arguments.Unpack && !arguments.Sng2Xml && !arguments.Xml2Sng) { ShowHelpfulError("Must especify a primary command as 'pack', 'unpack', 'sng2xml' or 'xml2sng'."); return 1; } if (arguments.Input == null && arguments.Input.Length <= 0) { ShowHelpfulError("Must specify at least one input file."); return 1; } if (arguments.Sng2Xml && arguments.Manifest == null && arguments.Manifest.Length <= 0) { Console.WriteLine("No manifest file was entered. The song xml file will be generated without song informations like song title, album, artist, tone names, etc."); } var srcFiles = new List<string>(); foreach (var name in arguments.Input) { if(name.IsDirectory()) srcFiles.AddRange(Directory.EnumerateFiles(Path.GetFullPath(name), "*.sng", SearchOption.AllDirectories)); if(File.Exists(name)) srcFiles.Add(name); } var errorCount = 0; var indexCount = 0; foreach (string inputFile in srcFiles) { if (!File.Exists(inputFile)) { Console.WriteLine(String.Format("File '{0}' doesn't exists.", inputFile)); continue; } if (arguments.Unpack || arguments.Sng2Xml) { if (Path.GetExtension(inputFile) != ".sng") { Console.WriteLine(String.Format("File '{0}' is not support. \nOnly *.sng are supported on this command.", inputFile)); continue; } } if (arguments.Pack || arguments.Unpack) { var outputFile = Path.Combine(Path.GetDirectoryName(inputFile), String.Format("{0}_{1}.sng", Path.GetFileNameWithoutExtension(inputFile), (arguments.Unpack) ? "decrypted" : "encrypted")); using (FileStream inputStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read)) using (FileStream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) { if (arguments.Pack) Sng2014File.PackSng(inputStream, outputStream, new Platform(arguments.Platform, GameVersion.RS2014)); else if (arguments.Unpack) Sng2014File.UnpackSng(inputStream, outputStream, new Platform(arguments.Platform, GameVersion.RS2014)); } } else if (arguments.Sng2Xml) { Attributes2014 att = null; if (arguments.ArrangementType != ArrangementType.Vocal && arguments.Manifest != null && arguments.Manifest.Length > indexCount) att = Manifest2014<Attributes2014>.LoadFromFile(arguments.Manifest[indexCount]).Entries.ToArray()[0].Value.ToArray()[0].Value; var sng = Sng2014File.LoadFromFile(inputFile, new Platform(arguments.Platform, GameVersion.RS2014)); var outputFile = Path.Combine(Path.GetDirectoryName(inputFile), String.Format("{0}.xml", Path.GetFileNameWithoutExtension(inputFile))); using (FileStream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) { dynamic xml = null; if (arguments.ArrangementType == ArrangementType.Vocal) xml = new Vocals(sng); else xml = new Song2014(sng, att ?? null); xml.Serialize(outputStream); } } else if (arguments.Xml2Sng) { var outputFile = Path.Combine(Path.GetDirectoryName(inputFile), String.Format("{0}.sng", Path.GetFileNameWithoutExtension(inputFile))); using (FileStream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) { Sng2014File sng = Sng2014File.ConvertXML(inputFile, arguments.ArrangementType); sng.WriteSng(outputStream, new Platform(arguments.Platform, GameVersion.RS2014)); } } } if (errorCount == 0) Console.WriteLine("Process sucessfully completed!"); else if (errorCount > 0 && errorCount < srcFiles.Count) Console.WriteLine("Process completed with errors!"); else Console.WriteLine("An error ocurred!"); } catch (OptionException ex) { ShowHelpfulError(ex.Message); return 1; } return 0; }