Start to implement the Secret Files SPR data format

This commit is contained in:
Michael Becker 2014-10-08 00:19:37 -04:00
parent 459468ff68
commit 724b1fd1eb
4 changed files with 353 additions and 0 deletions

View File

@ -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<string> stringTableEntries = new List<string>();
#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<string> stringTableEntries = new List<string>();
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<SPRDirectoryInfo> directoryTableEntries = new List<SPRDirectoryInfo>();
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<string> stringTableEntries = new List<string>();
#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
}
}
}

View File

@ -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;
}
}

View File

@ -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
{
}
}

View File

@ -77,6 +77,8 @@
<Compile Include="DataFormats\FileSystem\CPIO\CPIODataFormat.cs" />
<Compile Include="DataFormats\FileSystem\CPIO\CPIOEncoding.cs" />
<Compile Include="DataFormats\FileSystem\DeepSilver\SecretFiles\SPRDataFormat.cs" />
<Compile Include="DataFormats\FileSystem\DeepSilver\SecretFiles\SPRDirectoryInfo.cs" />
<Compile Include="DataFormats\FileSystem\DeepSilver\SecretFiles\SPRFileInfo.cs" />
<Compile Include="DataFormats\FileSystem\Dreamfall\PAKDataFormat.cs" />
<Compile Include="DataFormats\FileSystem\Eighting\FPK\FPKDataFormat.cs" />
<Compile Include="DataFormats\FileSystem\ElectronicArts\BIGFDataFormat.cs" />