/// <summary> /// Fixes missing and updates showlights to current standards /// </summary> private static void UpdateShowlights(string songDirectory, Platform targetPlatform) { bool hasShowlights = true; // TODO: provide some pb feedback for long process var info = DLCPackageData.LoadFromFolder(songDirectory, targetPlatform); var showlightsArr = info.Arrangements.Where(x => x.ArrangementType == ArrangementType.ShowLight).FirstOrDefault(); var showlightFilePath = showlightsArr.SongXml.File; if (String.IsNullOrEmpty(showlightFilePath)) { var xmlFilePath = info.Arrangements[0].SongXml.File; var xmlName = Path.GetFileNameWithoutExtension(xmlFilePath); showlightFilePath = Path.Combine(Path.GetDirectoryName(xmlFilePath), xmlName.Split('_')[0] + "_showlights.xml"); hasShowlights = false; } // Generate Showlights var showlight = new Showlights(); showlight.CreateShowlights(info); // need at least two showlight elements to be valid if (showlight.ShowlightList.Count > 1) { var showlightStream = new MemoryStream(); showlight.Serialize(showlightStream); using (FileStream file = new FileStream(showlightFilePath, FileMode.Create, FileAccess.Write)) showlightStream.WriteTo(file); // write xml comments Song2014.WriteXmlComments(showlightFilePath); } else { // insufficient showlight changes may crash game throw new InvalidOperationException("Detected insufficient showlight changes: " + showlight.ShowlightList.Count); } if (!hasShowlights) { UpdateAggegrateGraph(songDirectory, targetPlatform, info); } }
// TODO: apply before generate, like metronome arrangements does // only for RS2014 public static bool ApplyBassFix(Arrangement arr) { if (arr.TuningPitch.Equals(220.0)) { // MessageBox.Show("This song is already at 220Hz pitch (bass fixed applied already?)", MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } var songFile = arr.SongXml.File; var comments = Song2014.ReadXmlComments(songFile); Song2014 songXml = Song2014.LoadFromFile(songFile); // Force 220Hz arr.TuningPitch = 220.0; songXml.CentOffset = "-1200.0"; // Octave up for each string Int16[] strings = arr.TuningStrings.ToArray(); for (int s = 0; s < strings.Length; s++) { if (strings[s] < 0) { strings[s] += 12; } } //Detect tuning var tuning = TuningDefinitionRepository.Instance.Detect(new TuningStrings(strings), GameVersion.RS2014, true); arr.Tuning = tuning.UIName = tuning.Name = String.Format("{0} Fixed", tuning.Name);// bastartd bass hack, huh? arr.TuningStrings = songXml.Tuning = tuning.Tuning; TuningDefinitionRepository.Instance.Save(true); File.Delete(songFile); using (var stream = File.OpenWrite(songFile)) { songXml.Serialize(stream, true); } Song2014.WriteXmlComments(songFile, comments, false); return(true); }
private int ApplyPackageDD(string srcPath, int phraseLen, bool removeSus, string rampPath, string cfgPath, out string consoleOutput, bool overWrite = false, bool keepLog = false) { int result = 0; // Ends normally with no error DLCPackageData packageData; consoleOutput = String.Empty; try { using (var psarcOld = new PsarcPackager()) packageData = psarcOld.ReadPackage(srcPath); } catch (Exception ex) { consoleOutput = "Error Reading : " + srcPath + Environment.NewLine + ex.Message; return(-1); // Read Error } // Update arrangement song info foreach (Arrangement arr in packageData.Arrangements) { if (chkGenArrIds.Checked) { // generate new AggregateGraph arr.SongFile = new RocksmithToolkitLib.DLCPackage.AggregateGraph.SongFile() { File = "" }; // generate new Arrangement IDs arr.Id = IdGenerator.Guid(); arr.MasterId = RandomGenerator.NextInt(); } // skip vocal and showlight arrangements if (arr.ArrangementType == ArrangementType.Vocal || arr.ArrangementType == ArrangementType.ShowLight) { continue; } // validate (QC) RS2014 packageData if (packageData.GameVersion == GameVersion.RS2014) { // validate existing SongInfo var songXml = Song2014.LoadFromFile(arr.SongXml.File); songXml.ArtistName = packageData.SongInfo.Artist.GetValidAtaSpaceName(); songXml.Title = packageData.SongInfo.SongDisplayName.GetValidAtaSpaceName(); songXml.AlbumName = packageData.SongInfo.Album.GetValidAtaSpaceName(); songXml.ArtistNameSort = packageData.SongInfo.ArtistSort.GetValidSortableName(); songXml.SongNameSort = packageData.SongInfo.SongDisplayNameSort.GetValidSortableName(); songXml.AlbumNameSort = packageData.SongInfo.AlbumSort.GetValidSortableName(); songXml.AverageTempo = Convert.ToSingle(packageData.SongInfo.AverageTempo.ToString().GetValidTempo()); songXml.AlbumYear = packageData.SongInfo.SongYear.ToString().GetValidYear(); // update packageData with validated SongInfo packageData.SongInfo.Artist = songXml.ArtistName; packageData.SongInfo.SongDisplayName = songXml.Title; packageData.SongInfo.Album = songXml.AlbumName; packageData.SongInfo.ArtistSort = songXml.ArtistNameSort; packageData.SongInfo.SongDisplayNameSort = songXml.SongNameSort; packageData.SongInfo.AlbumSort = songXml.AlbumNameSort; packageData.SongInfo.AverageTempo = (int)songXml.AverageTempo; packageData.SongInfo.SongYear = Convert.ToInt32(songXml.AlbumYear); // write updated xml arrangement using (var stream = File.Open(arr.SongXml.File, FileMode.Create)) songXml.Serialize(stream, false); // restore arrangment comments Song2014.WriteXmlComments(arr.SongXml.File, arr.XmlComments); } // apply DD to xml arrangments... 0 = Ends normally with no error result = DDCreator.ApplyDD(arr.SongXml.File, phraseLen, removeSus, rampPath, cfgPath, out consoleOutput, true, keepLog); if (result == 0) // Ends normally with no error { /* DO NOTHING */ } else if (result == 1) // Ends with system error { consoleOutput = "DDC System Error: " + Environment.NewLine + "Arrangment file: " + Path.GetFileName(arr.SongXml.File) + Environment.NewLine + "CDLC file: " + srcPath; return(result); } else if (result == 2) // Ends with application error { consoleOutput = "DDC Application Error: " + Environment.NewLine + "Arrangment file: " + Path.GetFileName(arr.SongXml.File) + Environment.NewLine + "CDLC file: " + srcPath; return(result); } if (keepLog) { var unpackedDir = Path.GetDirectoryName(Path.GetDirectoryName(arr.SongXml.File)); var logFiles = Directory.EnumerateFiles(unpackedDir, "*.log", SearchOption.AllDirectories); var clogDir = Path.Combine(Path.GetDirectoryName(srcPath), "DDC_Log"); var plogDir = Path.Combine(clogDir, Path.GetFileNameWithoutExtension(srcPath).StripPlatformEndName().Replace("_DD", "").Replace("_NDD", "")); if (!Directory.Exists(clogDir)) { Directory.CreateDirectory(clogDir); } IOExtension.DeleteDirectory(plogDir); Directory.CreateDirectory(plogDir); foreach (var logFile in logFiles) { File.Copy(logFile, Path.Combine(plogDir, Path.GetFileName(logFile))); } } // put arrangment comments in correct order Song2014.WriteXmlComments(arr.SongXml.File); } if (chkGenArrIds.Checked) { // add comment to ToolkitInfo to identify CDLC var arrIdComment = packageData.ToolkitInfo.PackageComment; if (String.IsNullOrEmpty(arrIdComment)) { arrIdComment = TKI_ARRID; } else if (!arrIdComment.Contains(TKI_ARRID)) { arrIdComment = arrIdComment + " " + TKI_ARRID; } packageData.ToolkitInfo.PackageComment = arrIdComment; } // add comment to ToolkitInfo to identify CDLC var remasterComment = packageData.ToolkitInfo.PackageComment; if (String.IsNullOrEmpty(remasterComment)) { remasterComment = TKI_REMASTER; } else if (!remasterComment.Contains(TKI_REMASTER)) { remasterComment = remasterComment + " " + TKI_REMASTER; } packageData.ToolkitInfo.PackageComment = remasterComment; // add default package version if missing if (String.IsNullOrEmpty(packageData.ToolkitInfo.PackageVersion)) { packageData.ToolkitInfo.PackageVersion = "1"; } else { packageData.ToolkitInfo.PackageVersion = packageData.ToolkitInfo.PackageVersion.GetValidVersion(); } // validate packageData (important) packageData.Name = packageData.Name.GetValidKey(); // DLC Key var destPath = srcPath; if (!overWrite) { destPath = GenerateDdcFilePath(srcPath); } try { // regenerates the archive with DDC changes and repairs using (var psarcNew = new PsarcPackager(true)) psarcNew.WritePackage(destPath, packageData, srcPath); } catch (Exception ex) { consoleOutput = "Error Writing: " + srcPath + Environment.NewLine + ex.Message; result = -2; // Write Error } return(result); }
private void btnFixLowBassTuning_Click(object sender, EventArgs e) { string[] srcPaths; bool alreadyFixed; bool hasBass; // GET PATH using (var ofd = new OpenFileDialog()) { ofd.Title = "Select the CDLC(s) which to apply Bass Tuning Fix"; ofd.Filter = "All Files (*.*)|*.*|Rocksmith 2014 PC|*_p.psarc|Rocksmith 2014 Mac|*_m.psarc|Rocksmith 2014 Xbox|*_xbox|Rocksmith 2014 PS3|*.edat"; ofd.Multiselect = true; ofd.FileName = destPath; if (ofd.ShowDialog() != DialogResult.OK) { return; } srcPaths = ofd.FileNames; } ToggleUIControls(false); GlobalExtension.UpdateProgress = this.pbUpdateProgress; GlobalExtension.CurrentOperationLabel = this.lblCurrentOperation; Thread.Sleep(100); // give Globals a chance to initialize Stopwatch sw = new Stopwatch(); sw.Restart(); foreach (var srcPath in srcPaths) { // UNPACK var packagePlatform = srcPath.GetPlatform(); var tmpPath = Path.GetTempPath(); Application.DoEvents(); var unpackedDir = Packer.Unpack(srcPath, tmpPath, overwriteSongXml: true, predefinedPlatform: packagePlatform); destPath = Path.Combine(Path.GetDirectoryName(srcPaths[0]), Path.GetFileName(unpackedDir)); GlobalExtension.ShowProgress(String.Format("Loading '{0}' ...", Path.GetFileName(srcPath)), 40); // Same name xbox issue fix //if (packagePlatform.platform == GamePlatform.XBox360) // destPath = String.Format("{0}_{1}", destPath, GamePlatform.XBox360.ToString()); DirectoryExtension.Move(unpackedDir, destPath, true); unpackedDir = destPath; // Low Bass Tuning Fix is for Rocksmith 2014 Only packagePlatform = new Platform(packagePlatform.platform, GameVersion.RS2014); // LOAD DATA var info = DLCPackageData.LoadFromFolder(unpackedDir, packagePlatform, packagePlatform); switch (packagePlatform.platform) { case GamePlatform.Pc: info.Pc = true; break; case GamePlatform.Mac: info.Mac = true; break; case GamePlatform.XBox360: info.XBox360 = true; break; case GamePlatform.PS3: info.PS3 = true; break; } //apply bass fix GlobalExtension.ShowProgress(String.Format("Applying Bass Tuning Fix '{0}' ...", Path.GetFileName(srcPath)), 60); alreadyFixed = false; hasBass = false; for (int i = 0; i < info.Arrangements.Count; i++) { Arrangement arr = info.Arrangements[i]; if (arr.ArrangementType == ArrangementType.Bass) { hasBass = true; if (arr.TuningStrings.String0 < -4 && arr.TuningPitch == 440.0) { if (!TuningFrequency.ApplyBassFix(arr)) { if (chkVerbose.Checked) { MessageBox.Show(Path.GetFileName(srcPath) + " " + Environment.NewLine + "bass arrangement is already at 220Hz pitch. ", "Error ... Applying Low Bass Tuning Fix", MessageBoxButtons.OK, MessageBoxIcon.Error); } alreadyFixed = true; } else { // write xml comments back to fixed bass arrangement Song2014.WriteXmlComments(arr.SongXml.File, arr.XmlComments, customComment: " Low Bass Tuning Fixed "); } } else { if (chkVerbose.Checked) { MessageBox.Show(Path.GetFileName(srcPath) + " " + Environment.NewLine + "bass arrangement tuning does not need to be fixed. ", "Error ... Applying Low Bass Tuning Fix", MessageBoxButtons.OK, MessageBoxIcon.Error); } alreadyFixed = true; } } } // don't repackage a song that is already fixed or doesn't have bass if (alreadyFixed || !hasBass) { if (chkVerbose.Checked && !hasBass) { MessageBox.Show(Path.GetFileName(srcPath) + " " + Environment.NewLine + "has no bass arrangement. ", "Error ... Applying Low Bass Tuning Fix", MessageBoxButtons.OK, MessageBoxIcon.Error); } DirectoryExtension.SafeDelete(unpackedDir); continue; } var ndx = srcPath.LastIndexOf('_'); var srcName = srcPath.Substring(0, ndx); var srcExt = srcPath.Substring(ndx, srcPath.Length - ndx); if (!chkQuickBassFix.Checked) { using (var ofd = new SaveFileDialog()) { ofd.Title = "Select a name for the Low Bass Tuning Fixed file."; ofd.Filter = "All Files (*.*)|*.*|Rocksmith 2014 PC|*_p.psarc|Rocksmith 2014 Mac|*_m.psarc|Rocksmith 2014 Xbox|*_xbox|Rocksmith 2014 PS3|*.edat"; ofd.FileName = String.Format("{0}_{1}_bassfix{2}", info.SongInfo.ArtistSort, info.SongInfo.SongDisplayNameSort, srcExt); if (ofd.ShowDialog() != DialogResult.OK) { return; } destPath = ofd.FileName; } } else { destPath = String.Format("{0}_bassfix{1}", srcName, srcExt); } if (Path.GetFileName(destPath).Contains(" ") && info.PS3) { if (!ConfigRepository.Instance().GetBoolean("creator_ps3pkgnamewarn")) { MessageBox.Show(String.Format("PS3 package name can't support space character due to encryption limitation. {0} Spaces will be automatic removed for your PS3 package name.", Environment.NewLine), MESSAGEBOX_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Warning); } else { ConfigRepository.Instance()["creator_ps3pkgnamewarn"] = true.ToString(); } } if (chkDeleteSourceFile.Checked) { try { File.Delete(srcPath); } catch (Exception ex) { Console.Write(ex.Message); MessageBox.Show("Access rights required to delete source package, or an error occurred. Package still may exist. Try running as Administrator."); } } // reuse existing showlights.xml or generates new one if none is found info.Showlights = true; // Generate Fixed Low Bass Tuning Package GlobalExtension.ShowProgress(String.Format("Repackaging '{0}' ...", Path.GetFileName(srcPath)), 80); // TODO consider user of regular packer here RocksmithToolkitLib.DLCPackage.DLCPackageCreator.Generate(destPath, info, packagePlatform); #if !DEBUG DirectoryExtension.SafeDelete(unpackedDir); #endif } sw.Stop(); GlobalExtension.ShowProgress("Finished applying low bass tuning fix (elapsed time): " + sw.Elapsed, 100); PromptComplete(destPath); GlobalExtension.Dispose(); ToggleUIControls(true); }
/// <summary> /// Fill Arrangement 2014 from json and xml. /// </summary> /// <param name="attr"></param> /// <param name="xmlSongFile"></param> /// <param name="fixMultiTone">If set to <c>true</c> fix low bass tuning </param> /// <param name="fixLowBass">If set to <c>true</c> fix multitone exceptions </param> public Arrangement(Attributes2014 attr, string xmlSongFile, bool fixMultiTone = false, bool fixLowBass = false) { var isDirty = false; var song = Song2014.LoadFromFile(xmlSongFile); this.SongFile = new SongFile { File = "" }; this.SongXml = new SongXML { File = xmlSongFile }; //Properties Debug.Assert(attr.ArrangementType != null, "Missing information from manifest (ArrangementType)"); SetArrType(attr.ArrangementType); this.ArrangementPropeties = attr.ArrangementProperties; this.ArrangementSort = attr.ArrangementSort; this.Name = (ArrangementName)Enum.Parse(typeof(ArrangementName), attr.ArrangementName); this.ScrollSpeed = Convert.ToInt32(attr.DynamicVisualDensity.Last() * 10); this.PluckedType = (PluckedType)attr.ArrangementProperties.BassPick; this.RouteMask = (RouteMask)attr.ArrangementProperties.RouteMask; this.BonusArr = attr.ArrangementProperties.BonusArr == 1; this.Metronome = (Metronome)attr.ArrangementProperties.Metronome; this.ToneMultiplayer = attr.Tone_Multiplayer; this.Id = Guid.Parse(attr.PersistentID); this.MasterId = attr.MasterID_RDV; // Save xml comments this.XmlComments = Song2014.ReadXmlComments(xmlSongFile); // Filter out showlights\vocals if (ArrangementType != ArrangementType.Guitar && ArrangementType != ArrangementType.Bass) { return; } // Tones if (attr.Tones == null) // RS2012 { this.ToneBase = attr.Tone_Base; if (attr.Tone_A != null || attr.Tone_B != null || attr.Tone_C != null || attr.Tone_D != null) { throw new DataException("RS2012 CDLC has extraneous tone data."); } } else // RS2014 or Converter RS2012 { // TODO: optimize using common Arrangment.cs method // verify the xml Tone_ exists in tone.manifest.json foreach (var jsonTone in attr.Tones) { if (jsonTone == null) { continue; } // fix for tone.id (may not be needed/used by game) Int32 toneId = 0; // cleanup the xml arrangement file too if (jsonTone.Name.ToLower() == attr.Tone_Base.ToLower()) { this.ToneBase = song.ToneBase = attr.Tone_Base; } if (attr.Tone_A != null && jsonTone.Name.ToLower() == attr.Tone_A.ToLower()) { this.ToneA = song.ToneA = attr.Tone_A; } if (attr.Tone_B != null && jsonTone.Name.ToLower() == attr.Tone_B.ToLower()) { this.ToneB = song.ToneB = attr.Tone_B; toneId = 1; } if (attr.Tone_C != null && jsonTone.Name.ToLower() == attr.Tone_C.ToLower()) { this.ToneC = song.ToneC = attr.Tone_C; toneId = 2; } if (attr.Tone_D != null && jsonTone.Name.ToLower() == attr.Tone_D.ToLower()) { this.ToneD = song.ToneD = attr.Tone_D; toneId = 3; } // update EOF tone name and set tone id if (song.Tones != null) { foreach (var xmlTone in song.Tones) { // fix some old toolkit behavior if (xmlTone.Name == "ToneA") { xmlTone.Name = attr.Tone_A; } if (xmlTone.Name == "ToneB") { xmlTone.Name = attr.Tone_B; } if (xmlTone.Name == "ToneC") { xmlTone.Name = attr.Tone_C; } if (xmlTone.Name == "ToneD") { xmlTone.Name = attr.Tone_D; } if (xmlTone.Name.ToLower() == jsonTone.Name.ToLower() || jsonTone.Name.ToLower().EndsWith(xmlTone.Name.ToLower())) //todo: SAMENAME tone fix? { xmlTone.Name = jsonTone.Name; xmlTone.Id = toneId; } } } // song.Tones => id, name, time to apply tone is missing when song.Tones == null if (song.Tones == null && toneId > 0) { // convert the corrupt multitone to a single tone instead of throwing exception if (fixMultiTone) { song.Tones = new SongTone2014[0]; // => song.Tones.Length == 0 isDirty = true; } else { throw new InvalidDataException("Tone data is missing in CDLC and multitones will not change properly in game." + Environment.NewLine + "Please re-author XML arrangements in EOF and repair multitones name and time changes."); } } } // convert corrupt multitone to single tone and/or cleanup/repair old toolkit single tone // ToneA in single tone ODLC is null/empty if ((song.Tones == null || song.Tones.Length == 0) && !String.IsNullOrEmpty(song.ToneA)) { song.ToneA = song.ToneB = song.ToneC = String.Empty; song.ToneBase = attr.Tone_Base; this.ToneBase = attr.Tone_Base; this.ToneA = this.ToneB = this.ToneC = String.Empty; isDirty = true; } // set to standard tuning if no tuning exists if (song.Tuning == null) { song.Tuning = new TuningStrings { String0 = 0, String1 = 0, String2 = 0, String3 = 0, String4 = 0, String5 = 0 }; isDirty = true; } this.TuningStrings = song.Tuning; // NOTE: any serializing coverts abridged xml to standard xml arrangement // so only serialize if necessary to fix errors if (isDirty) { using (var stream = File.Open(xmlSongFile, FileMode.Create)) song.Serialize(stream, true); // write comments back to xml now so they are available for debugging (used for Guitar and Bass) Song2014.WriteXmlComments(xmlSongFile, XmlComments, writeNewVers: false); } // do a quick check/repair of low bass tuning, only for RS2014 bass arrangements if (fixLowBass && song.Version == "7" && this.ArrangementType == ArrangementType.Bass) { if (attr.Tuning.String0 < -4 && attr.CentOffset != -1200.0) { if (TuningFrequency.ApplyBassFix(this, fixLowBass)) { attr.CentOffset = -1200.0; // Force 220Hz song.Tuning = Song2014.LoadFromFile(xmlSongFile).Tuning; } } } } // Set Final Tuning DetectTuning(song); this.CapoFret = attr.CapoFret; if (attr.CentOffset != null) { this.TuningPitch = attr.CentOffset.Cents2Frequency(); } }
// only for RS2014 public static bool ApplyBassFix(Arrangement arr, bool saveTuningDefinition = false) { var debubMe = arr; // get the latest comments from the XML var xmlComments = Song2014.ReadXmlComments(arr.SongXml.File); var isBassFixed = xmlComments.Any(xComment => xComment.ToString().Contains("Low Bass Tuning Fixed")) || arr.TuningPitch.Equals(220.0); if (isBassFixed) { Debug.WriteLine("Low bass tuning may already be fixed: " + arr.SongXml.File); // return false; } // TODO: check guitar compatibility // Octave up for each string Int16[] strings = arr.TuningStrings.ToArray(); for (int s = 0; s < strings.Length; s++) { if (strings[s] < 0) { strings[s] += 12; } } // update XML arrangement Song2014 songXml = Song2014.LoadFromFile(arr.SongXml.File); songXml.CentOffset = "-1200.0"; // Force 220Hz songXml.Tuning = new TuningStrings(strings); // bass tuning definition gets auto added/saved to repository if (saveTuningDefinition) { var tuningDef = TuningDefinitionRepository.Instance.Detect(songXml.Tuning, GameVersion.RS2014, false); if (!tuningDef.Name.Contains("Fixed")) { var tuningUiName = String.Format("{0} Fixed", tuningDef.UIName); var bassTuning = new TuningDefinition { Custom = true, GameVersion = GameVersion.RS2014, Name = tuningUiName.Replace(" ", ""), Tuning = songXml.Tuning, UIName = tuningUiName }; TuningDefinitionRepository.SaveUnique(bassTuning); } } using (var stream = File.Open(arr.SongXml.File, FileMode.Create)) songXml.Serialize(stream, true); // write xml comments back to fixed bass arrangement if (!isBassFixed) { Song2014.WriteXmlComments(arr.SongXml.File, xmlComments, customComment: "Low Bass Tuning Fixed"); } return(true); }
public Arrangement(Attributes2014 attr, string xmlSongFile) { var song = Song2014.LoadFromFile(xmlSongFile); this.SongFile = new SongFile(); this.SongFile.File = ""; this.SongXml = new SongXML(); this.SongXml.File = xmlSongFile; //Tuning TuningDefinition tuning = null; switch ((ArrangementName)attr.ArrangementType) { case ArrangementName.Lead: case ArrangementName.Rhythm: case ArrangementName.Combo: this.ArrangementType = Sng.ArrangementType.Guitar; tuning = TuningDefinitionRepository.Instance().Select(song.Tuning, GameVersion.RS2014); break; case ArrangementName.Bass: this.ArrangementType = Sng.ArrangementType.Bass; // TODO: trying to fix bass tuning issue tuning = TuningDefinitionRepository.Instance().Select(song.Tuning, GameVersion.RS2014); // tuning = TuningDefinitionRepository.Instance().SelectForBass(song.Tuning, GameVersion.RS2014); break; case ArrangementName.Vocals: this.ArrangementType = Sng.ArrangementType.Vocal; break; } if (tuning == null) { tuning = new TuningDefinition(); tuning.UIName = tuning.Name = tuning.NameFromStrings(song.Tuning, this.ArrangementType == Sng.ArrangementType.Bass); tuning.Custom = true; tuning.GameVersion = GameVersion.RS2014; tuning.Tuning = song.Tuning; TuningDefinitionRepository.Instance().Add(tuning, true); } this.Tuning = tuning.UIName; this.TuningStrings = tuning.Tuning; this.CapoFret = attr.CapoFret; if (attr.CentOffset != null) { this.TuningPitch = attr.CentOffset.Cents2Frequency(); } //Properties this.ArrangementSort = attr.ArrangementSort; this.Name = (ArrangementName)Enum.Parse(typeof(ArrangementName), attr.ArrangementName); this.ScrollSpeed = Convert.ToInt32(attr.DynamicVisualDensity.Last() * 10); this.PluckedType = (PluckedType)attr.ArrangementProperties.BassPick; this.RouteMask = (RouteMask)attr.ArrangementProperties.RouteMask; this.BonusArr = attr.ArrangementProperties.BonusArr == 1; this.Metronome = (Metronome)attr.ArrangementProperties.Metronome; this.ToneMultiplayer = attr.Tone_Multiplayer; this.Id = Guid.Parse(attr.PersistentID); this.MasterId = attr.MasterID_RDV; // save xml comments this.XmlComments = Song2014.ReadXmlComments(xmlSongFile); //Tones if (attr.Tones == null) // RS2012 { this.ToneBase = attr.Tone_Base; if (attr.Tone_A != null || attr.Tone_B != null || attr.Tone_C != null || attr.Tone_D != null) { throw new DataException("RS2012 CDLC has extraneous tone data."); } } else // RS2014 or Converter RS2012 { // TODO: optimize using common Arrangment.cs method // verify the xml Tone_ exists in tone.manifest.json foreach (var jsonTone in attr.Tones) { if (jsonTone == null) { continue; } // fix for tone.id (may not be needed/used by game) Int32 toneId = 0; // cleanup the xml arrangement file too if (jsonTone.Name.ToLower() == attr.Tone_Base.ToLower()) { this.ToneBase = song.ToneBase = attr.Tone_Base; } if (attr.Tone_A != null && jsonTone.Name.ToLower() == attr.Tone_A.ToLower()) { this.ToneA = song.ToneA = attr.Tone_A; } if (attr.Tone_B != null && jsonTone.Name.ToLower() == attr.Tone_B.ToLower()) { this.ToneB = song.ToneB = attr.Tone_B; toneId = 1; } if (attr.Tone_C != null && jsonTone.Name.ToLower() == attr.Tone_C.ToLower()) { this.ToneC = song.ToneC = attr.Tone_C; toneId = 2; } if (attr.Tone_D != null && jsonTone.Name.ToLower() == attr.Tone_D.ToLower()) { this.ToneD = song.ToneD = attr.Tone_D; toneId = 3; } // update EOF tone name and set tone id if (song.Tones != null) { foreach (var xmlTone in song.Tones) { // fix some old toolkit behavior if (xmlTone.Name == "ToneA") { xmlTone.Name = attr.Tone_A; } if (xmlTone.Name == "ToneB") { xmlTone.Name = attr.Tone_B; } if (xmlTone.Name == "ToneC") { xmlTone.Name = attr.Tone_C; } if (xmlTone.Name == "ToneD") { xmlTone.Name = attr.Tone_D; } if (xmlTone.Name.ToLower() == jsonTone.Name.ToLower() || jsonTone.Name.ToLower().Contains(xmlTone.Name.ToLower())) { xmlTone.Name = jsonTone.Name; xmlTone.Id = toneId; } } } if (song.Tones == null && toneId > 0) { throw new InvalidDataException("Custom tones were not set properly in EOF" + Environment.NewLine + "Please reauthor XML arrangement in EOF and fix custom tone consistency."); } } // write changes to xml arrangement (w/o comments) using (var stream = File.Open(xmlSongFile, FileMode.Create)) song.Serialize(stream); // write comments back to xml now so they are available for debugging if (this.ArrangementType == ArrangementType.Guitar || this.ArrangementType == ArrangementType.Bass) { Song2014.WriteXmlComments(xmlSongFile, this.XmlComments, false); } } }
/// <summary> /// Fill Arrangement 2014 from json and xml. /// </summary> /// <param name="attr"></param> /// <param name="xmlSongFile"></param> public Arrangement(Attributes2014 attr, string xmlSongFile) { var song = Song2014.LoadFromFile(xmlSongFile); this.SongFile = new SongFile { File = "" }; this.SongXml = new SongXML { File = xmlSongFile }; //Properties Debug.Assert(attr.ArrangementType != null, "Missing information from manifest (ArrangementType)"); SetArrType(attr.ArrangementType); this.ArrangementPropeties = attr.ArrangementProperties; this.ArrangementSort = attr.ArrangementSort; this.Name = (ArrangementName)Enum.Parse(typeof(ArrangementName), attr.ArrangementName); this.ScrollSpeed = Convert.ToInt32(attr.DynamicVisualDensity.Last() * 10); this.PluckedType = (PluckedType)attr.ArrangementProperties.BassPick; this.RouteMask = (RouteMask)attr.ArrangementProperties.RouteMask; this.BonusArr = attr.ArrangementProperties.BonusArr == 1; this.Metronome = (Metronome)attr.ArrangementProperties.Metronome; this.ToneMultiplayer = attr.Tone_Multiplayer; this.Id = Guid.Parse(attr.PersistentID); this.MasterId = attr.MasterID_RDV; //Filter out showlights\vocals if (ArrangementType != ArrangementType.Guitar && ArrangementType != ArrangementType.Bass) { return; } //Tuning DetectTuning(song); this.CapoFret = attr.CapoFret; if (attr.CentOffset != null) { this.TuningPitch = attr.CentOffset.Cents2Frequency(); } // save xml comments this.XmlComments = Song2014.ReadXmlComments(xmlSongFile); //Tones if (attr.Tones == null) // RS2012 { this.ToneBase = attr.Tone_Base; if (attr.Tone_A != null || attr.Tone_B != null || attr.Tone_C != null || attr.Tone_D != null) { throw new DataException("RS2012 CDLC has extraneous tone data."); } } else // RS2014 or Converter RS2012 { // TODO: optimize using common Arrangment.cs method // verify the xml Tone_ exists in tone.manifest.json foreach (var jsonTone in attr.Tones) { if (jsonTone == null) { continue; } // fix for tone.id (may not be needed/used by game) Int32 toneId = 0; // cleanup the xml arrangement file too if (jsonTone.Name.ToLower() == attr.Tone_Base.ToLower()) { this.ToneBase = song.ToneBase = attr.Tone_Base; } if (attr.Tone_A != null && jsonTone.Name.ToLower() == attr.Tone_A.ToLower()) { this.ToneA = song.ToneA = attr.Tone_A; } if (attr.Tone_B != null && jsonTone.Name.ToLower() == attr.Tone_B.ToLower()) { this.ToneB = song.ToneB = attr.Tone_B; toneId = 1; } if (attr.Tone_C != null && jsonTone.Name.ToLower() == attr.Tone_C.ToLower()) { this.ToneC = song.ToneC = attr.Tone_C; toneId = 2; } if (attr.Tone_D != null && jsonTone.Name.ToLower() == attr.Tone_D.ToLower()) { this.ToneD = song.ToneD = attr.Tone_D; toneId = 3; } // update EOF tone name and set tone id if (song.Tones != null) { foreach (var xmlTone in song.Tones) { // fix some old toolkit behavior if (xmlTone.Name == "ToneA") { xmlTone.Name = attr.Tone_A; } if (xmlTone.Name == "ToneB") { xmlTone.Name = attr.Tone_B; } if (xmlTone.Name == "ToneC") { xmlTone.Name = attr.Tone_C; } if (xmlTone.Name == "ToneD") { xmlTone.Name = attr.Tone_D; } if (xmlTone.Name.ToLower() == jsonTone.Name.ToLower() || jsonTone.Name.ToLower().EndsWith(xmlTone.Name.ToLower())) //todo: SAMENAME tone fix? { xmlTone.Name = jsonTone.Name; xmlTone.Id = toneId; } } } if (song.Tones == null && toneId > 0) { throw new InvalidDataException("Custom tones were not set properly in EOF" + Environment.NewLine + "Please re-author XML arrangement in EOF and fix custom tone consistency."); } } // write changes to xml arrangement (w/o comments) using (var stream = File.Open(xmlSongFile, FileMode.Create)) song.Serialize(stream, true); // write comments back to xml now so they are available for debugging (used for Guitar and Bass) Song2014.WriteXmlComments(xmlSongFile, XmlComments, writeNewVers: false); } }
/// <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); }
private static void RemasterSong(string srcFilePath) { try { // SNG's needs to be regenerated // ArrangmentIDs are stored in multiple place and all need to be updated // therefore we are going to unpack, apply repair, and repack ShowMessage(" - Extracting CDLC artifacts ..."); DLCPackageData packageData; using (var psarcOld = new PsarcPackager()) packageData = psarcOld.ReadPackage(srcFilePath); // Update arrangement song info foreach (Arrangement arr in packageData.Arrangements) { if (!optionPre) { // generate new AggregateGraph arr.SongFile = new RocksmithToolkitLib.DLCPackage.AggregateGraph.SongFile { File = "" }; // generate new Arrangement IDs arr.Id = IdGenerator.Guid(); arr.MasterId = RandomGenerator.NextInt(); } // skip vocal and showlight arrangements if (arr.ArrangementType == ArrangementType.Vocal || arr.ArrangementType == ArrangementType.ShowLight) { continue; } // validate SongInfo var songXml = Song2014.LoadFromFile(arr.SongXml.File); songXml.AlbumYear = packageData.SongInfo.SongYear.ToString().GetValidYear(); songXml.ArtistName = packageData.SongInfo.Artist.GetValidAtaSpaceName(); songXml.Title = packageData.SongInfo.SongDisplayName.GetValidAtaSpaceName(); songXml.AlbumName = packageData.SongInfo.Album.GetValidAtaSpaceName(); songXml.ArtistNameSort = packageData.SongInfo.ArtistSort.GetValidSortableName(); songXml.SongNameSort = packageData.SongInfo.SongDisplayNameSort.GetValidSortableName(); songXml.AlbumNameSort = packageData.SongInfo.AlbumSort.GetValidSortableName(); songXml.AverageTempo = Convert.ToSingle(packageData.SongInfo.AverageTempo.ToString().GetValidTempo()); // write updated xml arrangement using (var stream = File.Open(arr.SongXml.File, FileMode.Create)) songXml.Serialize(stream, true); // add comments back to xml arrangement Song2014.WriteXmlComments(arr.SongXml.File, arr.XmlComments); } if (!optionPre) { // add comment to ToolkitInfo to identify CDLC var arrIdComment = packageData.PackageComment; if (String.IsNullOrEmpty(arrIdComment)) { arrIdComment = TKI_ARRID; } else if (!arrIdComment.Contains(TKI_ARRID)) { arrIdComment = arrIdComment + " " + TKI_ARRID; } packageData.PackageComment = arrIdComment; } // add comment to ToolkitInfo to identify CDLC var remasterComment = packageData.PackageComment; if (String.IsNullOrEmpty(remasterComment)) { remasterComment = TKI_REMASTER; } else if (!remasterComment.Contains(TKI_REMASTER)) { remasterComment = remasterComment + " " + TKI_REMASTER; } packageData.PackageComment = remasterComment; // add default package version if missing if (String.IsNullOrEmpty(packageData.PackageVersion)) { packageData.PackageVersion = "1"; } else { packageData.PackageVersion = packageData.PackageVersion.GetValidVersion(); } // validate packageData (important) packageData.Name = packageData.Name.GetValidKey(); // DLC Key ShowMessage(" - Repackaging remastered CDLC ..."); // regenerates the SNG with the repair and repackages using (var psarcNew = new PsarcPackager(true)) psarcNew.WritePackage(srcFilePath, packageData); ShowMessage(" - Repair was sucessful ..."); } catch (Exception ex) { ShowMessage(" - Repair failed ... " + SplitString(ex.Message, 55), MessageType.Warning); ShowMessage(" - See 'remastered_error.log' file ... ", MessageType.Warning); ShowMessage(srcFilePath + ", Corrupt File", MessageType.Error, postfixLine: true); // copy (org) to corrupt (cor), delete backup (org), delete original var properExt = Path.GetExtension(srcFilePath); var orgFilePath = String.Format(@"{0}{1}{2}", Path.Combine(workDirectory, Path.GetFileNameWithoutExtension(srcFilePath)), orgExt, properExt).Trim(); if (File.Exists(orgFilePath)) { var corFilePath = String.Format(@"{0}{1}{2}", Path.Combine(workDirectory, Path.GetFileNameWithoutExtension(srcFilePath)), corExt, properExt).Trim(); File.Copy(orgFilePath, corFilePath, true); File.Delete(orgFilePath); File.Delete(srcFilePath); } } }