HMX Forge (RB4/DCS/Amp16/FME) save game decryptor/encryptor

add serialization to amp2016 save classes

+31 -1
Saves/Amp2016/AmpCampaign.cs
··· 8 8 public byte mDifficulty; 9 9 #pragma warning restore CS8618 10 10 11 + public void WriteToStream(Stream stream, bool isBE = false) 12 + { 13 + if (isBE) 14 + stream.WriteUInt16BE(mScore); 15 + else 16 + stream.WriteUInt16LE(mScore); 17 + stream.WriteUInt8(mZero); 18 + stream.WriteUInt8(mDifficulty); 19 + } 20 + 11 21 public static AmpCampaignScore ReadFromStream(Stream stream, bool isBE = false) 12 22 { 13 23 AmpCampaignScore score = new(); ··· 28 38 #pragma warning disable CS8618 29 39 public byte mUnknown1; // possibly bool mCompletedCampaign or bool mStartedCampaign? 30 40 public byte mUnknown2; 31 - public byte mUnknownPS3Byte; // only exists in PS3 saves 41 + public byte mUnknownPS3Byte; // only exists in PS3 saves, 0x01? 32 42 public uint mUnknown3; // 127 on a complete save, 0 on fresh 33 43 public byte mUnknown4; // 0 on both 34 44 public byte mUnknown5; // 255 on a complete save, 2 on fresh ··· 38 48 public byte mUnknown7; // always 0xFF 39 49 public AmpCampaignScore[] mScores; 40 50 #pragma warning restore CS8618 51 + 52 + public void WriteToStream(Stream stream, bool isPS3 = false) 53 + { 54 + stream.WriteUInt8(mUnknown1); 55 + stream.WriteUInt8(mUnknown2); 56 + if (isPS3) 57 + stream.WriteUInt8(mUnknownPS3Byte); 58 + if (isPS3) 59 + stream.WriteUInt32BE(mUnknown3); 60 + else 61 + stream.WriteUInt32LE(mUnknown3); 62 + stream.WriteUInt8(mUnknown4); 63 + stream.WriteUInt8(mUnknown5); 64 + stream.WriteUInt8(mUnknown6); 65 + stream.WriteLengthUTF8(mNextSong, isPS3); 66 + stream.WriteUInt8(mSongIndex); 67 + stream.WriteUInt8(mUnknown7); 68 + for (int i = 0; i < 15; i++) 69 + mScores[i].WriteToStream(stream, isPS3); 70 + } 41 71 42 72 public static AmpCampaignPersistentData ReadFromStream(Stream stream, bool isPS3 = false) 43 73 {
+48
Saves/Amp2016/AmpProfile.cs
··· 8 8 public int mUnknown3; 9 9 #pragma warning restore CS8618 10 10 11 + public void WriteToStream(Stream stream, bool isBE = false) 12 + { 13 + stream.WriteUInt8(mUnknown1); 14 + stream.WriteUInt8(mUnknown2); 15 + if (isBE) 16 + stream.WriteUInt32BE((uint)mUnknown3); 17 + else 18 + stream.WriteInt32LE(mUnknown3); 19 + } 20 + 11 21 public static AmpProfileFooter ReadFromStream(Stream stream, bool isBE = false) 12 22 { 13 23 AmpProfileFooter footer = new(); ··· 35 45 public AmpCampaignPersistentData mCampaignData; 36 46 public AmpProfileFooter mFooter; 37 47 #pragma warning restore CS8618 48 + 49 + public void WriteToStream(Stream stream, bool isPS3 = false) 50 + { 51 + RevisionStream rev = new RevisionStream(stream, 0x1, isPS3); 52 + 53 + rev.WriteUInt8(mVersion); 54 + rev.WriteLengthUTF8(mUnused, isPS3); 55 + if (isPS3) 56 + rev.WriteUInt32BE((uint)mSongData.Length); 57 + else 58 + rev.WriteInt32LE(mSongData.Length); 59 + for (int i = 0; i < mSongData.Length; i++) 60 + mSongData[i].WriteToStream(rev, isPS3); 61 + 62 + mOptions.WriteToStream(rev, isPS3); 63 + rev.WriteUInt8(mUnknown); 64 + 65 + if (isPS3) 66 + rev.WriteUInt32BE((uint)mUnlocks.Length); 67 + else 68 + rev.WriteInt32LE(mUnlocks.Length); 69 + for (int i = 0; i < mUnlocks.Length; i++) 70 + rev.WriteLengthUTF8(mUnlocks[i], isPS3); 71 + 72 + mCampaignData.WriteToStream(rev, isPS3); 73 + mFooter.WriteToStream(rev, isPS3); 74 + 75 + // PS3 pads the save file 76 + if (isPS3) 77 + { 78 + // TODO: is this correct? 79 + long numPadding = 0x7FFE - (rev.Position) - 5 - 4; 80 + byte[] bytesToWrite = new byte[numPadding]; 81 + rev.Write(bytesToWrite, 0, (int)numPadding); 82 + } 83 + 84 + rev.FinishWriting(); 85 + } 38 86 39 87 public static AmpProfile ReadFromStream(Stream stream, bool isPS3 = false) 40 88 {
+14
Saves/Amp2016/AmpSong.cs
··· 12 12 public byte mProgress; // always 100 13 13 #pragma warning restore CS8618 14 14 15 + public void WriteToStream(Stream stream, bool isBE = false) 16 + { 17 + stream.WriteLengthUTF8(mSongName, isBE); 18 + if (isBE) 19 + stream.WriteUInt16BE(mScore); 20 + else 21 + stream.WriteUInt16LE(mScore); 22 + stream.WriteUInt8(mDifficulty); 23 + stream.WriteUInt8(mHighestStreak); 24 + stream.WriteUInt8(mPercentCleared); 25 + stream.WriteUInt8(mScoreType); 26 + stream.WriteUInt8(mProgress); 27 + } 28 + 15 29 public static AmpSongPersistentData ReadFromStream(Stream stream, bool isBE = false) 16 30 { 17 31 AmpSongPersistentData song = new();
+23
Saves/Amp2016/AmpSystemOptions.cs
··· 9 9 public int[] mFlags2; // always 2 entries 10 10 #pragma warning restore CS8618 11 11 12 + public void WriteToStream(Stream stream, bool isBE = false) 13 + { 14 + stream.WriteLengthUTF8(mControlScheme, isBE); 15 + if (isBE) 16 + { 17 + stream.WriteUInt32BE((uint)mFlags1.Length); 18 + for (int i = 0; i < mFlags1.Length; i++) 19 + stream.WriteUInt32BE((uint)mFlags1[i]); 20 + stream.WriteUInt32BE((uint)mFlags2.Length); 21 + for (int i = 0; i < mFlags2.Length; i++) 22 + stream.WriteUInt32BE((uint)mFlags2[i]); 23 + } 24 + else 25 + { 26 + stream.WriteInt32LE(mFlags1.Length); 27 + for (int i = 0; i < mFlags1.Length; i++) 28 + stream.WriteInt32LE(mFlags1[i]); 29 + stream.WriteInt32LE(mFlags2.Length); 30 + for (int i = 0; i < mFlags2.Length; i++) 31 + stream.WriteInt32LE(mFlags2[i]); 32 + } 33 + } 34 + 12 35 public static AmpSystemOptionsPersistentData ReadFromStream(Stream stream, bool isBE = false) 13 36 { 14 37 AmpSystemOptionsPersistentData options = new();
+97
Saves/RB4/RBSystemOptions.cs
··· 11 11 public bool mHasCalibrated; 12 12 #pragma warning restore CS8618 13 13 14 + public void WriteToStream(Stream stream) 15 + { 16 + RevisionStream rev = new RevisionStream(stream, 0x5); 17 + 18 + rev.WriteFloat(mOverscan); 19 + rev.WriteFloat(mAudioOffset); 20 + rev.WriteFloat(mVideoOffset); 21 + rev.WriteFloat(mDialogVolume); 22 + rev.WriteFloat(mGammaValue); 23 + rev.WriteUInt8(mHasCalibrated ? (byte)0x01 : (byte)0x00); 24 + 25 + rev.FinishWriting(); 26 + } 27 + 14 28 public static SystemOptions ReadFromStream(Stream stream) 15 29 { 16 30 SystemOptions opt = new(); ··· 66 80 public RBSongCache mSongCache; 67 81 #pragma warning restore CS8618 68 82 83 + public void WriteToStream(Stream stream) 84 + { 85 + RevisionStream rev = new RevisionStream(stream, 0x1C); 86 + 87 + mSystemOptions.WriteToStream(rev); 88 + rev.WriteInt32LE(mBackgroundVolume); 89 + rev.WriteInt32LE(mInstrumentsVolume); 90 + rev.WriteInt32LE(mSFXVolume); 91 + rev.WriteInt32LE(mCrowdVolume); 92 + rev.WriteUInt8(mDolby ? (byte)0x01 : (byte)0x00); 93 + rev.WriteUInt8(mBassBoost ? (byte)0x01 : (byte)0x00); 94 + rev.WriteUInt8(mOverscan ? (byte)0x01 : (byte)0x00); 95 + rev.WriteInt32LE(mProDrumCymbalLanes); 96 + rev.WriteUInt8(mIsDrumNavigationAllowed ? (byte)0x01 : (byte)0x00); 97 + rev.WriteUInt8(mNoFail ? (byte)0x01 : (byte)0x00); 98 + rev.WriteUInt8(mIndependentTrackSpeeds ? (byte)0x01 : (byte)0x00); 99 + rev.WriteUInt8(mImprovSolosScored ? (byte)0x01 : (byte)0x00); 100 + rev.WriteUInt8(mAwesomenessDetection ? (byte)0x01 : (byte)0x00); 101 + rev.WriteUInt8(mLeaderboardLadder ? (byte)0x01 : (byte)0x00); 102 + rev.WriteUInt8(mHide1RatedSongs ? (byte)0x01 : (byte)0x00); 103 + rev.WriteUInt8(mUseSubtitles ? (byte)0x01 : (byte)0x00); 104 + rev.WriteInt32LE(mSongLibSort); 105 + rev.WriteInt32LE(mLastPlayedPatchVersion); 106 + rev.WriteUInt8(mSeenRivalsUpsell ? (byte)0x01 : (byte)0x00); 107 + rev.WriteUInt8(mDrumAutoKickEnabled ? (byte)0x01 : (byte)0x00); 108 + rev.WriteLengthUTF8(mUnknown1); 109 + rev.WriteLengthUTF8(mUnknown2); 110 + rev.WriteUInt8(mHideUnavailableSOMPSongs ? (byte)0x01 : (byte)0x00); 111 + rev.WriteUInt8(mUnknown3 ? (byte)0x01 : (byte)0x00); 112 + rev.WriteUInt32LE(mUnknown4); 113 + rev.WriteUInt32LE(mUnknown5); 114 + rev.WriteUInt8(mUnknown6 ? (byte)0x01 : (byte)0x00); 115 + rev.WriteUInt32LE(mUnknown7); 116 + rev.WriteUInt32LE(mUnknown8); 117 + mSongCache.WriteToStream(rev); 118 + 119 + rev.FinishWriting(); 120 + } 121 + 69 122 public static RBSystemOptions ReadFromStream(Stream stream) 70 123 { 71 124 RBSystemOptions opt = new RBSystemOptions(); ··· 123 176 // the number of refreshes it's been since the song was last seen 124 177 // and the other is the most recent changelist version. 125 178 179 + public void WriteToStream(Stream stream) 180 + { 181 + stream.Write(mData, 0, 0x4B8); 182 + } 183 + 126 184 public static RBSongMetadata ReadFromStream(Stream stream) 127 185 { 128 186 RBSongMetadata meta = new(); ··· 143 201 public Dictionary<int, RBSongMetadata> mMetadataMap; 144 202 public int[] mRecentlyAcquiredSongs; 145 203 #pragma warning restore CS8618 204 + 205 + public void WriteToStream(Stream stream) 206 + { 207 + RevisionStream rev = new RevisionStream(stream, 0x8, false); 208 + 209 + rev.WriteInt32LE(mCacheVersion); 210 + 211 + rev.WriteInt32LE(mSongsInContent.Count); 212 + foreach (string content in mSongsInContent.Keys) 213 + { 214 + rev.WriteLengthUTF8(content); 215 + rev.WriteInt32LE(mSongsInContent[content].Length); 216 + for (int i = 0; i < mSongsInContent[content].Length; i++) 217 + rev.WriteInt32LE(mSongsInContent[content][i]); 218 + } 219 + 220 + rev.WriteInt32LE(mShortNametoSongDir.Count); 221 + foreach (string shortname in mShortNametoSongDir.Keys) 222 + { 223 + rev.WriteLengthUTF8(shortname); 224 + rev.WriteLengthUTF8(mShortNametoSongDir[shortname]); 225 + } 226 + 227 + rev.WriteInt32LE(0x4B8); 228 + 229 + rev.WriteInt32LE(mMetadataMap.Count); 230 + foreach (int songId in mMetadataMap.Keys) 231 + { 232 + rev.WriteInt32LE(songId); 233 + mMetadataMap[songId].WriteToStream(stream); 234 + } 235 + 236 + rev.WriteInt32LE(mRecentlyAcquiredSongs.Length); 237 + for (int i = 0; i < mRecentlyAcquiredSongs.Length; i++) 238 + rev.WriteInt32LE(mRecentlyAcquiredSongs[i]); 239 + 240 + rev.FinishWriting(); 241 + return; 242 + } 146 243 147 244 public static RBSongCache ReadFromStream(Stream stream) 148 245 {
+22
Saves/RevisionStream.cs
··· 7 7 private int _curVersion; 8 8 private bool _bigEndian; 9 9 10 + // Reading constructor 10 11 public RevisionStream(Stream original, int minVersion, int curVersion, bool bigEndian = false) 11 12 { 12 13 byte magic = original.ReadUInt8(); ··· 31 32 _bigEndian = bigEndian; 32 33 } 33 34 35 + // Writing constructor 36 + public RevisionStream(Stream original, int curVersion, bool bigEndian = false) 37 + { 38 + original.WriteUInt8(0x7A); 39 + 40 + if (bigEndian) 41 + original.WriteUInt32BE((uint)curVersion); 42 + else 43 + original.WriteInt32LE(curVersion); 44 + 45 + Version = curVersion; 46 + _stream = original; 47 + _curVersion = curVersion; 48 + _bigEndian = bigEndian; 49 + } 50 + 34 51 public override bool CanRead => _stream.CanRead; 35 52 36 53 public override bool CanSeek => _stream.CanSeek; ··· 46 63 byte magic = _stream.ReadUInt8(); 47 64 if (magic != 0x7B) 48 65 throw new Exception($"Unexpected {magic:X2} at end of RevisionStream!"); 66 + } 67 + 68 + public void FinishWriting() 69 + { 70 + _stream.WriteUInt8(0x7B); 49 71 } 50 72 51 73 public override void Flush()
+8
StreamExtensions.cs
··· 58 58 s.Write(tmp, 0, 2); 59 59 } 60 60 61 + public static void WriteUInt16BE(this Stream s, ushort i) 62 + { 63 + byte[] tmp = new byte[2]; 64 + tmp[0] = (byte)((i >> 8) & 0xFF); 65 + tmp[1] = (byte)(i & 0xFF); 66 + s.Write(tmp, 0, 2); 67 + } 68 + 61 69 /// <summary> 62 70 /// Read an unsigned 16-bit Big-endian integer from the stream. 63 71 /// </summary>