From 724b1fd1eb5d305aefadcec8a05213542afe4a0d Mon Sep 17 00:00:00 2001 From: alcexhim Date: Wed, 8 Oct 2014 00:19:37 -0400 Subject: [PATCH] Start to implement the Secret Files SPR data format --- .../DeepSilver/SecretFiles/SPRDataFormat.cs | 326 ++++++++++++++++++ .../SecretFiles/SPRDirectoryInfo.cs | 13 + .../DeepSilver/SecretFiles/SPRFileInfo.cs | 12 + .../UniversalEditor.Plugins.FileSystem.csproj | 2 + 4 files changed, 353 insertions(+) create mode 100644 CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDirectoryInfo.cs create mode 100644 CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRFileInfo.cs diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDataFormat.cs index e69de29b..4803ef41 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDataFormat.cs +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDataFormat.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.Accessors; +using UniversalEditor.IO; +using UniversalEditor.ObjectModels.FileSystem; + +namespace UniversalEditor.DataFormats.FileSystem.DeepSilver.SecretFiles +{ + public class SPRDataFormat : DataFormat + { + private static DataFormatReference _dfr = null; + public override DataFormatReference MakeReference() + { + if (_dfr == null) + { + _dfr = base.MakeReference(); + _dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All); + _dfr.ExportOptions.Add(new CustomOptionNumber("EncryptionDomain", "Encryption &domain:", 0xbebe2, Int32.MaxValue, Int32.MinValue)); + _dfr.ExportOptions.Add(new CustomOptionNumber("EncryptionSeed", "Encryption &seed:", 0, Int32.MaxValue, Int32.MinValue)); + _dfr.Filters.Add("Secret Files: Tunguska SPR archive", new byte?[][] { new byte?[] { (byte)'S', (byte)'P', (byte)'C', (byte)'D' } }, new string[] { "*.spr" }); + _dfr.Sources.Add("http://wiki.xentax.com/index.php?title=Secret_Files:_Tunguska_%28Demo%29_SPR"); + } + return _dfr; + } + + private int mvarEncryptionDomain = 0xbebe2; + public int EncryptionDomain { get { return mvarEncryptionDomain; } set { mvarEncryptionDomain = value; } } + + private int mvarEncryptionSeed = 0; + public int EncryptionSeed { get { return mvarEncryptionSeed; } set { mvarEncryptionSeed = value; } } + + private byte[] EncryptDecrypt(byte[] input) + { + byte[] output = (input.Clone() as byte[]); + + int KeyDomain = mvarEncryptionDomain * mvarEncryptionSeed; + int Key = ((mvarEncryptionSeed << 8) | ((~mvarEncryptionSeed) & 0xff)); + + for (int i = 0; i < input.Length; i++) + { + Key = (Key * Key) % KeyDomain; + output[i] = (byte)(output[i] ^ (Key & 0xFF)); + Key = (Key + output[i] + 1); + } + + return output; + } + + protected override void LoadInternal(ref ObjectModel objectModel) + { + FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + if (fsom == null) throw new ObjectModelNotSupportedException(); + + Reader reader = base.Accessor.Reader; + string signature = reader.ReadFixedLengthString(4); + if (signature != "SPCD") throw new InvalidDataFormatException("File does not begin with 'SPCD'"); + + uint unknown1 = reader.ReadUInt32(); + uint unknown2 = reader.ReadUInt32(); + mvarEncryptionSeed = reader.ReadInt32(); + uint unknown3 = reader.ReadUInt32(); + uint unknown4 = reader.ReadUInt32(); + uint stringTableEntryCount = reader.ReadUInt32(); + uint stringTableOffset = reader.ReadUInt32(); + uint stringTableLength = reader.ReadUInt32(); + uint fileTableEntryCount = reader.ReadUInt32(); + uint fileTableOffset = reader.ReadUInt32(); + uint fileTableLength = reader.ReadUInt32(); + uint unknown5 = reader.ReadUInt32(); + uint directoryTableEntryCount = reader.ReadUInt32(); + uint directoryTableOffset = reader.ReadUInt32(); + uint directoryTableLength = reader.ReadUInt32(); + + List stringTableEntries = new List(); + #region String Table + { + reader.Seek(stringTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(stringTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + ushort[] stringLengths = new ushort[stringTableEntryCount]; + for (int i = 0; i < stringTableEntryCount; i++) + { + stringLengths[i] = reader.ReadUInt16(); + } + for (int i = 0; i < stringTableEntryCount; i++) + { + string value = reader.ReadFixedLengthString(stringLengths[i]); + stringTableEntries.Add(value); + } + } + #endregion + + #region Directory Table + { + reader.Seek(directoryTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(directoryTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + for (int i = 0; i < directoryTableEntryCount; i++) + { + uint directoryNameIndex = reader.ReadUInt32(); + uint parentDirectoryIndex = reader.ReadUInt32(); // 0x1effffff for root + } + } + #endregion + + #region File Table + { + reader.Seek(fileTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(fileTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + for (int i = 0; i < fileTableEntryCount; i++) + { + uint parentDirectoryIndex = reader.ReadUInt32(); // 0x1effffff for root + uint fileNamePrefixIndex = reader.ReadUInt32(); // index into string table + uint fileNameSuffixIndex = reader.ReadUInt32(); + uint crc32 = reader.ReadUInt32(); + + #region File date + ushort year = reader.ReadUInt16(); + ushort month = reader.ReadUInt16(); + ushort dayOfWeek = reader.ReadUInt16(); + ushort day = reader.ReadUInt16(); + ushort hour = reader.ReadUInt16(); + ushort minute = reader.ReadUInt16(); + ushort second = reader.ReadUInt16(); + ushort millisecond = reader.ReadUInt16(); + + DateTime fileDate = new DateTime(year, month, day, hour, minute, second, millisecond); + #endregion + + uint decompressedLength = reader.ReadUInt32(); + + // 0xffffffff if file is not compressed + uint compressionHeaderOffset = reader.ReadUInt32(); + + // zero if file is not compressed + uint compressionHeaderLength = reader.ReadUInt32(); + + uint fileDataOffset = reader.ReadUInt32(); + uint fileDataLength = reader.ReadUInt32(); + } + } + #endregion + } + + protected override void SaveInternal(ObjectModel objectModel) + { + FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + if (fsom == null) throw new ObjectModelNotSupportedException(); + + Writer writer = base.Accessor.Writer; + writer.WriteFixedLengthString("SPCD"); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + writer.WriteInt32(mvarEncryptionSeed); + + writer.WriteUInt32(0); + writer.WriteUInt32(0); + + File[] files = fsom.GetAllFiles(); + uint baseOffset = 54; + for (uint i = 0; i < files.Length; i++) + { + baseOffset += (uint)files[i].Size; + } + + #region String Table Data + { + List stringTableEntries = new List(); + + MemoryAccessor ma = new MemoryAccessor(); + Writer bwst = new Writer(ma); + foreach (string s in stringTableEntries) + { + bwst.WriteUInt16((ushort)s.Length); + } + foreach (string s in stringTableEntries) + { + bwst.WriteFixedLengthString(s); + } + bwst.Flush(); + bwst.Close(); + byte[] stringTableData = ma.ToArray(); + stringTableData = EncryptDecrypt(stringTableData); + + uint stringTableOffset = baseOffset; + writer.WriteUInt32((uint)stringTableEntries.Count); + writer.WriteUInt32(stringTableOffset); + writer.WriteUInt32((uint)stringTableData.Length); + } + #endregion + #region Directory Table Data + { + List directoryTableEntries = new List(); + + MemoryAccessor ma = new MemoryAccessor(); + Writer bwst = new Writer(ma); + foreach (SPRDirectoryInfo s in directoryTableEntries) + { + bwst.WriteUInt16((ushort)s.Length); + } + foreach (string s in stringTableEntries) + { + bwst.WriteFixedLengthString(s); + } + bwst.Flush(); + bwst.Close(); + byte[] stringTableData = ma.ToArray(); + stringTableData = EncryptDecrypt(stringTableData); + + uint stringTableOffset = baseOffset; + writer.WriteUInt32((uint)stringTableEntries.Count); + writer.WriteUInt32(stringTableOffset); + writer.WriteUInt32((uint)stringTableData.Length); + } + #endregion + + uint fileTableEntryCount = reader.ReadUInt32(); + uint fileTableOffset = reader.ReadUInt32(); + uint fileTableLength = reader.ReadUInt32(); + uint unknown5 = reader.ReadUInt32(); + uint directoryTableEntryCount = reader.ReadUInt32(); + uint directoryTableOffset = reader.ReadUInt32(); + uint directoryTableLength = reader.ReadUInt32(); + + List stringTableEntries = new List(); + #region String Table + { + reader.Seek(stringTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(stringTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + ushort[] stringLengths = new ushort[stringTableEntryCount]; + for (int i = 0; i < stringTableEntryCount; i++) + { + stringLengths[i] = reader.ReadUInt16(); + } + for (int i = 0; i < stringTableEntryCount; i++) + { + string value = reader.ReadFixedLengthString(stringLengths[i]); + stringTableEntries.Add(value); + } + } + #endregion + + #region Directory Table + { + reader.Seek(directoryTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(directoryTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + for (int i = 0; i < directoryTableEntryCount; i++) + { + uint directoryNameIndex = reader.ReadUInt32(); + uint parentDirectoryIndex = reader.ReadUInt32(); // 0x1effffff for root + } + } + #endregion + + #region File Table + { + reader.Seek(fileTableOffset, SeekOrigin.Begin); + byte[] data = reader.ReadBytes(fileTableLength); + data = EncryptDecrypt(data); + + MemoryAccessor ma = new MemoryAccessor(data); + Reader rdr = new Reader(ma); + + for (int i = 0; i < fileTableEntryCount; i++) + { + uint parentDirectoryIndex = reader.ReadUInt32(); // 0x1effffff for root + uint fileNamePrefixIndex = reader.ReadUInt32(); // index into string table + uint fileNameSuffixIndex = reader.ReadUInt32(); + uint crc32 = reader.ReadUInt32(); + + #region File date + ushort year = reader.ReadUInt16(); + ushort month = reader.ReadUInt16(); + ushort dayOfWeek = reader.ReadUInt16(); + ushort day = reader.ReadUInt16(); + ushort hour = reader.ReadUInt16(); + ushort minute = reader.ReadUInt16(); + ushort second = reader.ReadUInt16(); + ushort millisecond = reader.ReadUInt16(); + + DateTime fileDate = new DateTime(year, month, day, hour, minute, second, millisecond); + #endregion + + uint decompressedLength = reader.ReadUInt32(); + + // 0xffffffff if file is not compressed + uint compressionHeaderOffset = reader.ReadUInt32(); + + // zero if file is not compressed + uint compressionHeaderLength = reader.ReadUInt32(); + + uint fileDataOffset = reader.ReadUInt32(); + uint fileDataLength = reader.ReadUInt32(); + } + } + #endregion + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDirectoryInfo.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDirectoryInfo.cs new file mode 100644 index 00000000..347bf4b7 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRDirectoryInfo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.DeepSilver.SecretFiles +{ + internal struct SPRDirectoryInfo + { + public int directoryNameIndex; + public int parentDirectoryIndex; + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRFileInfo.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRFileInfo.cs new file mode 100644 index 00000000..18713ccb --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/DeepSilver/SecretFiles/SPRFileInfo.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.DeepSilver.SecretFiles +{ + internal struct SPRFileInfo + { + + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj index d2d67315..82528a64 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj @@ -77,6 +77,8 @@ + +