/// <summary> /// Update encryption keys /// </summary> protected void UpdateKeys(byte ch) { keys[0] = Crc32Checksum.ComputeCrc32(keys[0], ch); keys[1] = keys[1] + (byte)keys[0]; keys[1] = keys[1] * 134775813 + 1; keys[2] = Crc32Checksum.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); }
/// <summary> /// Calculates the CRC checksum of the file. /// </summary> public static uint CalculateCrc(this FileInfo file) { using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None)) { return(Crc32Checksum.Generate(stream)); } }
/// <summary> /// Closes the zip input stream /// </summary> public override void Close() { internalReader = ReadingNotAvailable; crc = null; entry = null; base.Close(); }
public void Crc32Checksum_6() { if (ZLibInit.ZLibProvided) { byte[] sample = Encoding.UTF8.GetBytes("ABCDEF"); uint checksum = Crc32Checksum.Crc32(sample, 1, 3); Assert.IsTrue(checksum == 0x26BA19F3); // BCD } }
public void Crc32Checksum_5() { if (ZLibInit.ZLibProvided) { uint checksum = Crc32Checksum.Crc32(Encoding.UTF8.GetBytes("ABC")); Assert.IsTrue(checksum == 0xA3830348); // ABC checksum = Crc32Checksum.Crc32(checksum, Encoding.UTF8.GetBytes("DEF")); Assert.IsTrue(checksum == 0xBB76FE69); // ABCDEF } }
public void Crc32Checksum_4() { if (ZLibInit.ZLibProvided) { Crc32Checksum crc = new Crc32Checksum(); crc.Append(Encoding.UTF8.GetBytes("ABC")); Assert.IsTrue(crc.Checksum == 0xA3830348); // ABC crc.Append(Encoding.UTF8.GetBytes("DEF")); Assert.IsTrue(crc.Checksum == 0xBB76FE69); // ABCDEF } }
public void Crc32Checksum_7() { if (ZLibInit.ZLibProvided) { string filePath = Path.Combine(TestSetup.SampleDir, "ex3.jpg"); using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { uint checksum = Crc32Checksum.Crc32(fs); Assert.IsTrue(checksum == 0x63D4D64B); } } }
public void Crc32Checksum_8() { if (ZLibInit.ZLibProvided) { byte[] sample1 = Encoding.UTF8.GetBytes("ABC"); byte[] sample2 = Encoding.UTF8.GetBytes("DEF"); using (MemoryStream ms1 = new MemoryStream(sample1)) using (MemoryStream ms2 = new MemoryStream(sample2)) { uint checksum = Crc32Checksum.Crc32(ms1); Assert.IsTrue(checksum == 0xA3830348); // ABC checksum = Crc32Checksum.Crc32(checksum, ms2); Assert.IsTrue(checksum == 0xBB76FE69); // ABCDEF } } }
/// <summary> /// Generates new encryption keys based on given seed /// </summary> /// <param name="seed">The seed value to initialise keys with.</param> /// <returns>A new key value.</returns> static public byte[] GenerateKeys(byte[] seed) { if (seed == null) { throw new ArgumentNullException("seed"); } if (seed.Length == 0) { throw new ArgumentException("Length is zero", "seed"); } uint[] newKeys = new uint[] { 0x12345678, 0x23456789, 0x34567890 }; foreach (byte t in seed) { newKeys[0] = Crc32Checksum.ComputeCrc32(newKeys[0], t); newKeys[1] = newKeys[1] + (byte)newKeys[0]; newKeys[1] = newKeys[1] * 134775813 + 1; newKeys[2] = Crc32Checksum.ComputeCrc32(newKeys[2], (byte)(newKeys[1] >> 24)); } byte[] result = new byte[12]; result[0] = (byte)(newKeys[0] & 0xff); result[1] = (byte)((newKeys[0] >> 8) & 0xff); result[2] = (byte)((newKeys[0] >> 16) & 0xff); result[3] = (byte)((newKeys[0] >> 24) & 0xff); result[4] = (byte)(newKeys[1] & 0xff); result[5] = (byte)((newKeys[1] >> 8) & 0xff); result[6] = (byte)((newKeys[1] >> 16) & 0xff); result[7] = (byte)((newKeys[1] >> 24) & 0xff); result[8] = (byte)(newKeys[2] & 0xff); result[9] = (byte)((newKeys[2] >> 8) & 0xff); result[10] = (byte)((newKeys[2] >> 16) & 0xff); result[11] = (byte)((newKeys[2] >> 24) & 0xff); return(result); }
public void Crc32Checksum_1() { void Template(string path, uint checksum) { string filePath = Path.Combine(TestSetup.SampleDir, path); using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { Crc32Checksum crc = new Crc32Checksum(); crc.Append(fs); Assert.IsTrue(crc.Checksum == checksum); } } if (!ZLibInit.ZLibProvided) { return; } Template("ex1.jpg", 0x1961D0C6); Template("ex2.jpg", 0x7641A243); Template("ex3.jpg", 0x63D4D64B); }
void ReadHeader() { // 1. Check the two magic bytes Crc32Checksum headCRC = new Crc32Checksum(); int magic = baseInputStream.ReadByte(); if (magic < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(magic); if (magic != (GZipConstants.GzipMagic >> 8)) { throw new GZipException("Error GZIP header, first magic byte doesn't match"); } magic = baseInputStream.ReadByte(); if (magic < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } if (magic != (GZipConstants.GzipMagic & 0xFF)) { throw new GZipException("Error GZIP header, second magic byte doesn't match"); } headCRC.Update(magic); // 2. Check the compression type (must be 8) int compressionType = baseInputStream.ReadByte(); if (compressionType < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } if (compressionType != 8) { throw new GZipException("Error GZIP header, data not in deflate format"); } headCRC.Update(compressionType); // 3. Check the flags int flags = baseInputStream.ReadByte(); if (flags < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(flags); /* This flag byte is divided into individual bits as follows: * * bit 0 FTEXT * bit 1 FHCRC * bit 2 FEXTRA * bit 3 FNAME * bit 4 FCOMMENT * bit 5 reserved * bit 6 reserved * bit 7 reserved */ // 3.1 Check the reserved bits are zero if ((flags & 0xE0) != 0) { throw new GZipException("Reserved flag bits in GZIP header != 0"); } // 4.-6. Skip the modification time, extra flags, and OS type for (int i = 0; i < 6; i++) { int readByte = baseInputStream.ReadByte(); if (readByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(readByte); } // 7. Read extra field if ((flags & GZipConstants.FEXTRA) != 0) { // Skip subfield id for (int i = 0; i < 2; i++) { int readByte = baseInputStream.ReadByte(); if (readByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(readByte); } if (baseInputStream.ReadByte() < 0 || baseInputStream.ReadByte() < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } int len1, len2; len1 = baseInputStream.ReadByte(); len2 = baseInputStream.ReadByte(); if ((len1 < 0) || (len2 < 0)) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(len1); headCRC.Update(len2); int extraLen = (len1 << 8) | len2; for (int i = 0; i < extraLen; i++) { int readByte = baseInputStream.ReadByte(); if (readByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(readByte); } } // 8. Read file name if ((flags & GZipConstants.FNAME) != 0) { int readByte; while ((readByte = baseInputStream.ReadByte()) > 0) { headCRC.Update(readByte); } if (readByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(readByte); } // 9. Read comment if ((flags & GZipConstants.FCOMMENT) != 0) { int readByte; while ((readByte = baseInputStream.ReadByte()) > 0) { headCRC.Update(readByte); } if (readByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } headCRC.Update(readByte); } // 10. Read header CRC if ((flags & GZipConstants.FHCRC) != 0) { int tempByte; int crcval = baseInputStream.ReadByte(); if (crcval < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } tempByte = baseInputStream.ReadByte(); if (tempByte < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } crcval = (crcval << 8) | tempByte; if (crcval != ((int)headCRC.Value & 0xffff)) { throw new GZipException("Header CRC value mismatch"); } } readGZIPHeader = true; }
public EncodedFileInfo(Plugin p, string dirName, string fileName) { string section = $"EncodedFile-{dirName}-{fileName}"; if (p.Sections.ContainsKey(section) == false) { throw new FileDecodeFailException($"[{dirName}\\{fileName}] does not exists in [{p.FullPath}]"); } List <string> encodedList = p.Sections[$"EncodedFile-{dirName}-{fileName}"].GetLinesOnce(); if (Ini.GetKeyValueFromLine(encodedList[0], out string key, out string value)) { throw new FileDecodeFailException("Encoded lines are malformed"); } // [Stage 1] Concat sliced base64-encoded lines into one string byte[] decoded; { int.TryParse(value, out int blockCount); encodedList.RemoveAt(0); // Remove "lines=n" // Each line is 64KB block if (Ini.GetKeyValueFromLines(encodedList, out List <string> keys, out List <string> base64Blocks)) { throw new FileDecodeFailException("Encoded lines are malformed"); } StringBuilder b = new StringBuilder(); foreach (string block in base64Blocks) { b.Append(block); } switch (b.Length % 4) { case 0: break; case 1: throw new FileDecodeFailException("Encoded lines are malformed"); case 2: b.Append("=="); break; case 3: b.Append("="); break; } decoded = Convert.FromBase64String(b.ToString()); } // [Stage 2] Read final footer const int finalFooterLen = 0x24; int finalFooterIdx = decoded.Length - finalFooterLen; // 0x00 - 0x04 : 4B -> CRC32 uint full_crc32 = BitConverter.ToUInt32(decoded, finalFooterIdx + 0x00); // 0x0C - 0x0F : 4B -> Zlib Compressed Footer Length int compressedFooterLen = (int)BitConverter.ToUInt32(decoded, finalFooterIdx + 0x0C); int compressedFooterIdx = decoded.Length - (finalFooterLen + compressedFooterLen); // 0x10 - 0x17 : 8B -> Zlib Compressed File Length int compressedBodyLen = (int)BitConverter.ToUInt64(decoded, finalFooterIdx + 0x10); // [Stage 3] Validate final footer this.FinalFooterValid = true; if (compressedBodyLen != compressedFooterIdx) { this.FinalFooterValid = false; } uint calcFull_crc32 = Crc32Checksum.Crc32(decoded, 0, finalFooterIdx); if (full_crc32 != calcFull_crc32) { this.FinalFooterValid = false; } if (this.FinalFooterValid == false) { return; } // [Stage 4] Decompress first footer byte[] rawFooter; using (MemoryStream rawFooterStream = new MemoryStream()) { using (MemoryStream ms = new MemoryStream(decoded, compressedFooterIdx, compressedFooterLen)) using (ZLibStream zs = new ZLibStream(ms, CompressionMode.Decompress, CompressionLevel.Default)) { zs.CopyTo(rawFooterStream); } rawFooter = rawFooterStream.ToArray(); } // [Stage 5] Read first footer this.FirstFooterValid = true; // 0x200 - 0x207 : 8B -> Length of raw file, in little endian int rawBodyLen = (int)BitConverter.ToUInt32(rawFooter, 0x200); // 0x208 - 0x20F : 8B -> Length of zlib-compressed file, in little endian // Note: In Type 2, 0x208 entry is null - padded int compressedBodyLen2 = (int)BitConverter.ToUInt32(rawFooter, 0x208); // 0x220 - 0x223 : 4B -> CRC32C Checksum of zlib-compressed file uint compressedBody_crc32 = BitConverter.ToUInt32(rawFooter, 0x220); // 0x224 : 1B -> Compress Mode (Type 1 : 00, Type 2 : 01) byte compMode = rawFooter[0x224]; // 0x225 : 1B -> ZLib Compress Level (Type 1 : 01~09, Type 2 : 00) byte compLevel = rawFooter[0x225]; // [Stage 6] Validate first footer if (compMode == 0) { this.Mode = EncodedFile.EncodeMode.Compress; if (compLevel < 1 || 9 < compLevel) { this.FirstFooterValid = false; } if (compressedBodyLen2 == 0 || (compressedBodyLen2 != compressedBodyLen)) { this.FirstFooterValid = false; } } else if (compMode == 1) { this.Mode = EncodedFile.EncodeMode.Raw; if (compLevel != 0) { this.FirstFooterValid = false; } if (compressedBodyLen2 != 0) { this.FirstFooterValid = false; } } else // Wrong compMode { this.FirstFooterValid = false; } if (this.FirstFooterValid == false) { return; } // [Stage 7] Decompress body if (compMode == (ushort)EncodedFile.EncodeMode.Compress) { this.RawBodyStream = new MemoryStream(); using (MemoryStream ms = new MemoryStream(decoded, 0, compressedBodyLen)) using (ZLibStream zs = new ZLibStream(ms, CompressionMode.Decompress, CompressionLevel.Default)) { zs.CopyTo(this.RawBodyStream); } this.RawBodyStream.Position = 0; this.CompressedBodyValid = true; } else if (compMode == (ushort)EncodedFile.EncodeMode.Raw) { this.CompressedBodyValid = true; this.RawBodyStream = new MemoryStream(decoded, 0, rawBodyLen); } else { throw new InternalException($"Wrong EncodeMode [{compMode}]"); } // [Stage 8] Validate decompressed body this.RawBodyValid = true; uint calcCompBody_crc32 = Crc32Checksum.Crc32(RawBodyStream.ToArray()); if (compressedBody_crc32 != calcCompBody_crc32) { this.RawBodyValid = false; } // [Stage 9] Return decompressed body stream this.RawBodyStream.Position = 0; }
private static MemoryStream Decode(List <string> encodedList) { if (Ini.GetKeyValueFromLine(encodedList[0], out string key, out string value)) { throw new FileDecodeFailException("Encoded lines are malformed"); } // [Stage 1] Concat sliced base64-encoded lines into one string byte[] decoded; { int.TryParse(value, out int blockCount); encodedList.RemoveAt(0); // Remove "lines=n" // Each line is 64KB block if (Ini.GetKeyValueFromLines(encodedList, out List <string> keys, out List <string> base64Blocks)) { throw new FileDecodeFailException("Encoded lines are malformed"); } StringBuilder b = new StringBuilder(); foreach (string block in base64Blocks) { b.Append(block); } switch (b.Length % 4) { case 0: break; case 1: throw new FileDecodeFailException("Encoded lines are malformed"); case 2: b.Append("=="); break; case 3: b.Append("="); break; } decoded = Convert.FromBase64String(b.ToString()); } // [Stage 2] Read final footer const int finalFooterLen = 0x24; int finalFooterIdx = decoded.Length - finalFooterLen; // 0x00 - 0x04 : 4B -> CRC32 uint full_crc32 = BitConverter.ToUInt32(decoded, finalFooterIdx + 0x00); // 0x0C - 0x0F : 4B -> Zlib Compressed Footer Length int compressedFooterLen = (int)BitConverter.ToUInt32(decoded, finalFooterIdx + 0x0C); int compressedFooterIdx = decoded.Length - (finalFooterLen + compressedFooterLen); // 0x10 - 0x17 : 8B -> Zlib Compressed File Length int compressedBodyLen = (int)BitConverter.ToUInt64(decoded, finalFooterIdx + 0x10); // [Stage 3] Validate final footer if (compressedBodyLen != compressedFooterIdx) { throw new FileDecodeFailException($"Encoded file is corrupted"); } uint calcFull_crc32 = Crc32Checksum.Crc32(decoded, 0, finalFooterIdx); if (full_crc32 != calcFull_crc32) { throw new FileDecodeFailException($"Encoded file is corrupted"); } // [Stage 4] Decompress first footer byte[] rawFooter; using (MemoryStream rawFooterStream = new MemoryStream()) { using (MemoryStream ms = new MemoryStream(decoded, compressedFooterIdx, compressedFooterLen)) using (ZLibStream zs = new ZLibStream(ms, CompressionMode.Decompress, CompressionLevel.Default)) { zs.CopyTo(rawFooterStream); } rawFooter = rawFooterStream.ToArray(); } // [Stage 5] Read first footer // 0x200 - 0x207 : 8B -> Length of raw file, in little endian int rawBodyLen = (int)BitConverter.ToUInt32(rawFooter, 0x200); // 0x208 - 0x20F : 8B -> Length of zlib-compressed file, in little endian // Note: In Type 2, 0x208 entry is null - padded int compressedBodyLen2 = (int)BitConverter.ToUInt32(rawFooter, 0x208); // 0x220 - 0x223 : 4B -> CRC32C Checksum of zlib-compressed file uint compressedBody_crc32 = BitConverter.ToUInt32(rawFooter, 0x220); // 0x224 : 1B -> Compress Mode (Type 1 : 00, Type 2 : 01) byte compMode = rawFooter[0x224]; // 0x225 : 1B -> ZLib Compress Level (Type 1 : 01~09, Type 2 : 00) byte compLevel = rawFooter[0x225]; // [Stage 6] Validate first footer if (compMode == 0) // Type 1, zlib { if (compressedBodyLen2 == 0 || (compressedBodyLen2 != compressedBodyLen)) { throw new FileDecodeFailException($"Encoded file is corrupted: compMode"); } if (compLevel < 1 || 9 < compLevel) { throw new FileDecodeFailException($"Encoded file is corrupted: compLevel"); } } else if (compMode == 1) // Type 2, Raw { if (compressedBodyLen2 != 0) { throw new FileDecodeFailException($"Encoded file is corrupted: compMode"); } if (compLevel != 0) { throw new FileDecodeFailException($"Encoded file is corrupted: compLevel"); } } else // Wrong compMode { throw new FileDecodeFailException($"Encoded file is corrupted: compMode"); } // [Stage 7] Decompress body MemoryStream rawBodyStream; // This stream should be alive even after this method returns if (compMode == 0) // Type 1, zlib { rawBodyStream = new MemoryStream(); using (MemoryStream ms = new MemoryStream(decoded, 0, compressedBodyLen)) using (ZLibStream zs = new ZLibStream(ms, CompressionMode.Decompress, false)) { zs.CopyTo(rawBodyStream); } rawBodyStream.Position = 0; } else if (compMode == 1) // Type 2, raw { rawBodyStream = new MemoryStream(decoded, 0, rawBodyLen); } else { throw new FileDecodeFailException($"Encoded file is corrupted"); } // [Stage 8] Validate decompressed body uint calcCompBody_crc32 = Crc32Checksum.Crc32(rawBodyStream.ToArray()); if (compressedBody_crc32 != calcCompBody_crc32) { throw new FileDecodeFailException($"Encoded file is corrupted"); } // [Stage 9] Return decompressed body stream rawBodyStream.Position = 0; return(rawBodyStream); }
private static Plugin Encode(Plugin p, string dirName, string fileName, byte[] input, EncodeMode mode) { byte[] fileNameUTF8 = Encoding.UTF8.GetBytes(fileName); if (fileName.Length == 0 || 512 <= fileNameUTF8.Length) { throw new FileDecodeFailException($"Filename's UTF8 encoded length should be shorter than 512B"); } // Check Overwrite bool fileOverwrite = false; if (p.Sections.ContainsKey(dirName)) { // [{dirName}] section exists, check if there is already same file encoded List <string> lines = p.Sections[dirName].GetLines(); if (lines.FirstOrDefault(x => x.Equals(fileName, StringComparison.OrdinalIgnoreCase)) != null) { fileOverwrite = true; } } string encodedStr; using (MemoryStream bodyStream = new MemoryStream()) using (MemoryStream footerStream = new MemoryStream()) using (MemoryStream concatStream = new MemoryStream()) { // [Stage 1] Compress file with zlib switch (mode) { case EncodeMode.Compress: { using (ZLibStream zs = new ZLibStream(bodyStream, CompressionMode.Compress, CompressionLevel.Level6, true)) { zs.Write(input, 0, input.Length); } bodyStream.Position = 0; } break; case EncodeMode.Raw: { bodyStream.Write(input, 0, input.Length); bodyStream.Position = 0; } break; default: throw new InternalException($"Wrong EncodeMode [{mode}]"); } // [Stage 2] Generate first footer byte[] rawFooter = new byte[0x226]; // 0x550 { // 0x000 - 0x1FF : Filename and its length rawFooter[0] = (byte)fileNameUTF8.Length; fileNameUTF8.CopyTo(rawFooter, 1); for (int i = 1 + fileNameUTF8.Length; i < 0x200; i++) { rawFooter[i] = 0; // Null Pad } // 0x200 - 0x207 : 8B -> Length of raw file, in little endian BitConverter.GetBytes(input.Length).CopyTo(rawFooter, 0x200); switch (mode) { case EncodeMode.Compress: // Type 1 // 0x208 - 0x20F : 8B -> Length of zlibed body, in little endian BitConverter.GetBytes(bodyStream.Length).CopyTo(rawFooter, 0x208); // 0x210 - 0x21F : 16B -> Null padding for (int i = 0x210; i < 0x220; i++) { rawFooter[i] = 0; } break; case EncodeMode.Raw: // Type 2 // 0x208 - 0x21F : 16B -> Null padding for (int i = 0x208; i < 0x220; i++) { rawFooter[i] = 0; } break; default: throw new InternalException($"Wrong EncodeMode [{mode}]"); } // 0x220 - 0x223 : CRC32 of raw file uint crc32 = Crc32Checksum.Crc32(input); BitConverter.GetBytes(crc32).CopyTo(rawFooter, 0x220); // 0x224 : 1B -> Compress Mode (Type 1 : 00, Type 2 : 01) rawFooter[0x224] = (byte)mode; // 0x225 : 1B -> ZLib Compress Level (Type 1 : 01 ~ 09, Type 2 : 00) switch (mode) { case EncodeMode.Compress: // Type 1 rawFooter[0x225] = (byte)CompressionLevel.Level6; break; case EncodeMode.Raw: // Type 2 rawFooter[0x225] = 0; break; default: throw new InternalException($"Wrong EncodeMode [{mode}]"); } } // [Stage 3] Compress first footer using (ZLibStream zs = new ZLibStream(footerStream, CompressionMode.Compress, CompressionLevel.Default, true)) { zs.Write(rawFooter, 0, rawFooter.Length); } footerStream.Position = 0; // [Stage 4] Concat body and footer bodyStream.CopyTo(concatStream); footerStream.CopyTo(concatStream); bodyStream.Position = 0; footerStream.Position = 0; // [Stage 5] Generate final footer { byte[] finalFooter = new byte[0x24]; // 0x00 - 0x04 : 4B -> CRC32 of compressed body and compressed footer uint crc32 = Crc32Checksum.Crc32(concatStream.ToArray()); BitConverter.GetBytes(crc32).CopyTo(finalFooter, 0x00); // 0x04 - 0x08 : 4B -> Unknown - Always 1 BitConverter.GetBytes((uint)1).CopyTo(finalFooter, 0x04); // 0x08 - 0x0B : 4B -> ZLBArchive version (Always 2) BitConverter.GetBytes((uint)2).CopyTo(finalFooter, 0x08); // 0x0C - 0x0F : 4B -> Zlib Compressed Footer Length BitConverter.GetBytes((int)footerStream.Length).CopyTo(finalFooter, 0x0C); // 0x10 - 0x17 : 8B -> Zlib Compressed File Length BitConverter.GetBytes(bodyStream.Length).CopyTo(finalFooter, 0x10); // 0x18 - 0x1B : 4B -> Unknown - Always 1 BitConverter.GetBytes((uint)1).CopyTo(finalFooter, 0x18); // 0x1C - 0x23 : 8B -> Unknown - Always 0 for (int i = 0x1C; i < 0x24; i++) { finalFooter[i] = 0; } concatStream.Write(finalFooter, 0, finalFooter.Length); } // [Stage 6] Encode body, footer and finalFooter with Base64 encodedStr = Convert.ToBase64String(concatStream.ToArray()); // Remove Base64 Padding (==, =) if (encodedStr.EndsWith("==", StringComparison.Ordinal)) { encodedStr = encodedStr.Substring(0, encodedStr.Length - 2); } else if (encodedStr.EndsWith("=", StringComparison.Ordinal)) { encodedStr = encodedStr.Substring(0, encodedStr.Length - 1); } } // [Stage 7] Tokenize encoded string into 4090B. string section = $"EncodedFile-{dirName}-{fileName}"; List <IniKey> keys = new List <IniKey>(); for (int i = 0; i <= (encodedStr.Length / 4090); i++) { if (i < (encodedStr.Length / 4090)) // 1 Line is 4090 characters { keys.Add(new IniKey(section, i.ToString(), encodedStr.Substring(i * 4090, 4090))); // X=eJyFk0Fr20AQhe8G... } else // Last Iteration { keys.Add(new IniKey(section, i.ToString(), encodedStr.Substring(i * 4090, encodedStr.Length - (i * 4090)))); // X=N3q8ryccAAQWuBjqA5QvAAAAAA (end) keys.Insert(0, new IniKey(section, "lines", i.ToString())); // lines=X } } // [Stage 8] Before writing to file, backup original plugin string tempFile = Path.GetTempFileName(); File.Copy(p.FullPath, tempFile, true); // [Stage 9] Write to file try { // Write folder info to [EncodedFolders] bool writeFolderSection = true; if (p.Sections.ContainsKey("EncodedFolders")) { List <string> folders = p.Sections["EncodedFolders"].GetLines(); if (0 < folders.Count(x => x.Equals(dirName, StringComparison.OrdinalIgnoreCase))) { writeFolderSection = false; } } if (writeFolderSection) { Ini.WriteRawLine(p.FullPath, "EncodedFolders", dirName, false); } // Write file info into [{dirName}] Ini.SetKey(p.FullPath, dirName, fileName, $"{input.Length},{encodedStr.Length}"); // UncompressedSize,EncodedSize // Write encoded file into [EncodedFile-{dirName}-{fileName}] if (fileOverwrite) { Ini.DeleteSection(p.FullPath, section); // Delete existing encoded file } Ini.SetKeys(p.FullPath, keys); // Write into } catch { // Error -> Rollback! File.Copy(tempFile, p.FullPath, true); throw new FileDecodeFailException($"Error while writing encoded file into [{p.FullPath}]"); } finally { // Delete temp script File.Delete(tempFile); } // [Stage 10] Refresh Plugin return(p.Project.RefreshPlugin(p)); }