HMX Forge (RB4/DCS/Amp16/FME) save game decryptor/encryptor
at master 4.7 kB view raw
1/* 2 * EncryptedWriteRevisionStream.cs 3 * 4 * Copyright (c) 2015,2016,2017 maxton. All rights reserved. 5 * Copyright (c) 2025 Emma / InvoxiPlayGames 6 * 7 * 14-Oct-2025(Emma): Adapted the EncryptedWriteStream class to be 8 * more well-equipped for writing encrypted RevisionStreams. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 3.0 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; If not, see 22 * <http://www.gnu.org/licenses/>. 23 */ 24using System; 25using System.IO; 26 27namespace LibForge.Util 28{ 29 public class EncryptedWriteRevisionStream : Stream 30 { 31 private long position; 32 private int key; 33 private int curKey; 34 private long keypos; 35 private Stream file; 36 public byte xor; 37 38 39 internal EncryptedWriteRevisionStream(Stream file, int key, bool ps3 = false) 40 { 41 file.Position = 0; 42 // write the RevisionStream header 43 file.WriteUInt8(0x7A); 44 file.WriteUInt32LE(0); // 0 = 0 regardless of endianness 45 if (ps3) 46 { 47 file.WriteUInt32BE((uint)key); 48 } else 49 { 50 file.WriteInt32LE(key); 51 } 52 position = 0; 53 keypos = 0; 54 // The initial key is found in the first 4 bytes. 55 this.key = cryptRound(key); 56 this.curKey = this.key; 57 this.file = file; 58 if (key < 0) 59 this.xor = 0xFF; 60 else 61 this.xor = 0x00; 62 } 63 64 public override bool CanRead => false; 65 public override bool CanSeek => true; 66 public override bool CanWrite => file.CanWrite; 67 public override long Length => file.Length - (4 + 5); 68 69 public override long Position 70 { 71 get 72 { 73 return position; 74 } 75 76 set 77 { 78 Seek(value, SeekOrigin.Begin); 79 } 80 } 81 82 private void updateKey() 83 { 84 if (keypos == position) 85 return; 86 if (keypos > position) // reset key 87 { 88 keypos = 0; 89 curKey = key; 90 } 91 while (keypos < position) // don't think there's a faster way to do this 92 { 93 curKey = cryptRound(curKey); 94 keypos++; 95 } 96 } 97 98 private int cryptRound(int key) 99 { 100 int ret = (key - ((key / 0x1F31D) * 0x1F31D)) * 0x41A7 - (key / 0x1F31D) * 0xB14; 101 if (ret <= 0) 102 ret += 0x7FFFFFFF; 103 return ret; 104 } 105 106 public override int Read(byte[] buffer, int offset, int count) 107 { 108 throw new NotImplementedException(); 109 } 110 111 public override long Seek(long offset, SeekOrigin origin) 112 { 113 int adjust = origin == SeekOrigin.Current ? 0 : (4 + 5); 114 this.position = file.Seek(offset + adjust, origin) - (4 + 5); 115 updateKey(); 116 return position; 117 } 118 119 public void FinishWriting() 120 { 121 file.WriteUInt8(0x7B); 122 } 123 124 #region Not Used 125 126 public override void Flush() 127 { 128 throw new NotSupportedException(); 129 } 130 131 public override void SetLength(long value) 132 { 133 throw new NotSupportedException(); 134 } 135 136 public override void Write(byte[] buffer, int offset, int count) 137 { 138 if (offset + count > buffer.Length) 139 { 140 throw new IndexOutOfRangeException("Attempt to read buffer past its end"); 141 } 142 updateKey(); 143 var copy = new byte[count]; 144 Buffer.BlockCopy(buffer, offset, copy, 0, count); 145 for (uint i = 0; i < count; i++) 146 { 147 copy[i] ^= (byte)(this.curKey ^ xor); 148 position++; 149 updateKey(); 150 } 151 // ensure file is at correct offset 152 file.Seek(this.position + (4 + 5) - count, SeekOrigin.Begin); 153 file.Write(copy, 0, count); 154 } 155 156 #endregion 157 } 158}