├── Psarc ├── Models │ ├── Sng │ │ ├── Dna.cs │ │ ├── Tone.cs │ │ ├── Bpm.cs │ │ ├── Event.cs │ │ ├── Action.cs │ │ ├── SymbolsHeader.cs │ │ ├── PhraseIteration.cs │ │ ├── SymbolsTexture.cs │ │ ├── PhraseExtraInfoByLevel.cs │ │ ├── Vocal.cs │ │ ├── Phrase.cs │ │ ├── BendData.cs │ │ ├── SymbolDefinition.cs │ │ ├── Section.cs │ │ ├── Chord.cs │ │ ├── NLinkedDifficulty.cs │ │ ├── ChordNotes.cs │ │ ├── Metadata.cs │ │ └── Arrangement.cs │ ├── IBinarySerializable.cs │ ├── ToolkitInfo.cs │ └── Json │ │ └── SongArrangement.cs ├── Asset │ ├── PsarcAsset.cs │ ├── JsonLinqAsset.cs │ ├── JsonObjectAsset.cs │ ├── TextPsarcAsset.cs │ ├── ToolkitInfoAsset.cs │ ├── DdsAsset.cs │ └── SngAsset.cs ├── PsarcArchiveFlags.cs ├── PsarcTOCEntry.cs ├── PsarcFileHeader.cs ├── PsarcTOC.cs └── PsarcFile.cs ├── Rocksmith2014PsarcLib.csproj ├── ReaderExtensions ├── GeneralReaderExtensions.cs ├── ArrayReaderExtensions.cs ├── StructReaderExtensions.cs └── BigEndianReaderExtensions.cs ├── LICENSE ├── README.md ├── .gitignore └── Crypto └── DecryptStream.cs /Psarc/Models/Sng/Dna.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 2 | { 3 | public struct Dna 4 | { 5 | public float Time; 6 | public int DnaId; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Tone.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 2 | { 3 | public struct Tone 4 | { 5 | public float Time; 6 | public int ToneId; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Psarc/Models/IBinarySerializable.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models 4 | { 5 | public interface IBinarySerializable 6 | { 7 | IBinarySerializable Read(BinaryReader reader); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Psarc/Asset/PsarcAsset.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Asset 4 | { 5 | public abstract class PsarcAsset 6 | { 7 | public virtual void ReadFrom(MemoryStream stream) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Bpm.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 2 | { 3 | public struct Bpm 4 | { 5 | public float Time; 6 | public short Measure; 7 | public short Beat; 8 | public int PhraseIteration; 9 | public int Mask; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Event.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Event 6 | { 7 | public float Time; 8 | 9 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 10 | public string EventName; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Action.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Action 6 | { 7 | public float Time; 8 | 9 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 10 | public string ActionName; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/SymbolsHeader.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 2 | { 3 | public struct SymbolsHeader 4 | { 5 | public int Unk1; 6 | public int Unk2; 7 | public int Unk3; 8 | public int Unk4; 9 | public int Unk5; 10 | public int Unk6; 11 | public int Unk7; 12 | public int Unk8; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Psarc/PsarcArchiveFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc 4 | { 5 | [Flags] 6 | public enum PsarcArchiveFlags 7 | { 8 | NONE = 0, 9 | UNK1 = 1, 10 | UNK2 = 2, 11 | TOC_ENCRYPTED = 4, 12 | UNK8 = 8, 13 | UNK16 = 16, 14 | UNK32 = 32, 15 | UNK64 = 64, 16 | UNK128 = 128 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/PhraseIteration.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct PhraseIteration 6 | { 7 | public int PhraseId; 8 | public float StartTime; 9 | public float NextPhraseTime; 10 | 11 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 12 | public int[] Difficulty; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/SymbolsTexture.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct SymbolsTexture 6 | { 7 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 8 | public string Font; 9 | 10 | public int FontpathLength; 11 | public int Unk1_0; 12 | public int Width; 13 | public int Height; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Psarc/PsarcTOCEntry.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc 2 | { 3 | public class PsarcTOCEntry 4 | { 5 | public int Index { get; internal set; } 6 | public string Hash { get; internal set; } 7 | public uint StartBlock { get; internal set; } 8 | public ulong Length { get; internal set; } 9 | public ulong Offset { get; internal set; } 10 | public string Path { get; internal set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/PhraseExtraInfoByLevel.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 6 | public struct PhraseExtraInfoByLevel 7 | { 8 | public int PhraseId; 9 | public int Difficulty; 10 | public int Empty; 11 | public byte LevelJump; 12 | public short Redundant; 13 | public byte Padding; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Vocal.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Vocal 6 | { 7 | public float Time; 8 | public int Note; 9 | public float Length; 10 | 11 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)] 12 | public byte[] LyricBytes; 13 | 14 | public string Lyric => System.Text.Encoding.UTF8.GetString(LyricBytes); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Phrase.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Phrase 6 | { 7 | public byte Solo; 8 | public byte Disparity; 9 | public byte Ignore; 10 | public byte Padding; 11 | 12 | public int MaxDifficulty; 13 | public int PhraseIterationLinks; 14 | 15 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 16 | public string Name; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/BendData.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct BendData32 6 | { 7 | public float Time; 8 | public float Step; 9 | public short Unk3_0; 10 | public byte Unk4_0; 11 | public byte Unk5; 12 | } 13 | 14 | public struct BendData 15 | { 16 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 17 | public BendData32[] BendData32; 18 | 19 | public int UsedCount; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/SymbolDefinition.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Rect 6 | { 7 | public float yMin; 8 | public float xMin; 9 | public float yMax; 10 | public float xMax; 11 | } 12 | 13 | public struct SymbolDefinition 14 | { 15 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] 16 | public string Text; 17 | public Rect Rect_Outter; 18 | public Rect Rect_Inner; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Section.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Section 6 | { 7 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 8 | public string Name; 9 | 10 | public int Number; 11 | public float StartTime; 12 | public float EndTime; 13 | public int StartPhraseIterationId; 14 | public int EndPhraseIterationId; 15 | 16 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)] 17 | public string StringMask; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Chord.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct Chord 6 | { 7 | public uint Mask; 8 | 9 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 10 | public byte[] Frets; 11 | 12 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 13 | public byte[] Fingers; 14 | 15 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 16 | public int[] Notes; 17 | 18 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 19 | public string Name; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/NLinkedDifficulty.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Rocksmith2014PsarcLib.ReaderExtensions; 3 | 4 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 5 | { 6 | public struct NLinkedDifficulty : IBinarySerializable 7 | { 8 | public int LevelBreak; 9 | public int PhraseCount; 10 | 11 | public int[] NLD_Phrase; 12 | 13 | public IBinarySerializable Read(BinaryReader reader) 14 | { 15 | LevelBreak = reader.ReadInt32(); 16 | PhraseCount = reader.ReadInt32(); 17 | 18 | NLD_Phrase = reader.ReadIntArray(PhraseCount); 19 | 20 | return this; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/ChordNotes.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 4 | { 5 | public struct ChordNotes 6 | { 7 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 8 | public int[] NoteMask; 9 | 10 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 11 | public BendData[] BendData; 12 | 13 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 14 | public byte[] SlideTo; 15 | 16 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 17 | public byte[] SlideUnpitchTo; 18 | 19 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] 20 | public short[] Vibrato; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Rocksmith2014PsarcLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | default 5 | AnyCPU;x64 6 | enable 7 | true 8 | 9 | 10 | none 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Psarc/Asset/JsonLinqAsset.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System.IO; 4 | 5 | namespace Rocksmith2014PsarcLib.Psarc.Asset 6 | { 7 | public class JsonLinqAsset : PsarcAsset 8 | { 9 | public JObject JsonObject { get; private set; } 10 | 11 | public override void ReadFrom(MemoryStream stream) 12 | { 13 | base.ReadFrom(stream); 14 | 15 | using (var reader = new StreamReader(stream)) 16 | { 17 | using (var jsonReader = new JsonTextReader(reader)) 18 | { 19 | var serializer = new JsonSerializer(); 20 | 21 | JsonObject = serializer.Deserialize(jsonReader); 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ReaderExtensions/GeneralReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace Rocksmith2014PsarcLib.ReaderExtensions 5 | { 6 | public static class GeneralReaderExtensions 7 | { 8 | /// 9 | /// Reads a zero terminated string 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static string ReadString(this BinaryReader reader, int size, Encoding encoding = null) 16 | { 17 | var bytes = reader.ReadBytes(size); 18 | 19 | encoding ??= Encoding.UTF8; 20 | 21 | return encoding.GetString(bytes).TrimEnd((char)0); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Psarc/Asset/JsonObjectAsset.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.IO; 3 | 4 | namespace Rocksmith2014PsarcLib.Psarc.Asset 5 | { 6 | public class JsonObjectAsset : PsarcAsset 7 | { 8 | public T JsonObject { get; private set; } 9 | 10 | private static JsonSerializer Serializer { get; } = new JsonSerializer 11 | { 12 | TypeNameHandling = TypeNameHandling.None, 13 | MetadataPropertyHandling = MetadataPropertyHandling.Ignore 14 | }; 15 | 16 | public override void ReadFrom(MemoryStream stream) 17 | { 18 | base.ReadFrom(stream); 19 | 20 | using var reader = new StreamReader(stream); 21 | using var jsonReader = new JsonTextReader(reader); 22 | 23 | JsonObject = Serializer.Deserialize(jsonReader); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 kokolihapihvi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Psarc/Asset/TextPsarcAsset.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Rocksmith2014PsarcLib.Psarc.Asset 4 | { 5 | public class TextPsarcAsset : PsarcAsset 6 | { 7 | /// 8 | /// The text contents of this asset 9 | /// 10 | public string Text { get; private set; } 11 | 12 | /// 13 | /// Line separators for the property 14 | /// 15 | public string[] LineSeparators { get; set; } = new string[] { "\r\n", "\n" }; 16 | 17 | /// 18 | /// The text contents of this asset, split into an array of lines. 19 | /// 20 | /// Uses 21 | /// 22 | public string[] Lines 23 | { 24 | get 25 | { 26 | return Text.Split(LineSeparators, System.StringSplitOptions.None); 27 | } 28 | } 29 | 30 | public override void ReadFrom(MemoryStream stream) 31 | { 32 | using (var reader = new StreamReader(stream)) 33 | { 34 | Text = reader.ReadToEnd(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Psarc/Models/ToolkitInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Rocksmith2014PsarcLib.Psarc.Models 2 | { 3 | /// 4 | /// For CDLC provides information about Toolkit version, 5 | /// the package author, the package version and a comment. 6 | /// 7 | //https://github.com/rscustom/rocksmith-custom-song-toolkit/blob/master/RocksmithToolkitLib/DLCPackage/ToolkitInfo.cs 8 | public class ToolkitInfo 9 | { 10 | public string ToolkitVersion { get; set; } 11 | public string PackageAuthor { get; set; } 12 | public string PackageVersion { get; set; } 13 | public string PackageComment { get; set; } 14 | public string PackageRating { get; set; } 15 | 16 | /// 17 | /// Was the psarc file created with the rs custom toolkit 18 | /// 19 | public bool IsCustom 20 | { 21 | get 22 | { 23 | return ToolkitVersion != "Null"; 24 | } 25 | } 26 | 27 | public ToolkitInfo() 28 | { 29 | ToolkitVersion = "Null"; 30 | PackageAuthor = "Ubisoft"; 31 | PackageVersion = "0"; 32 | PackageComment = "Null"; 33 | PackageRating = "5"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Psarc/PsarcFileHeader.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.ReaderExtensions; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Rocksmith2014PsarcLib.Psarc 6 | { 7 | public class PsarcFileHeader 8 | { 9 | public string Identifier { get; private set; } 10 | public uint Version { get; private set; } 11 | public string Compression { get; private set; } 12 | public uint TOCSize { get; private set; } 13 | public uint TOCEntrySize { get; private set; } 14 | public long TOCOffset { get; private set; } 15 | public uint EntryCount { get; private set; } 16 | public uint BlockSize { get; private set; } 17 | public PsarcArchiveFlags ArchiveFlags { get; private set; } 18 | 19 | public PsarcFileHeader(PsarcFile psarcFile) 20 | { 21 | var reader = psarcFile.Reader; 22 | 23 | //Seek to the beginning 24 | reader.BaseStream.Seek(0, SeekOrigin.Begin); 25 | 26 | Identifier = Encoding.ASCII.GetString(reader.ReadBytes(4)); 27 | Version = reader.ReadUInt32Be(); 28 | Compression = Encoding.ASCII.GetString(reader.ReadBytes(4)); 29 | TOCSize = reader.ReadUInt32Be(); 30 | TOCEntrySize = reader.ReadUInt32Be(); // -32? 31 | EntryCount = reader.ReadUInt32Be(); 32 | BlockSize = reader.ReadUInt32Be(); 33 | ArchiveFlags = (PsarcArchiveFlags)reader.ReadUInt32Be(); 34 | 35 | TOCOffset = reader.BaseStream.Position; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Metadata.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.ReaderExtensions; 2 | using System.IO; 3 | 4 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 5 | { 6 | public struct Metadata : IBinarySerializable 7 | { 8 | public double MaxScore; 9 | public double MaxNotesAndChords; 10 | public double MaxNotesAndChords_Real; 11 | public double PointsPerNote; 12 | public float FirstBeatLength; 13 | public float StartTime; 14 | public byte CapoFretId; 15 | public string LastConversionDateTime; 16 | public short Part; 17 | public float SongLength; 18 | public int StringCount; 19 | public short[] Tuning; 20 | public float Unk11_FirstNoteTime; 21 | public float Unk12_FirstNoteTime; 22 | public int MaxDifficulty; 23 | 24 | public IBinarySerializable Read(BinaryReader reader) 25 | { 26 | MaxScore = reader.ReadDouble(); 27 | MaxNotesAndChords = reader.ReadDouble(); 28 | MaxNotesAndChords_Real = reader.ReadDouble(); 29 | PointsPerNote = reader.ReadDouble(); 30 | FirstBeatLength = reader.ReadSingle(); 31 | StartTime = reader.ReadSingle(); 32 | CapoFretId = reader.ReadByte(); 33 | LastConversionDateTime = reader.ReadString(32); 34 | Part = reader.ReadInt16(); 35 | SongLength = reader.ReadSingle(); 36 | 37 | StringCount = reader.ReadInt32(); 38 | Tuning = reader.ReadShortArray(StringCount); 39 | 40 | Unk11_FirstNoteTime = reader.ReadSingle(); 41 | Unk12_FirstNoteTime = reader.ReadSingle(); 42 | MaxDifficulty = reader.ReadInt32(); 43 | 44 | return this; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ReaderExtensions/ArrayReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Rocksmith2014PsarcLib.ReaderExtensions 4 | { 5 | public static class ArrayReaderExtensions 6 | { 7 | /// 8 | /// Read an array of count floats 9 | /// 10 | /// 11 | /// 12 | /// 13 | public static float[] ReadFloatArray(this BinaryReader reader, int count) 14 | { 15 | var arr = new float[count]; 16 | 17 | for (var i = 0; i < arr.Length; i++) 18 | { 19 | arr[i] = reader.ReadSingle(); 20 | } 21 | 22 | return arr; 23 | } 24 | 25 | /// 26 | /// Read an array of count ints 27 | /// 28 | /// 29 | /// 30 | /// 31 | public static int[] ReadIntArray(this BinaryReader reader, int count) 32 | { 33 | var arr = new int[count]; 34 | 35 | for (var i = 0; i < arr.Length; i++) 36 | { 37 | arr[i] = reader.ReadInt32(); 38 | } 39 | 40 | return arr; 41 | } 42 | 43 | /// 44 | /// Read an array of count shorts 45 | /// 46 | /// 47 | /// 48 | /// 49 | public static short[] ReadShortArray(this BinaryReader reader, int count) 50 | { 51 | var arr = new short[count]; 52 | 53 | for (var i = 0; i < arr.Length; i++) 54 | { 55 | arr[i] = reader.ReadInt16(); 56 | } 57 | 58 | return arr; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rocksmith2014PsarcLib 2 | Yet another rocksmith2014 psarc reader lib 3 | 4 | Designed to parse faster, but only supports unpacking 5 | 6 | # Example usage 7 | ```cs 8 | // Count total time elapsed 9 | var totaltime = new Stopwatch(); 10 | totaltime.Start(); 11 | 12 | // A concurrent collection for the dlc keys 13 | var allDlcKeys = new ConcurrentBag(); 14 | 15 | // Find all files in the dlc folder and subfolders 16 | var allfiles = Directory.GetFiles(@"G:\SteamLibrary\steamapps\common\Rocksmith2014\dlc", "*.psarc", SearchOption.AllDirectories).ToList(); 17 | // Manually add songs.psarc 18 | allfiles.Add(@"G:\SteamLibrary\steamapps\common\Rocksmith2014\songs.psarc"); 19 | 20 | // Go through all dlc files in parallel 21 | var partask = Parallel.ForEach(allfiles, (file) => 22 | { 23 | // Unnecessary list just for printing how many dlc keys it found in each psarc 24 | var dlckeys = new List(); 25 | try 26 | { 27 | // Use psarclib to parse the psarc 28 | using (var psarc = new PsarcFile(file)) 29 | { 30 | // Go through all manifests, add each arrangements song key to the list 31 | foreach (var manifest in psarc.ExtractArrangementManifests()) 32 | { 33 | dlckeys.Add(manifest.Attributes.SongKey); 34 | allDlcKeys.Add(manifest.Attributes.SongKey); 35 | } 36 | } 37 | 38 | Console.WriteLine($"Found {dlckeys.Distinct().Count()} dlc in {file}"); 39 | } 40 | // Handle exceptions, since corrupt cdlc is very common 41 | catch (Exception e) 42 | { 43 | Console.WriteLine($"Unable to read {file}"); 44 | Console.WriteLine(e.Message); 45 | Console.WriteLine(e.StackTrace); 46 | } 47 | }); 48 | 49 | // Wait for the parallel loop to complete 50 | while (!partask.IsCompleted) 51 | { 52 | Thread.Sleep(100); 53 | } 54 | 55 | // Stop timer 56 | totaltime.Stop(); 57 | 58 | // Remove all duplicate keys from the list, because all arrangements of a song have the same key 59 | var result = allDlcKeys.ToArray().Distinct().ToArray(); 60 | 61 | Console.WriteLine($"Completed in {totaltime.ElapsedMilliseconds}ms"); 62 | Console.WriteLine($"Parsed {allfiles.Count} files"); 63 | Console.WriteLine($"Found {result.Length} song keys"); 64 | 65 | Console.ReadLine(); 66 | ``` 67 | -------------------------------------------------------------------------------- /Psarc/Asset/ToolkitInfoAsset.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.Psarc.Models; 2 | using System; 3 | using System.IO; 4 | 5 | namespace Rocksmith2014PsarcLib.Psarc.Asset 6 | { 7 | public class ToolkitInfoAsset : TextPsarcAsset 8 | { 9 | public ToolkitInfo ToolkitInfo { get; private set; } = new ToolkitInfo(); 10 | 11 | public override void ReadFrom(MemoryStream stream) 12 | { 13 | base.ReadFrom(stream); 14 | 15 | //Single line means only toolkit version is included 16 | if (Lines.Length == 1) 17 | { 18 | ToolkitInfo.ToolkitVersion = Lines[0].Trim(); 19 | } 20 | else 21 | { 22 | //Go through all lines and parse "key: value" pairs 23 | foreach (var line in Lines) 24 | { 25 | var tokens = line.Split(':'); 26 | 27 | if (tokens.Length != 2) 28 | { 29 | Console.WriteLine(" Notice: Unrecognized line in toolkit.version: {0}", line); 30 | continue; 31 | } 32 | 33 | var key = tokens[0].Trim().ToLower(); 34 | 35 | switch (key) 36 | { 37 | case "toolkit version": 38 | ToolkitInfo.ToolkitVersion = tokens[1]; 39 | break; 40 | case "package author": 41 | ToolkitInfo.PackageAuthor = tokens[1]; 42 | break; 43 | case "package version": 44 | ToolkitInfo.PackageVersion = tokens[1]; 45 | break; 46 | case "package comment": 47 | ToolkitInfo.PackageComment = tokens[1]; 48 | break; 49 | case "package rating": 50 | ToolkitInfo.PackageRating = tokens[1]; 51 | break; 52 | default: 53 | Console.WriteLine(" Notice: Unknown key in toolkit.version: {0}", key); 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Psarc/Asset/DdsAsset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.IO; 6 | using System.Runtime.InteropServices; 7 | using Pfim; 8 | 9 | namespace Rocksmith2014PsarcLib.Psarc.Asset 10 | { 11 | public class DdsAsset : PsarcAsset 12 | { 13 | /// 14 | /// Uses ArrayPool to rent byte arrays to Pfim, by default Pfim creates a new byte array each time 15 | /// 16 | private class ArrayPoolAllocator : IImageAllocator 17 | { 18 | // Use the shared byte array pool 19 | private readonly ArrayPool pool = ArrayPool.Shared; 20 | 21 | public byte[] Rent(int size) 22 | { 23 | return pool.Rent(size); 24 | } 25 | 26 | public void Return(byte[] data) 27 | { 28 | pool.Return(data); 29 | } 30 | } 31 | 32 | /// 33 | /// Config used by Pfim to parse dds assets 34 | /// 35 | private readonly PfimConfig config = new PfimConfig(allocator: new ArrayPoolAllocator()); 36 | 37 | public Bitmap Bitmap { get; private set; } 38 | 39 | public override void ReadFrom(MemoryStream stream) 40 | { 41 | base.ReadFrom(stream); 42 | 43 | using var image = Pfimage.FromStream(stream, config); 44 | PixelFormat format; 45 | 46 | switch (image.Format) 47 | { 48 | case Pfim.ImageFormat.Rgba32: 49 | format = PixelFormat.Format32bppArgb; 50 | break; 51 | case Pfim.ImageFormat.Rgb24: 52 | format = PixelFormat.Format24bppRgb; 53 | break; 54 | default: 55 | // see the sample for more details 56 | throw new NotImplementedException(); 57 | } 58 | 59 | var handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); 60 | try 61 | { 62 | var data = Marshal.UnsafeAddrOfPinnedArrayElement(image.Data, 0); 63 | 64 | using var bm = new Bitmap(image.Width, image.Height, image.Stride, format, data); 65 | Bitmap = new Bitmap(bm); 66 | } 67 | finally 68 | { 69 | handle.Free(); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ReaderExtensions/StructReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using Rocksmith2014PsarcLib.Psarc.Models; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Rocksmith2014PsarcLib.ReaderExtensions 7 | { 8 | public static class StructReaderExtensions 9 | { 10 | /// 11 | /// Reads count structs from the stream 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static T[] ReadStructArray(this BinaryReader reader, int count) where T : struct 18 | { 19 | var arr = new T[count]; 20 | 21 | for (var i = 0; i < arr.Length; i++) 22 | { 23 | arr[i] = reader.ReadStruct(); 24 | } 25 | 26 | return arr; 27 | } 28 | 29 | /// 30 | /// Reads first 4 bytes as n, then reads n structs from the stream 31 | /// 32 | /// 33 | /// 34 | /// 35 | public static T[] ReadStructArray(this BinaryReader reader) where T : struct 36 | { 37 | var count = reader.ReadInt32(); 38 | 39 | return ReadStructArray(reader, count); 40 | } 41 | 42 | /// 43 | /// Reads a struct from the stream 44 | /// 45 | /// 46 | /// 47 | /// 48 | public static T ReadStruct(this BinaryReader reader) where T : struct 49 | { 50 | T _struct; 51 | 52 | //Some structs need more advanced logic, for example dynamic length arrays 53 | if (typeof(IBinarySerializable).IsAssignableFrom(typeof(T))) 54 | { 55 | _struct = new T(); 56 | return (T)(_struct as IBinarySerializable).Read(reader); 57 | } 58 | 59 | var size = Marshal.SizeOf(typeof(T)); 60 | 61 | var readBuffer = ArrayPool.Shared.Rent(size); 62 | reader.Read(readBuffer, 0, size); 63 | 64 | var handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned); 65 | 66 | _struct = Marshal.PtrToStructure(handle.AddrOfPinnedObject()); 67 | 68 | handle.Free(); 69 | 70 | ArrayPool.Shared.Return(readBuffer); 71 | 72 | return _struct; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ReaderExtensions/BigEndianReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Rocksmith2014PsarcLib.ReaderExtensions 5 | { 6 | public static class BigEndianReaderExtensions 7 | { 8 | /// 9 | /// Read bytes in big endian (reverse byte order) 10 | /// 11 | /// 12 | /// 13 | private static void ReadBytesBe(this BinaryReader reader, Span span) 14 | { 15 | reader.Read(span); 16 | span.Reverse(); 17 | } 18 | 19 | /// 20 | /// Read a 2 byte unsigned int in big endian (reverse byte order) 21 | /// 22 | /// 23 | /// 24 | public static ushort ReadUInt16Be(this BinaryReader reader) 25 | { 26 | Span span = stackalloc byte[2]; 27 | reader.ReadBytesBe(span); 28 | 29 | return BitConverter.ToUInt16(span); 30 | } 31 | 32 | /// 33 | /// Read a 3 byte unsigned int in big endian (reverse byte order) 34 | /// 35 | /// 36 | /// 37 | public static uint ReadUInt24Be(this BinaryReader reader) 38 | { 39 | //3 bytes 40 | var bytes = new byte[4]; 41 | 42 | Span span = stackalloc byte[3]; 43 | reader.ReadBytesBe(span); 44 | 45 | span.CopyTo(bytes); 46 | 47 | return BitConverter.ToUInt32(bytes, 0); 48 | } 49 | 50 | /// 51 | /// Read a 4 byte unsigned int in big endian (reverse byte order) 52 | /// 53 | /// 54 | /// 55 | public static uint ReadUInt32Be(this BinaryReader reader) 56 | { 57 | Span span = stackalloc byte[4]; 58 | reader.ReadBytesBe(span); 59 | 60 | return BitConverter.ToUInt32(span); 61 | } 62 | 63 | /// 64 | /// Read a 5 byte unsigned int in big endian (reverse byte order) 65 | /// 66 | /// 67 | /// 68 | public static ulong ReadUInt40Be(this BinaryReader reader) 69 | { 70 | //5 bytes 71 | var bytes = new byte[8]; 72 | 73 | Span span = stackalloc byte[5]; 74 | reader.ReadBytesBe(span); 75 | 76 | span.CopyTo(bytes); 77 | 78 | return BitConverter.ToUInt64(bytes, 0); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Psarc/PsarcTOC.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.Crypto; 2 | using Rocksmith2014PsarcLib.ReaderExtensions; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Rocksmith2014PsarcLib.Psarc 7 | { 8 | public class PsarcTOC : IDisposable 9 | { 10 | public List Entries { get; } 11 | public bool Encrypted { get; } 12 | 13 | private DecryptStream Decryptor { get; } 14 | 15 | public uint[] ZipBlockSizes { get; } 16 | 17 | public PsarcTOC(PsarcFile psarcFile) 18 | { 19 | Entries = new List((int)psarcFile.Header.EntryCount); 20 | 21 | Encrypted = psarcFile.Header.ArchiveFlags.HasFlag(PsarcArchiveFlags.TOC_ENCRYPTED); 22 | 23 | var reader = psarcFile.Reader; 24 | if (Encrypted) 25 | { 26 | //Setup decrypting stream 27 | Decryptor = new DecryptStream(psarcFile.FileStream, DecryptStream.DecryptMode.PSARC, psarcFile.Header.TOCSize); 28 | 29 | reader = Decryptor.Reader; 30 | } 31 | 32 | for (var i = 0; i < psarcFile.Header.EntryCount; i++) 33 | { 34 | Entries.Add(new PsarcTOCEntry 35 | { 36 | Index = i, 37 | Hash = BitConverter.ToString(reader.ReadBytes(16)).Replace("-", ""), 38 | StartBlock = reader.ReadUInt32Be(), 39 | Length = reader.ReadUInt40Be(), 40 | Offset = reader.ReadUInt40Be() 41 | }); 42 | } 43 | 44 | var bNum = (int)Math.Log(psarcFile.Header.BlockSize, 256); 45 | 46 | var tocChunkSize = (int)(psarcFile.Header.EntryCount * psarcFile.Header.TOCEntrySize);//(int)_reader.BaseStream.Position //don't alter this with. causes issues 47 | var zNum = (int)((psarcFile.Header.TOCSize - 32u - tocChunkSize) / bNum); 48 | ZipBlockSizes = new uint[zNum]; 49 | 50 | for (var i = 0; i < zNum; i++) 51 | { 52 | switch (bNum) 53 | { 54 | case 2://64KB 55 | ZipBlockSizes[i] = reader.ReadUInt16Be(); 56 | break; 57 | case 3://16MB 58 | ZipBlockSizes[i] = reader.ReadUInt24Be(); 59 | break; 60 | case 4://4GB 61 | ZipBlockSizes[i] = reader.ReadUInt32Be(); 62 | break; 63 | } 64 | } 65 | 66 | Decryptor?.Dispose(); 67 | Decryptor = null; 68 | } 69 | 70 | public void Dispose() 71 | { 72 | Decryptor?.Dispose(); 73 | GC.SuppressFinalize(this); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Psarc/Asset/SngAsset.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.Crypto; 2 | using Rocksmith2014PsarcLib.Psarc.Models.Sng; 3 | using Rocksmith2014PsarcLib.ReaderExtensions; 4 | using System; 5 | using System.IO; 6 | using Action = Rocksmith2014PsarcLib.Psarc.Models.Sng.Action; 7 | 8 | namespace Rocksmith2014PsarcLib.Psarc.Asset 9 | { 10 | public class SngAsset : PsarcAsset 11 | { 12 | [Flags] 13 | public enum AssetFlags 14 | { 15 | None = 0, 16 | Compressed = 1, 17 | Encrypted = 2 18 | } 19 | 20 | public Bpm[] BPMs; 21 | public Phrase[] Phrases; 22 | public Chord[] Chords; 23 | public ChordNotes[] ChordNotes; 24 | public Vocal[] Vocals; 25 | public SymbolsHeader[] SymbolHeaders; 26 | public SymbolsTexture[] SymbolTextures; 27 | public SymbolDefinition[] SymbolDefinitions; 28 | public PhraseIteration[] PhraseIterations; 29 | public PhraseExtraInfoByLevel[] PhraseExtraInfo; 30 | public NLinkedDifficulty[] NLD; 31 | public Action[] Actions; 32 | public Event[] Events; 33 | public Tone[] Tones; 34 | public Dna[] DNAs; 35 | public Section[] Sections; 36 | public Arrangement[] Arrangements; 37 | public Metadata Metadata; 38 | 39 | public override void ReadFrom(MemoryStream stream) 40 | { 41 | base.ReadFrom(stream); 42 | 43 | //Decrypt/uncompress 44 | using (var dcStream = new DecryptStream(stream, DecryptStream.DecryptMode.SNG, stream.Length)) 45 | { 46 | //Read beats 47 | BPMs = dcStream.Reader.ReadStructArray(); 48 | 49 | //Read phrases 50 | Phrases = dcStream.Reader.ReadStructArray(); 51 | 52 | //Read chords 53 | Chords = dcStream.Reader.ReadStructArray(); 54 | 55 | //Read chord notes 56 | ChordNotes = dcStream.Reader.ReadStructArray(); 57 | 58 | //Read vocals 59 | Vocals = dcStream.Reader.ReadStructArray(); 60 | 61 | //Read symbols if there were vocals 62 | if (Vocals.Length > 0) 63 | { 64 | //Read symbols header 65 | SymbolHeaders = dcStream.Reader.ReadStructArray(); 66 | 67 | //Read symbols texture 68 | SymbolTextures = dcStream.Reader.ReadStructArray(); 69 | 70 | //Read symbols definition 71 | SymbolDefinitions = dcStream.Reader.ReadStructArray(); 72 | } 73 | 74 | //Read phrase iterations 75 | PhraseIterations = dcStream.Reader.ReadStructArray(); 76 | 77 | //Read phrase extra info 78 | PhraseExtraInfo = dcStream.Reader.ReadStructArray(); 79 | 80 | //Read linked difficulties 81 | NLD = dcStream.Reader.ReadStructArray(); 82 | 83 | //Read actions 84 | Actions = dcStream.Reader.ReadStructArray(); 85 | 86 | //Read events 87 | Events = dcStream.Reader.ReadStructArray(); 88 | 89 | //Read tones switch events 90 | Tones = dcStream.Reader.ReadStructArray(); 91 | 92 | //Read DNAs 93 | DNAs = dcStream.Reader.ReadStructArray(); 94 | 95 | //Read sections 96 | Sections = dcStream.Reader.ReadStructArray
(); 97 | 98 | //Read arrangements 99 | Arrangements = dcStream.Reader.ReadStructArray(); 100 | 101 | //Read metadata 102 | Metadata = dcStream.Reader.ReadStruct(); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | .idea 244 | -------------------------------------------------------------------------------- /Psarc/Models/Sng/Arrangement.cs: -------------------------------------------------------------------------------- 1 | using Rocksmith2014PsarcLib.ReaderExtensions; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Rocksmith2014PsarcLib.Psarc.Models.Sng 6 | { 7 | public struct Anchor 8 | { 9 | public float StartBeatTime; 10 | public float EndBeatTime; 11 | public float Unk3_FirstNoteTime; 12 | public float Unk4_LastNoteTime; 13 | public byte FretId; 14 | 15 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 16 | public byte[] Padding; 17 | 18 | public int Width; 19 | public int PhraseIterationId; 20 | } 21 | 22 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 23 | public struct AnchorExtension 24 | { 25 | public float BeatTime; 26 | public byte FretId; 27 | public int Unk2_0; 28 | public short Unk3_0; 29 | public byte Unk4_0; 30 | } 31 | 32 | public struct Fingerprint 33 | { 34 | public int ChordId; 35 | public float StartTime; 36 | public float EndTime; 37 | public float Unk3_FirstNoteTime; 38 | public float Unk4_LastNoteTime; 39 | } 40 | 41 | public struct Note : IBinarySerializable 42 | { 43 | public uint NoteMask; 44 | public uint NoteFlags; 45 | public uint Hash; 46 | public float Time; 47 | public byte StringIndex; 48 | public byte FretId; 49 | public byte AnchorFretId; 50 | public byte AnchorWidth; 51 | public int ChordId; 52 | public int ChordNotesId; 53 | public int PhraseId; 54 | public int PhraseIterationId; 55 | public short[] FingerPrintId; 56 | public short NextIterNote; 57 | public short PrevIterNote; 58 | public short ParentPrevNote; 59 | public byte SlideTo; 60 | public byte SlideUnpitchTo; 61 | public byte LeftHand; 62 | public byte Tap; 63 | public byte PickDirection; 64 | public byte Slap; 65 | public byte Pluck; 66 | public short Vibrato; 67 | public float Sustain; 68 | public float MaxBend; 69 | public BendData32[] BendData; 70 | 71 | public IBinarySerializable Read(BinaryReader reader) 72 | { 73 | NoteMask = reader.ReadUInt32(); 74 | NoteFlags = reader.ReadUInt32(); 75 | Hash = reader.ReadUInt32(); 76 | Time = reader.ReadSingle(); 77 | StringIndex = reader.ReadByte(); 78 | FretId = reader.ReadByte(); 79 | AnchorFretId = reader.ReadByte(); 80 | AnchorWidth = reader.ReadByte(); 81 | ChordId = reader.ReadInt32(); 82 | ChordNotesId = reader.ReadInt32(); 83 | PhraseId = reader.ReadInt32(); 84 | PhraseIterationId = reader.ReadInt32(); 85 | 86 | FingerPrintId = reader.ReadShortArray(2); 87 | 88 | NextIterNote = reader.ReadInt16(); 89 | PrevIterNote = reader.ReadInt16(); 90 | ParentPrevNote = reader.ReadInt16(); 91 | SlideTo = reader.ReadByte(); 92 | SlideUnpitchTo = reader.ReadByte(); 93 | LeftHand = reader.ReadByte(); 94 | Tap = reader.ReadByte(); 95 | PickDirection = reader.ReadByte(); 96 | Slap = reader.ReadByte(); 97 | Pluck = reader.ReadByte(); 98 | Vibrato = reader.ReadInt16(); 99 | Sustain = reader.ReadSingle(); 100 | MaxBend = reader.ReadSingle(); 101 | BendData = reader.ReadStructArray(); 102 | 103 | return this; 104 | } 105 | } 106 | 107 | public struct Arrangement : IBinarySerializable 108 | { 109 | public int Difficulty; 110 | public Anchor[] Anchors; 111 | public AnchorExtension[] AnchorExtensions; 112 | public Fingerprint[] Fingerprints1; 113 | public Fingerprint[] Fingerprints2; 114 | public Note[] Notes; 115 | 116 | public int PhraseCount; 117 | public float[] AverageNotesPerIteration; 118 | public int PhraseIterationCount1; 119 | public int[] NotesInIteration1; 120 | public int PhraseIterationCount2; 121 | public int[] NotesInIteration2; 122 | 123 | public IBinarySerializable Read(BinaryReader reader) 124 | { 125 | Difficulty = reader.ReadInt32(); 126 | 127 | Anchors = reader.ReadStructArray(); 128 | AnchorExtensions = reader.ReadStructArray(); 129 | Fingerprints1 = reader.ReadStructArray(); 130 | Fingerprints2 = reader.ReadStructArray(); 131 | Notes = reader.ReadStructArray(); 132 | 133 | PhraseCount = reader.ReadInt32(); 134 | AverageNotesPerIteration = reader.ReadFloatArray(PhraseCount); 135 | 136 | PhraseIterationCount1 = reader.ReadInt32(); 137 | NotesInIteration1 = reader.ReadIntArray(PhraseIterationCount1); 138 | 139 | PhraseIterationCount2 = reader.ReadInt32(); 140 | NotesInIteration2 = reader.ReadIntArray(PhraseIterationCount2); 141 | 142 | return this; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Crypto/DecryptStream.cs: -------------------------------------------------------------------------------- 1 | using Org.BouncyCastle.Utilities.Zlib; 2 | using Rocksmith2014PsarcLib.Psarc.Asset; 3 | using System; 4 | using System.Buffers; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Security.Cryptography; 8 | 9 | namespace Rocksmith2014PsarcLib.Crypto 10 | { 11 | public class DecryptStream : IDisposable 12 | { 13 | public static readonly byte[] PsarcKey = new byte[32] 14 | { 15 | 0xC5, 0x3D, 0xB2, 0x38, 0x70, 0xA1, 0xA2, 0xF7, 16 | 0x1C, 0xAE, 0x64, 0x06, 0x1F, 0xDD, 0x0E, 0x11, 17 | 0x57, 0x30, 0x9D, 0xC8, 0x52, 0x04, 0xD4, 0xC5, 18 | 0xBF, 0xDF, 0x25, 0x09, 0x0D, 0xF2, 0x57, 0x2C 19 | }; 20 | 21 | public static readonly byte[] PsarcIV = new byte[16] 22 | { 23 | 0xE9, 0x15, 0xAA, 0x01, 24 | 0x8F, 0xEF, 0x71, 0xFC, 25 | 0x50, 0x81, 0x32, 0xE4, 26 | 0xBB, 0x4C, 0xEB, 0x42 27 | }; 28 | 29 | public static byte[] SngKeyPC = new byte[32] 30 | { 31 | 0xCB, 0x64, 0x8D, 0xF3, 0xD1, 0x2A, 0x16, 0xBF, 32 | 0x71, 0x70, 0x14, 0x14, 0xE6, 0x96, 0x19, 0xEC, 33 | 0x17, 0x1C, 0xCA, 0x5D, 0x2A, 0x14, 0x2E, 0x3E, 34 | 0x59, 0xDE, 0x7A, 0xDD, 0xA1, 0x8A, 0x3A, 0x30 35 | }; 36 | 37 | public static byte[] PCMetaDatKey = new byte[32] 38 | { 39 | 0x5F, 0xB0, 0x23, 0xEF, 0x19, 0xD5, 0xDC, 0x37, 40 | 0xAD, 0xDA, 0xC8, 0xF0, 0x17, 0xF8, 0x8F, 0x0E, 41 | 0x98, 0x18, 0xA3, 0xAC, 0x2F, 0x72, 0x46, 0x96, 42 | 0xA5, 0x9D, 0xE2, 0xBF, 0x05, 0x25, 0x12, 0xEB 43 | }; 44 | 45 | public enum DecryptMode 46 | { 47 | PSARC, 48 | SNG 49 | } 50 | 51 | public DecryptMode Mode { get; private set; } 52 | 53 | public BinaryReader Reader { get; private set; } 54 | 55 | private MemoryStream _decryptedStream; 56 | 57 | public DecryptStream(Stream input, DecryptMode mode, long length) 58 | { 59 | Mode = mode; 60 | 61 | switch (Mode) 62 | { 63 | case DecryptMode.PSARC: 64 | InitializePsarcDecryptor(input, length); 65 | break; 66 | case DecryptMode.SNG: 67 | InitializeSngDecryptor(input, length); 68 | break; 69 | default: 70 | throw new ArgumentException("Invalid decrypt mode", nameof(mode)); 71 | } 72 | } 73 | 74 | private void InitializePsarcDecryptor(Stream input, long length) 75 | { 76 | _decryptedStream = new MemoryStream((int)length); 77 | 78 | //Setup decrypting stream 79 | using (var decryptor = new RijndaelManaged()) 80 | { 81 | decryptor.Mode = CipherMode.CFB; 82 | decryptor.Padding = PaddingMode.None; 83 | decryptor.BlockSize = 128; 84 | decryptor.IV = new byte[16]; 85 | decryptor.Key = PsarcKey; 86 | 87 | using (var decryptStream = new CryptoStream(_decryptedStream, decryptor.CreateDecryptor(), CryptoStreamMode.Write)) 88 | { 89 | var buffer = new byte[512]; 90 | var pad = buffer.Length - (int)(length % buffer.Length); 91 | 92 | while (input.Position < length) 93 | { 94 | var size = (int)Math.Min(length - input.Position, buffer.Length); 95 | input.Read(buffer, 0, size); 96 | decryptStream.Write(buffer, 0, size); 97 | } 98 | 99 | if (pad > 0) 100 | decryptStream.Write(new byte[pad], 0, pad); 101 | 102 | decryptStream.Flush(); 103 | _decryptedStream = new MemoryStream(_decryptedStream.ToArray()); 104 | } 105 | } 106 | 107 | Reader = new BinaryReader(_decryptedStream); 108 | } 109 | 110 | private void InitializeSngDecryptor(Stream input, long length) 111 | { 112 | var sngFlags = SngAsset.AssetFlags.None; 113 | 114 | var decryptIV = new byte[16]; 115 | 116 | //Check and prepare for sng decrypting 117 | using (var rdr = new BinaryReader(input, System.Text.Encoding.Default, true)) 118 | { 119 | //Identifier? 120 | if (rdr.ReadUInt32() != 0x4A) 121 | { 122 | throw new InvalidDataException("Not a valid sng file"); 123 | } 124 | 125 | //Read asset flags 126 | sngFlags = (SngAsset.AssetFlags)rdr.ReadUInt32(); 127 | 128 | //Read encryption IV 129 | decryptIV = rdr.ReadBytes(16); 130 | 131 | //Adjust length by removing header size 132 | length -= 24; 133 | } 134 | 135 | _decryptedStream = new MemoryStream((int)length); 136 | 137 | // Decrypt using aes counter mode 138 | AesCtrTransform(SngKeyPC, decryptIV, input, _decryptedStream); 139 | 140 | Reader = new BinaryReader(_decryptedStream); 141 | _decryptedStream.Seek(0, SeekOrigin.Begin); 142 | 143 | if (sngFlags.HasFlag(SngAsset.AssetFlags.Compressed)) 144 | { 145 | //Uncompress 146 | long uncompressedSize = Reader.ReadUInt32(); 147 | 148 | _decryptedStream.Seek(4, SeekOrigin.Begin); 149 | using (var zOutputStream = new ZInputStream(_decryptedStream)) 150 | { 151 | var unzippedStream = new MemoryStream((int)uncompressedSize); 152 | 153 | zOutputStream.CopyTo(unzippedStream); 154 | 155 | unzippedStream.Seek(0, SeekOrigin.Begin); 156 | 157 | // Dispose the previous reader 158 | Reader.Dispose(); 159 | 160 | Reader = new BinaryReader(unzippedStream); 161 | } 162 | 163 | _decryptedStream.Dispose(); 164 | } 165 | } 166 | 167 | /// 168 | /// AES counter mode transform 169 | /// https://stackoverflow.com/a/51188472 170 | /// 171 | /// 172 | /// 173 | /// 174 | /// 175 | private static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, Stream outputStream) 176 | { 177 | SymmetricAlgorithm aes = new AesManaged { Mode = CipherMode.ECB, Padding = PaddingMode.None }; 178 | 179 | var blockSize = aes.BlockSize / 8; 180 | 181 | if (salt.Length != blockSize) 182 | { 183 | throw new ArgumentException($"Salt size must be same as block size (actual: {salt.Length}, expected: {blockSize})"); 184 | } 185 | 186 | var counter = (byte[])salt.Clone(); 187 | 188 | var xorMask = new Queue(); 189 | 190 | var zeroIv = new byte[blockSize]; 191 | var counterEncryptor = aes.CreateEncryptor(key, zeroIv); 192 | 193 | var counterModeBlock = ArrayPool.Shared.Rent(blockSize); 194 | 195 | int b; 196 | while ((b = inputStream.ReadByte()) != -1) 197 | { 198 | if (xorMask.Count == 0) 199 | { 200 | counterEncryptor.TransformBlock(counter, 0, counter.Length, counterModeBlock, 0); 201 | 202 | for (var i2 = counter.Length - 1; i2 >= 0; i2--) 203 | { 204 | if (++counter[i2] != 0) 205 | { 206 | break; 207 | } 208 | } 209 | 210 | for (var index = 0; index < blockSize; index++) 211 | { 212 | var b2 = counterModeBlock[index]; 213 | xorMask.Enqueue(b2); 214 | } 215 | } 216 | 217 | var mask = xorMask.Dequeue(); 218 | outputStream.WriteByte((byte)(((byte)b) ^ mask)); 219 | } 220 | 221 | ArrayPool.Shared.Return(counterModeBlock); 222 | } 223 | 224 | #region Disposable 225 | private bool disposedValue; 226 | protected virtual void Dispose(bool disposing) 227 | { 228 | if (!disposedValue) 229 | { 230 | if (disposing) 231 | { 232 | // TODO: dispose managed state (managed objects) 233 | Reader.Dispose(); 234 | _decryptedStream.Dispose(); 235 | } 236 | 237 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 238 | // TODO: set large fields to null 239 | disposedValue = true; 240 | } 241 | } 242 | 243 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 244 | // ~DecryptStream() 245 | // { 246 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 247 | // Dispose(disposing: false); 248 | // } 249 | 250 | public void Dispose() 251 | { 252 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 253 | Dispose(disposing: true); 254 | GC.SuppressFinalize(this); 255 | } 256 | #endregion 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Psarc/PsarcFile.cs: -------------------------------------------------------------------------------- 1 | using Org.BouncyCastle.Utilities.Zlib; 2 | using Rocksmith2014PsarcLib.Psarc.Asset; 3 | using Rocksmith2014PsarcLib.Psarc.Models; 4 | using Rocksmith2014PsarcLib.Psarc.Models.Json; 5 | using Rocksmith2014PsarcLib.ReaderExtensions; 6 | using System; 7 | using System.Buffers; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.IO.Compression; 11 | using System.Linq; 12 | 13 | namespace Rocksmith2014PsarcLib.Psarc 14 | { 15 | public class PsarcFile : IDisposable 16 | { 17 | public string FilePath { get; } 18 | 19 | public PsarcFileHeader Header { get; } 20 | 21 | public PsarcTOC TOC { get; } 22 | 23 | internal FileStream FileStream { get; } 24 | 25 | internal BinaryReader Reader { get; } 26 | 27 | public PsarcFile(string filePath) 28 | { 29 | FilePath = filePath; 30 | 31 | // Setup file stream 32 | FileStream = File.OpenRead(filePath); 33 | 34 | // Setup reader 35 | Reader = new BinaryReader(FileStream); 36 | 37 | // Read header 38 | Header = new PsarcFileHeader(this); 39 | 40 | // Load/decrypt table of contents 41 | TOC = new PsarcTOC(this); 42 | 43 | // Read manifest 44 | ReadManifest(); 45 | } 46 | 47 | public PsarcFile(FileInfo fileInfo) : this(fileInfo.FullName) 48 | { 49 | } 50 | 51 | /// 52 | /// Reads the filepath of all assets inside the psarc 53 | /// 54 | private void ReadManifest() 55 | { 56 | TOC.Entries[0].Path = "NamesBlock.bin"; 57 | 58 | var asset = InflateEntry(TOC.Entries[0]); 59 | 60 | var names = asset.Lines; 61 | 62 | for (var i = 0; i < names.Length; i++) 63 | { 64 | TOC.Entries[i + 1].Path = names[i]; 65 | } 66 | } 67 | 68 | /// 69 | /// Uncompresses a ZIP compressed byte array 70 | /// 71 | /// 72 | /// 73 | private byte[] UnzipBlock(byte[] zipped) 74 | { 75 | using var inStream = new MemoryStream(zipped); 76 | using var zOutputStream = new ZInputStream(inStream); 77 | using var outStream = new MemoryStream(); 78 | 79 | zOutputStream.CopyTo(outStream); 80 | 81 | return outStream.ToArray(); 82 | } 83 | 84 | private unsafe void UnzipBlock(BinaryReader from, Stream to, int size) 85 | { 86 | // Skip the header bytes, because DeflateStream does not recognize them for some reason 87 | var readsize = size - 2; 88 | Reader.BaseStream.Seek(2, SeekOrigin.Current); 89 | 90 | Span span = stackalloc byte[readsize]; 91 | 92 | from.Read(span); 93 | 94 | fixed (byte* pBuffer = &span[0]) 95 | { 96 | using var inStream = new UnmanagedMemoryStream(pBuffer, span.Length); 97 | using var decompressStream = new DeflateStream(inStream, CompressionMode.Decompress); 98 | 99 | decompressStream.CopyTo(to); 100 | } 101 | } 102 | 103 | /// 104 | /// Inflates entry into a stream, uncompressing if needed 105 | /// 106 | /// 107 | /// 108 | public void InflateEntry(PsarcTOCEntry entry, Stream stream) 109 | { 110 | const int ZipHeader = 0x78DA; 111 | var blockSize = (int)Header.BlockSize; 112 | 113 | var lastBlock = (int)(Math.Ceiling(entry.Length / (float)Header.BlockSize) + entry.StartBlock - 1); 114 | 115 | Reader.BaseStream.Seek((long)entry.Offset, SeekOrigin.Begin); 116 | 117 | Span buffer = stackalloc byte[blockSize]; 118 | 119 | for (var block = entry.StartBlock; block <= lastBlock; block++) 120 | { 121 | // Size of this zip block (0 for uncompressed) 122 | var zipblockSize = (int)TOC.ZipBlockSizes[block]; 123 | 124 | if (zipblockSize == 0) // raw. full cluster used. 125 | { 126 | Reader.Read(buffer); 127 | stream.Write(buffer); 128 | } 129 | else 130 | { 131 | var header = Reader.ReadUInt16Be(); 132 | // Seek 2 bytes backwards to include the header (or first 2 data bytes) still in the stream 133 | Reader.BaseStream.Seek(-2, SeekOrigin.Current); 134 | 135 | if (header == ZipHeader) // compressed 136 | { 137 | UnzipBlock(Reader, stream, zipblockSize); 138 | } 139 | else // uncompressed block 140 | { 141 | stream.Write(Reader.ReadBytes(zipblockSize), 0, zipblockSize); 142 | } 143 | } 144 | } 145 | 146 | stream.Seek(0, SeekOrigin.Begin); 147 | stream.Flush(); 148 | } 149 | 150 | /// 151 | /// Inflates entry into an asset object 152 | /// 153 | /// 154 | /// 155 | /// 156 | public T InflateEntry(PsarcTOCEntry entry) where T : PsarcAsset, new() 157 | { 158 | if (entry == null) return null; 159 | 160 | var asset = new T(); 161 | 162 | // Rent a buffer for inflating 163 | var inflatebuffer = ArrayPool.Shared.Rent((int)entry.Length); 164 | 165 | using var ms = new MemoryStream(inflatebuffer); 166 | // Resize the stream, as the buffer may be larger 167 | ms.SetLength((int)entry.Length); 168 | 169 | try 170 | { 171 | InflateEntry(entry, ms); 172 | 173 | asset.ReadFrom(ms); 174 | } 175 | catch (Exception e) 176 | { 177 | Console.WriteLine($"Error inflating entry {entry.Path}"); 178 | Console.WriteLine(e); 179 | throw; 180 | } 181 | finally // Always return rented buffer 182 | { 183 | ArrayPool.Shared.Return(inflatebuffer); 184 | } 185 | 186 | return asset; 187 | } 188 | 189 | /// 190 | /// Inflates the first entry that matches the pattern 191 | /// 192 | /// 193 | /// 194 | /// 195 | public T InflateEntry(Func where) where T : PsarcAsset, new() 196 | { 197 | var entry = TOC.Entries.FirstOrDefault(where); 198 | 199 | return InflateEntry(entry); 200 | } 201 | 202 | public List InflateEntries(Func where) where T : PsarcAsset, new() 203 | { 204 | var inflatedEntries = new List(); 205 | 206 | var entries = TOC.Entries.Where(where); 207 | 208 | foreach (var entry in entries) 209 | { 210 | inflatedEntries.Add(InflateEntry(entry)); 211 | } 212 | 213 | return inflatedEntries; 214 | } 215 | 216 | #region Convenience Zone 217 | 218 | public DdsAsset ExtractAlbumArt(SongArrangement.ArrangementAttributes attr) 219 | { 220 | string artPath = $"gfxassets/album_art/{attr.AlbumArt.Substring(14)}_256.dds"; 221 | 222 | var entry = TOC.Entries.FirstOrDefault(a => a.Path == artPath); 223 | 224 | if (entry == null) throw new KeyNotFoundException(); 225 | 226 | return InflateEntry(entry); 227 | } 228 | 229 | /// 230 | /// Extract toolkitinfo, returns default values if not found 231 | /// 232 | /// 233 | public ToolkitInfo ExtractToolkitInfo() 234 | { 235 | var tkAsset = InflateEntry(entry => entry.Path?.Equals("toolkit.version") ?? false); 236 | 237 | if (tkAsset != null) return tkAsset.ToolkitInfo; 238 | 239 | return new ToolkitInfo(); 240 | } 241 | 242 | /// 243 | /// Extracts all json files and assumes they are song arrangements, should work for all song psarc files 244 | /// 245 | /// 246 | public List ExtractArrangementManifests() 247 | { 248 | var list = InflateEntries>(asset => asset.Path.EndsWith(".json")); 249 | 250 | return list.Select(l => l.JsonObject).ToList(); 251 | } 252 | 253 | #endregion 254 | 255 | #region Disposable 256 | 257 | private bool disposedValue; 258 | 259 | protected virtual void Dispose(bool disposing) 260 | { 261 | if (!disposedValue) 262 | { 263 | if (disposing) 264 | { 265 | // TODO: dispose managed state (managed objects) 266 | Reader.Dispose(); 267 | FileStream.Dispose(); 268 | } 269 | 270 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 271 | // TODO: set large fields to null 272 | disposedValue = true; 273 | } 274 | } 275 | 276 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 277 | // ~PsarcFile() 278 | // { 279 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 280 | // Dispose(disposing: false); 281 | // } 282 | 283 | public void Dispose() 284 | { 285 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 286 | Dispose(disposing: true); 287 | GC.SuppressFinalize(this); 288 | } 289 | 290 | #endregion 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Psarc/Models/Json/SongArrangement.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Rocksmith2014PsarcLib.Psarc.Models.Json 7 | { 8 | [Serializable] 9 | public class SongArrangement 10 | { 11 | [Serializable] 12 | public class ArrangementAttributes 13 | { 14 | [Serializable] 15 | public class Properties 16 | { 17 | [JsonProperty("represent")] 18 | public int Represent { get; set; } 19 | [JsonProperty("bonusArr")] 20 | public int BonusArr { get; set; } 21 | [JsonProperty("standardTuning")] 22 | public int StandardTuning { get; set; } 23 | [JsonProperty("nonStandardChords")] 24 | public int NonStandardChords { get; set; } 25 | [JsonProperty("barreChords")] 26 | public int BarreChords { get; set; } 27 | [JsonProperty("powerChords")] 28 | public int PowerChords { get; set; } 29 | [JsonProperty("dropDPower")] 30 | public int DropDPower { get; set; } 31 | [JsonProperty("openChords")] 32 | public int OpenChords { get; set; } 33 | [JsonProperty("fingerPicking")] 34 | public int FingerPicking { get; set; } 35 | [JsonProperty("pickDirection")] 36 | public int PickDirection { get; set; } 37 | [JsonProperty("doubleStops")] 38 | public int DoubleStops { get; set; } 39 | [JsonProperty("palmMutes")] 40 | public int PalmMutes { get; set; } 41 | [JsonProperty("harmonics")] 42 | public int Harmonics { get; set; } 43 | [JsonProperty("pinchHarmonics")] 44 | public int PinchHarmonics { get; set; } 45 | [JsonProperty("hopo")] 46 | public int Hopo { get; set; } 47 | [JsonProperty("tremolo")] 48 | public int Tremolo { get; set; } 49 | [JsonProperty("slides")] 50 | public int Slides { get; set; } 51 | [JsonProperty("unpitchedSlides")] 52 | public int UnpitchedSlides { get; set; } 53 | [JsonProperty("bends")] 54 | public int Bends { get; set; } 55 | [JsonProperty("tapping")] 56 | public int Tapping { get; set; } 57 | [JsonProperty("vibrato")] 58 | public int Vibrato { get; set; } 59 | [JsonProperty("fretHandMutes")] 60 | public int FretHandMutes { get; set; } 61 | [JsonProperty("slapPop")] 62 | public int SlapPop { get; set; } 63 | [JsonProperty("twoFingerPicking")] 64 | public int TwoFingerPicking { get; set; } 65 | [JsonProperty("fifthsAndOctaves")] 66 | public int FifthsAndOctaves { get; set; } 67 | [JsonProperty("syncopation")] 68 | public int Syncopation { get; set; } 69 | [JsonProperty("bassPick")] 70 | public int BassPick { get; set; } 71 | [JsonProperty("sustain")] 72 | public int Sustain { get; set; } 73 | [JsonProperty("pathLead")] 74 | public int PathLead { get; set; } 75 | [JsonProperty("pathRhythm")] 76 | public int PathRhythm { get; set; } 77 | [JsonProperty("pathBass")] 78 | public int PathBass { get; set; } 79 | [JsonProperty("routeMask")] 80 | public int RouteMask { get; set; } 81 | } 82 | 83 | [Serializable] 84 | public class PhraseIteration 85 | { 86 | public int PhraseIndex { get; set; } 87 | public int MaxDifficulty { get; set; } 88 | 89 | public string Name { get; set; } 90 | 91 | public float StartTime { get; set; } 92 | public float EndTime { get; set; } 93 | } 94 | 95 | [Serializable] 96 | public class Phrase 97 | { 98 | public int MaxDifficulty { get; set; } 99 | 100 | public string Name { get; set; } 101 | 102 | public int IterationCount { get; set; } 103 | } 104 | 105 | [Serializable] 106 | public class Section 107 | { 108 | public string Name { get; set; } 109 | public string UIName { get; set; } 110 | 111 | public int Number { get; set; } 112 | 113 | public float StartTime { get; set; } 114 | public float EndTime { get; set; } 115 | 116 | public int StartPhraseIterationIndex { get; set; } 117 | public int EndPhraseIterationIndex { get; set; } 118 | 119 | public bool IsSolo { get; set; } 120 | } 121 | 122 | [Serializable] 123 | public class Tone 124 | { 125 | [Serializable] 126 | public class ToneGear 127 | { 128 | [JsonProperty("Type")] 129 | public string Type { get; set; } 130 | public string Key { get; set; } 131 | public string Category { get; set; } 132 | 133 | public Dictionary KnobValues { get; set; } 134 | } 135 | 136 | public Dictionary> GearList { get; set; } 137 | public List ToneDescriptors { get; set; } 138 | 139 | public string NameSeparator { get; set; } 140 | public bool IsCustom { get; set; } 141 | public string Volume { get; set; } 142 | public string Key { get; set; } 143 | public string Name { get; set; } 144 | public float SortOrder { get; set; } 145 | } 146 | 147 | public class ArrangementTuning 148 | { 149 | [JsonProperty("string0")] 150 | public int String0 { get; set; } 151 | [JsonProperty("string1")] 152 | public int String1 { get; set; } 153 | [JsonProperty("string2")] 154 | public int String2 { get; set; } 155 | [JsonProperty("string3")] 156 | public int String3 { get; set; } 157 | [JsonProperty("string4")] 158 | public int String4 { get; set; } 159 | [JsonProperty("string5")] 160 | public int String5 { get; set; } 161 | } 162 | 163 | public string AlbumArt { get; set; } 164 | public string AlbumName { get; set; } 165 | public string AlbumNameSort { get; set; } 166 | public string ArrangementName { get; set; } 167 | 168 | public Properties ArrangementProperties { get; set; } 169 | 170 | public int ArrangementSort { get; set; } 171 | public int ArrangementType { get; set; } 172 | 173 | public string ArtistName { get; set; } 174 | public string ArtistNameSort { get; set; } 175 | public string BlockAsset { get; set; } 176 | 177 | public float CapoFret { get; set; } 178 | public float CentOffset { get; set; } 179 | 180 | public bool DLC { get; set; } 181 | 182 | public float DNA_Chords { get; set; } 183 | public float DNA_Riffs { get; set; } 184 | public float DNA_Solo { get; set; } 185 | 186 | public List DynamicVisualDensity { get; set; } 187 | 188 | public float EasyMastery { get; set; } 189 | 190 | public string FullName { get; set; } 191 | public string LastConversionDateTime { get; set; } 192 | 193 | public int? LeaderboardChallengeRating { get; set; } 194 | 195 | public string ManifestUrn { get; set; } 196 | 197 | public int MasterID_PS3 { get; set; } 198 | public int MasterID_RDV { get; set; } 199 | public int MasterID_XBox360 { get; set; } 200 | public int MaxPhraseDifficulty { get; set; } 201 | 202 | public float MediumMastery { get; set; } 203 | public float NotesEasy { get; set; } 204 | public float NotesHard { get; set; } 205 | public float NotesMedium { get; set; } 206 | 207 | public List PhraseIterations { get; set; } 208 | public List Phrases { get; set; } 209 | 210 | public string PreviewBankPath { get; set; } 211 | 212 | public int RelativeDifficulty { get; set; } 213 | 214 | public float Score_MaxNotes { get; set; } 215 | public float Score_PNV { get; set; } 216 | 217 | public List
Sections { get; set; } 218 | 219 | public bool Shipping { get; set; } 220 | 221 | public string ShowlightsXML { get; set; } 222 | public string SKU { get; set; } 223 | public string SongAsset { get; set; } 224 | 225 | public float SongAverageTempo { get; set; } 226 | 227 | public string SongBank { get; set; } 228 | 229 | public float SongDiffEasy { get; set; } 230 | public float SongDiffHard { get; set; } 231 | public float SongDifficulty { get; set; } 232 | public float SongDiffMed { get; set; } 233 | 234 | public string SongEvent { get; set; } 235 | public string SongKey { get; set; } 236 | 237 | public float SongLength { get; set; } 238 | 239 | public string SongName { get; set; } 240 | public string SongNameSort { get; set; } 241 | 242 | public float SongOffset { get; set; } 243 | 244 | public int SongPartition { get; set; } 245 | 246 | public string SongXml { get; set; } 247 | 248 | public int SongYear { get; set; } 249 | 250 | public long TargetScore { get; set; } 251 | 252 | public Dictionary>> Techniques { get; set; } 253 | 254 | public string Tone_A { get; set; } 255 | public string Tone_B { get; set; } 256 | public string Tone_Base { get; set; } 257 | public string Tone_C { get; set; } 258 | public string Tone_D { get; set; } 259 | public string Tone_Multiplayer { get; set; } 260 | 261 | //public List Tones { get; set; } 262 | 263 | public ArrangementTuning Tuning { get; set; } 264 | 265 | public string PersistentID { get; set; } 266 | } 267 | 268 | public ArrangementAttributes Attributes { get; private set; } 269 | 270 | /// 271 | /// This is probably illegal in more than 100 countries, but who needs json converters when you can do this 272 | /// 273 | public Dictionary> Entries 274 | { 275 | set 276 | { 277 | Attributes = value.First().Value.First().Value; 278 | } 279 | } 280 | 281 | public string ModelName { get; set; } 282 | public int IterationVersion { get; set; } 283 | public string InsertRoot { get; set; } 284 | } 285 | } 286 | --------------------------------------------------------------------------------