Code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Windows.Forms; using System.Runtime.InteropServices; using Fesersoft.Hashing; namespace S4Resource { class ZipFolder { #region Dll-Imports [DllImport("lzo.dll")] private static extern int __lzo_init3(); [DllImport("lzo.dll")] private static extern string lzo_version_string(); [DllImport("lzo.dll")] private static extern string lzo_version_date(); [DllImport("lzo.dll")] private static extern int lzo1x_1_compress( byte[] src, int src_len, byte[] dst, ref int dst_len, byte[] wrkmem ); [DllImport("lzo.dll")] private static extern int lzo1x_decompress( byte[] src, int src_len, byte[] dst, ref int dst_len, byte[] wrkmem); #endregion private byte[] _workMemory = new byte[16384L * 4]; private class FileHeader { public string FileName; public long CRC; public int FileSize; public int Unknown; } private bool initialized = false; private bool decrypted = false; private BinaryReader fileReader; private String fileName; private int numberOfFiles; private Dictionary<String, FileHeader> fileHeaders; private static byte[] Key = { // Only the first 40 keys copied from S4 0x82, 0x53, 0x43, 0x4C, 0x2B, 0x0D, 0x37, 0xD7, 0xD9, 0xD8, 0x1B, 0x6D, 0xA0, 0xC3, 0x2B, 0xEE, 0x45, 0x88, 0x1A, 0xA6, 0x18, 0x1D, 0x9D, 0x38, 0x2A, 0x55, 0x03, 0x1D, 0xCD, 0xA6, 0x73, 0x07, 0xED, 0x8D, 0xC5, 0xDB, 0xA3, 0xBD, 0xB6, 0xD5 }; private static char[] trimChars = { '\0' }; private string workingPath; public ZipFolder(string filename) { int init = __lzo_init3(); if (init != 0) { throw new Exception("Initialization of LZO-Compressor failed !"); } this.fileName = filename; if (!File.Exists(filename)) return; if (!Directory.Exists("S4Resource.backup")) { Directory.CreateDirectory("S4Resource.backup"); } workingPath = Directory.GetParent(Application.ExecutablePath).FullName; Directory.SetCurrentDirectory(workingPath); byte[] data; try { data = File.ReadAllBytes(filename); } catch (Exception e) { MessageBox.Show("Can not open file " + filename + "\nException: " + e.Message); return; } if (data.Length < 8) { MessageBox.Show("File too small!"); return; } fileReader = new BinaryReader(new MemoryStream(data)); int fileVersion = fileReader.ReadInt32(); if (fileVersion != 1) { MessageBox.Show("File format has changed!"); return; } numberOfFiles = fileReader.ReadInt32(); initialized = true; decryptHeaders(); } private void decryptHeaders() { if (!initialized) return; fileHeaders = new Dictionary<String, FileHeader>(numberOfFiles); int fileCounter = numberOfFiles; while (fileCounter > 0) { int blockSize = fileReader.ReadInt32(); byte[] header = fileReader.ReadBytes(blockSize); decryptCapped256(ref header, header.Length); swapBytes(ref header, header.Length); header = Decompress(header,272); decryptCapped256(ref header, 272); swapBytes(ref header, 272); BinaryReader headerReader = new BinaryReader(new MemoryStream(header)); FileHeader realHeader = new FileHeader(); realHeader.FileName = Encoding.ASCII.GetString(headerReader.ReadBytes(256)).Trim(trimChars); realHeader.CRC = headerReader.ReadInt64(); realHeader.FileSize = headerReader.ReadInt32(); realHeader.Unknown = headerReader.ReadInt32(); fileHeaders.Add(realHeader.FileName, realHeader); fileCounter--; } decrypted = true; } public List<String> getFileNames() { if (!initialized || !decrypted) return new List<String>(); return fileHeaders.Keys.ToList<String>(); } private void swapBytes(ref byte[] data, int size) { int sizeCapped; int i = 0; byte swap; int j; if (size >= 128) sizeCapped = 128; else sizeCapped = size; while (i < sizeCapped / 2) { j = size - 1 - i; swap = data[j]; data[j] = data[i]; data[i++] = swap; } } private void decryptCapped256(ref byte[] data, int size) { int sizeCapped; int i = 0; if (size >= 256) sizeCapped = 256; else sizeCapped = size; while (i < sizeCapped) { data[i] = (byte)(((data[i] >> 1) & 0x7F) | ((data[i] & 1) << 7)); data[i] ^= Key[i % 32]; i++; } } private void encryptCapped256(ref byte[] data, int size) { int sizeCapped; int i = 0; if (size >= 256) sizeCapped = 256; else sizeCapped = size; while (i < sizeCapped) { data[i] ^= Key[i % 32]; data[i] = (byte)(((data[i] & 0x7F) << 1) | ((data[i] & 0x80) >> 7)); i++; } } internal void replaceFile(String fullPath, ref byte[] fileData, bool correctCRC) { if (!initialized || !decrypted) return; Directory.SetCurrentDirectory(workingPath); FileHeader fileHeader = fileHeaders[fullPath]; byte[] data = fileData; if (fullPath.EndsWith(".x4")) { data = encryptX4(fileData); } if (fileHeader.FileName == null) { fileHeader.FileName = fullPath; fileHeader.CRC = GenerateCRC(fullPath, ref data); fileHeader.FileSize = fileData.Length; fileHeader.Unknown = 0; fileHeaders.Add(fileHeader.FileName, fileHeader); } else { fileHeader.FileSize = data.Length; if (correctCRC) { fileHeader.CRC = GenerateCRC(fileHeader.FileName, ref data); } } if (File.Exists("_resources\\" + fileHeader.CRC.ToString("X"))) { File.Copy("_resources\\" + fileHeader.CRC.ToString("X"), "S4Resource.backup\\" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "_" + +DateTime.Now.Hour + "-" + DateTime.Now.Minute + "-" + DateTime.Now.Second + "_" + fileHeader.CRC.ToString("X")); } saveEncryptedFile(fileHeader, ref data); saveFolder(); } private byte[] encryptX4(byte[] fileData) { byte[] compressed = Compress(fileData); encrypt(ref compressed, compressed.Length); byte[] final = new byte[compressed.Length + 4]; BinaryWriter br = new BinaryWriter(new MemoryStream(final)); br.Write(fileData.Length); br.Write(compressed); br.Close(); return final; } private void encrypt(ref byte[] data, int size) { int i = 0; while (i < size) { data[i] ^= Key[i % 40]; data[i] = (byte)(((data[i] & 0x7F) << 1) | ((data[i] & 0x80) >> 7)); ++i; } } private void decrypt(ref byte[] data, int size) { int i = 0; while (i < size) { data[i] = (byte)(((data[i] >> 1) & 0x7F) | ((data[i] & 1) << 7)); data[i] ^= Key[i % 40]; ++i; } } private void saveEncryptedFile(FileHeader fileHeader, ref byte[] fileData) { if (!initialized || !decrypted) return; encryptCapped256(ref fileData, fileData.Length); byte[] compressed = Compress(fileData); swapBytes(ref compressed, compressed.Length); File.WriteAllBytes("_resources\\" + fileHeader.CRC.ToString("X"), compressed); } private void saveFolder() { File.Copy("resource.s4hd", "S4Resource.backup\\" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "_" + +DateTime.Now.Hour + "-" + DateTime.Now.Minute + "-" + DateTime.Now.Second + "_" + "_resource.s4hd"); BinaryWriter bw = new BinaryWriter(new FileStream(fileName, FileMode.OpenOrCreate)); bw.Write((int)1); List<FileHeader> fileHeaderList = fileHeaders.Values.ToList<FileHeader>(); bw.Write(fileHeaderList.Count); foreach (FileHeader fileHeader in fileHeaderList) { byte[] header = new byte[272]; BinaryWriter bw2 = new BinaryWriter(new MemoryStream(header, true)); byte[] name = Encoding.ASCII.GetBytes(fileHeader.FileName); if (name.Length > 256) continue; byte[] name256 = new byte[256]; name.CopyTo(name256, 0); bw2.Write(name256); bw2.Write(fileHeader.CRC); bw2.Write(fileHeader.FileSize); bw2.Write(fileHeader.Unknown); bw2.Close(); swapBytes(ref header, 272); encryptCapped256(ref header, 272); byte[] compressed = Compress(header); swapBytes(ref compressed, compressed.Length); encryptCapped256(ref compressed, compressed.Length); bw.Write(compressed.Length); bw.Write(compressed); } bw.Close(); } private long GenerateCRC(string fullPath, ref byte[] fileData) { crc32 crc = new crc32(); long dataCRC = crc.CRC(fileData); long nameCRC = crc.CRC(Encoding.ASCII.GetBytes(fullPath)); long finalCRC = dataCRC | (nameCRC << 32); byte[] byteCRC = new byte[8]; BinaryWriter bw = new BinaryWriter(new MemoryStream(byteCRC, true)); bw.Write(finalCRC); encryptCapped256(ref byteCRC, 8); BinaryReader br = new BinaryReader(new MemoryStream(byteCRC)); return br.ReadInt64(); } internal void exportFile(string fullPath, ref Stream file) { try { Directory.SetCurrentDirectory(workingPath); FileHeader fileHeader = fileHeaders[fullPath]; if (fileHeader.FileName == null) return; String path = "_resources\\" + fileHeader.CRC.ToString("X"); byte[] data = File.ReadAllBytes(path); swapBytes(ref data, data.Length); byte[] decompressed = Decompress(data,fileHeader.FileSize); int size = fileHeader.FileSize; decryptCapped256(ref decompressed, size); if (fullPath.EndsWith(".x4")) { decompressed = decryptX4(decompressed, size, out size); } String test = Encoding.UTF8.GetString(decompressed); file.Write(decompressed, 0, size); file.Close(); } catch (Exception e) { MessageBox.Show("Exception: "+e.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } private byte[] decryptX4(byte[] decompressed, int size, out int realSize) { BinaryReader br = new BinaryReader(new MemoryStream(decompressed)); realSize = br.ReadInt32(); byte[] data = br.ReadBytes(size - 4); decrypt(ref data, data.Length); return Decompress(data, realSize); } internal void clean() { if (!Directory.Exists("_resources") || Directory.Exists("_resources.bkp")) return; Directory.SetCurrentDirectory(workingPath); Directory.Move("_resources", "_resources.bkp"); Directory.CreateDirectory("_resources"); foreach (FileHeader fileHeader in fileHeaders.Values.ToList<FileHeader>()) { if (File.Exists("_resources.bkp\\" + fileHeader.CRC.ToString("X"))) { File.Move("_resources.bkp\\" + fileHeader.CRC.ToString("X"), "_resources\\" + fileHeader.CRC.ToString("X")); } } MessageBox.Show("You can delete the folder _resources.bkp now.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); } public byte[] Compress(byte[] src) { byte[] dst = new byte[src.Length + src.Length / 64 + 16 + 3 + 4]; int outlen = 0; lzo1x_1_compress(src, src.Length, dst, ref outlen, _workMemory); byte[] ret = new byte[outlen]; Array.Copy(dst, 0, ret, 0, outlen); return ret; } public byte[] Decompress(byte[] src, int size) { int origlen = size; byte[] dst = new byte[origlen]; int outlen = origlen; lzo1x_decompress(src, src.Length - 4, dst, ref outlen, _workMemory); return dst; } } }