diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/SlightlyMadStudiosBFF.uexml b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/SlightlyMadStudiosBFF.uexml
new file mode 100644
index 00000000..8ab7f8a8
--- /dev/null
+++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/SlightlyMadStudiosBFF.uexml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ *.bff
+
+
+
+ KAP
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
index 0d4678c1..4bf7a4f3 100644
--- a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
+++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
@@ -648,6 +648,7 @@
+
diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFCompressionType.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFCompressionType.cs
new file mode 100644
index 00000000..520888dc
--- /dev/null
+++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFCompressionType.cs
@@ -0,0 +1,31 @@
+//
+// BFFCompressionType.cs
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.FileSystem.SlightlyMadStudios
+{
+ public enum BFFCompressionType : byte
+ {
+ None = 0,
+ Zlib = 1,
+ XMemDecompress = 2,
+ Oodle = 3
+ }
+}
diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFDataFormat.cs
new file mode 100644
index 00000000..9aba8256
--- /dev/null
+++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/SlightlyMadStudios/BFFDataFormat.cs
@@ -0,0 +1,399 @@
+//
+// BFFDataFormat.cs - Slightly Mad Studios BFF archive (transcribed from BMS 0.2.5)
+// Used in Need for Speed: Shift 1 and 2, Project Cars, Project Cars 2, Test Drive: Ferrari Racing Legends, etc.
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using UniversalEditor.Accessors;
+using UniversalEditor.IO;
+using UniversalEditor.ObjectModels.FileSystem;
+
+namespace UniversalEditor.DataFormats.FileSystem.SlightlyMadStudios
+{
+ public class BFFDataFormat : DataFormat
+ {
+ int PCARS_ERROR = 0;
+ int PCARS_KEY_SET = 0;
+ int X12d = 0;
+ int PCARS_NUM = 0;
+
+ public int FormatVersion { get; set; } = 0;
+
+ private static DataFormatReference _dfr = null;
+ protected override DataFormatReference MakeReferenceInternal()
+ {
+ if (_dfr == null)
+ {
+ _dfr = base.MakeReferenceInternal();
+ _dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All);
+ }
+ return _dfr;
+ }
+
+ protected override void LoadInternal(ref ObjectModel objectModel)
+ {
+ FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
+ if (fsom == null)
+ throw new ObjectModelNotSupportedException();
+
+ Reader reader = Accessor.Reader;
+ string signature = reader.ReadFixedLengthString(4);
+ if (signature == "PAK ")
+ {
+ // big-endian
+ reader.Endianness = Endianness.BigEndian;
+ }
+ else if (signature == " KAP")
+ {
+ // little-endian
+ reader.Endianness = Endianness.LittleEndian;
+ }
+
+ FormatVersion = reader.ReadInt32();
+ FormatVersion &= 0xff;
+ int fileCount = reader.ReadInt32();
+
+ Accessor.Seek(0x118, SeekOrigin.Begin);
+ int X118 = reader.ReadInt32();
+ Accessor.Seek(0x120, SeekOrigin.Begin);
+ int X120 = reader.ReadInt32();
+ X120 -= 0x308; // 24547
+ Accessor.Seek(0x12d, SeekOrigin.Begin);
+ X12d = reader.ReadInt32();
+
+ if (X12d == 1)
+ {
+ throw new NotSupportedException("X12d equal to %X12d% has not been tested");
+ }
+
+ PCARS_NUM = 0;
+ int TMP_OFF = 0;
+ if (FormatVersion == 4)
+ {
+ // lame scanner for Project Cars, sorry...
+ if (signature == "PAK ")
+ {
+ TMP_OFF = 0x8;
+ }
+ else
+ {
+ TMP_OFF = 0xc;
+ }
+
+ int ZERO = 0;
+ for (PCARS_NUM = 0; PCARS_NUM < 0x16; PCARS_NUM++)
+ {
+ X12dset();
+
+ if (PCARS_ERROR != 0)
+ {
+ PCARS_NUM = -1;
+ PCARS_KEY_SET += 1;
+ ZERO = 1;
+ }
+ else
+ {
+ // log MEMORY_FILE 0x130 0x10
+ reader.Seek(0x130, SeekOrigin.Begin);
+ byte[] m_file = reader.ReadBytes(0x10);
+ MemoryAccessor ma_file = new MemoryAccessor(m_file);
+
+ // getvarchr DEST SRC OFFSET [TYPE=byte]
+ // i.e. SRC.Seek(OFFSET); DEST = SRC.Read();
+ // getvarchr ZERO MEMORY_FILE TMP_OFF long
+ ma_file.Seek(TMP_OFF, SeekOrigin.Begin);
+ ZERO = ma_file.Reader.ReadInt32();
+ }
+
+ if (ZERO == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ X12dset();
+
+ int X120OFFSET = 0;
+
+ reader.Seek(0x130, SeekOrigin.Begin);
+ byte[] m_file1 = reader.ReadBytes(X118);
+ MemoryAccessor ma_file1 = new MemoryAccessor(m_file1);
+
+ byte[] m_file2 = null;
+ MemoryAccessor ma_file2 = null;
+
+ if (FormatVersion == 4) // Project Cars
+ {
+ // names are not supported because they use a boring
+ // and chaotic obfuscation method (no rc4), if one
+ // day I will have time and desire maybe I will look
+ // at them
+ X120OFFSET = 0;
+ }
+ else
+ {
+ X12dset();
+
+ X120OFFSET = 0x438;
+
+ X120OFFSET += X118;
+
+ reader.Seek(X120OFFSET, SeekOrigin.Begin);
+ m_file2 = reader.ReadBytes(X120);
+ ma_file2 = new MemoryAccessor(m_file2);
+ }
+
+ // goto 0 MEMORY_FILE
+ for (int i = 0; i < fileCount; i++)
+ {
+ // encryption "" "" // reset here instead to do it after each *log
+ int dummy1 = ma_file1.Reader.ReadInt32();
+ int dummy2 = ma_file1.Reader.ReadInt32();
+ long offset = ma_file1.Reader.ReadInt64();
+ int zsize = ma_file1.Reader.ReadInt32();
+ int size = ma_file1.Reader.ReadInt32();
+ int dummy3 = ma_file1.Reader.ReadInt32();
+ int dummy4 = ma_file1.Reader.ReadInt32();
+ BFFCompressionType TYPE = (BFFCompressionType) ma_file1.Reader.ReadByte();
+ byte dummy5 = ma_file1.Reader.ReadByte();
+ int crc = ma_file1.Reader.ReadInt32();
+ string FILEEXT = ma_file1.Reader.ReadFixedLengthString(4);
+ string NAME = null;
+ if (X120OFFSET != 0)
+ {
+ long NAMEOFF = ma_file2.Reader.ReadInt64();
+ int dummy21 = ma_file2.Reader.ReadInt32();
+ int dummy22 = ma_file2.Reader.ReadInt32();
+
+ long tmp = ma_file2.Reader.Accessor.Position;
+ NAMEOFF -= X120OFFSET;
+
+ ma_file2.Reader.Seek(NAMEOFF, SeekOrigin.Begin);
+ byte NAMESZ = ma_file2.Reader.ReadByte();
+
+ NAME = ma_file2.Reader.ReadFixedLengthString(NAMESZ);
+
+ ma_file2.Seek(tmp, SeekOrigin.Begin);
+ }
+ else
+ {
+ NAME = String.Format("{0}.{1}", offset, FILEEXT);
+ }
+
+ File f = fsom.AddFile(NAME);
+ f.Size = size;
+ f.Properties.Add("size", size);
+ f.Properties.Add("zsize", zsize);
+ f.Properties.Add("offset", offset);
+ f.Properties.Add("compressionMethod", TYPE);
+ f.DataRequest += f_DataRequest;
+
+ X12dset();
+ }
+ }
+
+ void f_DataRequest(object sender, DataRequestEventArgs e)
+ {
+ File f = (File)sender;
+ int size = (int)f.Properties["size"];
+ int zsize = (int)f.Properties["zsize"];
+ long offset = (long)f.Properties["offset"];
+ BFFCompressionType TYPE = (BFFCompressionType)f.Properties["compressionMethod"];
+
+ Accessor.Reader.Seek(offset, SeekOrigin.Begin);
+ byte[] compressedData = Accessor.Reader.ReadBytes(zsize);
+ byte[] decompressedData = compressedData;
+
+ switch (TYPE)
+ {
+ case BFFCompressionType.None:
+ {
+ break;
+ }
+ case BFFCompressionType.Zlib:
+ {
+ // comtype zlib;
+ // clog NAME OFFSET ZSIZE SIZE;
+ break;
+ }
+ case BFFCompressionType.XMemDecompress:
+ {
+ // comtype XMemDecompress
+ // clog NAME OFFSET ZSIZE SIZE
+ decompressedData = Compression.CompressionModule.FromKnownCompressionMethod(Compression.CompressionMethod.XMemLZX).Decompress(compressedData, size);
+ break;
+ }
+ case BFFCompressionType.Oodle:
+ {
+ // comtype oodle
+ // clog NAME OFFSET ZSIZE SIZE
+ break;
+ }
+ default:
+ {
+ throw new NotSupportedException(String.Format("compression type {0} not supported", TYPE));
+ }
+ }
+
+ e.Data = decompressedData;
+ }
+
+
+ void X12dset()
+ {
+ if (X12d == 1) // ignored at the moment
+ {
+ // TMPSZ += 0xf;
+ // TMPSZ &= 0xfffffff0;
+ }
+ if (X12d == 2)
+ {
+ // encryption rc4("@lLy0urRaC3ar3bE");
+ }
+ if (FormatVersion == 4) // Project Cars
+ {
+ if (X12d == 0)
+ {
+ // nothing: World of Speed and Redbull Air Race
+ }
+ else
+ {
+ PROJECTCARS_KEY();
+ }
+ }
+ }
+
+ void PROJECTCARS_KEY()
+ {
+ PCARS_ERROR = 1;
+
+ StringAccessor maKey = null;
+ if (PCARS_KEY_SET == -1)
+ {
+ // nothing, just a placeholder
+ }
+ else if (PCARS_KEY_SET == 0) // pCars patch 10.0
+ {
+ maKey = new StringAccessor("\xe3\xf4\xd1\xd5\xeb\xcc\xcf\xbc\xa1\xcd\xeb\xf9\xf7\xeb\xec\xc3\x9c\xe4\xdd\x94\xb5\xcb\xe6\xe6\x00\x4b\x00\x9f\xfd\xe2\x80\x81\xe3\xea\x81\xd5\x9d\xe2\xf3\xd2\xb9\xfc\xf3\xf7\xc8\xde\x8d\x00\x39\x45\x51\x2b\x3f\x00\x86\xad\xa9\xc4\x80\xe4\x94\xbf\xe5\xe1\xe6\xe1\x8d\xa4\xf2\xf6\xf3\x00\x70\x3d\x41\x6d\x75\x39\x61\x70\x00\xfe\xa9\xe7\xd1\x84\xc1\xe6\xfa\xbb\xd2\xf4\xbc\x8f\x97\xe0\x8f\x81\xd2\x00\x39\x5f\x75\x38\x65\x32\x72\x00\xcf\xa4\xd4\xe6\xff\xc7\xc4\xfa\xde\xcc\xa0\xa2\xe5\x8f\xb4\x84\x8c\xda\xfc\xe9\xdc\xf3\xb1\xfb\xd8\xbc\x00\xe4\xbb\xdd\xf4\x9e\xa5\xd8\xb1\xe1\x86\x97\xc4\xfc\xf1\xd0\xff\xb9\xf1\xc6\xaf\xdf\x9e\x83\x00\x2a\x75\x00\x95\xb9\xd0\x96\xb7\xaf\xd2\x92\xff\xe5\x88\xed\xeb\xa6\xd2\x8b\xed\xfd\xe2\x8b\xdb\xcb\x88\xb5\x00\x53\x00\xf4\xfd\xe0\xe4\x87\xf7\x80\xf6\xcb\x81\xe6\xc8\xef\xf8\xca\x88\xa6\xb0\x9f\x00\x52\x75\x6b\x45\x64\x72\x00\x80\xa6\xd0\x82\x82\xab\xd7\xa2\xf6\xe2\x95\xbd\xea\xa0\xcc\xe8\xb0\xb5\xdd\xaa\x00\x75\x21\x65\x52\x65\x00\xe5\xe4\xea\x92\x89\xf9\xdb\xa4\xfc\xca\xa5\xa1\xf6\x93\xa6\xdc\xbe\xbb\x9b\xac\xf2\xce\x00\x2d\x61\x66\x00\xdc\xfd\xfa\xfe\x84\xae\xd9\xeb\xc8\xd1\xbf\xd6\x8b\xf0\xfd\xed\xe4\xd7\xf2\xab\xc8\xd8\xbd\xf0\x00\x33\x00\xd5\x81\xa5\x9b\xfa\xa8\xf7\xfc\xde\xf3\x91\xc5\xf5\x95\xc5\xfb\xfe\xcc\xec\x93\x00\x61\x74\x68\x65\x3f\x00\x99\xbf\xfd\xd4\xaf\xd0\x80\xbd\xb6\x8f\xfe\xf3\xf8\x91\xb8\x8b\xb7\xc3\x8f\x94\xab\xe8\x00\x70\x2b\x73\x00\xe1\xa3\xe2\x94\xb7\xd6\xff\xa9\xd1\xe2\x8d\xd2\xe6\x93\xa7\xc5\x88\xde\x89\xec\xbf\x00\x65\x6a\x61\x77\x00\xda\xfa\xef\xc9\xe4\xd2\x8f\xf3\xc5\x93\xb2\xf8\x9f\xfd\xf4\xe7\xb9\xa7\x88\xbe\xf6\xc5\xf3\xfe\xc4\x00\x00\xe4\x81\xbc\xd7\x90\xf5\xe4\xb7\xcc\xce\xbb\xaa\xf7\x94\xe4\xde\x91\xf6\xe5\xef\xa7\xe8\xef\xab\x00\x53\x00\xe1\xf4\xc9\xef\x97\xc6\xf9\x91\xa0\xf1\xf6\xd0\xce\xaa\xeb\x8d\xbe\xc4\x99\xb7\xfd\x97\xfa\xc6\x97\x00\x00\xca\xb9\xb4\x9d\x99\xce\xe8\x95\xae\xc2\xe3\xd3\xd8\x88\xf4\xf9\x8c\xa2\xd0\xfa\xd0\xd6\x00\x75\x77\x65\x00");
+ }
+ else if (PCARS_KEY_SET == 1) // pCars
+ {
+ maKey = new StringAccessor("\xe3\xf4\xd1\xd5\xeb\xcc\xcf\xbc\xa1\xcd\xeb\xf9\xf7\xeb\xec\xc3\x9c\xe4\xdd\x94\xb5\xcb\xe6\xe6\x00\x4b\x00\x9f\xfd\xe2\x80\x81\xe3\xea\x81\xd5\x9d\xe2\xf3\xd2\xb9\xfc\xf3\xf7\xc8\xde\x8d\x00\x39\x45\x51\x2b\x3f\x00\x86\xad\xa9\xc4\x80\xe4\x94\xbf\xe5\xe1\xe6\xe1\x8d\xa4\xf2\xf6\xf3\x00\x70\x3d\x41\x6d\x75\x39\x61\x70\x00\xfe\xa9\xe7\xd1\x84\xc1\xe6\xfa\xbb\xd2\xf4\xbc\x8f\x97\xe0\x8f\x81\xd2\x00\x39\x5f\x75\x38\x65\x32\x72\x00\xcf\xa4\xd4\xe6\xff\xc7\xc4\xfa\xde\xcc\xa0\xa2\xe5\x8f\xb4\x84\x8c\xda\xfc\xe9\xdc\xf3\xb1\xfb\xd8\xbc\x00\xe4\xbb\xdd\xf4\x9e\xa5\xd8\xb1\xe1\x86\x97\xc4\xfc\xf1\xd0\xff\xb9\xf1\xc6\xaf\xdf\x9e\x83\x00\x2a\x75\x00\x95\xb9\xd0\x96\xb7\xaf\xd2\x92\xff\xe5\x88\xed\xeb\xa6\xd2\x8b\xed\xfd\xe2\x8b\xdb\xcb\x88\xb5\x00\x53\x00\xf4\xfd\xe0\xe4\x87\xf7\x80\xf6\xcb\x81\xe6\xc8\xef\xf8\xca\x88\xa6\xb0\x9f\x00\x52\x75\x6b\x45\x64\x72\x00\x80\xa6\xd0\x82\x82\xab\xd7\xa2\xf6\xe2\x95\xbd\xea\xa0\xcc\xe8\xb0\xb5\xdd\xaa\x00\x75\x21\x65\x52\x65\x00\xe5\xe4\xea\x92\x89\xf9\xdb\xa4\xfc\xca\xa5\xa1\xf6\x93\xa6\xdc\xbe\xbb\x9b\xac\xf2\xce\x00\x2d\x61\x66\x00\xdc\xfd\xfa\xfe\x84\xae\xd9\xeb\xc8\xd1\xbf\xd6\x8b\xf0\xfd\xed\xe4\xd7\xf2\xab\xc8\xd8\xbd\xf0\x00\x33\x00\xd5\x81\xa5\x9b\xfa\xa8\xf7\xfc\xde\xf3\x91\xc5\xf5\x95\xc5\xfb\xfe\xcc\xec\x93\x00\x61\x74\x68\x65\x3f\x00\x99\xbf\xfd\xd4\xaf\xd0\x80\xbd\xb6\x8f\xfe\xf3\xf8\x91\xb8\x8b\xb7\xc3\x8f\x94\xab\xe8\x00\x70\x2b\x73\x00\xe1\xa3\xe2\x94\xb7\xd6\xff\xa9\xd1\xe2\x8d\xd2\xe6\x93\xa7\xc5\x88\xde\x89\xec\xbf\x00\x65\x6a\x61\x77\x00\x99\xff\xb4\x93\xf2\xdc\x8d\xa2\xf4\xc9\xb0\xcb\xfe\x88\xf6\xd9\x9e\xba\xe6\x00\x70\x52\x65\x64\x72\x65\x00\xe4\x81\xbc\xd7\x90\xf5\xe4\xb7\xcc\xce\xbb\xaa\xf7\x94\xe4\xde\x91\xf6\xe5\xef\xa7\xe8\xef\xab\x00\x53\x00\xe1\xf4\xc9\xef\x97\xc6\xf9\x91\xa0\xf1\xf6\xd0\xce\xaa\xeb\x8d\xbe\xc4\x99\xb7\xfd\x97\xfa\xc6\x97\x00\x00\xca\xb9\xb4\x9d\x99\xce\xe8\x95\xae\xc2\xe3\xd3\xd8\x88\xf4\xf9\x8c\xa2\xd0\xfa\xd0\xd6\x00\x75\x77\x65\x00");
+ }
+ else if (PCARS_KEY_SET == 2) // Test Drive: Ferrari Racing Legends
+ {
+ maKey = new StringAccessor("\x87\xaf\xa2\x84\xaf\xb0\xc1\x8d\xe5\xda\xef\xef\x95\x97\xef\xeb\x82\xa5\xcb\xf1\xbd\xf2\x00\x53\x30\x4b\x00\x8d\x89\xa8\xce\xa4\xeb\xc7\xa6\xc7\xe9\xa2\xc4\xdb\xb7\xb2\xf9\xf0\xfd\xd1\xaa\x00\x39\x45\x51\x2b\x3f\x00\x9d\x91\xc2\xe2\xff\xfa\x82\xbd\xfe\xc6\x84\xf8\xd7\xaf\xae\xea\x8e\xd7\xc5\xad\xfb\xe4\x00\x39\x61\x70\x00\xce\xee\xe6\xe6\xa1\xe1\xcd\xe4\xa7\xd0\x8b\xdb\x86\xa2\xd0\x82\x97\xda\x9d\xaf\xf4\x00\x38\x65\x32\x72\x00\xc8\xba\xe8\xc9\x80\xbb\xc0\xb7\xc4\xe2\xa5\xa5\x87\xf3\xec\x81\x94\xcb\x00\x41\x2b\x65\x70\x65\x74\x3f\x00\x95\xf6\xe0\xc8\xbf\xd0\xc5\xbd\xcc\xd4\xb0\xd0\xd1\x88\xfc\xeb\x8a\xa0\x8d\x80\xcf\x00\x50\x41\x2a\x75\x00\xc8\xfa\xcf\xfe\x89\xe6\x86\x85\xf6\x80\xad\xe9\xf9\x96\xff\xff\xb5\xdf\xfc\xb6\xc4\xee\xf5\x00\x45\x53\x00\xef\xae\xe1\xf2\xfa\xa7\xf6\xa4\xa9\xc3\xb9\xcf\xcf\xbf\xe3\xc0\x00\x68\x75\x57\x52\x75\x6b\x45\x64\x72\x00\xde\xfd\xfa\x9b\xbc\xfa\xca\x90\xf8\x80\x83\xd9\xfa\xa1\xa1\x94\x95\xc1\xfe\xff\xac\xe7\x8d\x00\x52\x65\x00\x99\x83\xc8\xf5\xfc\xbb\x9b\xe4\xcb\x99\x97\xeb\xff\x84\xcb\xd4\x00\x40\x75\x73\x50\x61\x2d\x2d\x61\x66\x00\xca\xb3\xd2\xd1\xb2\xac\x9a\xf1\xc6\xf2\xa0\xff\xcb\x88\xbc\xc4\x9f\xfc\xc9\xbc\xbc\xe3\x90\x00\x2a\x33\x00\xfa\xb9\xe6\x8d\xb2\xe7\x99\xef\xdc\xdc\xae\xc9\xca\xf6\xb0\xcd\x00\x61\x6e\x65\x6d\x61\x74\x68\x65\x3f\x00\x99\xbe\xb0\xca\x8c\xa3\xe3\xf0\xe5\x95\xf2\xd5\x97\xf8\xbc\xf7\x9f\xa2\x00\x76\x65\x34\x55\x70\x2b\x73\x00\xd2\xa3\xc9\x94\xe3\xf9\xe5\xa7\xd4\xf6\xec\xb8\xeb\xa6\xa3\xed\xbc\x00\x42\x72\x45\x5a\x65\x6a\x61\x77\x00\x81\x86\xf1\x98\xa9\xfd\xc3\xfc\xb9\x96\x8e\xc6\x85\xa1\xea\xc0\xa6\xfd\x85\xad\xfc\x00\x65\x64\x72\x65\x00\xdd\xfa\xc0\x93\xe9\xf2\xdb\xae\xa6\xca\x8e\xb2\xc5\xe4\xf5\xec\xfa\xbd\x00\x54\x61\x23\x55\x70\x23\x53\x00\x87\x92\xc4\x8d\x92\xfa\x8d\x90\xe0\xe6\xf6\xa0\xc0\x96\xc3\x98\xf6\xd2\xcc\x00\x61\x6d\x61\x73\x74\x61\x00\x94\x96\xc7\x80\xa2\xa3\x9e\x8e\xd0\xc4\xad\xe0\xd1\xa5\xf0\xee\xf8\xf7\x97\x00\x38\x40\x79\x75\x77\x65\x00\xe1\xab\xac\xe8\x99\xc0\xc0\x8c\xc5\xf7\x86\xba\xfd\x83\xa1\x87\xa5\xd9\xf5\xfc\xd7\x00\x63\x68\x24\x26\x00");
+ }
+ else if (PCARS_KEY_SET == 3) // pCars 2
+ {
+ maKey = new StringAccessor("\xee\xf3\xdb\x82\xe0\xf8\xca\xf2\xed\xc7\xad\xa6\xd7\x8a\xc3\xfa\xf2\xf5\xef\xf2\xa8\xf9\xb3\xc6\xe3\xe3\x00\xe8\xea\xa6\x8b\x91\xba\xc1\xf0\xde\xcd\x86\xc7\xef\x85\xa5\x86\xf6\xdb\xc8\x89\xa9\xe1\xe4\xd0\x00\x3f\x00\xd1\xba\xe4\xc8\x93\xd7\x9c\xe0\xa0\xc1\xba\xbc\xf8\xf2\xf7\x8b\x87\xa6\x87\xf2\x00\x6d\x75\x39\x61\x70\x00\xe4\x86\xde\xea\xbf\xeb\xe7\xbd\xe9\xe5\x8d\xd2\xf9\xf2\xb2\x87\x8c\xb5\xfc\xa3\xfa\xc4\xf7\xf8\x00\x72\x00\x99\xaa\xe8\x9f\xad\xd5\x8b\xe4\xa3\xf4\x8a\xfc\x85\x8b\xe3\x96\xf7\xe3\x99\x89\xa7\xcc\x00\x65\x74\x3f\x00\xd2\xf7\xed\xf1\xf8\xbc\xd1\xea\xac\xc5\xed\xd4\xfe\xba\xd2\x95\xac\xd4\xe3\xfc\xeb\xf3\xf8\x00\x2a\x75\x00\xdc\xee\xe2\xfb\xa1\xe1\xe3\xad\xc1\x94\xe4\xa7\x8d\xf2\xdc\xe8\xa2\xa9\x9a\xa6\xcf\x86\xe3\xa8\xef\x00\x00\x8d\x90\xfd\xfd\xaf\xbd\xc1\x97\xa8\xc4\xa5\xfc\x98\x84\xa5\x97\x82\xfc\xda\xef\xa6\xfb\xbe\xcf\xe7\x93\x00\x81\xf4\xa0\xe6\xe6\xc6\xd5\x85\xfa\xce\x91\xf5\xe8\xe8\xf8\xc1\x83\xbc\x8f\xf7\xda\x00\x21\x65\x52\x65\x00\xc6\xf6\xe5\x9e\xea\xa0\xfc\x89\xb5\xc8\x8e\xf1\x93\xea\xce\xd1\x87\xa4\x9f\x92\xa6\xfd\x00\x2d\x61\x66\x00\x93\xae\xa1\xd5\x99\xc7\x91\x80\xda\xe1\xac\xd1\xed\xf5\xbc\xfc\xf5\xdc\x85\x84\xf5\xf0\xee\xab\x00\x33\x00\x93\x86\xa4\x9e\xf1\xec\xe4\xbb\xba\xd9\xf3\xf0\x93\xf3\xa9\x91\xfa\xba\x94\x97\xce\xcb\x00\x68\x65\x3f\x00\xfb\xb3\xc7\xde\xff\xa3\xe4\xac\xc6\xd8\xa6\xa0\xd9\xa5\xcc\xd4\xf3\xe8\xe8\xf6\xa5\x00\x55\x70\x2b\x73\x00\xef\xbe\xc3\xc6\x93\xc1\xea\xa3\xf5\xda\x97\xcb\x97\xb6\xf9\x9a\xe9\xfa\xf0\x98\xb8\x00\x65\x6a\x61\x77\x00\xd8\xf8\xba\x9a\x96\xd0\xca\x8a\xb0\x91\xe9\xa2\x94\xed\xb0\x98\xaa\xf9\x87\x81\xd2\xed\x84\xac\xc8\x00\x00\xe5\xb5\xec\xdc\xb5\xb0\xd6\xaa\xfc\xd2\xbf\xf7\xe2\xa3\xfd\x9c\xef\xae\x9d\x8e\xa7\x86\x00\x70\x23\x53\x00\x8f\xea\xc4\xe1\xf5\xce\xc3\xbe\xc3\x9d\x8d\xc3\xe0\x87\xe4\xec\x86\xa0\x93\x88\xeb\xd5\xbd\xb2\x00\x61\x00\xe4\xff\xfc\x9d\xb6\xce\x86\xb6\xf3\x81\x8b\xe9\x82\x95\xd6\x9f\x97\xfe\xec\xfc\xc6\xe9\x00\x75\x77\x65\x00");
+ }
+ else if (PCARS_KEY_SET == 4) // pCars 197
+ {
+ maKey = new StringAccessor("\xE8\xEC\xF0\xFF\xF2\xA9\xC4\x87\xA2\xE5\xA1\xEA\x8B\xAF\xD4\xEE\x00\x57\x4F\x66\x59\x34\x71\x53\x30\x4B\x00\xEB\xF3\xBC\xEF\xFD\xE1\xC5\x97\xB0\xE5\x9A\xAC\xC6\xB4\xBB\xD7\xB2\xBD\x9D\x89\xD5\x80\xFC\xE2\xEB\x00\x00\xCC\xEC\xA9\xE1\x83\xD4\xEA\x90\xE4\xC9\xFA\xA0\x94\x88\xBA\xCB\xF0\xEF\x80\xAE\xAC\x00\x75\x39\x61\x70\x00\xCC\x8A\xE1\x8D\xE9\xBF\x88\xB0\xC2\xD9\xA7\xA0\xEB\xE2\xF3\x99\x87\xD9\xCF\xB3\xAE\xDC\xA9\xF5\xDC\x00\x00\xC6\xF6\xB5\xC2\xED\xF3\xEC\x91\xC2\xC3\x81\xCA\x89\xF1\xA1\xDB\x92\xC6\xEF\xE9\xF9\xD6\xBD\x00\x74\x3F\x00\x94\x8E\xD0\xD4\x8C\xA8\x96\xBA\xB9\x87\xE9\xFE\xE4\xF1\xD1\x86\x94\xC7\x00\x67\x35\x73\x50\x41\x2A\x75\x00\x88\x93\xAE\x8D\xE6\xB8\xEA\x89\xDE\x95\x92\xD6\xD0\xB0\xB2\xD7\xA6\xDD\x9B\xF7\xF5\x00\x73\x35\x45\x53\x00\xF8\x8F\xF9\xC6\xB9\xAC\xC6\xA4\xF4\xED\xB0\xDE\x81\xF3\xC7\xED\x8D\xAB\xE3\xB4\x00\x75\x6B\x45\x64\x72\x00\xFA\xA0\xA6\xF5\x93\xCF\xCA\xFA\xDF\xDA\x81\xB9\xDA\x87\xFA\xD4\xA8\xD0\x00\x66\x3F\x75\x21\x65\x52\x65\x00\xD1\xF1\xA3\xCF\xAE\xFC\x93\xE3\xAC\xF2\xA5\xEA\x82\xAD\xDA\xF6\xA5\xF4\x84\xBF\xBC\xCC\x00\x2D\x61\x66\x00\xEF\x8E\xAB\xFC\xF6\xC1\xC4\x98\xE1\x89\xFD\xA0\xCC\x91\xEF\x95\xF0\xFE\x84\xFF\xFB\xE6\x91\xB2\xDE\x91\x00\xDD\xA6\xAC\xDC\xF4\xC7\xFE\x81\xDD\xD7\xBD\xF8\xDB\xFA\xA7\xD5\xF8\xC4\xF7\xF1\xF1\xDE\xA0\x00\x65\x3F\x00\xFB\x98\xDF\xE4\xA5\xDD\xFB\x80\xC8\xF3\xBB\xA4\xD8\x86\xB9\xED\xE2\xCE\x82\xAF\xC8\x88\xA3\xFD\x00\x73\x00\xE2\xEB\xD4\xFF\xF8\xF3\x89\xE2\xFC\xD4\xBB\xB5\xD7\xE6\xCA\xF6\x8A\xFD\xCC\xAC\xA1\xF6\x00\x6A\x61\x77\x00\xE0\xF4\xFD\xCF\xB9\xED\xF1\x86\xF0\x97\x81\xEC\xC1\xF3\xD3\x82\x95\xD9\xF1\xEC\x00\x52\x65\x64\x72\x65\x00\xE3\xA2\xC0\xCD\xED\xA3\xDF\xAB\xFA\xF2\x92\xE8\x9C\xB6\xFE\xC5\xB2\xE1\x82\xBD\xD5\x00\x55\x70\x23\x53\x00\xD2\x8B\xF3\xFD\xE6\xC9\xFF\xF4\xEC\x88\x97\xA1\x93\xB2\xE0\xF5\x8E\xCB\x99\x88\xAB\xCE\x99\xB8\x00\x61\x00\xED\xEB\xC4\xDC\xEF\xC7\xCF\x81\xE8\x8F\xA1\xFB\xC4\xE4\xBB\xCF\x9C\xB2\xEC\x95\xBB\xC8\xF0\xCB\x86\x00\x00");
+ }
+ else if (PCARS_KEY_SET == 5) // pCars before 197
+ {
+ maKey = new StringAccessor("\xfe\xb5\xaa\xe7\x8b\xe2\xcc\xb1\xd8\x9e\xa3\xe0\xde\xbf\xbf\x87\x00\x57\x4f\x66\x59\x34\x71\x53\x30\x4b\x00\x99\xaf\xda\x8f\x92\xcd\xf1\xa2\xdc\x94\x9b\xaa\x94\xf2\xce\xca\xed\xf2\xe4\x00\x65\x39\x45\x51\x2b\x3f\x00\xf6\x9a\xd1\x9f\xad\xe0\x87\xe3\xbd\xd1\x86\xfc\xed\x84\xf9\xdf\xb4\xba\xe1\xa8\xe5\xd8\xf2\xff\x00\x70\x00\x9d\x8f\xca\x9e\x85\xea\xdc\xf6\xaa\xe2\x9c\xc6\xce\x97\xae\x91\x96\xf5\xda\xf2\xa4\x84\x00\x65\x32\x72\x00\xc0\x93\xd8\xd7\xf0\xa2\xc5\xec\xa3\xc2\xe4\xb2\xce\xec\xdf\xc1\x8a\x00\x51\x41\x2b\x65\x70\x65\x74\x3f\x00\xe5\x89\xe7\xfd\x86\xdc\xc3\xa2\xc9\xe8\xb2\xd1\xeb\xbb\xb9\x84\x89\xa5\xd8\x00\x35\x73\x50\x41\x2a\x75\x00\xd8\x9d\xa7\xcb\x81\xaa\x84\xe0\xfd\x9c\xb5\xcf\x84\xaa\xe0\x99\x85\x00\x72\x65\x4a\x65\x73\x35\x45\x53\x00\xde\xef\xce\xef\x82\xa6\x94\xf3\xdc\x94\xe4\xe4\xd1\xbf\xd1\x93\x00\x68\x75\x57\x52\x75\x6b\x45\x64\x72\x00\xef\x8d\xe3\xf6\xf4\xc9\xeb\x9c\xbe\xf3\xb2\xa0\xda\x88\xf3\xde\x95\xbc\xe6\x9f\x00\x75\x21\x65\x52\x65\x00\xd2\x91\xeb\xe3\x96\xc0\xd6\xb1\xe9\xfe\xa8\xe3\xe1\xaa\xab\xf0\x97\xd1\xda\xa9\xd0\xe3\xe9\xbc\xec\x00\x00\x9b\xb5\xe8\xd8\xf7\xf1\xc3\x8c\xd3\x98\xb5\xc0\xd4\xb7\xe6\xf5\x8a\xf4\x92\xf4\xe9\xc3\xaa\x00\x2a\x33\x00\xd4\x80\xb2\x86\xe0\xbe\xe3\xf5\xe8\xcc\xbf\xc7\xdf\xf8\xf5\xfd\x96\xbd\x8b\x83\xf3\x00\x74\x68\x65\x3f\x00\x89\x82\xc4\xdb\xab\xb2\xc1\xf0\xcf\x92\xfd\xa2\xed\x93\xc9\xeb\xfe\xf1\xe8\xef\xa2\x00\x55\x70\x2b\x73\x00\x92\x89\xfa\xcd\x91\xd4\xd1\xb7\xec\x87\xbf\xec\xcf\x9e\xcd\xfc\xf4\x00\x42\x72\x45\x5a\x65\x6a\x61\x77\x00\xdf\xaf\xfe\xfb\x8f\xa5\x9d\xec\xf6\xe9\xa1\xc6\xc6\x8d\xeb\xd5\x00\x53\x57\x65\x70\x52\x65\x64\x72\x65\x00\xd6\xb4\xbf\xf1\x85\xe3\xf9\xa9\xaf\xcc\xe9\xa0\xe3\x95\xd8\x85\xbb\xaa\xe7\xee\x00\x23\x55\x70\x23\x53\x00\xd7\xa8\xd5\xea\xeb\xa6\xe6\x8f\xae\xf2\x91\xac\xdd\xf4\xa0\x82\xb9\xb5\x87\x8d\xab\x9d\xea\xd8\x9b\x00\x00\x88\xb1\xc8\xfa\xfe\xf3\xcf\xbd\xc0\xe0\xa3\xda\xd7\xec\xe9\xd0\xb3\xd0\xe5\xa0\xfb\xe1\xf1\xfd\x99\x00\x00");
+ }
+ else if (PCARS_KEY_SET == 6) // pCars 2
+ {
+ maKey = new StringAccessor("\x99\x89\xcb\xf7\x9f\xb8\xf2\x83\xea\xdb\xb2\xa0\xce\x98\xae\xf1\x87\xa3\xf8\x8f\xa5\xed\x00\x53\x30\x4b\x00\xce\x91\xa5\xf9\xe6\xd9\xf6\x80\xc8\xca\xa3\xc7\xed\x9f\xba\xf5\xef\xa7\xd9\xaa\xa5\xff\x00\x51\x2b\x3f\x00\xe2\xb1\xf0\xda\xae\xa7\x8d\x8a\xfb\xef\x83\xa2\xce\x90\xf7\xfa\x88\xa1\xed\x8c\xe7\x9c\x00\x39\x61\x70\x00\xee\xf2\xa6\xd7\x92\xdf\xd2\x82\xc0\x85\xa2\xbc\x94\xec\xb4\xfa\x86\xd9\x98\xf5\xf8\x86\x00\x65\x32\x72\x00\xc8\x88\xf4\xcf\xbf\xe3\xd8\x96\xdb\xfd\xa5\xd8\xc3\xbf\xd3\xe0\x91\xda\xdc\xf0\xf4\xfc\x00\x65\x74\x3f\x00\xeb\xa3\xde\xca\x84\xf8\xca\xa3\xa3\xff\xab\xdc\xfa\x8f\xeb\xf9\xbd\xe8\xe4\xa4\xf4\xe5\x00\x41\x2a\x75\x00\xc6\x97\xa7\xde\xa5\xbb\x8f\xf1\xae\x86\xb0\xe0\xe0\xbc\xeb\xdd\xf8\xde\xe4\xf6\xb5\xff\x00\x35\x45\x53\x00\xc7\x82\xe4\xc7\xf5\xf6\xdb\x84\xf9\xed\x96\xc8\x9e\x8d\xe7\xc4\xb7\xff\xdf\x81\xa0\xee\x00\x45\x64\x72\x00\xd9\x9d\xc5\xcb\xbd\xfa\xe3\xac\xc6\xf4\x9e\xfe\xda\x93\xc9\xdd\x83\xe1\xe6\x8b\xff\xd8\x00\x65\x52\x65\x00\xfe\xf2\xd7\xe4\x82\xd9\x95\x95\xa6\xeb\xa2\xa3\xeb\x96\xfa\xc0\xaf\xe9\xe1\x81\xff\xc0\x00\x2d\x61\x66\x00\xc4\xf7\xd5\xdb\xf6\xcb\xdf\xe3\xe8\x84\x9d\xc1\xd8\x9a\xd3\xec\x83\xdd\xfa\x8d\xa9\xdd\x00\x66\x2a\x33\x00\xf6\xba\xf2\x82\xab\xba\x89\xac\xfb\x9f\xff\xc8\x98\xb7\xd2\xfd\x98\xa1\xcc\xf5\xa7\xd4\x00\x68\x65\x3f\x00\xed\xf3\xc3\xc1\x8b\xe8\xc0\xf3\xdb\xe6\x85\xf2\xdf\x91\xe3\x9b\xb1\xa7\xe3\xad\xc6\xda\x00\x70\x2b\x73\x00\xe7\x90\xe6\x94\x9f\xc0\xdd\x95\xe1\xfb\xb0\xc2\xf5\x80\xd2\xf6\xbf\xc2\xe5\xbf\xfe\xda\x00\x6a\x61\x77\x00\xf4\xbe\xfb\x8f\x8f\xfa\xc9\x83\xcc\xd8\xf8\xe5\x9f\xed\xdc\xff\xae\xff\x9a\x8b\xb0\x94\x00\x64\x72\x65\x00\xd4\xfe\xc0\x87\xbf\xa1\x87\xa0\xa7\xda\xe9\xa8\xd0\xa1\xbc\xce\x8c\xc1\x9e\x83\xd5\xc7\x00\x70\x23\x53\x00\xc7\x81\xae\xd0\x95\xfc\x9d\x9c\xd5\xfd\x8c\xf7\x84\xf1\xb5\xde\xed\xbc\xf1\xbe\xf3\xd7\x00\x73\x74\x61\x00\xfa\x82\xfe\xc6\x81\xa7\xd7\x81\xc0\x9a\xb7\xf5\xdb\x9c\xce\xd0\xa1\xbd\xe7\xa7\xb5\xfa\x00\x75\x77\x65\x00");
+ }
+ else
+ {
+ throw new NotSupportedException(String.Format("unavailable key for key set {0}", PCARS_KEY_SET));
+ }
+
+ int PCARS_OFF = PCARS_NUM;
+ PCARS_OFF *= 0x1b;
+
+ int PCARS_MEM_SIZE = (int)maKey.Length;
+
+ if (PCARS_MEM_SIZE >= PCARS_OFF)
+ {
+ PCARS_MEM_SIZE -= PCARS_OFF;
+ if (PCARS_MEM_SIZE >= 0x1b)
+ {
+ PCARS_ERROR = 0;
+
+ maKey.Seek(PCARS_OFF, SeekOrigin.Begin);
+ string PCARS_KEY = maKey.Reader.ReadFixedLengthString(0x1b);
+ int PCARS_KEYSZ = PCARS_KEY.Length;
+
+ // xor 0x1b and 3 are multiples so no need of byte per byte xor
+ // encryption xor "\xac\xc7\x91";
+ int TMP = (int)maKey.Length;
+
+
+ // log MEMORY_FILE3 0 TMP MEMORY_FILE3
+
+ // reverse short
+ int j = PCARS_OFF;
+
+ for (int x = PCARS_KEYSZ; x > 1; x-=2)
+ {
+ // getvarchr TMP MEMORY_FILE3 j short
+
+ // reverseshort TMP
+
+ // putvarchr MEMORY_FILE3 j TMP short
+
+ j += 2;
+ }
+
+ // get key
+ maKey.Seek(PCARS_OFF, SeekOrigin.Begin);
+ PCARS_KEY = maKey.Reader.ReadFixedLengthString(PCARS_KEYSZ);
+
+ // encryption key
+ // encryption rc4 PCARS_KEY "" 0 PCARS_KEYSZ
+
+ // other pre-built keys used somewhere else:
+ // encryption rc4 "QgT~(x/x}yON{0Db02,M?_1g_W+FuI}kGE[sZ"
+ // encryption rc4 "4!spu3eyEpED?+ud6E4etHe=7?!fa"
+ // encryption rc4 "9TEXU+u#U*h+D=efUcRudach$&"
+ // encryption rc4 "jetracR--acr_t-tr*7*dadrab"
+ // encryption rc4 "j@yutranemuwece9Azatra?wa*"
+ // encryption rc4 "pUphe-apru=UWraBRu7@SW-xa#"
+ // encryption rc4 "d+!ugapH=stephacUchucresUC"
+ // encryption rc4 "we$r8truSpaT7aT7Edasw7sPap"
+ // encryption rc4 "+h5@u7!a7h9w8aCaqeya&rutac"
+ // encryption rc4 "PrEthAk$ke5RaxudrEKunAVabE"
+ // encryption rc4 "quwa?7@tew8era!EcRE@uf+FRu"
+ // encryption rc4 "j*qabaqu#7ufr7y=trUb!P@afe"
+ // encryption rc4 "B5u3AS6eQUwruY&?!u-rA4*te6"
+ // encryption rc4 "sPE32u2aS!ezEyakawuKE7uhes"
+ // encryption rc4 "fr2BaPujuwekasa$e5ap*te2ax"
+ // encryption rc4 "tuth6&r=swexe6uCedabr#vefa"
+ }
+ }
+ }
+
+ protected override void SaveInternal(ObjectModel objectModel)
+ {
+ FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
+ if (fsom == null)
+ throw new ObjectModelNotSupportedException();
+
+
+ }
+ }
+}
diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj
index 4f3d0fa3..5ff1d0e7 100644
--- a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj
+++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj
@@ -230,6 +230,8 @@
+
+
@@ -267,6 +269,7 @@
+