diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIODataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIODataFormat.cs new file mode 100644 index 00000000..5c3136fb --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIODataFormat.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; +using UniversalEditor.ObjectModels.FileSystem; + +namespace UniversalEditor.DataFormats.FileSystem.CPIO +{ + public class CPIODataFormat : DataFormat + { + private static DataFormatReference _dfr = null; + public override DataFormatReference MakeReference() + { + if (_dfr == null) + { + _dfr = base.MakeReference(); + _dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All); + _dfr.Sources.Add("http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt"); + _dfr.ExportOptions.Add(new CustomOptionChoice("Encoding", "&Encoding: ", true, new CustomOptionFieldChoice[] + { + new CustomOptionFieldChoice("Binary (little-endian)", CPIOEncoding.BinaryLittleEndian, true), + new CustomOptionFieldChoice("Binary (big-endian)", CPIOEncoding.BinaryBigEndian), + new CustomOptionFieldChoice("ASCII", CPIOEncoding.ASCII) + })); + _dfr.Filters.Add("CPIO archive", new byte?[][] { new byte?[] { 0xC7, 0x71 }, new byte?[] { 0x71, 0xC7 }, new byte?[] { (byte)'0', (byte)'7', (byte)'0', (byte)'7', (byte)'0', (byte)'1' } }, new string[] { "*.cpio" }); + } + return _dfr; + } + + private CPIOEncoding mvarEncoding = CPIOEncoding.BinaryLittleEndian; + public CPIOEncoding Encoding { get { return mvarEncoding; } set { mvarEncoding = value; } } + + protected override void LoadInternal(ref ObjectModel objectModel) + { + FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + if (fsom == null) throw new ObjectModelNotSupportedException(); + + Reader reader = base.Accessor.Reader; + + while (!reader.EndOfStream) + { + byte[] c_magic = reader.ReadBytes(2); + if (c_magic[0] == 0xC7 && c_magic[1] == 0x71) + { + mvarEncoding = CPIOEncoding.BinaryLittleEndian; + } + else if (c_magic[0] == 0x71 && c_magic[1] == 0xC7) + { + mvarEncoding = CPIOEncoding.BinaryBigEndian; + reader.Endianness = Endianness.BigEndian; + } + else + { + base.Accessor.Seek(-2, SeekOrigin.Current); + string c_magic_str = reader.ReadFixedLengthString(5); + if (!(c_magic_str == "070701" || c_magic_str == "070702")) throw new InvalidDataFormatException("File does not begin with one of either { 0xC7, 0x71 }, { 0x71, 0xC7 }, { '070701' }, or { '070702' }"); + + mvarEncoding = CPIOEncoding.ASCII; + } + + bool fin = false; + + switch (mvarEncoding) + { + case CPIOEncoding.BinaryBigEndian: + case CPIOEncoding.BinaryLittleEndian: + { + ushort c_dev = reader.ReadUInt16(); + ushort c_ino = reader.ReadUInt16(); + ushort c_mode = reader.ReadUInt16(); + ushort c_uid = reader.ReadUInt16(); + ushort c_gid = reader.ReadUInt16(); + ushort c_nlink = reader.ReadUInt16(); + ushort c_rdev = reader.ReadUInt16(); + + // Modification time of the file, indicated as the number of seconds since the + // start of the epoch, 00:00:00 UTC January 1, 1970. The four-byte integer is + // stored with the most-significant 16 bits first followed by the + // least-significant 16 bits. Each of the two 16 bit values are stored in + // machine-native byte order. + uint c_mtime = ReadTransEndianUInt32(reader); + + // The number of bytes in the pathname that follows the header. This count + // includes the trailing NUL byte. + ushort c_namesize = reader.ReadUInt16(); + + // The size of the file. Note that this archive format is limited to four + // gigabyte file sizes. See mtime above for a description of the storage of + // four-byte integers. + uint c_filesize = ReadTransEndianUInt32(reader); + + // The pathname immediately follows the fixed header. If the namesize is odd, + // an additional NUL byte is added after the pathname. The file data is then + // appended, padded with NUL bytes to an even length. + string c_filename = reader.ReadFixedLengthString(c_namesize).TrimNull(); + reader.Align(2); + + if (c_filename == "TRAILER!!!") + { + fin = true; + break; + } + + long offset = base.Accessor.Position; + + // Hardlinked files are not given special treatment; the full file contents are + // included with each copy of the file. + File file = fsom.AddFile(c_filename); + file.Size = c_filesize; + file.DataRequest += file_DataRequest; + file.Properties.Add("reader", reader); + file.Properties.Add("offset", offset); + file.Properties.Add("length", c_filesize); + + base.Accessor.Seek(c_filesize, SeekOrigin.Current); + break; + } + } + + if (fin) break; + } + } + + private void file_DataRequest(object sender, DataRequestEventArgs e) + { + File file = (sender as File); + Reader reader = (Reader)file.Properties["reader"]; + long offset = (long)file.Properties["offset"]; + uint length = (uint)file.Properties["length"]; + reader.Seek(offset, SeekOrigin.Begin); + e.Data = reader.ReadBytes(length); + } + + private uint ReadTransEndianUInt32(Reader reader) + { + // The four-byte integer is stored with the most-significant 16 bits first followed by + // the least-significant 16 bits (big-endian). Each of the two 16 bit values are stored + // in machine-native byte order. + + // TODO: TEST THIS!!! + + ushort part1 = reader.ReadUInt16(); + ushort part2 = reader.ReadUInt16(); + + byte[] part1bytes = BitConverter.GetBytes(part1); + byte[] part2bytes = BitConverter.GetBytes(part2); + + uint result = 0; + switch (reader.Endianness) + { + case Endianness.LittleEndian: + { + result = BitConverter.ToUInt32(new byte[] + { + part2bytes[0], part2bytes[1], + part1bytes[0], part1bytes[1] + }, 0); + break; + } + case Endianness.BigEndian: + { + result = BitConverter.ToUInt32(new byte[] + { + part1bytes[0], part1bytes[1], + part2bytes[0], part2bytes[1] + }, 0); + break; + } + } + return result; + } + + protected override void SaveInternal(ObjectModel objectModel) + { + throw new NotImplementedException(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIOEncoding.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIOEncoding.cs new file mode 100644 index 00000000..b5242bb1 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/CPIO/CPIOEncoding.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.CPIO +{ + public enum CPIOEncoding + { + BinaryLittleEndian, + BinaryBigEndian, + ASCII + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj index 78094416..b4fe5aa4 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/UniversalEditor.Plugins.FileSystem.csproj @@ -74,6 +74,8 @@ + +