private InstrumentTuning UpdateTuning(InstrumentTuning tuning, bool guitar) { if (string.IsNullOrEmpty(tuning.Name)) { // Return default guitar/bass tuning return(guitar ? InstrumentTuning.Guitar_EStandard : InstrumentTuning.Bass_EStandard); } if (guitar) { switch (tuning.Name.Value.ToLower()) { default: case "e standard": return(InstrumentTuning.Guitar_EStandard); case "drop d": return(InstrumentTuning.Guitar_DropD); case "e♭ standard": case "eb standard": return(InstrumentTuning.Guitar_EbStandard); } } else // Bass { switch (tuning.Name.Value.ToLower()) { default: case "e standard": return(InstrumentTuning.Bass_EStandard); } } }
public static int GetMidiNote(this InstrumentTuning tuning, ArrangementType arrangementType, int stringNumber, int fret) { if (fret == -1) { return(-1); } var strNote = StandardMidiNotes[stringNumber] + tuning.GetOffsets()[stringNumber]; strNote -= arrangementType == ArrangementType.Bass ? 12 : 0; return(strNote + fret); }
// COMPLETE private static void WriteSngFile(Song rocksmithSong, InstrumentTuning tuning, ArrangementType arrangementType, string outputFile, EndianBitConverter bitConverter) { var iterationInfo = CreatePhraseIterationInfo(rocksmithSong); // WRITE THE .SNG FILE using (FileStream fs = new FileStream(outputFile, FileMode.Create)) using (EndianBinaryWriter w = new EndianBinaryWriter(bitConverter, fs)) { // HEADER WriteRocksmithSngHeader(w, arrangementType); // EBEATS DATA WriteRocksmithSngEbeats(w, rocksmithSong.Ebeats); // PHRASES WriteRocksmithSngPhrases(w, rocksmithSong.Phrases, rocksmithSong.PhraseIterations); // CHORD TEMPLATES WriteRocksmithSngChordTemplates(w, rocksmithSong.ChordTemplates, tuning, arrangementType); // FRET HAND MUTE TEMPLATE WriteRocksmithSngFretHandMuteTemplates(w, rocksmithSong.FretHandMuteTemplates); // VOCALS TEMPLATE w.Write(new byte[4]); // not used on song file // PHRASE ITERATIONS WriteRocksmithSngPhraseIterations(w, rocksmithSong.PhraseIterations, rocksmithSong.SongLength); // PHRASE PROPERTIES WriteRocksmithSngPhraseProperties(w, rocksmithSong.PhraseProperties); // LINKED DIFFS WriteRocksmithSngLinkedDiffs(w, rocksmithSong.LinkedDiffs); // CONTROLS WriteRocksmithSngControls(w, rocksmithSong.Controls); // EVENTS WriteRocksmithSngEvents(w, rocksmithSong.Events); // SECTIONS WriteRocksmithSngSections(w, rocksmithSong.Sections, rocksmithSong.PhraseIterations, rocksmithSong.SongLength); // LEVELS WriteRocksmithSngLevels(w, rocksmithSong.Levels, rocksmithSong.SongLength, iterationInfo, arrangementType); // SONG META DATA WriteRocksmithSngMetaDetails(w, rocksmithSong, tuning, iterationInfo); } }
public static IList <Int16> GetOffsets(this InstrumentTuning tuning) { switch (tuning) { case InstrumentTuning.Standard: return(StandardOffsets); case InstrumentTuning.DropD: return(DropDOffsets); case InstrumentTuning.EFlat: return(EFlatOffsets); case InstrumentTuning.OpenG: return(OpenGOffsets); default: throw new InvalidOperationException("Unexpected tuning value"); } }
public List <ZObject> ExportZObjects(HKey directory, InstrumentTuning leadGtr, InstrumentTuning rhythmGtr, InstrumentTuning bass) { List <ZObject> objects = new List <ZObject>(); objects.AddRange(CreateInstrument(directory, "master", "", InstrumentTuning.Guitar_EStandard)); objects.AddRange(CreateInstrument(directory, "vox", "", InstrumentTuning.Guitar_EStandard)); // Guitar tracks objects.AddRange(CreateInstrument(directory, "guitar", "jam", rhythmGtr)); objects.AddRange(CreateInstrument(directory, "guitar", "nov", rhythmGtr)); objects.AddRange(CreateInstrument(directory, "guitar", "beg", rhythmGtr)); objects.AddRange(CreateInstrument(directory, "guitar", "int", rhythmGtr)); objects.AddRange(CreateInstrument(directory, "guitar", "rhy", rhythmGtr)); objects.AddRange(CreateInstrument(directory, "guitar", "adv", leadGtr)); // Bass tracks objects.AddRange(CreateInstrument(directory, "bass", "jam", bass)); objects.AddRange(CreateInstrument(directory, "bass", "nov", bass)); objects.AddRange(CreateInstrument(directory, "bass", "beg", bass)); objects.AddRange(CreateInstrument(directory, "bass", "int", bass)); objects.AddRange(CreateInstrument(directory, "bass", "adv", bass)); return(objects); }
// COMPLETE private static void WriteRocksmithSngChordTemplates(EndianBinaryWriter w, SongChordTemplate[] chordTemplates, InstrumentTuning tuning, ArrangementType arrangementType) { // output header if (chordTemplates == null || chordTemplates.Length == 0) { w.Write(new byte[4]); // empty header return; } // output header count w.Write(chordTemplates.Length); // output chord templates foreach (SongChordTemplate chordTemplate in chordTemplates) { // fret numbers w.Write(chordTemplate.Fret0); w.Write(chordTemplate.Fret1); w.Write(chordTemplate.Fret2); w.Write(chordTemplate.Fret3); w.Write(chordTemplate.Fret4); w.Write(chordTemplate.Fret5); // finger positions w.Write(chordTemplate.Finger0); w.Write(chordTemplate.Finger1); w.Write(chordTemplate.Finger2); w.Write(chordTemplate.Finger3); w.Write(chordTemplate.Finger4); w.Write(chordTemplate.Finger5); // note values w.Write(tuning.GetMidiNote(arrangementType, 0, chordTemplate.Fret0)); w.Write(tuning.GetMidiNote(arrangementType, 1, chordTemplate.Fret1)); w.Write(tuning.GetMidiNote(arrangementType, 2, chordTemplate.Fret2)); w.Write(tuning.GetMidiNote(arrangementType, 3, chordTemplate.Fret3)); w.Write(tuning.GetMidiNote(arrangementType, 4, chordTemplate.Fret4)); w.Write(tuning.GetMidiNote(arrangementType, 5, chordTemplate.Fret5)); // chord name string name = chordTemplate.ChordName; if (name.Length > 32) { name = name.Substring(0, 32); } foreach (char c in name) { w.Write(Convert.ToByte(c)); } // padding after name w.Write(new byte[32 - name.Length]); } }
// INCOMPLETE private static void WriteRocksmithSngMetaDetails(EndianBinaryWriter w, Song s, InstrumentTuning tuning, List <PhraseIterationInfo> iterationInfo) { // Max score when fully leveled (minus bonuses - always 100000). Or possible Master mode unlock score w.Write((double)100000); //This needs to be calculated to take real charts into account. double totalNotes = 0; foreach (var iteration in iterationInfo) { if (s.Levels.Length <= iteration.MaxDifficulty) { throw new Exception("There is a phrase defined with maxDifficulty=" + iteration.MaxDifficulty + ", but the highest difficulty level is " + (s.Levels.Length - 1)); } var level = s.Levels[iteration.MaxDifficulty]; if (level.Notes != null) { totalNotes += level.Notes.Count(note => note.Time >= iteration.StartTime && note.Time < iteration.EndTime); } if (level.Chords != null) { totalNotes += level.Chords.Count(chord => chord.Time >= iteration.StartTime && chord.Time < iteration.EndTime); } } // Total notes when fully leveled w.Write(totalNotes); // points per note w.Write((double)((float)(100000f / (float)totalNotes))); // song beat timing if (s.Ebeats.Length < 2) { throw new InvalidDataException("Song must contain at least 2 beats"); } // this is not 100% accurate unless all beats are evenly spaced in a song; // still trying to determine exactly how Rocksmith is deriving this time value w.Write(s.Ebeats[1].Time - s.Ebeats[0].Time); // first beat time(?); confirmed as not first phraseIteration time and not first section time w.Write(s.Ebeats[0].Time); // song conversion date var lastConvertDate = s.LastConversionDateTime; if (lastConvertDate.Length > 32) { lastConvertDate = lastConvertDate.Substring(0, 32); } foreach (char c in lastConvertDate) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - lastConvertDate.Length]); //pad to 32 bytes // song title var title = s.Title; if (title.Length > 64) { title = title.Substring(0, 64); } foreach (char c in title) { w.Write(Convert.ToByte(c)); } w.Write(new byte[64 - title.Length]); // pad to 64 bytes // arrangement var arrangement = s.Arrangement; if (arrangement.Length > 32) { arrangement = arrangement.Substring(0, 32); } foreach (char c in arrangement) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - arrangement.Length]); //pad to 32 bytes // artist string artistValue = string.IsNullOrEmpty(s.ArtistName) ? "DUMMY" : s.ArtistName; if (artistValue.Length > 32) { artistValue = artistValue.Substring(0, 32); } foreach (char c in artistValue) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - artistValue.Length]); //pad to 32 bytes // song part w.Write(s.Part); // song length w.Write(s.SongLength); // tuning w.Write((Int32)tuning); //Song difficulty float difficulty = (float)iterationInfo.Average(it => it.MaxDifficulty); w.Write(difficulty); // float with 10.2927 in NumberThirteen_Lead.sng // unknown // from 6AMSalvation_Combo.xml value = 11.525 // which does not match any phrase iteration start time // and does not match first ebeat time // and does not match any section start time // does match first event time of E3 (although this is not a match in other files) // does not match any note times on 1st difficulty level // nor an anchor time on 1st difficulty level //It appears to be the time of the first note. float firstNote = s.Levels.Min(level => level.Notes == null || level.Notes.Length == 0 ? float.MaxValue : level.Notes.Min(note => note.Time)); float firstChord = s.Levels.Min(level => level.Chords == null || level.Chords.Length == 0 ? float.MaxValue : level.Chords.Min(chord => chord.Time)); float first = Math.Min(firstChord, firstNote); w.Write(first); w.Write(first); // max difficulty int maxDifficulty = s.Levels.Length - 1; w.Write(maxDifficulty); // unknown section w.Write(new byte[4]); // header with repeating array; song works in game if array is defaulted to 0 count so will leave this alone for now // unknown section // There seems to be 1 entry per letter in the chord templates, although there are songs with chord templates that don't have this section. w.Write(new byte[4]); // header with repeating array - only populated in limited songs }
// INCOMPLETE private static void WriteRocksmithSngMetaDetails(EndianBinaryWriter w, Song s, InstrumentTuning tuning, List<PhraseIterationInfo> iterationInfo) { // Max score when fully leveled (minus bonuses - always 100000). Or possible Master mode unlock score w.Write((double)100000); //This needs to be calculated to take real charts into account. double totalNotes = 0; foreach (var iteration in iterationInfo) { if (s.Levels.Length <= iteration.MaxDifficulty) { throw new Exception("There is a phrase defined with maxDifficulty=" + iteration.MaxDifficulty + ", but the highest difficulty level is " + (s.Levels.Length - 1)); } var level = s.Levels[iteration.MaxDifficulty]; if (level.Notes != null) { totalNotes += level.Notes.Where(note => note.Time >= iteration.StartTime && note.Time < iteration.EndTime).Count(); } if (level.Chords != null) { totalNotes += level.Chords.Where(chord => chord.Time >= iteration.StartTime && chord.Time < iteration.EndTime).Count(); } } // Total notes when fully leveled w.Write(totalNotes); // points per note w.Write((double)((float)(100000f / (float)totalNotes))); // song beat timing if (s.Ebeats.Length < 2) throw new InvalidDataException("Song must contain at least 2 beats"); // this is not 100% accurate unless all beats are evenly spaced in a song; // still trying to determine exactly how Rocksmith is deriving this time value w.Write(s.Ebeats[1].Time - s.Ebeats[0].Time); // first beat time(?); confirmed as not first phraseIteration time and not first section time w.Write(s.Ebeats[0].Time); // song conversion date var lastConvertDate = s.LastConversionDateTime; if (lastConvertDate.Length > 32) { lastConvertDate = lastConvertDate.Substring(0, 32); } foreach (char c in lastConvertDate) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - lastConvertDate.Length]); //pad to 32 bytes // song title var title = s.Title; if (title.Length > 64) { title = title.Substring(0, 64); } foreach (char c in title) { w.Write(Convert.ToByte(c)); } w.Write(new byte[64 -title.Length]); // pad to 64 bytes // arrangement var arrangement = s.Arrangement; if (arrangement.Length > 32) { arrangement = arrangement.Substring(0, 32); } foreach (char c in arrangement) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - arrangement.Length]); //pad to 32 bytes // artist string artistValue = string.IsNullOrEmpty(s.Artist) ? "DUMMY" : s.Artist; if (artistValue.Length > 32) { artistValue = artistValue.Substring(0, 32); } foreach (char c in artistValue) { w.Write(Convert.ToByte(c)); } w.Write(new byte[32 - artistValue.Length]); //pad to 32 bytes // song part w.Write(s.Part); // song length w.Write(s.SongLength); // tuning w.Write((Int32)tuning); //Song difficulty float difficulty = (float)iterationInfo.Average(it => it.MaxDifficulty); w.Write(difficulty); // float with 10.2927 in NumberThirteen_Lead.sng // unknown // from 6AMSalvation_Combo.xml value = 11.525 // which does not match any pharse iteration start time // and does not match first ebeat time // and does not match any section start time // does match first event time of E3 (although this is not a match in other files) // does not match any note times on 1st difficulty level // nor an anchor time on 1st difficulty level //It appears to be the time of the first note. float firstNote = s.Levels.Min(level => level.Notes == null || level.Notes.Length == 0 ? float.MaxValue : level.Notes.Min(note => note.Time)); float firstChord = s.Levels.Min(level => level.Chords == null || level.Chords.Length == 0 ? float.MaxValue : level.Chords.Min(chord => chord.Time)); float first = Math.Min(firstChord, firstNote); w.Write(first); w.Write(first); // max difficulty int maxDifficulty = s.Levels.Length-1; w.Write(maxDifficulty); // unknown section w.Write(new byte[4]); // header with repeating array; song works in game if array is defaulted to 0 count so will leave this alone for now // unknown section // There seems to be 1 entry per letter in the chord templates, although there are songs with chord templates that don't have this section. w.Write(new byte[4]); // header with repeating array - only populated in limited songs }
// COMPLETE private static void WriteRocksmithSngFile(Song rocksmithSong, InstrumentTuning tuning, ArrangementType arrangementType, string outputFile, EndianBitConverter bitConverter) { var iterationInfo = CreatePhraseIterationInfo(rocksmithSong); // WRITE THE .SNG FILE using (FileStream fs = new FileStream(outputFile, FileMode.Create)) using (EndianBinaryWriter w = new EndianBinaryWriter(bitConverter, fs)) { // HEADER WriteRocksmithSngHeader(w, arrangementType); // EBEATS DATA WriteRocksmithSngEbeats(w, rocksmithSong.Ebeats); // PHRASES WriteRocksmithSngPhrases(w, rocksmithSong.Phrases, rocksmithSong.PhraseIterations); // CHORD TEMPLATES WriteRocksmithSngChordTemplates(w, rocksmithSong.ChordTemplates, tuning, arrangementType); // FRET HAND MUTE TEMPLATE WriteRocksmithSngFretHandMuteTemplates(w, rocksmithSong.FretHandMuteTemplates); // VOCALS TEMPLATE w.Write(new byte[4]); // not used on song file // PHRASE ITERATIONS WriteRocksmithSngPhraseIterations(w, rocksmithSong.PhraseIterations, rocksmithSong.SongLength); // PHRASE PROPERTIES WriteRocksmithSngPhraseProperties(w, rocksmithSong.PhraseProperties); // LINKED DIFFS WriteRocksmithSngLinkedDiffs(w, rocksmithSong.LinkedDiffs); // CONTROLS WriteRocksmithSngControls(w, rocksmithSong.Controls); // EVENTS WriteRocksmithSngEvents(w, rocksmithSong.Events); // SECTIONS WriteRocksmithSngSections(w, rocksmithSong.Sections, rocksmithSong.PhraseIterations, rocksmithSong.SongLength); // LEVELS WriteRocksmithSngLevels(w, rocksmithSong.Levels, rocksmithSong.SongLength, iterationInfo, arrangementType); // SONG META DATA WriteRocksmithSngMetaDetails(w, rocksmithSong, tuning, iterationInfo); } }
public static void Write(string inputFile, string outputFile, ArrangementType arrangementType, GamePlatform platform, InstrumentTuning tuning) { using (var reader = new StreamReader(inputFile)) { var bitConverter = platform == GamePlatform.Pc ? (EndianBitConverter)EndianBitConverter.Little : (EndianBitConverter)EndianBitConverter.Big; if (arrangementType == ArrangementType.Vocal) { var serializer = new XmlSerializer(typeof(Vocals)); var vocals = (Vocals)serializer.Deserialize(reader); WriteRocksmithVocalsFile(vocals, outputFile, bitConverter); } else { var serializer = new XmlSerializer(typeof(Song)); var song = (Song)serializer.Deserialize(reader); WriteRocksmithSngFile(song, tuning, arrangementType, outputFile, bitConverter); } } }
private void AddToCatalog(Song song, InstrumentTuning leadGtr, InstrumentTuning rhythmGtr, InstrumentTuning bass) { // Add song to catalog2 entries Catalog2 catalog = _packageManager["catalog2"] as Catalog2; Catalog2Entry entry = new Catalog2Entry() { Identifier = song.DirectoryPath + ".MediaEntry2", SongType = 1, Title = song.Title, Artist = song.Artist, Album = song.Album, Description = song.Description, LegendTag = song.LegendTag, SongLength = song.SongLength, GuitarIntensity = song.GuitarIntensity, BassIntensity = song.BassIntensity, VoxIntensity = song.VoxIntensity, EraTag = song.EraTag, Year = song.Year, LeadGuitarTuning = leadGtr, RhythmGuitarTuning = rhythmGtr, BassTuning = bass, Labels = song.Labels, SongPath = song.FilePath, TexturePath = song.TexturePath, PreviewPath = song.PreviewPath, MetadataTags = song.MetadataTags, GenreTags = song.GenreTags }; // Removes previous entry and adds to pending change catalog.Entries.RemoveAll(x => x.Identifier == entry.Identifier); catalog.Entries.Add(entry); // Sorts alphabetically and ensures "ShredUs" entries are first catalog.Entries.Sort((x, y) => { string a = x.Identifier, b = y.Identifier; if (a.StartsWith("ShredUs", StringComparison.CurrentCultureIgnoreCase) && b.StartsWith("ShredUs", StringComparison.CurrentCultureIgnoreCase)) { return(string.Compare(a, b)); } else if (a.StartsWith("ShredUs", StringComparison.CurrentCultureIgnoreCase)) { return(-1); } else if (b.StartsWith("ShredUs", StringComparison.CurrentCultureIgnoreCase)) { return(1); } else { return(string.Compare(a, b)); } }); _packageManager.AddZObjectAsPending(catalog); }
private List <ZObject> CreateInstrument(HKey directoryPath, string instrumentType, string difficulty, InstrumentTuning tuning) { // Sets directory name if (instrumentType == "guitar" || instrumentType == "bass") { directoryPath += (instrumentType == "guitar" ? ".gtr_" : ".bss_") + difficulty; } else { directoryPath += "." + instrumentType; } // Creates instrument Instrument instrument = new Instrument(directoryPath + ".instrument", directoryPath); instrument.InstrumentType = instrumentType == "vox" ? "vocals" : instrumentType; // Sets difficulty + tuning if (instrumentType == "vox" || instrumentType == "master") { instrument.Difficulty = ""; instrument.Tuning = InstrumentTuning.Guitar_EStandard; } else { instrument.Difficulty = difficulty; instrument.Tuning = tuning; } // Creates tracks for instrument List <ZObject> objects; switch (instrumentType.ToLower()) { case "bass": case "guitar": objects = GetGuitarObjects(directoryPath); break; case "master": // event, measure, section, tempo, timesignature objects = GetMasterObjects(directoryPath); break; case "vox": // vox, voxpushphrase, voxspread objects = GetVoxObjects(directoryPath); break; default: // Shouldn't really return anything return(new List <ZObject>()); } objects.ForEach(x => instrument.TrackPaths.Add(x.FilePath)); objects.Add(instrument); return(objects); }